]> sjero.net Git - wget/blob - lib/getpass.c
--restrict-file-names=ascii
[wget] / lib / getpass.c
1 /* Copyright (C) 1992-2001, 2003, 2004, 2005, 2006, 2007 Free Software
2    Foundation, Inc.
3
4    This file is part of the GNU C Library.
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3, or (at your option)
9    any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License along
17    with this program; if not, write to the Free Software Foundation,
18    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
19
20 #ifndef _LIBC
21 # include <config.h>
22 #endif
23
24 #include "getpass.h"
25
26 #include <stdio.h>
27
28 #if !((defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__)
29
30 #include <stdbool.h>
31
32 #if HAVE_DECL___FSETLOCKING && HAVE___FSETLOCKING
33 # if HAVE_STDIO_EXT_H
34 #  include <stdio_ext.h>
35 # endif
36 #else
37 # define __fsetlocking(stream, type)    /* empty */
38 #endif
39
40 #if HAVE_TERMIOS_H
41 # include <termios.h>
42 #endif
43
44 #if USE_UNLOCKED_IO
45 # include "unlocked-io.h"
46 #else
47 # if !HAVE_DECL_FFLUSH_UNLOCKED
48 #  undef fflush_unlocked
49 #  define fflush_unlocked(x) fflush (x)
50 # endif
51 # if !HAVE_DECL_FLOCKFILE
52 #  undef flockfile
53 #  define flockfile(x) ((void) 0)
54 # endif
55 # if !HAVE_DECL_FUNLOCKFILE
56 #  undef funlockfile
57 #  define funlockfile(x) ((void) 0)
58 # endif
59 # if !HAVE_DECL_FPUTS_UNLOCKED
60 #  undef fputs_unlocked
61 #  define fputs_unlocked(str,stream) fputs (str, stream)
62 # endif
63 # if !HAVE_DECL_PUTC_UNLOCKED
64 #  undef putc_unlocked
65 #  define putc_unlocked(c,stream) putc (c, stream)
66 # endif
67 #endif
68
69 /* It is desirable to use this bit on systems that have it.
70    The only bit of terminal state we want to twiddle is echoing, which is
71    done in software; there is no need to change the state of the terminal
72    hardware.  */
73
74 #ifndef TCSASOFT
75 # define TCSASOFT 0
76 #endif
77
78 static void
79 call_fclose (void *arg)
80 {
81   if (arg != NULL)
82     fclose (arg);
83 }
84
85 char *
86 getpass (const char *prompt)
87 {
88   FILE *tty;
89   FILE *in, *out;
90   struct termios s, t;
91   bool tty_changed = false;
92   static char *buf;
93   static size_t bufsize;
94   ssize_t nread;
95
96   /* Try to write to and read from the terminal if we can.
97      If we can't open the terminal, use stderr and stdin.  */
98
99   tty = fopen ("/dev/tty", "w+");
100   if (tty == NULL)
101     {
102       in = stdin;
103       out = stderr;
104     }
105   else
106     {
107       /* We do the locking ourselves.  */
108       __fsetlocking (tty, FSETLOCKING_BYCALLER);
109
110       out = in = tty;
111     }
112
113   flockfile (out);
114
115   /* Turn echoing off if it is on now.  */
116 #if HAVE_TCGETATTR
117   if (tcgetattr (fileno (in), &t) == 0)
118     {
119       /* Save the old one. */
120       s = t;
121       /* Tricky, tricky. */
122       t.c_lflag &= ~(ECHO | ISIG);
123       tty_changed = (tcsetattr (fileno (in), TCSAFLUSH | TCSASOFT, &t) == 0);
124     }
125 #endif
126
127   /* Write the prompt.  */
128   fputs_unlocked (prompt, out);
129   fflush_unlocked (out);
130
131   /* Read the password.  */
132   nread = getline (&buf, &bufsize, in);
133
134   /* According to the C standard, input may not be followed by output
135      on the same stream without an intervening call to a file
136      positioning function.  Suppose in == out; then without this fseek
137      call, on Solaris, HP-UX, AIX, OSF/1, the previous input gets
138      echoed, whereas on IRIX, the following newline is not output as
139      it should be.  POSIX imposes similar restrictions if fileno (in)
140      == fileno (out).  The POSIX restrictions are tricky and change
141      from POSIX version to POSIX version, so play it safe and invoke
142      fseek even if in != out.  */
143   fseeko (out, 0, SEEK_CUR);
144
145   if (buf != NULL)
146     {
147       if (nread < 0)
148         buf[0] = '\0';
149       else if (buf[nread - 1] == '\n')
150         {
151           /* Remove the newline.  */
152           buf[nread - 1] = '\0';
153           if (tty_changed)
154             {
155               /* Write the newline that was not echoed.  */
156               putc_unlocked ('\n', out);
157             }
158         }
159     }
160
161   /* Restore the original setting.  */
162 #if HAVE_TCSETATTR
163   if (tty_changed)
164     tcsetattr (fileno (in), TCSAFLUSH | TCSASOFT, &s);
165 #endif
166
167   funlockfile (out);
168
169   call_fclose (tty);
170
171   return buf;
172 }
173
174 #else /* W32 native */
175
176 /* Windows implementation by Martin Lambers <marlam@marlam.de>,
177    improved by Simon Josefsson. */
178
179 /* For PASS_MAX. */
180 #include <limits.h>
181 /* For _getch(). */
182 #include <conio.h>
183 /* For strdup(). */
184 #include <string.h>
185
186 #ifndef PASS_MAX
187 # define PASS_MAX 512
188 #endif
189
190 char *
191 getpass (const char *prompt)
192 {
193   char getpassbuf[PASS_MAX + 1];
194   size_t i = 0;
195   int c;
196
197   if (prompt)
198     {
199       fputs (prompt, stderr);
200       fflush (stderr);
201     }
202
203   for (;;)
204     {
205       c = _getch ();
206       if (c == '\r')
207         {
208           getpassbuf[i] = '\0';
209           break;
210         }
211       else if (i < PASS_MAX)
212         {
213           getpassbuf[i++] = c;
214         }
215
216       if (i >= PASS_MAX)
217         {
218           getpassbuf[i] = '\0';
219           break;
220         }
221     }
222
223   if (prompt)
224     {
225       fputs ("\r\n", stderr);
226       fflush (stderr);
227     }
228
229   return strdup (getpassbuf);
230 }
231 #endif