xref: /haiku/src/bin/unzip/ttyio.c (revision bc3955fea5b07e2e94a27fc05e4bb58fe6f0319b)
1 /*
2   Copyright (c) 1990-2001 Info-ZIP.  All rights reserved.
3 
4   See the accompanying file LICENSE, version 2000-Apr-09 or later
5   (the contents of which are also included in zip.h) for terms of use.
6   If, for some reason, all these files are missing, the Info-ZIP license
7   also may be found at:  ftp://ftp.info-zip.org/pub/infozip/license.html
8 */
9 /*---------------------------------------------------------------------------
10 
11   ttyio.c
12 
13   This file contains routines for doing console input/output, including code
14   for non-echoing input.  It is used by the encryption/decryption code but
15   does not contain any restricted code itself.  This file is shared between
16   Info-ZIP's Zip and UnZip.
17 
18   Contains:  echo()         (VMS only)
19              Echon()        (Unix only)
20              Echoff()       (Unix only)
21              screensize()   (Unix only)
22              zgetch()       (Unix, VMS, and non-Unix/VMS versions)
23              getp()         ("PC," Unix/Atari/Be, VMS/VMCMS/MVS)
24 
25   ---------------------------------------------------------------------------*/
26 
27 #define __TTYIO_C       /* identifies this source module */
28 
29 #include "zip.h"
30 #include "crypt.h"
31 
32 #if (CRYPT || (defined(UNZIP) && !defined(FUNZIP)))
33 /* Non-echo console/keyboard input is needed for (en/de)cryption's password
34  * entry, and for UnZip(SFX)'s MORE and Pause features.
35  * (The corresponding #endif is found at the end of this module.)
36  */
37 
38 #include "ttyio.h"
39 
40 #ifndef PUTC
41 #  define PUTC putc
42 #endif
43 
44 #ifdef ZIP
45 #  ifdef GLOBAL          /* used in Amiga system headers, maybe others too */
46 #    undef GLOBAL
47 #  endif
48 #  define GLOBAL(g) g
49 #else
50 #  define GLOBAL(g) G.g
51 #endif
52 
53 #if (defined(__BEOS__) || defined(__HAIKU__))                /* why yes, we do */
54 #  define HAVE_TERMIOS_H
55 #endif
56 
57 #ifdef _POSIX_VERSION
58 #  ifndef USE_POSIX_TERMIOS
59 #    define USE_POSIX_TERMIOS  /* use POSIX style termio (termios) */
60 #  endif
61 #  ifndef HAVE_TERMIOS_H
62 #    define HAVE_TERMIOS_H     /* POSIX termios.h */
63 #  endif
64 #endif /* _POSIX_VERSION */
65 
66 #ifdef UNZIP            /* Zip handles this with the unix/configure script */
67 #  ifndef _POSIX_VERSION
68 #    if (defined(SYSV) || defined(CRAY)) &&  !defined(__MINT__)
69 #      ifndef USE_SYSV_TERMIO
70 #        define USE_SYSV_TERMIO
71 #      endif
72 #      ifdef COHERENT
73 #        ifndef HAVE_TERMIO_H
74 #          define HAVE_TERMIO_H
75 #        endif
76 #        ifdef HAVE_SYS_TERMIO_H
77 #          undef HAVE_SYS_TERMIO_H
78 #        endif
79 #      else /* !COHERENT */
80 #        ifdef HAVE_TERMIO_H
81 #          undef HAVE_TERMIO_H
82 #        endif
83 #        ifndef HAVE_SYS_TERMIO_H
84 #           define HAVE_SYS_TERMIO_H
85 #        endif
86 #      endif /* ?COHERENT */
87 #    endif /* (SYSV || CRAY) && !__MINT__ */
88 #  endif /* !_POSIX_VERSION */
89 #  if !(defined(BSD4_4) || defined(SYSV) || defined(__convexc__))
90 #    ifndef NO_FCNTL_H
91 #      define NO_FCNTL_H
92 #    endif
93 #  endif /* !(BSD4_4 || SYSV || __convexc__) */
94 #endif /* UNZIP */
95 
96 #ifdef HAVE_TERMIOS_H
97 #  ifndef USE_POSIX_TERMIOS
98 #    define USE_POSIX_TERMIOS
99 #  endif
100 #endif
101 
102 #if (defined(HAVE_TERMIO_H) || defined(HAVE_SYS_TERMIO_H))
103 #  ifndef USE_SYSV_TERMIO
104 #    define USE_SYSV_TERMIO
105 #  endif
106 #endif
107 
108 #if (defined(UNZIP) && !defined(FUNZIP) && defined(UNIX) && defined(MORE))
109 #  include <sys/ioctl.h>
110 #  define GOT_IOCTL_H
111    /* int ioctl OF((int, int, zvoid *));   GRR: may need for some systems */
112 #endif
113 
114 #ifndef HAVE_WORKING_GETCH
115    /* include system support for switching of console echo */
116 #  ifdef VMS
117 #    include <descrip.h>
118 #    include <iodef.h>
119 #    include <ttdef.h>
120 #    include <starlet.h>
121 #    include <ssdef.h>
122 #  else /* !VMS */
123 #    ifdef HAVE_TERMIOS_H
124 #      include <termios.h>
125 #      define sgttyb termios
126 #      define sg_flags c_lflag
127 #      define GTTY(f, s) tcgetattr(f, (zvoid *) s)
128 #      define STTY(f, s) tcsetattr(f, TCSAFLUSH, (zvoid *) s)
129 #    else /* !HAVE_TERMIOS_H */
130 #      ifdef USE_SYSV_TERMIO           /* Amdahl, Cray, all SysV? */
131 #        ifdef HAVE_TERMIO_H
132 #          include <termio.h>
133 #        endif
134 #        ifdef HAVE_SYS_TERMIO_H
135 #          include <sys/termio.h>
136 #        endif
137 #        ifdef NEED_PTEM
138 #          include <sys/stream.h>
139 #          include <sys/ptem.h>
140 #        endif
141 #        define sgttyb termio
142 #        define sg_flags c_lflag
143 #        define GTTY(f,s) ioctl(f,TCGETA,(zvoid *)s)
144 #        define STTY(f,s) ioctl(f,TCSETAW,(zvoid *)s)
145 #      else /* !USE_SYSV_TERMIO */
146 #        ifndef CMS_MVS
147 #          if (!defined(MINIX) && !defined(GOT_IOCTL_H))
148 #            include <sys/ioctl.h>
149 #          endif
150 #          include <sgtty.h>
151 #          define GTTY gtty
152 #          define STTY stty
153 #          ifdef UNZIP
154              /*
155               * XXX : Are these declarations needed at all ????
156               */
157              /*
158               * GRR: let's find out...   Hmmm, appears not...
159              int gtty OF((int, struct sgttyb *));
160              int stty OF((int, struct sgttyb *));
161               */
162 #          endif
163 #        endif /* !CMS_MVS */
164 #      endif /* ?USE_SYSV_TERMIO */
165 #    endif /* ?HAVE_TERMIOS_H */
166 #    ifndef NO_FCNTL_H
167 #      ifndef UNZIP
168 #        include <fcntl.h>
169 #      endif
170 #    else
171        char *ttyname OF((int));
172 #    endif
173 #  endif /* ?VMS */
174 #endif /* !HAVE_WORKING_GETCH */
175 
176 
177 
178 #ifndef HAVE_WORKING_GETCH
179 #ifdef VMS
180 
181 static struct dsc$descriptor_s DevDesc =
182         {11, DSC$K_DTYPE_T, DSC$K_CLASS_S, "SYS$COMMAND"};
183      /* {dsc$w_length, dsc$b_dtype, dsc$b_class, dsc$a_pointer}; */
184 
185 /*
186  * Turn keyboard echoing on or off (VMS).  Loosely based on VMSmunch.c
187  * and hence on Joe Meadows' file.c code.
188  */
echo(opt)189 int echo(opt)
190     int opt;
191 {
192     /*
193      * For VMS v5.x:
194      *   IO$_SENSEMODE/SETMODE info:  Programming, Vol. 7A, System Programming,
195      *     I/O User's: Part I, sec. 8.4.1.1, 8.4.3, 8.4.5, 8.6
196      *   sys$assign(), sys$qio() info:  Programming, Vol. 4B, System Services,
197      *     System Services Reference Manual, pp. sys-23, sys-379
198      *   fixed-length descriptor info:  Programming, Vol. 3, System Services,
199      *     Intro to System Routines, sec. 2.9.2
200      * Greg Roelofs, 15 Aug 91
201      */
202 
203     short           DevChan, iosb[4];
204     long            status;
205     unsigned long   ttmode[2];  /* space for 8 bytes */
206 
207 
208     /* assign a channel to standard input */
209     status = sys$assign(&DevDesc, &DevChan, 0, 0);
210     if (!(status & 1))
211         return status;
212 
213     /* use sys$qio and the IO$_SENSEMODE function to determine the current
214      * tty status (for password reading, could use IO$_READVBLK function
215      * instead, but echo on/off will be more general)
216      */
217     status = sys$qiow(0, DevChan, IO$_SENSEMODE, &iosb, 0, 0,
218                      ttmode, 8, 0, 0, 0, 0);
219     if (!(status & 1))
220         return status;
221     status = iosb[0];
222     if (!(status & 1))
223         return status;
224 
225     /* modify mode buffer to be either NOECHO or ECHO
226      * (depending on function argument opt)
227      */
228     if (opt == 0)   /* off */
229         ttmode[1] |= TT$M_NOECHO;                       /* set NOECHO bit */
230     else
231         ttmode[1] &= ~((unsigned long) TT$M_NOECHO);    /* clear NOECHO bit */
232 
233     /* use the IO$_SETMODE function to change the tty status */
234     status = sys$qiow(0, DevChan, IO$_SETMODE, &iosb, 0, 0,
235                      ttmode, 8, 0, 0, 0, 0);
236     if (!(status & 1))
237         return status;
238     status = iosb[0];
239     if (!(status & 1))
240         return status;
241 
242     /* deassign the sys$input channel by way of clean-up */
243     status = sys$dassgn(DevChan);
244     if (!(status & 1))
245         return status;
246 
247     return SS$_NORMAL;   /* we be happy */
248 
249 } /* end function echo() */
250 
251 
252 /*
253  * Read a single character from keyboard in non-echoing mode (VMS).
254  * (returns EOF in case of errors)
255  */
tt_getch()256 int tt_getch()
257 {
258     short           DevChan, iosb[4];
259     long            status;
260     char            kbbuf[16];  /* input buffer with - some - excess length */
261 
262     /* assign a channel to standard input */
263     status = sys$assign(&DevDesc, &DevChan, 0, 0);
264     if (!(status & 1))
265         return EOF;
266 
267     /* read a single character from SYS$COMMAND (no-echo) and
268      * wait for completion
269      */
270     status = sys$qiow(0,DevChan,
271                       IO$_READVBLK|IO$M_NOECHO|IO$M_NOFILTR,
272                       &iosb, 0, 0,
273                       &kbbuf, 1, 0, 0, 0, 0);
274     if ((status&1) == 1)
275         status = iosb[0];
276 
277     /* deassign the sys$input channel by way of clean-up
278      * (for this step, we do not need to check the completion status)
279      */
280     sys$dassgn(DevChan);
281 
282     /* return the first char read, or EOF in case the read request failed */
283     return (int)(((status&1) == 1) ? (uch)kbbuf[0] : EOF);
284 
285 } /* end function tt_getch() */
286 
287 
288 #else /* !VMS:  basically Unix */
289 
290 
291 /* For VM/CMS and MVS, non-echo terminal input is not (yet?) supported. */
292 #ifndef CMS_MVS
293 
294 #ifdef ZIP                      /* moved to globals.h for UnZip */
295    static int echofd=(-1);      /* file descriptor whose echo is off */
296 #endif
297 
298 /*
299  * Turn echo off for file descriptor f.  Assumes that f is a tty device.
300  */
301 void Echoff(__G__ f)
302     __GDEF
303     int f;                    /* file descriptor for which to turn echo off */
304 {
305     struct sgttyb sg;         /* tty device structure */
306 
307     GLOBAL(echofd) = f;
308     GTTY(f, &sg);             /* get settings */
309     sg.sg_flags &= ~ECHO;     /* turn echo off */
310     STTY(f, &sg);
311 }
312 
313 /*
314  * Turn echo back on for file descriptor echofd.
315  */
Echon(__G)316 void Echon(__G)
317     __GDEF
318 {
319     struct sgttyb sg;         /* tty device structure */
320 
321     if (GLOBAL(echofd) != -1) {
322         GTTY(GLOBAL(echofd), &sg);    /* get settings */
323         sg.sg_flags |= ECHO;  /* turn echo on */
324         STTY(GLOBAL(echofd), &sg);
325         GLOBAL(echofd) = -1;
326     }
327 }
328 
329 #endif /* !CMS_MVS */
330 #endif /* ?VMS */
331 
332 
333 #if (defined(UNZIP) && !defined(FUNZIP))
334 
335 #if (defined(UNIX) || (defined(__BEOS__) || defined(__HAIKU__)))
336 #ifdef MORE
337 
338 /*
339  * Get the number of lines on the output terminal.  SCO Unix apparently
340  * defines TIOCGWINSZ but doesn't support it (!M_UNIX).
341  *
342  * GRR:  will need to know width of terminal someday, too, to account for
343  *       line-wrapping.
344  */
345 
346 #if (defined(TIOCGWINSZ) && !defined(M_UNIX))
347 
screensize(tt_rows,tt_cols)348 int screensize(tt_rows, tt_cols)
349     int *tt_rows;
350     int *tt_cols;
351 {
352     struct winsize wsz;
353 #ifdef DEBUG_WINSZ
354     static int firsttime = TRUE;
355 #endif
356 
357     /* see termio(4) under, e.g., SunOS */
358     if (ioctl(1, TIOCGWINSZ, &wsz) == 0) {
359 #ifdef DEBUG_WINSZ
360         if (firsttime) {
361             firsttime = FALSE;
362             fprintf(stderr, "ttyio.c screensize():  ws_row = %d\n",
363               wsz.ws_row);
364             fprintf(stderr, "ttyio.c screensize():  ws_col = %d\n",
365               wsz.ws_col);
366         }
367 #endif
368         /* number of rows */
369         if (tt_rows != NULL)
370             *tt_rows = (int)((wsz.ws_row > 0) ? wsz.ws_row : 24);
371         /* number of columns */
372         if (tt_cols != NULL)
373             *tt_cols = (int)((wsz.ws_col > 0) ? wsz.ws_col : 80);
374         return 0;    /* signal success */
375     } else {         /* this happens when piping to more(1), for example */
376 #ifdef DEBUG_WINSZ
377         if (firsttime) {
378             firsttime = FALSE;
379             fprintf(stderr,
380               "ttyio.c screensize():  ioctl(TIOCGWINSZ) failed\n"));
381         }
382 #endif
383         /* VT-100 assumed to be minimal hardware */
384         if (tt_rows != NULL)
385             *tt_rows = 24;
386         if (tt_cols != NULL)
387             *tt_cols = 80;
388         return 1;       /* signal failure */
389     }
390 }
391 
392 #else /* !TIOCGWINSZ: service not available, fall back to semi-bogus method */
393 
screensize(tt_rows,tt_cols)394 int screensize(tt_rows, tt_cols)
395     int *tt_rows;
396     int *tt_cols;
397 {
398     char *envptr, *getenv();
399     int n;
400     int errstat = 0;
401 
402     /* GRR:  this is overly simplistic, but don't have access to stty/gtty
403      * system anymore
404      */
405     if (tt_rows != NULL) {
406         envptr = getenv("LINES");
407         if (envptr == (char *)NULL || (n = atoi(envptr)) < 5) {
408             /* VT-100 assumed to be minimal hardware */
409             *tt_rows = 24;
410             errstat = 1;    /* signal failure */
411         } else {
412             *tt_rows = n;
413         }
414     }
415     if (tt_cols != NULL) {
416         envptr = getenv("COLUMNS");
417         if (envptr == (char *)NULL || (n = atoi(envptr)) < 5) {
418             *tt_cols = 80;
419             errstat = 1;    /* signal failure */
420         } else {
421             *tt_cols = n;
422         }
423     }
424     return errstat;
425 }
426 
427 #endif /* ?(TIOCGWINSZ && !M_UNIX) */
428 #endif /* MORE */
429 
430 
431 /*
432  * Get a character from the given file descriptor without echo or newline.
433  */
434 int zgetch(__G__ f)
435     __GDEF
436     int f;                      /* file descriptor from which to read */
437 {
438 #if (defined(USE_SYSV_TERMIO) || defined(USE_POSIX_TERMIOS))
439     char oldmin, oldtim;
440 #endif
441     char c;
442     struct sgttyb sg;           /* tty device structure */
443 
444     GTTY(f, &sg);               /* get settings */
445 #if (defined(USE_SYSV_TERMIO) || defined(USE_POSIX_TERMIOS))
446     oldmin = sg.c_cc[VMIN];     /* save old values */
447     oldtim = sg.c_cc[VTIME];
448     sg.c_cc[VMIN] = 1;          /* need only one char to return read() */
449     sg.c_cc[VTIME] = 0;         /* no timeout */
450     sg.sg_flags &= ~ICANON;     /* canonical mode off */
451 #else
452     sg.sg_flags |= CBREAK;      /* cbreak mode on */
453 #endif
454     sg.sg_flags &= ~ECHO;       /* turn echo off, too */
455     STTY(f, &sg);               /* set cbreak mode */
456     GLOBAL(echofd) = f;         /* in case ^C hit (not perfect: still CBREAK) */
457 
458     read(f, &c, 1);             /* read our character */
459 
460 #if (defined(USE_SYSV_TERMIO) || defined(USE_POSIX_TERMIOS))
461     sg.c_cc[VMIN] = oldmin;     /* restore old values */
462     sg.c_cc[VTIME] = oldtim;
463     sg.sg_flags |= ICANON;      /* canonical mode on */
464 #else
465     sg.sg_flags &= ~CBREAK;     /* cbreak mode off */
466 #endif
467     sg.sg_flags |= ECHO;        /* turn echo on */
468     STTY(f, &sg);               /* restore canonical mode */
469     GLOBAL(echofd) = -1;
470 
471     return (int)(uch)c;
472 }
473 
474 
475 #else /* !UNIX && !__BEOS__ */
476 #ifndef VMS     /* VMS supplies its own variant of getch() */
477 
478 
479 int zgetch(__G__ f)
480     __GDEF
481     int f;    /* file descriptor from which to read (must be open already) */
482 {
483     char c, c2;
484 
485 /*---------------------------------------------------------------------------
486     Get a character from the given file descriptor without echo; can't fake
487     CBREAK mode (i.e., newline required), but can get rid of all chars up to
488     and including newline.
489   ---------------------------------------------------------------------------*/
490 
491     echoff(f);
492     read(f, &c, 1);
493     if (c != '\n')
494         do {
495             read(f, &c2, 1);   /* throw away all other chars up thru newline */
496         } while (c2 != '\n');
497     echon();
498     return (int)c;
499 }
500 
501 #endif /* !VMS */
502 #endif /* ?(UNIX || __BEOS__) */
503 
504 #endif /* UNZIP && !FUNZIP */
505 #endif /* !HAVE_WORKING_GETCH */
506 
507 
508 #if CRYPT                       /* getp() is only used with full encryption */
509 
510 /*
511  * Simple compile-time check for source compatibility between
512  * zcrypt and ttyio:
513  */
514 #if (!defined(CR_MAJORVER) || (CR_MAJORVER < 2) || (CR_MINORVER < 7))
515    error:  This Info-ZIP tool requires zcrypt 2.7 or later.
516 #endif
517 
518 /*
519  * Get a password of length n-1 or less into *p using the prompt *m.
520  * The entered password is not echoed.
521  */
522 
523 #ifdef HAVE_WORKING_GETCH
524 /*
525  * For the AMIGA, getch() is defined as Agetch(), which is in
526  * amiga/filedate.c; SAS/C 6.x provides a getch(), but since Agetch()
527  * uses the infrastructure that is already in place in filedate.c, it is
528  * smaller.  With this function, echoff() and echon() are not needed.
529  *
530  * For the MAC, a non-echo macgetch() function is defined in the MacOS
531  * specific sources which uses the event handling mechanism of the
532  * desktop window manager to get a character from the keyboard.
533  *
534  * For the other systems in this section, a non-echo getch() function
535  * is either contained the C runtime library (conio package), or getch()
536  * is defined as an alias for a similar system specific RTL function.
537  */
538 
539 #ifndef WINDLL   /* WINDLL does not support a console interface */
540 #ifndef QDOS     /* QDOS supplies a variant of this function */
541 
542 /* This is the getp() function for all systems (with TTY type user interface)
543  * that supply a working `non-echo' getch() function for "raw" console input.
544  */
545 char *getp(__G__ m, p, n)
546     __GDEF
547     ZCONST char *m;             /* prompt for password */
548     char *p;                    /* return value: line input */
549     int n;                      /* bytes available in p[] */
550 {
551     char c;                     /* one-byte buffer for read() to use */
552     int i;                      /* number of characters input */
553     char *w;                    /* warning on retry */
554 
555     /* get password */
556     w = "";
557     do {
558         fputs(w, stderr);       /* warning if back again */
559         fputs(m, stderr);       /* display prompt and flush */
560         fflush(stderr);
561         i = 0;
562         do {                    /* read line, keeping first n characters */
563             if ((c = (char)getch()) == '\r')
564                 c = '\n';       /* until user hits CR */
565             if (c == 8 || c == 127) {
566                 if (i > 0) i--; /* the `backspace' and `del' keys works */
567             }
568             else if (i < n)
569                 p[i++] = c;     /* truncate past n */
570         } while (c != '\n');
571         PUTC('\n', stderr);  fflush(stderr);
572         w = "(line too long--try again)\n";
573     } while (p[i-1] != '\n');
574     p[i-1] = 0;                 /* terminate at newline */
575 
576     return p;                   /* return pointer to password */
577 
578 } /* end function getp() */
579 
580 #endif /* !QDOS */
581 #endif /* !WINDLL */
582 
583 
584 #else /* !HAVE_WORKING_GETCH */
585 
586 
587 #if (defined(UNIX) || defined(__MINT__) || (defined(__BEOS__) || defined(__HAIKU__)))
588 
589 #ifndef _PATH_TTY
590 #  ifdef __MINT__
591 #    define _PATH_TTY ttyname(2)
592 #  else
593 #    define _PATH_TTY "/dev/tty"
594 #  endif
595 #endif
596 
597 char *getp(__G__ m, p, n)
598     __GDEF
599     ZCONST char *m;             /* prompt for password */
600     char *p;                    /* return value: line input */
601     int n;                      /* bytes available in p[] */
602 {
603     char c;                     /* one-byte buffer for read() to use */
604     int i;                      /* number of characters input */
605     char *w;                    /* warning on retry */
606     int f;                      /* file descriptor for tty device */
607 
608 #ifdef PASSWD_FROM_STDIN
609     /* Read from stdin. This is unsafe if the password is stored on disk. */
610     f = 0;
611 #else
612     /* turn off echo on tty */
613 
614     if ((f = open(_PATH_TTY, 0)) == -1)
615         return NULL;
616 #endif
617     /* get password */
618     w = "";
619     do {
620         fputs(w, stderr);       /* warning if back again */
621         fputs(m, stderr);       /* prompt */
622         fflush(stderr);
623         i = 0;
624         echoff(f);
625         do {                    /* read line, keeping n */
626             read(f, &c, 1);
627             if (i < n)
628                 p[i++] = c;
629         } while (c != '\n');
630         echon();
631         PUTC('\n', stderr);  fflush(stderr);
632         w = "(line too long--try again)\n";
633     } while (p[i-1] != '\n');
634     p[i-1] = 0;                 /* terminate at newline */
635 
636 #ifndef PASSWD_FROM_STDIN
637     close(f);
638 #endif
639 
640     return p;                   /* return pointer to password */
641 
642 } /* end function getp() */
643 
644 #endif /* UNIX || __MINT__ || __BEOS__ */
645 
646 
647 
648 #if (defined(VMS) || defined(CMS_MVS))
649 
650 char *getp(__G__ m, p, n)
651     __GDEF
652     ZCONST char *m;             /* prompt for password */
653     char *p;                    /* return value: line input */
654     int n;                      /* bytes available in p[] */
655 {
656     char c;                     /* one-byte buffer for read() to use */
657     int i;                      /* number of characters input */
658     char *w;                    /* warning on retry */
659     FILE *f;                    /* file structure for SYS$COMMAND device */
660 
661 #ifdef PASSWD_FROM_STDIN
662     f = stdin;
663 #else
664     if ((f = fopen(ctermid(NULL), "r")) == NULL)
665         return NULL;
666 #endif
667 
668     /* get password */
669     fflush(stdout);
670     w = "";
671     do {
672         if (*w)                 /* bug: VMS apparently adds \n to NULL fputs */
673             fputs(w, stderr);   /* warning if back again */
674         fputs(m, stderr);       /* prompt */
675         fflush(stderr);
676         i = 0;
677         echoff(f);
678         do {                    /* read line, keeping n */
679             if ((c = (char)getc(f)) == '\r')
680                 c = '\n';
681             if (i < n)
682                 p[i++] = c;
683         } while (c != '\n');
684         echon();
685         PUTC('\n', stderr);  fflush(stderr);
686         w = "(line too long--try again)\n";
687     } while (p[i-1] != '\n');
688     p[i-1] = 0;                 /* terminate at newline */
689 #ifndef PASSWD_FROM_STDIN
690     fclose(f);
691 #endif
692 
693     return p;                   /* return pointer to password */
694 
695 } /* end function getp() */
696 
697 #endif /* VMS || CMS_MVS */
698 #endif /* ?HAVE_WORKING_GETCH */
699 #endif /* CRYPT */
700 #endif /* CRYPT || (UNZIP && !FUNZIP) */
701