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