Initial import from FreeBSD RELENG_4:
[dragonfly.git] / contrib / opie / libopie / readpass.c
1 /* readpass.c: The opiereadpass() library function.
2
3 %%% portions-copyright-cmetz-96
4 Portions of this software are Copyright 1996-1999 by Craig Metz, All Rights
5 Reserved. The Inner Net License Version 2 applies to these portions of
6 the software.
7 You should have received a copy of the license with this software. If
8 you didn't get a copy, you may request one from <license@inner.net>.
9
10 Portions of this software are Copyright 1995 by Randall Atkinson and Dan
11 McDonald, All Rights Reserved. All Rights under this copyright are assigned
12 to the U.S. Naval Research Laboratory (NRL). The NRL Copyright Notice and
13 License Agreement applies to this software.
14
15         History:
16
17         Modified by cmetz for OPIE 2.31. Use usleep() to delay after setting
18                 the terminal attributes; this might help certain buggy
19                 systems.
20         Modified by cmetz for OPIE 2.3. Use TCSAFLUSH always.
21         Modified by cmetz for OPIE 2.22. Replaced echo w/ flags.
22                Really use FUNCTION.
23         Modified by cmetz for OPIE 2.2. Use FUNCTION declaration et al.
24                Flush extraneous characters up to eol. Handle gobs of possible
25                erase and kill keys if on a terminal. To do so, use RAW
26                terminal I/O and handle echo ourselves. (should also help
27                DOS et al portability). Fixed include order. Re-did MSDOS
28                and OS/2 includes. Set up VMIN and VTIME. Added some non-UNIX
29                portability cruft. Limit backspacing and killing. In terminal
30                mode, eat random other control characters. Added eof handling.
31         Created at NRL for OPIE 2.2 from opiesubr.c. Change opiestrip_crlf to
32                opiestripcrlf. Don't strip to seven bits. 
33 */
34 #include "opie_cfg.h"
35
36 #include <stdio.h>
37 #include <string.h>
38 #include <stdlib.h>     /* ANSI C standard library */
39
40 #ifdef unix
41 #include <fcntl.h>      /* POSIX file control function headers */
42 #include <termios.h>    /* POSIX Terminal I/O functions */ 
43 #if HAVE_UNISTD_H
44 #include <unistd.h>     /* POSIX standard definitions */
45 #endif /* HAVE_UNISTD_H */
46 #include <signal.h>
47 #include <setjmp.h>
48 #endif /* unix */
49
50 #ifdef __MSDOS__
51 #include <dos.h>
52 #endif /* __MSDOS__ */
53
54 #ifdef __OS2__
55 #define INCL_KBD
56 #include <os2.h>
57 #include <io.h>
58 #endif /* __OS2__ */
59
60 #include "opie.h"
61
62 #define CONTROL(x) (x - 64)
63
64 char *bsseq = "\b \b";
65
66 #ifdef unix
67 static jmp_buf jmpbuf;
68
69 static VOIDRET catch FUNCTION((i), int i)
70 {
71   longjmp(jmpbuf, 1);
72 }
73 #endif /* unix */
74
75 char *opiereadpass FUNCTION((buf, len, flags), char *buf AND int len AND int flags)
76 {
77 #ifdef unix
78   struct termios attr, orig_attr;
79 #endif /* unix */
80   char erase[5];
81   char kill[4];
82   char eof[4];
83
84   memset(erase, 0, sizeof(erase));
85   memset(kill, 0, sizeof(kill));
86   memset(eof, 0, sizeof(eof));
87
88   /* This section was heavily rewritten by rja following the model of code
89      samples circa page 151 of the POSIX Programmer's Guide by Donald Lewine,
90      ISBN 0-937175-73-0. That book is Copyright 1991 by O'Reilly &
91      Associates, Inc. All Rights Reserved. I recommend the book to anyone
92      trying to write portable software. rja */
93
94 #ifdef unix
95   if (setjmp(jmpbuf))
96     goto error;
97
98   signal(SIGINT, catch);
99 #endif /* unix */
100
101   /* Flush any pending output */
102   fflush(stderr);
103   fflush(stdout);
104
105 #ifdef unix
106   /* Get original terminal attributes */
107   if (isatty(0)) {
108     if (tcgetattr(0, &orig_attr))
109       return NULL;
110
111     /* copy terminal settings into attr */
112     memcpy(&attr, &orig_attr, sizeof(struct termios));
113
114     attr.c_lflag &= ~(ECHO | ICANON);
115     attr.c_lflag |= ISIG;
116
117     attr.c_cc[VMIN] = 1;
118     attr.c_cc[VTIME] = 0;
119
120     erase[0] = CONTROL('H');
121     erase[1] = 127;
122
123 #ifdef CERASE
124     {
125     char *e = erase;
126
127     while(*e)
128       if (*(e++) == CERASE)
129         break;
130
131     if (!*e)
132       *e = CERASE;
133     }
134 #endif /* CERASE */
135 #ifdef VERASE
136     {
137     char *e = erase;
138
139     while(*e)
140       if (*(e++) == attr.c_cc[VERASE])
141         break;
142
143     if (!*e)
144       *e = attr.c_cc[VERASE];
145     }
146 #endif /* VERASE */
147
148     kill[0] = CONTROL('U');
149 #ifdef CKILL
150     {
151     char *e = kill;
152
153     while(*e)
154       if (*(e++) == CKILL)
155         break;
156
157     if (!*e)
158       *e = CKILL;
159     }
160 #endif /* CKILL */
161 #ifdef VKILL
162     {
163     char *e = kill;
164
165     while(*e)
166       if (*(e++) == attr.c_cc[VKILL])
167         break;
168
169     if (!*e)
170       *e = attr.c_cc[VKILL];
171     }
172 #endif /* VKILL */
173
174     eof[0] = CONTROL('D');
175 #ifdef CEOF
176     {
177     char *e = eof;
178
179     while(*e)
180       if (*(e++) == CEOF)
181         break;
182
183     if (!*e)
184       *e = CEOF;
185     }
186 #endif /* CEOF */
187 #ifdef VEOF
188     {
189     char *e = eof;
190
191     while(*e)
192       if (*(e++) == attr.c_cc[VEOF])
193         break;
194
195     if (!*e)
196       *e = VEOF;
197     }
198 #endif /* VEOF */
199
200 #if HAVE_USLEEP
201     usleep(1);
202 #endif /* HAVE_USLEEP */
203
204     if (tcsetattr(0, TCSAFLUSH, &attr))
205       goto error;
206
207 #if HAVE_USLEEP
208     usleep(1);
209 #endif /* HAVE_USLEEP */
210   }
211 #else /* unix */
212   erase[0] = CONTROL('H');
213   erase[1] = 127;
214   kill[0] = CONTROL('U');
215   eof[0] = CONTROL('D');
216   eof[1] = CONTROL('Z');
217 #endif /* unix */
218
219   {
220   char *c = buf, *end = buf + len, *e;
221 #ifdef __OS2__
222   KBDKEYINFO keyInfo;
223 #endif /* __OS2__ */
224
225 loop:
226 #ifdef unix
227   if (read(0, c, 1) != 1)
228     goto error;
229 #endif /* unix */
230 #ifdef MSDOS
231   *c = bdos(7, 0, 0);
232 #endif /* MSDOS */
233 #ifdef __OS2__
234   KbdCharIn(&keyInfo, 0, 0);
235   *c = keyInfo.chChar;
236 #endif /* __OS2__ */
237
238   if ((*c == '\r') || (*c == '\n')) {
239     *c = 0;
240     goto restore;
241   }
242
243   e = eof;
244   while(*e)
245     if (*(e++) == *c)
246       goto error;
247
248   e = erase;
249   while(*e)
250     if (*(e++) == *c) {
251       if (c <= buf)
252         goto beep;
253
254       if (flags & 1)
255         write(1, bsseq, sizeof(bsseq) - 1);
256       c--;
257       goto loop;
258     }
259
260   e = kill;
261   while(*e)
262     if (*(e++) == *c) {
263       if (c <= buf)
264         goto beep;
265
266       if (flags & 1)
267         while(c-- > buf)
268           write(1, bsseq, sizeof(bsseq) - 1);
269
270       c = buf;
271       goto loop;
272     }
273
274   if (c < end) {
275     if (*c < 32)
276       goto beep;
277     if (flags & 1)
278       write(1, c, 1);
279     c++;
280   } else {
281   beep:
282     *c = CONTROL('G');
283     write(1, c, 1);
284   }
285
286   goto loop;
287   }
288
289 restore:
290 #ifdef unix
291   /* Restore previous tty modes */
292   if (isatty(0))
293     if (tcsetattr(0, TCSAFLUSH, &orig_attr))
294       return NULL;
295
296   signal(SIGINT, SIG_DFL);
297 #endif /* unix */
298
299   /* After the secret key is taken from the keyboard, the line feed is
300      written to standard error instead of standard output.  That means that
301      anyone using the program from a terminal won't notice, but capturing
302      standard output will get the key words without a newline in front of
303      them. */
304   if (!(flags & 4)) {
305     fprintf(stderr, "\n");
306     fflush(stderr);
307   }
308
309   return buf;
310
311 error:
312   *buf = 0;
313   buf = NULL;
314   goto restore;
315 }