xref: /dragonfly/contrib/cvs-1.12/lib/getpass.c (revision 49781055)
1 /* Copyright (C) 1992-2001, 2003, 2004, 2005 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3 
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.
8 
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.
13 
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.  */
17 
18 #ifdef HAVE_CONFIG_H
19 # include <config.h>
20 #endif
21 
22 #include "getpass.h"
23 
24 #include <stdio.h>
25 
26 #if !defined _WIN32
27 
28 #include <stdbool.h>
29 
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
36 
37 #if HAVE_TERMIOS_H
38 # include <termios.h>
39 #endif
40 
41 #include "getline.h"
42 
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
67 
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.  */
72 
73 #ifndef TCSASOFT
74 # define TCSASOFT 0
75 #endif
76 
77 static void
78 call_fclose (void *arg)
79 {
80   if (arg != NULL)
81     fclose (arg);
82 }
83 
84 char *
85 getpass (const char *prompt)
86 {
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;
94 
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.  */
97 
98   tty = fopen ("/dev/tty", "w+");
99   if (tty == NULL)
100     {
101       in = stdin;
102       out = stderr;
103     }
104   else
105     {
106       /* We do the locking ourselves.  */
107       __fsetlocking (tty, FSETLOCKING_BYCALLER);
108 
109       out = in = tty;
110     }
111 
112   flockfile (out);
113 
114   /* Turn echoing off if it is on now.  */
115 #if HAVE_TCGETATTR
116   if (tcgetattr (fileno (in), &t) == 0)
117     {
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);
123     }
124 #endif
125 
126   /* Write the prompt.  */
127   fputs_unlocked (prompt, out);
128   fflush_unlocked (out);
129 
130   /* Read the password.  */
131   nread = getline (&buf, &bufsize, in);
132 
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);
143 
144   if (buf != NULL)
145     {
146       if (nread < 0)
147 	buf[0] = '\0';
148       else if (buf[nread - 1] == '\n')
149 	{
150 	  /* Remove the newline.  */
151 	  buf[nread - 1] = '\0';
152 	  if (tty_changed)
153 	    {
154 	      /* Write the newline that was not echoed.  */
155 	      putc_unlocked ('\n', out);
156 	    }
157 	}
158     }
159 
160   /* Restore the original setting.  */
161 #if HAVE_TCSETATTR
162   if (tty_changed)
163     tcsetattr (fileno (in), TCSAFLUSH | TCSASOFT, &s);
164 #endif
165 
166   funlockfile (out);
167 
168   call_fclose (tty);
169 
170   return buf;
171 }
172 
173 #else /* WIN32 */
174 
175 /* Windows implementation by Martin Lambers <marlam@marlam.de>,
176    improved by Simon Josefsson. */
177 
178 /* For PASS_MAX. */
179 #include <limits.h>
180 
181 #ifndef PASS_MAX
182 # define PASS_MAX 512
183 #endif
184 
185 char *
186 getpass (const char *prompt)
187 {
188   char getpassbuf[PASS_MAX + 1];
189   size_t i = 0;
190   int c;
191 
192   if (prompt)
193     {
194       fputs (prompt, stderr);
195       fflush (stderr);
196     }
197 
198   for (;;)
199     {
200       c = _getch ();
201       if (c == '\r')
202 	{
203 	  getpassbuf[i] = '\0';
204 	  break;
205 	}
206       else if (i < PASS_MAX)
207 	{
208 	  getpassbuf[i++] = c;
209 	}
210 
211       if (i >= PASS_MAX)
212 	{
213 	  getpassbuf[i] = '\0';
214 	  break;
215 	}
216     }
217 
218   if (prompt)
219     {
220       fputs ("\r\n", stderr);
221       fflush (stderr);
222     }
223 
224   return strdup (getpassbuf);
225 }
226 #endif
227