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