1/* 2 Copyright (c) 1990-2005 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, all these files are missing, the Info-ZIP license 7 also may be found at: ftp://ftp.info-zip.org/pub/infozip/license.html 8*/ 9/*--------------------------------------------------------------------------- 10 11 theos.c 12 13 Theos-specific routines for use with Info-ZIP's UnZip 5.41 and later. 14 15 Contains: do_wild() <-- generic enough to put in fileio.c? 16 mapattr() 17 mapname() 18 checkdir() 19 close_outfile() 20 defer_dir_attribs() 21 set_direc_attribs() 22 stamp_file() 23 version() 24 25 ---------------------------------------------------------------------------*/ 26 27 28#define UNZIP_INTERNAL 29#include "unzip.h" 30#include <direct.h> 31#include <sc.h> 32#include <fdb.h> 33#include <nuc.h> 34#include <peek.h> 35 36/* standard function doesn't work with a trailing / */ 37#define opendir(a) _opendir(a) 38extern DIR* _opendir(const char* dirpath); 39 40#ifdef SET_DIR_ATTRIB 41typedef struct uxdirattr { /* struct for holding unix style directory */ 42 struct uxdirattr *next; /* info until can be sorted and set at end */ 43 char *fn; /* filename of directory */ 44 union { 45 iztimes t3; /* mtime, atime, ctime */ 46 ztimbuf t2; /* modtime, actime */ 47 } u; 48 unsigned perms; /* same as min_info.file_attr */ 49 char fnbuf[1]; /* buffer stub for directory name */ 50} uxdirattr; 51#define UxAtt(d) ((uxdirattr *)d) /* typecast shortcut */ 52#endif /* SET_DIR_ATTRIB */ 53 54#ifdef ACORN_FTYPE_NFS 55/* Acorn bits for NFS filetyping */ 56typedef struct { 57 uch ID[2]; 58 uch size[2]; 59 uch ID_2[4]; 60 uch loadaddr[4]; 61 uch execaddr[4]; 62 uch attr[4]; 63} RO_extra_block; 64 65#endif /* ACORN_FTYPE_NFS */ 66 67static int created_dir; /* used in mapname(), checkdir() */ 68static int renamed_fullpath; /* ditto */ 69 70#define _FDB_SHARED_EXECUTE_PROTECT _FDB_MODIFIED 71 72uch _um2tm_(ush mask); 73 74int chmodv2_3(const char *fname, short mask) 75{ 76 return _filechange(fname,'p',(size_t) _um2tm_(mask)|0x80); 77} 78 79int chlen(char *fname, unsigned short reclen, unsigned short keylen) 80{ 81 size_t a = reclen + ((size_t) keylen << 16); 82 return _filechange(fname,'ma',(size_t) &a); 83} 84 85#define chgrow(a,b) ((int) _filechange(a,'g',(size_t)(b))) 86#define chorg(a,b) ((int) _filechange(a,'m',(size_t)(b))) 87 88#ifndef SFX 89 90/*************************/ 91/* Function dateformat() */ 92/*************************/ 93 94int dateformat() 95{ 96/*--------------------------------------------------------------------------- 97 For those operating systems that support it, this function returns a 98 value that tells how national convention says that numeric dates are 99 displayed. Return values are DF_YMD, DF_DMY and DF_MDY (the meanings 100 should be fairly obvious). 101 ---------------------------------------------------------------------------*/ 102 103 switch (peeknuc(&NUC->dateopt) & (aform|eform|iform)) { 104 case aform: return DF_MDY; 105 case eform: return DF_DMY; 106 } 107 return DF_YMD; 108} 109 110 111/* usual THEOS match function for filenames */ 112 113/* match from Phase One Systems */ 114 115/* Returns non-zero if string matches the literal mask */ 116int match(string, pattern, ignore_case __WDL) 117 ZCONST char *string; 118 ZCONST char *pattern; 119 int ignore_case; /* unused in this variant of match()! */ 120 __WDLDEF 121{ 122 int matched, k; 123 124 if (!(*pattern)) 125 return 1; 126 for(;;) { 127 if ( (!(*string)) && (!(*pattern)) ) 128 return(1); 129 else if ( !(*pattern) ) 130 return(0); 131 else if (*pattern == '*') { 132 if (!*(pattern+1)) 133 return(1); 134 k=0; 135 do { 136 matched = match(string+k, pattern+1, ignore_case __WDL); 137 k++; 138 } while ( (!matched) && *(string+k)); 139 return(matched); 140 } else if (*pattern == '@') { 141 if (!((*string >= 'a' && *string <= 'z') 142 ||(*string >= 'A' && *string <= 'Z'))) 143 return(0); 144 } else if (*pattern == '#') { 145 if (*string < '0' || *string > '9') 146 return(0); 147 } else if (*pattern != '?') { 148 if (toupper(*string) != toupper(*pattern)) 149 return(0); 150 151 } 152 string++; pattern++; 153 } 154} /* end function match() */ 155 156 157 158/**********************/ 159/* Function do_wild() */ /* for porting: dir separator; match(ignore_case) */ 160/**********************/ 161 162char *do_wild(__G__ wildspec) 163 __GDEF 164 ZCONST char *wildspec; /* only used first time on a given dir */ 165{ 166 static DIR *wild_dir = (DIR *)NULL; 167 static ZCONST char *wildname; 168 static char *dirname, matchname[FILNAMSIZ]; 169 static int notfirstcall=FALSE, have_dirname, dirnamelen; 170 struct dirent *file; 171 172 /* Even when we're just returning wildspec, we *always* do so in 173 * matchname[]--calling routine is allowed to append four characters 174 * to the returned string, and wildspec may be a pointer to argv[]. 175 */ 176 if (!notfirstcall) { /* first call: must initialize everything */ 177 notfirstcall = TRUE; 178 179 if (!iswild(wildspec)) { 180 strcpy(matchname, wildspec); 181 have_dirname = FALSE; 182 wild_dir = NULL; 183 return matchname; 184 } 185 186 /* break the wildspec into a directory part and a wildcard filename */ 187 if ((wildname = (ZCONST char *)strrchr(wildspec, '/')) == 188 (ZCONST char *)NULL) 189 { 190 dirname = "."; 191 dirnamelen = 1; 192 have_dirname = FALSE; 193 wildname = wildspec; 194 } else { 195 ++wildname; /* point at character after '/' */ 196 dirnamelen = wildname - wildspec; 197 if ((dirname = (char *)malloc(dirnamelen+1)) == (char *)NULL) { 198 Info(slide, 0x201, ((char *)slide, 199 "warning: cannot allocate wildcard buffers\n")); 200 strncpy(matchname, wildspec, FILNAMSIZ); 201 matchname[FILNAMSIZ-1] = '\0'; 202 return matchname; /* but maybe filespec was not a wildcard */ 203 } 204 strncpy(dirname, wildspec, dirnamelen); 205 dirname[dirnamelen] = '\0'; /* terminate for strcpy below */ 206 have_dirname = TRUE; 207 } 208 209 if ((wild_dir = opendir(dirname)) != (DIR *)NULL) { 210 while ((file = readdir(wild_dir)) != (struct dirent *)NULL) { 211 Trace((stderr, "do_wild: readdir returns %s\n", 212 FnFilter1(file->d_name))); 213 if (match(file->d_name, wildname, 1 WISEP)) { /*1=ignore case*/ 214 Trace((stderr, "do_wild: match() succeeds\n")); 215 if (have_dirname) { 216 strcpy(matchname, dirname); 217 strcpy(matchname+dirnamelen, file->d_name); 218 } else 219 strcpy(matchname, file->d_name); 220 return matchname; 221 } 222 } 223 /* if we get to here directory is exhausted, so close it */ 224 closedir(wild_dir); 225 wild_dir = (DIR *)NULL; 226 } 227 228 /* return the raw wildspec in case that works (e.g., directory not 229 * searchable, but filespec was not wild and file is readable) */ 230 strncpy(matchname, wildspec, FILNAMSIZ); 231 matchname[FILNAMSIZ-1] = '\0'; 232 return matchname; 233 } 234 235 /* last time through, might have failed opendir but returned raw wildspec */ 236 if (wild_dir == (DIR *)NULL) { 237 notfirstcall = FALSE; /* nothing left to try--reset for new wildspec */ 238 if (have_dirname) 239 free(dirname); 240 return (char *)NULL; 241 } 242 243 /* If we've gotten this far, we've read and matched at least one entry 244 * successfully (in a previous call), so dirname has been copied into 245 * matchname already. 246 */ 247 while ((file = readdir(wild_dir)) != (struct dirent *)NULL) { 248 Trace((stderr, "do_wild: readdir returns %s\n", 249 FnFilter1(file->d_name))); 250 if (file->d_name[0] == '.' && wildname[0] != '.') 251 continue; /* Unix: '*' and '?' do not match leading dot */ 252 if (match(file->d_name, wildname, 1 WISEP)) { /* 1 == ignore case */ 253 Trace((stderr, "do_wild: match() succeeds\n")); 254 if (have_dirname) { 255 /* strcpy(matchname, dirname); */ 256 strcpy(matchname+dirnamelen, file->d_name); 257 } else 258 strcpy(matchname, file->d_name); 259 return matchname; 260 } 261 } 262 263 closedir(wild_dir); /* have read at least one entry; nothing left */ 264 wild_dir = (DIR *)NULL; 265 notfirstcall = FALSE; /* reset for new wildspec */ 266 if (have_dirname) 267 free(dirname); 268 return (char *)NULL; 269 270} /* end function do_wild() */ 271 272#endif /* !SFX */ 273 274 275 276 277 278/**********************/ 279/* Function mapattr() */ 280/**********************/ 281 282int mapattr(__G) 283 __GDEF 284{ 285 ulg tmp = G.crec.external_file_attributes; 286 287 G.pInfo->file_attr = 0; 288 /* initialized to 0 for check in "default" branch below... */ 289 290 switch (G.pInfo->hostnum) { 291 case AMIGA_: 292 tmp = (unsigned)(tmp>>17 & 7); /* Amiga RWE bits */ 293 G.pInfo->file_attr = (unsigned)(tmp<<6 | tmp<<3 | tmp); 294 break; 295 case UNIX_: 296 case VMS_: 297 case ACORN_: 298 case ATARI_: 299 case ATHEOS_: 300 case BEOS_: 301 case QDOS_: 302 case TANDEM_: 303 case THEOS_: 304 G.pInfo->file_attr = (unsigned)(tmp >> 16); 305 if (G.pInfo->file_attr != 0 || !G.extra_field) { 306 return 0; 307 } else { 308 /* Some (non-Info-ZIP) implementations of Zip for Unix and 309 * VMS (and probably others ??) leave 0 in the upper 16-bit 310 * part of the external_file_attributes field. Instead, they 311 * store file permission attributes in some extra field. 312 * As a work-around, we search for the presence of one of 313 * these extra fields and fall back to the MSDOS compatible 314 * part of external_file_attributes if one of the known 315 * e.f. types has been detected. 316 * Later, we might implement extraction of the permission 317 * bits from the VMS extra field. But for now, the work-around 318 * should be sufficient to provide "readable" extracted files. 319 * (For ASI Unix e.f., an experimental remap from the e.f. 320 * mode value IS already provided!) 321 */ 322 ush ebID; 323 unsigned ebLen; 324 uch *ef = G.extra_field; 325 unsigned ef_len = G.crec.extra_field_length; 326 int r = FALSE; 327 328 while (!r && ef_len >= EB_HEADSIZE) { 329 ebID = makeword(ef); 330 ebLen = (unsigned)makeword(ef+EB_LEN); 331 if (ebLen > (ef_len - EB_HEADSIZE)) 332 /* discoverd some e.f. inconsistency! */ 333 break; 334 switch (ebID) { 335 case EF_ASIUNIX: 336 if (ebLen >= (EB_ASI_MODE+2)) { 337 G.pInfo->file_attr = 338 (unsigned)makeword(ef+(EB_HEADSIZE+EB_ASI_MODE)); 339 /* force stop of loop: */ 340 ef_len = (ebLen + EB_HEADSIZE); 341 break; 342 } 343 /* else: fall through! */ 344 case EF_PKVMS: 345 /* "found nondecypherable e.f. with perm. attr" */ 346 r = TRUE; 347 default: 348 break; 349 } 350 ef_len -= (ebLen + EB_HEADSIZE); 351 ef += (ebLen + EB_HEADSIZE); 352 } 353 if (!r) 354 return 0; 355 } 356 /* fall through! */ 357 /* all remaining cases: expand MSDOS read-only bit into write perms */ 358 case FS_FAT_: 359 /* PKWARE's PKZip for Unix marks entries as FS_FAT_, but stores the 360 * Unix attributes in the upper 16 bits of the external attributes 361 * field, just like Info-ZIP's Zip for Unix. We try to use that 362 * value, after a check for consistency with the MSDOS attribute 363 * bits (see below). 364 */ 365 G.pInfo->file_attr = (unsigned)(tmp >> 16); 366 /* fall through! */ 367 case FS_HPFS_: 368 case FS_NTFS_: 369 case MAC_: 370 case TOPS20_: 371 default: 372 /* Ensure that DOS subdir bit is set when the entry's name ends 373 * in a '/'. Some third-party Zip programs fail to set the subdir 374 * bit for directory entries. 375 */ 376 if ((tmp & 0x10) == 0) { 377 extent fnlen = strlen(G.filename); 378 if (fnlen > 0 && G.filename[fnlen-1] == '/') 379 tmp |= 0x10; 380 } 381 /* read-only bit --> write perms; subdir bit --> dir exec bit */ 382 tmp = !(tmp & 1) << 1 | (tmp & 0x10) >> 4; 383 if ((G.pInfo->file_attr & 0700) == (unsigned)(0400 | tmp<<6)) 384 /* keep previous G.pInfo->file_attr setting, when its "owner" 385 * part appears to be consistent with DOS attribute flags! 386 */ 387 return 0; 388 G.pInfo->file_attr = (unsigned)(0444 | tmp<<6 | tmp<<3 | tmp); 389 break; 390 } /* end switch (host-OS-created-by) */ 391 392 /* for originating systems with no concept of "group," "other," "system": */ 393 G.pInfo->file_attr &= ~tmp; 394 395 return 0; 396 397} /* end function mapattr() */ 398 399 400 401/* portabilibity functions to ensure access to port 2.0 and new ports */ 402 403int isv2_3() 404{ 405 return G.extra_field[0] == 'T' && G.extra_field[1] == 'h'; 406} 407 408int isv2_3lib() 409{ 410 return isv2_3() && S_ISLIB((G.pInfo->file_attr >> 8) & 0xFF); 411} 412 413int isv2_3dir() 414{ 415 return isv2_3() && S_ISDIR((G.pInfo->file_attr >> 8) & 0xFF); 416} 417 418#ifdef OLD_THEOS_EXTRA 419#define S_IFMT_ 0xf800 /* type of file */ 420#define S_IFLIB_ 0x8000 /* library */ 421#define S_IFDIR_ 0x4000 /* directory */ 422 423#define S_ISLIB_(a) (((a) & 0xff) == _FDB_STAT_LIBRARY) 424#define S_ISDIR_(a) (((a) & 0xff) == _FDB_STAT_DIRECTORY) 425 426struct extra_block 427{ 428 ush signature; 429 ush size; 430 ush flags; 431 ulg filesize; 432 ush reclen; 433 ush keylen; 434 uch filegrow; 435 uch reserved[3]; 436}; 437 438struct extra_block *v2_0extra() 439{ 440 return (struct extra_block *) G.extra_field; 441} 442 443int isv2_0() 444{ 445 return ((struct extra_block *) G.extra_field)->signature == 0x4854; 446} 447 448int isv2_0lib() 449{ 450 return isv2_0() && S_ISLIB_(G.pInfo->file_attr); 451} 452 453int isv2_0dir() 454{ 455 return isv2_0() && S_ISDIR_(G.pInfo->file_attr); 456} 457 458#define islib() (isv2_0lib() || isv2_3lib()) 459#define isdir() (isv2_0dir() || isv2_3dir()) 460#define chmodv2_0(a,b) ((int) _sc_168(a,'p',(size_t)(b)|0x80)) 461#undef chmod 462#define chmod(a,b) (isv2_0() && chmodv2_0(a,b))||(isv2_3() && chmodv2_3(a,b)) 463#else 464#define islib() isv2_3lib() 465#define isdir() isv2_3dir() 466#undef chmod 467#define chmod(a,b) chmodv2_0(a,b) 468#endif 469 470/************************/ 471/* Function mapname() */ 472/************************/ 473 474int mapname(__G__ renamed) 475 __GDEF 476 int renamed; 477/* 478 * returns: 479 * MPN_OK - no problem detected 480 * MPN_INF_TRUNC - caution (truncated filename) 481 * MPN_INF_SKIP - info "skip entry" (dir doesn't exist) 482 * MPN_ERR_SKIP - error -> skip entry 483 * MPN_ERR_TOOLONG - error -> path is too long 484 * MPN_NOMEM - error (memory allocation failed) -> skip entry 485 * [also MPN_VOL_LABEL, MPN_CREATED_DIR] 486 */ 487{ 488 char pathcomp[FILNAMSIZ]; /* path-component buffer */ 489 char *pp, *cp=(char *)NULL; /* character pointers */ 490 char *lastsemi=(char *)NULL; /* pointer to last semi-colon in pathcomp */ 491#ifdef ACORN_FTYPE_NFS 492 char *lastcomma=(char *)NULL; /* pointer to last comma in pathcomp */ 493 RO_extra_block *ef_spark; /* pointer Acorn FTYPE ef block */ 494#endif 495 int killed_ddot = FALSE; /* is set when skipping "../" pathcomp */ 496 int error = MPN_OK; 497 register unsigned workch; /* hold the character being tested */ 498 499 500/*--------------------------------------------------------------------------- 501 Initialize various pointers and counters and stuff. 502 ---------------------------------------------------------------------------*/ 503 504 if (G.pInfo->vollabel) 505 return MPN_VOL_LABEL; /* can't set disk volume labels in Unix */ 506 507 /* can create path as long as not just freshening, or if user told us */ 508 G.create_dirs = (!uO.fflag || renamed); 509 510 created_dir = FALSE; /* not yet */ 511 512 /* user gave full pathname: don't prepend rootpath */ 513 renamed_fullpath = (renamed && (*G.filename == '/')); 514 515 if (checkdir(__G__ (char *)NULL, INIT) == MPN_NOMEM) 516 return MPN_NOMEM; /* initialize path buffer, unless no memory */ 517 518 *pathcomp = '\0'; /* initialize translation buffer */ 519 pp = pathcomp; /* point to translation buffer */ 520 if (uO.jflag) /* junking directories */ 521 cp = (char *)strrchr(G.filename, '/'); 522 if (cp == (char *)NULL) /* no '/' or not junking dirs */ 523 cp = G.filename; /* point to internal zipfile-member pathname */ 524 else 525 ++cp; /* point to start of last component of path */ 526 527/*--------------------------------------------------------------------------- 528 Begin main loop through characters in filename. 529 ---------------------------------------------------------------------------*/ 530 531 while ((workch = (uch)*cp++) != 0) { 532 533 switch (workch) { 534 case '/': /* can assume -j flag not given */ 535 *pp = '\0'; 536 if (strcmp(pathcomp, ".") == 0) { 537 /* don't bother appending "./" to the path */ 538 *pathcomp = '\0'; 539 } else if (!uO.ddotflag && strcmp(pathcomp, "..") == 0) { 540 /* "../" dir traversal detected, skip over it */ 541 *pathcomp = '\0'; 542 killed_ddot = TRUE; /* set "show message" flag */ 543 } 544 /* when path component is not empty, append it now */ 545 if (*pathcomp != '\0' && 546 ((error = checkdir(__G__ pathcomp, APPEND_DIR)) 547 & MPN_MASK) > MPN_INF_TRUNC) 548 return error; 549 pp = pathcomp; /* reset conversion buffer for next piece */ 550 lastsemi = (char *)NULL; /* leave direct. semi-colons alone */ 551 break; 552 553#ifdef ACORN_FTYPE_NFS 554 case ',': /* NFS filetype extension */ 555 lastcomma = pp; 556 *pp++ = ','; /* keep for now; may need to remove */ 557 break; /* later, if requested */ 558#endif 559 560 default: 561 if (isfnsym(workch) || workch == '.') 562 *pp++ = (char)workch; 563 else 564 *pp++ = '_'; 565 } /* end switch */ 566 567 } /* end while loop */ 568 569 /* Show warning when stripping insecure "parent dir" path components */ 570 if (killed_ddot && QCOND2) { 571 Info(slide, 0, ((char *)slide, 572 "warning: skipped \"../\" path component(s) in %s\n", 573 FnFilter1(G.filename))); 574 if (!(error & ~MPN_MASK)) 575 error = (error & MPN_MASK) | PK_WARN; 576 } 577 578/*--------------------------------------------------------------------------- 579 Report if directory was created (and no file to create: filename ended 580 in '/'), check name to be sure it exists, and combine path and name be- 581 fore exiting. 582 ---------------------------------------------------------------------------*/ 583 584 if (G.filename[strlen(G.filename) - 1] == '/') { 585 checkdir(__G__ G.filename, GETPATH); 586 if (islib() && G.filename[strlen(G.filename) - 1] == '/') 587 G.filename[strlen(G.filename) - 1] = '\0'; 588 589 if (created_dir) { 590 if (QCOND2) { 591 Info(slide, 0, ((char *)slide, " creating: %s\n", 592 FnFilter1(G.filename))); 593 } 594 /* set dir time (note trailing '/') */ 595 return (error & ~MPN_MASK) | MPN_CREATED_DIR; 596 } 597 /* dir existed already; don't look for data to extract */ 598 return (error & ~MPN_MASK) | MPN_INF_SKIP; 599 } 600 601 *pp = '\0'; /* done with pathcomp: terminate it */ 602 603#ifdef ACORN_FTYPE_NFS 604 /* translate Acorn filetype information if asked to do so */ 605 if (uO.acorn_nfs_ext && 606 (ef_spark = (RO_extra_block *) 607 getRISCOSexfield(G.extra_field, G.lrec.extra_field_length)) 608 != (RO_extra_block *)NULL) 609 { 610 /* file *must* have a RISC OS extra field */ 611 long ft = (long)makelong(ef_spark->loadaddr); 612 /*32-bit*/ 613 if (lastcomma) { 614 pp = lastcomma + 1; 615 while (isxdigit((uch)(*pp))) ++pp; 616 if (pp == lastcomma+4 && *pp == '\0') *lastcomma='\0'; /* nuke */ 617 } 618 if ((ft & 1<<31)==0) ft=0x000FFD00; 619 sprintf(pathcomp+strlen(pathcomp), ",%03x", (int)(ft>>8) & 0xFFF); 620 } 621#endif /* ACORN_FTYPE_NFS */ 622 623 if (*pathcomp == '\0') { 624 Info(slide, 1, ((char *)slide, "mapname: conversion of %s failed\n", 625 FnFilter1(G.filename))); 626 return (error & ~MPN_MASK) | MPN_ERR_SKIP; 627 } 628 629 checkdir(__G__ pathcomp, APPEND_NAME); /* returns 1 if truncated: care? */ 630 checkdir(__G__ G.filename, GETPATH); 631 632 return error; 633 634} /* end function mapname() */ 635 636 637 638 639#if 0 /*========== NOTES ==========*/ 640 641 extract-to dir: a:path/ 642 buildpath: path1/path2/ ... (NULL-terminated) 643 pathcomp: filename 644 645 mapname(): 646 loop over chars in zipfile member name 647 checkdir(path component, COMPONENT | CREATEDIR) --> map as required? 648 (d:/tmp/unzip/) (disk:[tmp.unzip.) 649 (d:/tmp/unzip/jj/) (disk:[tmp.unzip.jj.) 650 (d:/tmp/unzip/jj/temp/) (disk:[tmp.unzip.jj.temp.) 651 finally add filename itself and check for existence? (could use with rename) 652 (d:/tmp/unzip/jj/temp/msg.outdir) (disk:[tmp.unzip.jj.temp]msg.outdir) 653 checkdir(name, GETPATH) --> copy path to name and free space 654 655#endif /* 0 */ 656 657 658 659 660/***********************/ 661/* Function checkdir() */ 662/***********************/ 663 664int checkdir(__G__ pathcomp, flag) 665 __GDEF 666 char *pathcomp; 667 int flag; 668/* 669 * returns: 670 * MPN_OK - no problem detected 671 * MPN_INF_TRUNC - (on APPEND_NAME) truncated filename 672 * MPN_INF_SKIP - path doesn't exist, not allowed to create 673 * MPN_ERR_SKIP - path doesn't exist, tried to create and failed; or path 674 * exists and is not a directory, but is supposed to be 675 * MPN_ERR_TOOLONG - path is too long 676 * MPN_NOMEM - can't allocate memory for filename buffers 677 */ 678{ 679 static int rootlen = 0; /* length of rootpath */ 680 static char *rootpath; /* user's "extract-to" directory */ 681 static char rootdisk[3]; /* user's "extract-to" disk */ 682 static char *buildpath; /* full path (so far) to extracted file */ 683 static char *end; /* pointer to end of buildpath ('\0') */ 684 685# define FN_MASK 7 686# define FUNCTION (flag & FN_MASK) 687 688 689 690/*--------------------------------------------------------------------------- 691 APPEND_DIR: append the path component to the path being built and check 692 for its existence. If doesn't exist and we are creating directories, do 693 so for this one; else signal success or error as appropriate. 694 ---------------------------------------------------------------------------*/ 695 696 if (FUNCTION == APPEND_DIR) { 697 int too_long = FALSE; 698#ifdef SHORT_NAMES 699 char *old_end = end; 700#endif 701 702 Trace((stderr, "appending dir segment [%s]\n", FnFilter1(pathcomp))); 703 while ((*end = *pathcomp++) != '\0') 704 ++end; 705#ifdef SHORT_NAMES /* path components restricted to 14 chars, typically */ 706 if ((end-old_end) > FILENAME_MAX) /* GRR: proper constant? */ 707 *(end = old_end + FILENAME_MAX) = '\0'; 708#endif 709 710 /* GRR: could do better check, see if overrunning buffer as we go: 711 * check end-buildpath after each append, set warning variable if 712 * within 20 of FILNAMSIZ; then if var set, do careful check when 713 * appending. Clear variable when begin new path. */ 714 715 if ((end-buildpath) > FILNAMSIZ-3) /* need '/', one-char name, '\0' */ 716 too_long = TRUE; /* check if extracting directory? */ 717 Trace((stderr, "appending disk segment [%s]\n", FnFilter1(rootdisk))); 718 strcat(buildpath, rootdisk); 719 if (stat(buildpath, &G.statbuf)) { /* path doesn't exist */ 720 if (!G.create_dirs) { /* told not to create (freshening) */ 721 free(buildpath); 722 return MPN_INF_SKIP; /* path doesn't exist: nothing to do */ 723 } 724 if (too_long) { 725 Info(slide, 1, ((char *)slide, 726 "checkdir error: path too long: %s\n", 727 FnFilter1(buildpath))); 728 free(buildpath); 729 /* no room for filenames: fatal */ 730 return MPN_ERR_TOOLONG; 731 } 732 if (islib()) { 733 ulg size; 734#ifdef OLD_THEOS_EXTRA 735 if (isv2_0lib()) 736 size = v2_0extra()->filesize; 737 else 738#endif 739 { 740 size = (ulg) G.extra_field[5] | 741 ((ulg) G.extra_field[6] << 8) | 742 ((ulg) G.extra_field[7] << 16) | 743 ((ulg) G.extra_field[8] << 24); 744 } 745 if (makelib(buildpath, size / 64)) { 746 Info(slide, 1, ((char*)slide, 747 "checkdir error: can't create library %s\n\ 748 unable to process %s.\n", 749 FnFilter2(buildpath), FnFilter1(G.filename)); 750 free(buildpath); 751 /* path didn't exist, tried to create, failed */ 752 return MPN_ERR_SKIP; 753 } 754 } else if (mkdir(buildpath) == -1) { /* create the directory */ 755 Info(slide, 1, ((char *)slide, 756 "checkdir error: cannot create %s\n\ 757 unable to process %s.\n", 758 FnFilter2(buildpath), FnFilter1(G.filename)); 759 free(buildpath); 760 /* path didn't exist, tried to create, failed */ 761 return MPN_ERR_SKIP; 762 } 763 created_dir = TRUE; 764 } else if (!S_ISDIR(G.statbuf.st_mode) && isdir()) { 765 Info(slide, 1, ((char *)slide, 766 "checkdir error: %s exists but is not directory\n\ 767 unable to process %s.\n", 768 FnFilter2(buildpath), FnFilter1(G.filename)); 769 free(buildpath); 770 /* path existed but wasn't dir */ 771 return MPN_ERR_SKIP; 772 } else if (!S_ISLIB(G.statbuf.st_mode) && islib()) { 773 Info(slide, 1, ((char *)slide, 774 "checkdir error: %s exists but is not library\n\ 775 unable to process %s.\n", 776 FnFilter2(buildpath), FnFilter1(G.filename)); 777 free(buildpath); 778 /* path existed but wasn't lib */ 779 return MPN_ERR_SKIP; 780 } 781 if (too_long) { 782 Info(slide, 1, ((char *)slide, 783 "checkdir error: path too long: %s\n", FnFilter1(buildpath))); 784 free(buildpath); 785 /* no room for filenames: fatal */ 786 return MPN_ERR_TOOLONG; 787 } 788 *end++ = '/'; 789 *end = '\0'; 790 Trace((stderr, "buildpath now = [%s]\n", FnFilter1(buildpath))); 791 return MPN_OK; 792 793 } /* end if (FUNCTION == APPEND_DIR) */ 794 795/*--------------------------------------------------------------------------- 796 GETPATH: copy full path to the string pointed at by pathcomp, and free 797 buildpath. 798 ---------------------------------------------------------------------------*/ 799 800 if (FUNCTION == GETPATH) { 801 strcpy(pathcomp, buildpath); 802 Trace((stderr, "getting and freeing path [%s]\n", 803 FnFilter1(pathcomp))); 804 free(buildpath); 805 buildpath = end = (char *)NULL; 806 return MPN_OK; 807 } 808 809/*--------------------------------------------------------------------------- 810 APPEND_NAME: assume the path component is the filename; append it and 811 return without checking for existence. 812 ---------------------------------------------------------------------------*/ 813 814 if (FUNCTION == APPEND_NAME) { 815#ifdef SHORT_NAMES 816 char *old_end = end; 817#endif 818 819 Trace((stderr, "appending filename [%s]\n", FnFilter1(pathcomp))); 820 while ((*end = *pathcomp++) != '\0') { 821 ++end; 822#ifdef SHORT_NAMES /* truncate name at 14 characters, typically */ 823 if ((end-old_end) > FILENAME_MAX) /* GRR: proper constant? */ 824 *(end = old_end + FILENAME_MAX) = '\0'; 825#endif 826 if ((end-buildpath) >= FILNAMSIZ) { 827 *--end = '\0'; 828 Info(slide, 0x201, ((char *)slide, 829 "checkdir warning: path too long; truncating\n\ 830 %s\n -> %s\n", 831 FnFilter1(G.filename), FnFilter2(buildpath))); 832 return MPN_INF_TRUNC; /* filename truncated */ 833 } 834 } 835 strcat(buildpath, rootdisk); 836 Trace((stderr, "buildpath now = [%s]\n", FnFilter1(buildpath))); 837 /* could check for existence here, prompt for new name... */ 838 return MPN_OK; 839 } 840 841/*--------------------------------------------------------------------------- 842 INIT: allocate and initialize buffer space for the file currently being 843 extracted. If file was renamed with an absolute path, don't prepend the 844 extract-to path. 845 ---------------------------------------------------------------------------*/ 846 847/* GRR: for VMS and TOPS-20, add up to 13 to strlen */ 848 849 if (FUNCTION == INIT) { 850 Trace((stderr, "initializing buildpath to ")); 851#ifdef ACORN_FTYPE_NFS 852 if ((buildpath = (char *)malloc(strlen(G.filename)+rootlen+ 853 (uO.acorn_nfs_ext ? 5 : 1))) 854#else 855 if ((buildpath = (char *)malloc(strlen(G.filename)+rootlen+1)) 856#endif 857 == (char *)NULL) 858 return MPN_NOMEM; 859 if ((rootlen > 0) && !renamed_fullpath) { 860 strcpy(buildpath, rootpath); 861 end = buildpath + rootlen; 862 } else { 863 *buildpath = '\0'; 864 end = buildpath; 865 } 866 Trace((stderr, "[%s]\n", FnFilter1(buildpath))); 867 return MPN_OK; 868 } 869 870/*--------------------------------------------------------------------------- 871 ROOT: if appropriate, store the path in rootpath and create it if 872 necessary; else assume it's a zipfile member and return. This path 873 segment gets used in extracting all members from every zipfile specified 874 on the command line. 875 ---------------------------------------------------------------------------*/ 876 877#if (!defined(SFX) || defined(SFX_EXDIR)) 878 if (FUNCTION == ROOT) { 879 if (pathcomp == (char *)NULL) { 880 rootlen = 0; 881 return MPN_OK; 882 } 883 if (rootlen > 0) /* rootpath was already set, nothing to do */ 884 return MPN_OK; 885 if ((rootlen = strlen(pathcomp)) > 0) { 886 int prepend_slash = 0; 887 char *tmproot, *p; 888 889 if (*pathcomp == ':') { 890 preprend_slash = 1; 891 rootlen++; 892 } 893 if ((tmproot = (char *)malloc(rootlen+2)) == (char *)NULL) { 894 rootlen = 0; 895 return MPN_NOMEM; 896 } 897 if (prepend_slash) 898 strcpy(tmproot, "/"); 899 else 900 *tmproot = '\0'; 901 strcat(tmproot, pathcomp); 902 Trace((stderr, "initializing root path to [%s]\n", 903 FnFilter1(tmproot))); 904 if (tmproot[rootlen-1] == '/') { 905 tmproot[--rootlen] = '\0'; 906 } 907 if (rootlen > 0 && (SSTAT(tmproot, &G.statbuf) || 908 !S_ISDIR(G.statbuf.st_mode))) 909 { /* path does not exist */ 910 if (!G.create_dirs /* || iswild(tmproot) */ ) { 911 free(tmproot); 912 rootlen = 0; 913 /* skip (or treat as stored file) */ 914 return MPN_INF_SKIP; 915 } 916 /* create the directory (could add loop here scanning tmproot 917 * to create more than one level, but why really necessary?) */ 918 if (mkdir(tmproot) == -1) { 919 Info(slide, 1, ((char *)slide, 920 "checkdir: cannot create extraction directory: %s\n", 921 FnFilter1(tmproot))); 922 free(tmproot); 923 rootlen = 0; 924 /* path didn't exist, tried to create, and failed: */ 925 /* file exists, or 2+ subdir levels required */ 926 return MPN_ERR_SKIP; 927 } 928 } 929 /* split rootpath in path and disk */ 930 if ((p = strchr(tmproot, ':')) != NULL) { 931 strncpy(rootdisk, p, 2); 932 rootdisk[2] = '\0'; 933 *p = '\0'; 934 rootlen = p - tmproot; 935 } else 936 rootdisk[0] = '\0'; 937 if (rootpath[rootlen - 1] != '/') { 938 rootpath[rootlen++] = '/'; 939 rootpath[rootlen] = '\0'; 940 } 941 if ((rootpath = (char *)realloc(tmproot, rootlen+1)) == NULL) { 942 free(tmproot); 943 rootlen = 0; 944 return MPN_NOMEM; 945 } 946 Trace((stderr, "rootpath now = [%s], rootdisk now = [%s]\n", 947 FnFilter1(rootpath), FnFilter2(rootdisk))); 948 } 949 return MPN_OK; 950 } 951#endif /* !SFX || SFX_EXDIR */ 952 953/*--------------------------------------------------------------------------- 954 END: free rootpath, immediately prior to program exit. 955 ---------------------------------------------------------------------------*/ 956 957 if (FUNCTION == END) { 958 Trace((stderr, "freeing rootpath\n")); 959 if (rootlen > 0) { 960 free(rootpath); 961 rootlen = 0; 962 } 963 return MPN_OK; 964 } 965 966 return MPN_INVALID; /* should never reach */ 967 968} /* end function checkdir() */ 969 970 971 972 973#ifdef MORE 974 975/**************************/ 976/* Function screenlines() */ 977/**************************/ 978 979int screenlines() 980{ 981 return getpl(27) + 1; 982} 983 984#endif /* MORE */ 985 986 987 988 989 990#if (!defined(MTS) || defined(SET_DIR_ATTRIB)) 991static void get_extattribs OF((__GPRO__ iztimes *pzt)); 992 993static int get_extattribs(__G__ pzt) 994 __GDEF 995 iztimes *pzt; 996{ 997/*--------------------------------------------------------------------------- 998 Convert from MSDOS-format local time and date to Unix-format 32-bit GMT 999 time: adjust base year from 1980 to 1970, do usual conversions from 1000 yy/mm/dd hh:mm:ss to elapsed seconds, and account for timezone and day- 1001 light savings time differences. If we have a Unix extra field, however, 1002 we're laughing: both mtime and atime are ours. 1003 ---------------------------------------------------------------------------*/ 1004#ifdef USE_EF_UT_TIME 1005 unsigned eb_izux_flg; 1006 1007 eb_izux_flg = (G.extra_field ? ef_scan_for_izux(G.extra_field, 1008 G.lrec.extra_field_length, 0, G.lrec.last_mod_dos_datetime, 1009#ifdef IZ_CHECK_TZ 1010 (G.tz_is_valid ? pzt : NULL), 1011#else 1012 pzt, 1013#endif 1014 z_uidgid) : 0); 1015 if (eb_izux_flg & EB_UT_FL_MTIME) { 1016 TTrace((stderr, "\nget_extattribs: Unix e.f. modif. time = %ld\n", 1017 pzt->mtime)); 1018 } else { 1019 pzt->mtime = dos_to_unix_time(G.lrec.last_mod_dos_datetime); 1020 } 1021 if (eb_izux_flg & EB_UT_FL_ATIME) { 1022 TTrace((stderr, "get_extattribs: Unix e.f. access time = %ld\n", 1023 pzt->atime)); 1024 } else { 1025 pzt->atime = pzt->mtime; 1026 TTrace((stderr, "\nget_extattribs: modification/access times = %ld\n", 1027 pzt->mtime)); 1028 } 1029#else 1030 pzt->mtime = dos_to_unix_time(G.lrec.last_mod_dos_datetime); 1031 pzt->atime = pzt->mtime; 1032#endif 1033} 1034#endif /* !MTS || SET_DIR_ATTRIB */ 1035 1036 1037 1038#ifndef MTS 1039 1040/****************************/ 1041/* Function close_outfile() */ 1042/****************************/ 1043 1044void close_outfile(__G) /* GRR: change to return PK-style warning level */ 1045 __GDEF 1046{ 1047 union { 1048 iztimes t3; /* mtime, atime, ctime */ 1049 ztimbuf t2; /* modtime, actime */ 1050 } zt; 1051 1052/*--------------------------------------------------------------------------- 1053 If symbolic links are supported, allocate a storage area, put the uncom- 1054 pressed "data" in it, and create the link. Since we know it's a symbolic 1055 link to start with, we shouldn't have to worry about overflowing unsigned 1056 ints with unsigned longs. 1057 ---------------------------------------------------------------------------*/ 1058 1059 fclose(G.outfile); 1060 1061 get_extattribs(__G__ &(zt.t3)); 1062 1063 /* set the file's access and modification times */ 1064 if (utime(G.filename, &(zt.t2))) { 1065 if (uO.qflag) 1066 Info(slide, 0x201, ((char *)slide, 1067 "warning: cannot set times for %s\n", FnFilter1(G.filename))); 1068 else 1069 Info(slide, 0x201, ((char *)slide, 1070 " (warning) cannot set times")); 1071 } 1072 1073/*--------------------------------------------------------------------------- 1074 Change the file permissions from default ones to those stored in the 1075 zipfile. 1076 ---------------------------------------------------------------------------*/ 1077 1078 if (chmod(G.filename, 0xffff & G.pInfo->file_attr)) 1079 perror("chmod (file attributes) error"); 1080 1081/*--------------------------------------------------------------------------- 1082 Change the file structure and set native . 1083 ---------------------------------------------------------------------------*/ 1084 1085 if (isv2_3()) { 1086 chorg(G.filename, G.extra_field[9]); 1087 chlen(G.filename, 1088 (ush) G.extra_field[10] | ((ush) G.extra_field[11] << 8), 1089 (ush) G.extra_field[12] | ((ush) G.extra_field[13] << 8)); 1090 chgrow(G.filename, G.extra_field[14]); 1091 } 1092#if OLD_THEOS_EXTRA 1093 else if (isv2_0()) { 1094 chorg(G.filename, G.pInfo->file_attr & 0xFF); 1095 chlen(G.filename, v2_0extra()->reclen, v2_0extra()->keylen); 1096 chgrow(G.filename, v2_0extra()->filegrow); 1097 } 1098#endif 1099} /* end function close_outfile() */ 1100 1101#endif /* !MTS */ 1102 1103 1104 1105 1106#ifdef SET_DIR_ATTRIB 1107/* messages of code for setting directory attributes */ 1108static ZCONST char Far DirlistUidGidFailed[] = 1109 "warning: cannot set UID %d and/or GID %d for %s\n"; 1110static ZCONST char Far DirlistUtimeFailed[] = 1111 "warning: cannot set modification, access times for %s\n"; 1112# ifndef NO_CHMOD 1113 static ZCONST char Far DirlistChmodFailed[] = 1114 "warning: cannot set permissions for %s\n"; 1115# endif 1116 1117 1118int defer_dir_attribs(__G__ pd) 1119 __GDEF 1120 direntry **pd; 1121{ 1122 uxdirattr *d_entry; 1123 1124 d_entry = (uxdirattr *)malloc(sizeof(uxdirattr) + strlen(G.filename)); 1125 *pd = (direntry *)d_entry; 1126 if (d_entry == (uxdirattr *)NULL) { 1127 return PK_MEM; 1128 } 1129 d_entry->fn = d_entry->fnbuf; 1130 strcpy(d_entry->fn, G.filename); 1131 1132 d_entry->perms = G.pInfo->file_attr; 1133 1134 get_extattribs(__G__ &(d_entry->u.t3)); 1135 return PK_OK; 1136} /* end function defer_dir_attribs() */ 1137 1138 1139int set_direc_attribs(__G__ d) 1140 __GDEF 1141 direntry *d; 1142{ 1143 int errval = PK_OK; 1144 1145 if (utime(d->fn, &UxAtt(d)->u.t2)) { 1146 Info(slide, 0x201, ((char *)slide, 1147 LoadFarString(DirlistUtimeFailed), FnFilter1(d->fn))); 1148 if (!errval) 1149 errval = PK_WARN; 1150 } 1151 if (chmod(d->fn, 0xffff & UxAtt(d)->perms)) { 1152 Info(slide, 0x201, ((char *)slide, 1153 LoadFarString(DirlistChmodFailed), FnFilter1(d->fn))); 1154 /* perror("chmod (file attributes) error"); */ 1155 if (!errval) 1156 errval = PK_WARN; 1157 } 1158 return errval; 1159} /* end function set_direc_attribs() */ 1160 1161#endif /* SET_DIR_ATTRIB */ 1162 1163 1164 1165 1166#ifdef TIMESTAMP 1167 1168/***************************/ 1169/* Function stamp_file() */ 1170/***************************/ 1171 1172int stamp_file(fname, modtime) 1173 ZCONST char *fname; 1174 time_t modtime; 1175{ 1176 ztimbuf tp; 1177 1178 tp.modtime = tp.actime = modtime; 1179 return (utime(fname, &tp)); 1180 1181} /* end function stamp_file() */ 1182 1183#endif /* TIMESTAMP */ 1184 1185 1186 1187 1188#ifndef SFX 1189 1190/************************/ 1191/* Function version() */ 1192/************************/ 1193 1194void version(__G) 1195 __GDEF 1196{ 1197 char buf1[40]; 1198 extern char Far CompiledWith[]; 1199 1200 sprintf(slide, CompiledWith, 1201 "THEOS C ","5.28", "THEOS ", "4.x", " on ", __DATE__); 1202 (*G.message)((zvoid *)&G, slide, (ulg)strlen((char *)slide), 0); 1203 1204} /* end function version() */ 1205 1206#endif /* !SFX */ 1207