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
181static 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 */
189int 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 */
256int 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 */
301void 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 */
316void 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
348int 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
394int 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 */
434int 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
479int 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 */
545char *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
597char *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
650char *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