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