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 45char _prog_name[] = "UnZip"; 46#else 47char _prog_name[] = "??Special Flag for unzipsfx hack ??"; 48#endif 49/* sorrid hack at request of GRR follows; hope the compiler stays kind to us */ 50char _version[] = {UZ_MAJORVER+'0','.',UZ_MINORVER+'0',UZ_PATCHLEVEL+'0'}; 51char _extra[] = " " UZ_BETALEVEL; 52char _copyright[] = "(c) Info-ZIP Group"; 53char * _endmsg = NULL; 54long _stack = 16*1024; /* huge stack (for qdos) */ 55 56extern void consetup_title(chanid_t,struct WINDOWDEF *); 57void (*_consetup)(chanid_t,struct WINDOWDEF *) = consetup_title; 58 59struct WINDOWDEF _condetails = 60{ 61 2, 62 1, 63 0, 64 7, 65 500, 66 220, 67 2, 68 30 69}; 70 71 72static 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 85int 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 110static int created_dir; /* used in mapname(), checkdir() */ 111static int renamed_fullpath; /* ditto */ 112 113char *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 124void 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 144int 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 163int 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 177int 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 201char *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 231char *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 351int 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/************************/ 477int 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 631int 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 888static 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 962void 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 1050int 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 1072void 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 1088char *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