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