1 /*
2   Copyright (c) 1990-2007 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 unzip.h) for terms of use.
6   If, for some reason, 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   qdos.c
12 
13   QDOS-specific routines for use with Info-ZIP's UnZip 5.3 and later.
14 
15   Contains:  Qstrfix()
16              QFilename()
17              QMatch()
18              chowner()
19              Qgetch()
20              QReturn()
21              LastDir()
22              screensize()
23              do_wild()           <-- generic enough to put in file_io.c?
24              mapattr()
25              mapname()
26              checkdir()
27              qfix()
28              close_outfile()
29              stamp_file()
30              getp()
31              version()
32 
33   ---------------------------------------------------------------------------*/
34 
35 #define UNZIP_INTERNAL
36 
37 #include "unzip.h"
38 #include "crypt.h"
39 #include "ttyio.h"
40 #include <dirent.h>
41 #include "izqdos.h"
42 #include "unzvers.h"
43 
44 #ifndef SFX
45 char _prog_name[] = "UnZip";
46 #else
47 char _prog_name[] = "??Special Flag for unzipsfx hack  ??";
48 #endif
49 /* sorrid hack at request of GRR follows; hope the compiler stays kind to us */
50 char _version[] = {UZ_MAJORVER+'0','.',UZ_MINORVER+'0',UZ_PATCHLEVEL+'0'};
51 char _extra[] = " " UZ_BETALEVEL;
52 char _copyright[] = "(c) Info-ZIP Group";
53 char *  _endmsg = NULL;
54 long _stack = 16*1024;         /* huge stack (for qdos) */
55 
56 extern void consetup_title(chanid_t,struct WINDOWDEF *);
57 void (*_consetup)(chanid_t,struct WINDOWDEF *) = consetup_title;
58 
59 struct WINDOWDEF _condetails =
60 {
61     2,
62     1,
63     0,
64     7,
65     500,
66     220,
67     2,
68     30
69 };
70 
71 
chowner(chanid_t chan)72 static jobid_t chowner(chanid_t chan)
73 {
74     extern char *_sys_var;
75     char *scht;
76     long *cdb;
77     long jid;
78 
79     scht = *((char **)(_sys_var + 0x78));
80     cdb = *(long **)((long *)scht  + (chan & 0xffff));
81     jid = *(cdb + 2);
82     return jid;
83 }
84 
QReturn(int err)85 int QReturn(int err)
86 {
87     jobid_t me,you;
88 
89     me = getpid();
90     you = chowner(getchid(0));
91 
92     if((me == you) && ((qlflag & 4) == 0))
93     {
94         if(isatty(0) && isatty(2) && qlwait)
95         {
96             char c = 0;
97             fputs("Press a key to exit", stderr);
98             if((io_fbyte(getchid(0), qlwait, &c) == 0) && c == 27)
99             {
100                 io_fbyte(getchid(0), -1, &c);
101             }
102         }
103     }
104     if(err > 0) err = -err;     /* We like -ve err nos (exclusively, alas) */
105     exit(err);
106 }
107 
108 #ifndef FUNZIP
109 
110 static int created_dir;        /* used in mapname(), checkdir() */
111 static int renamed_fullpath;   /* ditto */
112 
Qstrfix(char * p)113 char *Qstrfix (char *p)
114 {
115     char *q;
116     for (q = p; (q = strstr(q, ".zip"));)
117     {
118         *q = '_';
119         q += 4;
120     }
121     return p;
122 }
123 
QFilename(char * f)124 void QFilename(char *f)
125 {
126     char *o,*p,*q = strdup(f);
127     p = q;
128 
129     if(*q == '.' && *(q+1) == '/') q += 2;
130     o = q;
131 
132     for(;*q;q++)
133     {
134         if(*q == '/') *q = '_';
135         if((qlflag & 1) == 0)
136         {
137             if(*q == '.') *q = '_';
138         }
139     }
140     strcpy(f,o);
141     free(p);
142 }
143 
QMatch(uch c1,uch c2)144 int QMatch(uch c1, uch c2)
145 {
146     int m =0;
147 
148     if(c1 != c2)
149     {
150         if(c1 == '_' && (c2 == '.' || c2 == '/'))
151         {
152             m = 1;
153         }
154     }
155     else
156     {
157         m = 1;
158     }
159     return m;
160 }
161 
162 
Qgetch(void)163 int Qgetch(void)
164 {
165     char ch;
166 
167     if(io_fbyte(getchid(0), -1, &ch) < 0)
168     {
169         return EOF;
170     }
171     else
172     {
173         return (int) ch;
174     }
175 }
176 
screensize(int * tt_rows,int * tt_cols)177 int screensize(int *tt_rows, int *tt_cols)
178 {
179     QLRECT_t rect;
180 
181     if(0 == sd_chenq(getchid(1), -1, &rect))
182     {
183         if(tt_cols)
184             *tt_cols = rect.q_width;
185         if(tt_rows)
186             *tt_rows = rect.q_height;
187     }
188     else
189     {
190         if(tt_cols)
191             *tt_cols = 80;
192         if(tt_rows)
193             *tt_rows = 24;
194     }
195     return 0;
196 }
197 
198 
199 
200 #ifndef SFX
LastDir(char * ws)201 char *LastDir(char *ws)
202 {
203     char *p;
204     char *q = ws;
205     struct stat s;
206 
207     for(p = ws; *p; p++)
208     {
209         if(*p == '_')
210         {
211             char c;
212 
213             p++;
214             c = *p;
215             *p = 0;
216             if(stat(ws, &s) == 0 && S_ISDIR(s.st_mode))
217             {
218                 q = p;
219             }
220             *p = c;
221         }
222     }
223     return q;
224 }
225 
226 
227 /**********************/
228 /* Function do_wild() */   /* for porting: dir separator; match(ignore_case) */
229 /**********************/
230 
231 char *do_wild(__G__ wildspec)
232     __GDEF
233     ZCONST char *wildspec;  /* only used first time on a given dir */
234 {
235     static DIR *wild_dir = (DIR *)NULL;
236     static ZCONST char *wildname;
237     static char *dirname, matchname[FILNAMSIZ];
238     static int notfirstcall=FALSE, have_dirname, dirnamelen;
239     struct dirent *file;
240     char basedir[40];
241 
242     /* Even when we're just returning wildspec, we *always* do so in
243      * matchname[]--calling routine is allowed to append four characters
244      * to the returned string, and wildspec may be a pointer to argv[].
245      */
246     if (!notfirstcall) {    /* first call:  must initialize everything */
247         char *ws = NULL, *us = NULL;
248 
249         notfirstcall = TRUE;
250 
251         /* break the wildspec into a directory part and a wildcard filename */
252 
253         ws = (char *) iswild(wildspec);
254 
255         if(ws == NULL)
256         {
257             strncpy(matchname, wildspec, FILNAMSIZ);
258             matchname[FILNAMSIZ-1] = '\0';
259             return matchname;
260         }
261 
262         us = LastDir(wildspec);
263 
264         if(us == wildspec)
265         {
266             dirname = basedir;
267             getcwd(basedir, sizeof(basedir)-1);
268             dirnamelen = strlen(basedir);
269             have_dirname = FALSE;
270             wildname = wildspec;
271         } else {
272             wildname = us;     /* point at character after '/' */
273             dirnamelen = wildname - wildspec;
274             if ((dirname = (char *)malloc(dirnamelen+1)) == (char *)NULL) {
275                 Info(slide, 0x201, ((char *)slide,
276                   "warning:  cannot allocate wildcard buffers\n"));
277                 strncpy(matchname, wildspec, FILNAMSIZ);
278                 matchname[FILNAMSIZ-1] = '\0';
279                 return matchname;   /* but maybe filespec was not a wildcard */
280             }
281             strncpy(dirname, wildspec, dirnamelen);
282             dirname[dirnamelen] = '\0';   /* terminate for strcpy below */
283             have_dirname = TRUE;
284         }
285 
286         if ((wild_dir = opendir(dirname)) != (DIR *)NULL) {
287             while ((file = readdir(wild_dir)) != (struct dirent *)NULL) {
288                 if (match(file->d_name, wildname, 0 WISEP)) { /* 0=case sens.*/
289                     if (have_dirname) {
290                         strcpy(matchname, dirname);
291                         strcpy(matchname+dirnamelen, file->d_name);
292                     } else
293                         strcpy(matchname, file->d_name);
294                     return matchname;
295                 }
296             }
297             /* if we get to here directory is exhausted, so close it */
298             closedir(wild_dir);
299             wild_dir = (DIR *)NULL;
300         }
301 
302         /* return the raw wildspec in case that works (e.g., directory not
303          * searchable, but filespec was not wild and file is readable) */
304         strncpy(matchname, wildspec, FILNAMSIZ);
305         matchname[FILNAMSIZ-1] = '\0';
306         return matchname;
307     }
308 
309     /* last time through, might have failed opendir but returned raw wildspec */
310     if (wild_dir == (DIR *)NULL) {
311         notfirstcall = FALSE; /* nothing left to try--reset for new wildspec */
312         if (have_dirname)
313             free(dirname);
314         return (char *)NULL;
315     }
316 
317     /* If we've gotten this far, we've read and matched at least one entry
318      * successfully (in a previous call), so dirname has been copied into
319      * matchname already.
320      */
321     while ((file = readdir(wild_dir)) != (struct dirent *)NULL) {
322         if (match(file->d_name, wildname, 0 WISEP)) {   /* 0 == case sens. */
323             if (have_dirname) {
324                 /* strcpy(matchname, dirname); */
325                 strcpy(matchname+dirnamelen, file->d_name);
326             } else
327                 strcpy(matchname, file->d_name);
328             return matchname;
329         }
330     }
331 
332     closedir(wild_dir);     /* have read at least one entry; nothing left */
333     wild_dir = (DIR *)NULL;
334     notfirstcall = FALSE;   /* reset for new wildspec */
335     if (have_dirname)
336         free(dirname);
337     return (char *)NULL;
338 
339 } /* end function do_wild() */
340 
341 #endif /* !SFX */
342 
343 
344 
345 
346 
347 /**********************/
348 /* Function mapattr() */
349 /**********************/
350 
mapattr(__G)351 int mapattr(__G)
352     __GDEF
353 {
354     ulg tmp = G.crec.external_file_attributes;
355 
356     switch (G.pInfo->hostnum) {
357         case AMIGA_:
358             tmp = (unsigned)(tmp>>17 & 7);   /* Amiga RWE bits */
359             G.pInfo->file_attr = (unsigned)(tmp<<6 | tmp<<3 | tmp);
360             break;
361         case THEOS_:
362             tmp &= 0xF1FFFFFFL;
363             if ((tmp & 0xF0000000L) != 0x40000000L)
364                 tmp &= 0x01FFFFFFL;     /* not a dir, mask all ftype bits */
365             else
366                 tmp &= 0x41FFFFFFL;     /* leave directory bit as set */
367             /* fall through! */
368         case QDOS_:
369         case UNIX_:
370         case VMS_:
371         case ACORN_:
372         case ATARI_:
373         case ATHEOS_:
374         case BEOS_:
375         case TANDEM_:
376             G.pInfo->file_attr = (unsigned)(tmp >> 16);
377             if (G.pInfo->file_attr != 0 || !G.extra_field) {
378                 return 0;
379             } else {
380                 /* Some (non-Info-ZIP) implementations of Zip for Unix and
381                    VMS (and probably others ??) leave 0 in the upper 16-bit
382                    part of the external_file_attributes field. Instead, they
383                    store file permission attributes in some extra field.
384                    As a work-around, we search for the presence of one of
385                    these extra fields and fall back to the MSDOS compatible
386                    part of external_file_attributes if one of the known
387                    e.f. types has been detected.
388                    Later, we might implement extraction of the permission
389                    bits from the VMS extra field. But for now, the work-around
390                    should be sufficient to provide "readable" extracted files.
391                    (For ASI Unix e.f., an experimental remap of the e.f.
392                    mode value IS already provided!)
393                  */
394                 ush ebID;
395                 unsigned ebLen;
396                 uch *ef = G.extra_field;
397                 unsigned ef_len = G.crec.extra_field_length;
398                 int r = FALSE;
399 
400                 while (!r && ef_len >= EB_HEADSIZE) {
401                     ebID = makeword(ef);
402                     ebLen = (unsigned)makeword(ef+EB_LEN);
403                     if (ebLen > (ef_len - EB_HEADSIZE))
404                         /* discoverd some e.f. inconsistency! */
405                         break;
406                     switch (ebID) {
407                       case EF_ASIUNIX:
408                         if (ebLen >= (EB_ASI_MODE+2)) {
409                             G.pInfo->file_attr =
410                               (unsigned)makeword(ef+(EB_HEADSIZE+EB_ASI_MODE));
411                             /* force stop of loop: */
412                             ef_len = (ebLen + EB_HEADSIZE);
413                             break;
414                         }
415                         /* else: fall through! */
416                       case EF_PKVMS:
417                         /* "found nondecypherable e.f. with perm. attr" */
418                         r = TRUE;
419                       default:
420                         break;
421                     }
422                     ef_len -= (ebLen + EB_HEADSIZE);
423                     ef += (ebLen + EB_HEADSIZE);
424                 }
425                 if (!r)
426                     return 0;
427             }
428             /* fall through! */
429         /* all remaining cases:  expand MSDOS read-only bit into write perms */
430         case FS_FAT_:
431             /* PKWARE's PKZip for Unix marks entries as FS_FAT_, but stores the
432              * Unix attributes in the upper 16 bits of the external attributes
433              * field, just like Info-ZIP's Zip for Unix.  We try to use that
434              * value, after a check for consistency with the MSDOS attribute
435              * bits (see below).
436              */
437             G.pInfo->file_attr = (unsigned)(tmp >> 16);
438             /* fall through! */
439         case FS_HPFS_:
440         case FS_NTFS_:
441         case MAC_:
442         case TOPS20_:
443         default:
444             /* Ensure that DOS subdir bit is set when the entry's name ends
445              * in a '/'.  Some third-party Zip programs fail to set the subdir
446              * bit for directory entries.
447              */
448             if ((tmp & 0x10) == 0) {
449                 extent fnlen = strlen(G.filename);
450                 if (fnlen > 0 && G.filename[fnlen-1] == '/')
451                     tmp |= 0x10;
452             }
453             /* read-only bit --> write perms; subdir bit --> dir exec bit */
454             tmp = !(tmp & 1) << 1  |  (tmp & 0x10) >> 4;
455             if ((G.pInfo->file_attr & 0700) == (unsigned)(0400 | tmp<<6))
456                 /* keep previous G.pInfo->file_attr setting, when its "owner"
457                  * part appears to be consistent with DOS attribute flags!
458                  */
459                 return 0;
460             G.pInfo->file_attr = (unsigned)(0444 | tmp<<6 | tmp<<3 | tmp);
461             break;
462     } /* end switch (host-OS-created-by) */
463 
464     /* for originating systems with no concept of "group," "other," "system": */
465     umask( (int)(tmp=umask(0)) );    /* apply mask to expanded r/w(/x) perms */
466     G.pInfo->file_attr &= ~tmp;
467 
468     return 0;
469 
470 } /* end function mapattr() */
471 
472 
473 
474 /************************/
475 /*  Function mapname()  */
476 /************************/
477 int mapname(__G__ renamed)
478     __GDEF
479     int renamed;
480 /*
481  * returns:
482  *  MPN_OK          - no problem detected
483  *  MPN_INF_TRUNC   - caution (truncated filename)
484  *  MPN_INF_SKIP    - info "skip entry" (dir doesn't exist)
485  *  MPN_ERR_SKIP    - error -> skip entry
486  *  MPN_ERR_TOOLONG - error -> path is too long
487  *  MPN_NOMEM       - error (memory allocation failed) -> skip entry
488  *  [also MPN_VOL_LABEL, MPN_CREATED_DIR]
489  */
490 {
491     char pathcomp[FILNAMSIZ];      /* path-component buffer */
492     char *pp, *cp=(char *)NULL;    /* character pointers */
493     char *lastsemi=(char *)NULL;   /* pointer to last semi-colon in pathcomp */
494     int killed_ddot = FALSE;       /* is set when skipping "../" pathcomp */
495     int error = MPN_OK;
496     register unsigned workch;      /* hold the character being tested */
497 
498 
499 /*---------------------------------------------------------------------------
500     Initialize various pointers and counters and stuff.
501   ---------------------------------------------------------------------------*/
502 
503     if (G.pInfo->vollabel)
504         return MPN_VOL_LABEL;   /* can't set disk volume labels in SMS/QDOS */
505 
506     /* can create path as long as not just freshening, or if user told us */
507     G.create_dirs = (!uO.fflag || renamed);
508 
509     created_dir = FALSE;        /* not yet */
510 
511     /* user gave full pathname:  don't prepend rootpath */
512     renamed_fullpath = (renamed && (*G.filename == '/'));
513 
514     if (checkdir(__G__ (char *)NULL, INIT) == MPN_NOMEM)
515         return MPN_NOMEM;       /* initialize path buffer, unless no memory */
516 
517     *pathcomp = '\0';           /* initialize translation buffer */
518     pp = pathcomp;              /* point to translation buffer */
519     if (uO.jflag)               /* junking directories */
520         cp = (char *)strrchr(G.filename, '/');
521     if (cp == (char *)NULL)     /* no '/' or not junking dirs */
522         cp = G.filename;        /* point to internal zipfile-member pathname */
523     else
524         ++cp;                   /* point to start of last component of path */
525 
526 /*---------------------------------------------------------------------------
527     Begin main loop through characters in filename.
528   ---------------------------------------------------------------------------*/
529 
530     while ((workch = (uch)*cp++) != 0) {
531 
532         switch (workch) {
533             case '/':             /* can assume -j flag not given */
534                 *pp = '\0';
535                 if (((error = checkdir(__G__ pathcomp, APPEND_DIR))
536                      & MPN_MASK) > MPN_INF_TRUNC)
537                     return error;
538                 pp = pathcomp;    /* reset conversion buffer for next piece */
539                 lastsemi = (char *)NULL; /* leave direct. semi-colons alone */
540                 break;
541 
542             case '.':
543                 if (pp == pathcomp) {   /* nothing appended yet... */
544                     if (*cp == '/') {   /* don't bother appending "./" to */
545                         ++cp;           /*  the path: skip behind the '/' */
546                         break;
547                     } else if (!uO.ddotflag && *cp == '.' && cp[1] == '/') {
548                         /* "../" dir traversal detected */
549                         cp += 2;        /*  skip over behind the '/' */
550                         killed_ddot = TRUE; /*  set "show message" flag */
551                         break;
552                     }
553                 }
554                 *pp++ = (((qlflag & 1) == 0) ? '_' : '.');
555                 break;
556 
557             case ';':             /* VMS version (or DEC-20 attrib?) */
558                 lastsemi = pp;
559                 *pp++ = ';';      /* keep for now; remove VMS ";##" */
560                 break;            /*  later, if requested */
561 
562             default:
563                 /* allow European characters in filenames: */
564                 if (isprint(workch) || (128 <= workch && workch <= 254))
565                     *pp++ = (char)workch;
566         } /* end switch */
567 
568     } /* end while loop */
569 
570     /* Show warning when stripping insecure "parent dir" path components */
571     if (killed_ddot && QCOND2) {
572         Info(slide, 0, ((char *)slide,
573           "warning:  skipped \"../\" path component(s) in %s\n",
574           FnFilter1(G.filename)));
575         if (!(error & ~MPN_MASK))
576             error = (error & MPN_MASK) | PK_WARN;
577     }
578 
579 /*---------------------------------------------------------------------------
580     Report if directory was created (and no file to create:  filename ended
581     in '/'), check name to be sure it exists, and combine path and name be-
582     fore exiting.
583   ---------------------------------------------------------------------------*/
584 
585     if (G.filename[strlen(G.filename) - 1] == '/') {
586         G.filename[strlen(G.filename) - 1] = '_';
587         checkdir(__G__ G.filename, GETPATH);
588         if (created_dir) {
589             if (QCOND2) {
590                 Info(slide, 0, ((char *)slide, "   creating: %s\n",
591                   FnFilter1(G.filename)));
592             }
593             /* set dir time (note trailing '/') */
594             return (error & ~MPN_MASK) | MPN_CREATED_DIR;
595         }
596         /* dir existed already; don't look for data to extract */
597         return (error & ~MPN_MASK) | MPN_INF_SKIP;
598     }
599 
600     *pp = '\0';                   /* done with pathcomp:  terminate it */
601 
602     /* if not saving them, remove VMS version numbers (appended ";###") */
603     if (!uO.V_flag && lastsemi) {
604         pp = lastsemi + 1;
605         while (isdigit((uch)(*pp)))
606             ++pp;
607         if (*pp == '\0')          /* only digits between ';' and end:  nuke */
608             *lastsemi = '\0';
609     }
610 
611     if (*pathcomp == '\0') {
612         Info(slide, 1, ((char *)slide, "mapname:  conversion of %s failed\n",
613           FnFilter1(G.filename)));
614         return (error & ~MPN_MASK) | MPN_ERR_SKIP;
615     }
616 
617     checkdir(__G__ pathcomp, APPEND_NAME);  /* returns 1 if truncated: care? */
618     checkdir(__G__ G.filename, GETPATH);
619 
620     return error;
621 
622 } /* end function mapname() */
623 
624 
625 
626 
627 /***********************/
628 /* Function checkdir() */
629 /***********************/
630 
631 int checkdir(__G__ pathcomp, flag)
632     __GDEF
633     char *pathcomp;
634     int flag;
635 /*
636  * returns:
637  *  MPN_OK          - no problem detected
638  *  MPN_INF_TRUNC   - (on APPEND_NAME) truncated filename
639  *  MPN_INF_SKIP    - path doesn't exist, not allowed to create
640  *  MPN_ERR_SKIP    - path doesn't exist, tried to create and failed; or path
641  *                    exists and is not a directory, but is supposed to be
642  *  MPN_ERR_TOOLONG - path is too long
643  *  MPN_NOMEM       - can't allocate memory for filename buffers
644  */
645 {
646     static int rootlen = 0;   /* length of rootpath */
647     static char *rootpath;    /* user's "extract-to" directory */
648     static char *buildpath;   /* full path (so far) to extracted file */
649     static char *end;         /* pointer to end of buildpath ('\0') */
650 
651 #   define FN_MASK   7
652 #   define FUNCTION  (flag & FN_MASK)
653 
654 
655 /*---------------------------------------------------------------------------
656     APPEND_DIR:  append the path component to the path being built and check
657     for its existence.  If doesn't exist and we are creating directories, do
658     so for this one; else signal success or error as appropriate.
659   ---------------------------------------------------------------------------*/
660 
661     if (FUNCTION == APPEND_DIR) {
662         int too_long = FALSE;
663 #ifdef SHORT_NAMES
664         char *old_end = end;
665 #endif
666 
667         Trace((stderr, "appending dir segment [%s]\n", FnFilter1(pathcomp)));
668         while ((*end = *pathcomp++) != '\0')
669             ++end;
670 #ifdef SHORT_NAMES   /* path components restricted to 14 chars, typically */
671         if ((end-old_end) > FILENAME_MAX)  /* GRR:  proper constant? */
672             *(end = old_end + FILENAME_MAX) = '\0';
673 #endif
674 
675         /* GRR:  could do better check, see if overrunning buffer as we go:
676          * check end-buildpath after each append, set warning variable if
677          * within 20 of FILNAMSIZ; then if var set, do careful check when
678          * appending.  Clear variable when begin new path. */
679 
680         if ((end-buildpath) > FILNAMSIZ-2)  /* need '/', one-char name, '\0' */
681             too_long = TRUE;                /* check if extracting directory? */
682         if (stat(buildpath, &G.statbuf)) {  /* path doesn't exist */
683             if (!G.create_dirs) { /* told not to create (freshening) */
684                 free(buildpath);
685                 return MPN_INF_SKIP;    /* path doesn't exist: nothing to do */
686             }
687             if (too_long) {
688                 Info(slide, 1, ((char *)slide,
689                   "checkdir error:  path too long: %s\n",
690                   FnFilter1(buildpath)));
691                 free(buildpath);
692                 /* no room for filenames:  fatal */
693                 return MPN_ERR_TOOLONG;
694             }
695             if (mkdir(buildpath, 0777) == -1) {   /* create the directory */
696                 Info(slide, 1, ((char *)slide,
697                   "checkdir error:  cannot create %s\n\
698                  unable to process %s.\n",
699                   FnFilter2(buildpath), FnFilter1(G.filename)));
700                 free(buildpath);
701                 /* path didn't exist, tried to create, failed */
702                 return MPN_ERR_SKIP;
703             }
704             created_dir = TRUE;
705         } else if (!S_ISDIR(G.statbuf.st_mode)) {
706             Info(slide, 1, ((char *)slide,
707               "checkdir error:  %s exists but is not directory\n\
708                  unable to process %s.\n",
709               FnFilter2(buildpath), FnFilter1(G.filename)));
710             free(buildpath);
711             /* path existed but wasn't dir */
712             return MPN_ERR_SKIP;
713         }
714         if (too_long) {
715             Info(slide, 1, ((char *)slide,
716               "checkdir error:  path too long: %s\n", FnFilter1(buildpath)));
717             free(buildpath);
718             /* no room for filenames:  fatal */
719             return MPN_ERR_TOOLONG;
720         }
721         *end++ = '_';
722         *end = '\0';
723         Trace((stderr, "buildpath now = [%s]\n", FnFilter1(buildpath)));
724         return MPN_OK;
725 
726     } /* end if (FUNCTION == APPEND_DIR) */
727 
728 /*---------------------------------------------------------------------------
729     GETPATH:  copy full path to the string pointed at by pathcomp, and free
730     buildpath.
731   ---------------------------------------------------------------------------*/
732 
733     if (FUNCTION == GETPATH) {
734         strcpy(pathcomp, buildpath);
735         Trace((stderr, "getting and freeing path [%s]\n",
736           FnFilter1(pathcomp)));
737         free(buildpath);
738         buildpath = end = (char *)NULL;
739         return MPN_OK;
740     }
741 
742 /*---------------------------------------------------------------------------
743     APPEND_NAME:  assume the path component is the filename; append it and
744     return without checking for existence.
745   ---------------------------------------------------------------------------*/
746 
747     if (FUNCTION == APPEND_NAME) {
748 #ifdef SHORT_NAMES
749         char *old_end = end;
750 #endif
751         short dlen;
752 
753         Trace((stderr, "appending filename [%s]\n", FnFilter1(pathcomp)));
754         while ((*end = *pathcomp++) != '\0') {
755             ++end;
756 #ifdef SHORT_NAMES  /* truncate name at 14 characters, typically */
757             if ((end-old_end) > FILENAME_MAX)      /* GRR:  proper constant? */
758                 *(end = old_end + FILENAME_MAX) = '\0';
759 #endif
760             if (isdirdev(buildpath))
761             {
762                 dlen = 5;
763             }
764             else
765             {
766                 dlen = 0;
767             }
768 
769             if ((end-buildpath-dlen) >= FILNAMSIZ) {
770                 *--end = '\0';
771                 Info(slide, 0x201, ((char *)slide,
772                   "checkdir warning:  path too long; truncating\n\
773                    %s\n                -> %s\n",
774                   FnFilter1(G.filename), FnFilter2(buildpath)));
775                 return MPN_INF_TRUNC;   /* filename truncated */
776             }
777         }
778         Trace((stderr, "buildpath now = [%s]\n", FnFilter1(buildpath)));
779         /* could check for existence here, prompt for new name... */
780         return MPN_OK;
781     }
782 
783 /*---------------------------------------------------------------------------
784     INIT:  allocate and initialize buffer space for the file currently being
785     extracted.  If file was renamed with an absolute path, don't prepend the
786     extract-to path.
787   ---------------------------------------------------------------------------*/
788 
789 /* GRR:  for VMS and TOPS-20, add up to 13 to strlen */
790 
791     if (FUNCTION == INIT) {
792         Trace((stderr, "initializing buildpath to "));
793         if ((buildpath = (char *)malloc(strlen(G.filename)+rootlen+1))
794             == (char *)NULL)
795             return MPN_NOMEM;
796         if ((rootlen > 0) && !renamed_fullpath) {
797             strcpy(buildpath, rootpath);
798             end = buildpath + rootlen;
799         } else {
800             *buildpath = '\0';
801             end = buildpath;
802         }
803         Trace((stderr, "[%s]\n", FnFilter1(buildpath)));
804         return MPN_OK;
805     }
806 
807 /*---------------------------------------------------------------------------
808     ROOT:  if appropriate, store the path in rootpath and create it if
809     necessary; else assume it's a zipfile member and return.  This path
810     segment gets used in extracting all members from every zipfile specified
811     on the command line.
812   ---------------------------------------------------------------------------*/
813 
814 #if (!defined(SFX) || defined(SFX_EXDIR))
815     if (FUNCTION == ROOT) {
816         Trace((stderr, "initializing root path to [%s]\n",
817           FnFilter1(pathcomp)));
818         if (pathcomp == (char *)NULL) {
819             rootlen = 0;
820             return MPN_OK;
821         }
822         if (rootlen > 0)        /* rootpath was already set, nothing to do */
823             return MPN_OK;
824         if ((rootlen = strlen(pathcomp)) > 0) {
825             char *tmproot;
826 
827             if ((tmproot = (char *)malloc(rootlen+2)) == (char *)NULL) {
828                 rootlen = 0;
829                 return MPN_NOMEM;
830             }
831             strcpy(tmproot, pathcomp);
832             if ((stat(tmproot, &G.statbuf) ||
833                 !S_ISDIR(G.statbuf.st_mode)))
834             {   /* path does not exist */
835                 if (!G.create_dirs /* || iswild(tmproot) */ ) {
836                     free(tmproot);
837                     rootlen = 0;
838                     /* skip (or treat as stored file) */
839                     return MPN_INF_SKIP;
840                 }
841                 /* create the directory (could add loop here scanning tmproot
842                  * to create more than one level, but why really necessary?) */
843                 if (mkdir(tmproot, 0777) == -1) {
844                     Info(slide, 1, ((char *)slide,
845                       "checkdir:  cannot create extraction directory: %s\n",
846                       FnFilter1(tmproot)));
847                     free(tmproot);
848                     rootlen = 0;
849                     /* path didn't exist, tried to create, and failed: */
850                     /* file exists, or 2+ subdir levels required */
851                     return MPN_ERR_SKIP;
852                 }
853             }
854             if (tmproot[rootlen-1] == '/' || tmproot[rootlen-1] == '_') {
855                 tmproot[--rootlen] = '\0';
856             }
857             tmproot[rootlen++] = '_';
858             tmproot[rootlen] = '\0';
859             if ((rootpath = (char *)realloc(tmproot, rootlen+1)) == NULL) {
860                 free(tmproot);
861                 rootlen = 0;
862                 return MPN_NOMEM;
863             }
864         }
865         Trace((stderr, "rootpath now = [%s]\n", FnFilter1(rootpath)));
866         return MPN_OK;
867     }
868 #endif /* !SFX || SFX_EXDIR */
869 
870 /*---------------------------------------------------------------------------
871     END:  free rootpath, immediately prior to program exit.
872   ---------------------------------------------------------------------------*/
873 
874     if (FUNCTION == END) {
875         Trace((stderr, "freeing rootpath\n"));
876         if (rootlen > 0) {
877             free(rootpath);
878             rootlen = 0;
879         }
880         return MPN_OK;
881     }
882 
883     return MPN_INVALID; /* should never reach */
884 
885 } /* end function checkdir() */
886 
887 
888 static void qfix(__G__ ef_ptr, ef_len)
889     __GDEF
890     uch *ef_ptr;
891     unsigned ef_len;
892 {
893     qdosextra qextra;
894 
895     while (ef_len >= EB_HEADSIZE)
896     {
897         qdosextra   *extra = &qextra;
898         jbextra     *jbp   = (jbextra *)&qextra;
899         unsigned    eb_len = makeword(EB_LEN + ef_ptr);
900 
901         if (eb_len > (ef_len - EB_HEADSIZE)) {
902             /* discovered some extra field inconsistency! */
903             Trace((stderr,
904               "qfix: block length %u > rest ef_size %u\n", eb_len,
905               ef_len - EB_HEADSIZE));
906             break;
907         }
908 
909         /* Must ensure that we don't use ODD addresses here */
910 
911         memcpy(&qextra, ef_ptr, sizeof(qdosextra));
912         switch (extra->shortid) {
913           case SHORTID:
914             if (!strncmp(extra->longid, LONGID, strlen(LONGID)))
915             {
916                 if (eb_len != EXTRALEN)
917                     fputs("warning: invalid length in Qdos field", stderr);
918                 if (extra->header.d_type)
919                 {
920                     fs_heads(fgetchid(G.outfile), (timeout_t)-1,
921                              &extra->header, 14);
922                     G.pInfo->file_attr |= S_IXUSR;
923                 }
924             }
925 
926             if (!strncmp(jbp->longid, JBLONGID, strlen(JBLONGID)))
927             {
928                 if (eb_len != JBEXTRALEN)
929                     fputs("warning: invalid length in QZ field", stderr);
930                 if (jbp->header.d_type)
931                 {
932                     fs_heads(fgetchid(G.outfile), (timeout_t)-1,
933                              &jbp->header, 14);
934                     G.pInfo->file_attr |= S_IXUSR;
935                 }
936             }
937             break;
938 
939           default:
940             Trace((stderr,"qfix: unknown extra field block, ID=%d\n",
941                extra->shortid));
942             break;
943         }
944 
945         /* Skip this extra field block */
946         ef_ptr += (eb_len + EB_HEADSIZE);
947         ef_len -= (eb_len + EB_HEADSIZE);
948     }
949 }
950 
951 
952 #ifdef QDOS
953 #  include <utime.h>
954    long timezone = 0;
955 #endif
956 
957 
958 /****************************/
959 /* Function close_outfile() */
960 /****************************/
961 
close_outfile(__G)962 void close_outfile(__G)
963     __GDEF
964 {
965     union {
966         iztimes t3;             /* mtime, atime, ctime */
967         struct utimbuf t2;      /* modtime, actime */
968     } zt;
969 #ifdef USE_EF_UT_TIME
970     unsigned eb_izux_flg;
971 #endif
972 
973     if (G.extra_field) {
974         qfix(__G__ G.extra_field, G.lrec.extra_field_length);
975     }
976 
977     fclose(G.outfile);
978 
979 /*---------------------------------------------------------------------------
980     Change the file permissions from default ones to those stored in the
981     zipfile.
982   ---------------------------------------------------------------------------*/
983 
984 #ifndef NO_CHMOD
985     if (chmod(G.filename, 0xffff & G.pInfo->file_attr))
986         perror("chmod (file attributes) error");
987 #endif
988 
989 /*---------------------------------------------------------------------------
990     Convert from MSDOS-format local time and date to Unix-format 32-bit GMT
991     time:  adjust base year from 1980 to 1970, do usual conversions from
992     yy/mm/dd hh:mm:ss to elapsed seconds, and account for timezone and day-
993     light savings time differences.  If we have a Unix extra field, however,
994     we're laughing:  both mtime and atime are ours.
995   ---------------------------------------------------------------------------*/
996 
997     /* skip restoring time stamps on user's request */
998     if (uO.D_flag <= 1) {
999 #ifdef USE_EF_UT_TIME
1000         eb_izux_flg = (G.extra_field ? ef_scan_for_izux(G.extra_field,
1001                        G.lrec.extra_field_length, 0,
1002                        G.lrec.last_mod_dos_datetime,
1003 #ifdef IZ_CHECK_TZ
1004                        (G.tz_is_valid ? &(zt.t3) : NULL),
1005 #else
1006                        &(zt.t3),
1007 #endif
1008                        NULL) : 0);
1009         if (eb_izux_flg & EB_UT_FL_MTIME) {
1010             TTrace((stderr,
1011               "\nclose_outfile:  Unix e.f. modif. time = %ld\n",
1012               zt.t3.mtime));
1013         } else {
1014             zt.t3.mtime = dos_to_unix_time(G.lrec.last_mod_dos_datetime);
1015         }
1016         if (eb_izux_flg & EB_UT_FL_ATIME) {
1017             TTrace((stderr,
1018               "close_outfile:  Unix e.f. access time = %ld\n",
1019               zt.t3.atime));
1020         } else {
1021             zt.t3.atime = zt.t3.mtime;
1022             TTrace((stderr,
1023               "\nclose_outfile:  modification/access times = %ld\n",
1024               zt.t3.mtime));
1025         }
1026 #else
1027         zt.t3.atime = zt.t3.mtime
1028           = dos_to_unix_time(G.lrec.last_mod_dos_datetime);
1029 #endif
1030 
1031         /* set the file's access and modification times */
1032         if (utime(G.filename, &(zt.t2))) {
1033             Info(slide, 0x201, ((char *)slide,
1034               "warning:  cannot set the time for %s\n",
1035               FnFilter1(G.filename)));
1036         }
1037     }
1038 
1039 } /* end function close_outfile() */
1040 
1041 
1042 
1043 
1044 #ifdef TIMESTAMP
1045 
1046 /***************************/
1047 /*  Function stamp_file()  */
1048 /***************************/
1049 
stamp_file(fname,modtime)1050 int stamp_file(fname, modtime)
1051     ZCONST char *fname;
1052     time_t modtime;
1053 {
1054     struct utimbuf tp;
1055 
1056     tp.modtime = tp.actime = modtime;
1057     return (utime(fname, &tp));
1058 
1059 } /* end function stamp_file() */
1060 
1061 #endif /* TIMESTAMP */
1062 
1063 
1064 
1065 
1066 #ifndef SFX
1067 
1068 /************************/
1069 /*  Function version()  */
1070 /************************/
1071 
version(__G)1072 void version(__G)
1073     __GDEF
1074 {
1075 
1076     sprintf((char *)slide, LoadFarString(CompiledWith),
1077            "c68", " v4.2x", "SMS/QDOS",
1078             " on ", __DATE__, "","");
1079     (*G.message)((zvoid *)&G, slide, (ulg)strlen((char *)slide), 0);
1080 
1081 } /* end function version() */
1082 
1083 #endif /* !SFX */
1084 #endif /* !FUNZIP */
1085 
1086 #if CRYPT
1087 
1088 char *getp(__G__ m, p, n)
1089     __GDEF
1090     const char *m;              /* prompt for password */
1091     char *p;                    /* return value: line input */
1092     int n;                      /* bytes available in p[] */
1093 {
1094     int c;                      /* one-byte buffer for read() to use */
1095     int i;                      /* number of characters input */
1096     char *w;                    /* warning on retry */
1097 
1098     /* get password */
1099     w = "";
1100     sd_cure(getchid(0), -1);    /* enable cursor */
1101     do {
1102         fputs(w, stderr);       /* warning if back again */
1103         fputs(m, stderr);       /* display prompt and flush */
1104         fflush(stderr);
1105         i = 0;
1106         do {
1107             c = getch();
1108             if (c == 0xc2) {
1109                 if (i > 0) {
1110                     i--; /* the `del' keys works */
1111                     fputs("\b \b", stderr);
1112                 }
1113             }
1114             else if (i < n) {
1115                 p[i++] = c;     /* truncate past n */
1116                 if(c != '\n') putc('*', stderr);
1117             }
1118         } while (c != '\n');
1119 
1120         putc('\n', stderr);  fflush(stderr);
1121         w = "(line too long--try again)\n";
1122     } while (p[i-1] != '\n');
1123 
1124     p[i-1] = 0;                 /* terminate at newline */
1125     sd_curs(getchid(0), -1);    /* suppress cursor */
1126     return p;                   /* return pointer to password */
1127 
1128 } /* end function getp() */
1129 
1130 #endif /* CRYPT */
1131