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