1/* 2 Copyright (c) 1990-2008 Info-ZIP. All rights reserved. 3 4 See the accompanying file LICENSE, version 2000-Apr-09 or later 5 (the contents of which are also included in 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 atari.c 12 13 Atari-specific routines for use with Info-ZIP's UnZip 5.1 and later. 14 15 Contains: readdir() 16 do_wild() <-- generic enough to put in fileio.c? 17 mapattr() 18 mapname() 19 checkdir() 20 mkdir() 21 close_outfile() 22 stamp_file() [optional feature] 23 version() 24 25 Due to the amazing MiNT library being very, very close to BSD unix's 26 library, I'm using the unix.c as a base for this. Note: If you're not 27 going to compile this with the MiNT libraries (for GNU C, Turbo C, Pure C, 28 Lattice C, or Heat & Serve C), you're going to be in for some nasty work. 29 Most of the modifications in this file were made by Chris Herborth 30 (cherborth@semprini.waterloo-rdp.on.ca) and /should/ be marked with [cjh]. 31 32 ---------------------------------------------------------------------------*/ 33 34 35#define UNZIP_INTERNAL 36#include "unzip.h" 37#include <dirent.h> /* MiNTlibs has dirent [cjh] */ 38 39static int created_dir; /* used in mapname(), checkdir() */ 40static int renamed_fullpath; /* ditto */ 41 42 43#ifndef SFX 44 45/**********************/ 46/* Function do_wild() */ /* for porting: dir separator; match(ignore_case) */ 47/**********************/ 48 49char *do_wild(__G__ wildspec) 50 __GDEF 51 ZCONST char *wildspec; /* only used first time on a given dir */ 52{ 53 static DIR *wild_dir = (DIR *)NULL; 54 static ZCONST char *wildname; 55 static char *dirname, matchname[FILNAMSIZ]; 56 static int notfirstcall=FALSE, have_dirname, dirnamelen; 57 struct dirent *file; 58 59 /* Even when we're just returning wildspec, we *always* do so in 60 * matchname[]--calling routine is allowed to append four characters 61 * to the returned string, and wildspec may be a pointer to argv[]. 62 */ 63 if (!notfirstcall) { /* first call: must initialize everything */ 64 notfirstcall = TRUE; 65 66 if (!iswild(wildspec)) { 67 strncpy(matchname, wildspec, FILNAMSIZ); 68 matchname[FILNAMSIZ-1] = '\0'; 69 have_dirname = FALSE; 70 dir = NULL; 71 return matchname; 72 } 73 74 /* break the wildspec into a directory part and a wildcard filename */ 75 if ((wildname = strrchr(wildspec, '/')) == (ZCONST char *)NULL) { 76 dirname = "."; 77 dirnamelen = 1; 78 have_dirname = FALSE; 79 wildname = wildspec; 80 } else { 81 ++wildname; /* point at character after '/' */ 82 dirnamelen = wildname - wildspec; 83 if ((dirname = (char *)malloc(dirnamelen+1)) == (char *)NULL) { 84 Info(slide, 0x201, ((char *)slide, 85 "warning: cannot allocate wildcard buffers\n")); 86 strncpy(matchname, wildspec, FILNAMSIZ); 87 matchname[FILNAMSIZ-1] = '\0'; 88 return matchname; /* but maybe filespec was not a wildcard */ 89 } 90 strncpy(dirname, wildspec, dirnamelen); 91 dirname[dirnamelen] = '\0'; /* terminate for strcpy below */ 92 have_dirname = TRUE; 93 } 94 95 if ((wild_dir = opendir(dirname)) != (DIR *)NULL) { 96 while ((file = readdir(wild_dir)) != (struct dirent *)NULL) { 97 Trace((stderr, "do_wild: readdir returns %s\n", file->d_name)); 98 if (file->d_name[0] == '.' && wildname[0] != '.') 99 continue; /* Unix: '*' and '?' do not match leading dot */ 100 /* Need something here for TOS filesystem? [cjh] */ 101 if (match(file->d_name, wildname, 0 WISEP) && /* 0=case sens.*/ 102 /* skip "." and ".." directory entries */ 103 strcmp(file->d_name, ".") && strcmp(file->d_name, "..")) { 104 Trace((stderr, "do_wild: match() succeeds\n")); 105 if (have_dirname) { 106 strcpy(matchname, dirname); 107 strcpy(matchname+dirnamelen, file->d_name); 108 } else 109 strcpy(matchname, file->d_name); 110 return matchname; 111 } 112 } 113 /* if we get to here directory is exhausted, so close it */ 114 closedir(wild_dir); 115 wild_dir = (DIR *)NULL; 116 } 117 118 /* return the raw wildspec in case that works (e.g., directory not 119 * searchable, but filespec was not wild and file is readable) */ 120 strncpy(matchname, wildspec, FILNAMSIZ); 121 matchname[FILNAMSIZ-1] = '\0'; 122 return matchname; 123 } 124 125 /* last time through, might have failed opendir but returned raw wildspec */ 126 if (wild_dir == (DIR *)NULL) { 127 notfirstcall = FALSE; /* nothing left to try--reset for new wildspec */ 128 if (have_dirname) 129 free(dirname); 130 return (char *)NULL; 131 } 132 133 /* If we've gotten this far, we've read and matched at least one entry 134 * successfully (in a previous call), so dirname has been copied into 135 * matchname already. 136 */ 137 while ((file = readdir(wild_dir)) != (struct dirent *)NULL) { 138 /* May need special TOS handling here. [cjh] */ 139 Trace((stderr, "do_wild: readdir returns %s\n", file->d_name)); 140 if (file->d_name[0] == '.' && wildname[0] != '.') 141 continue; /* Unix: '*' and '?' do not match leading dot */ 142 if (match(file->d_name, wildname, 0 WISEP)) { /* 0 = case sens. */ 143 Trace((stderr, "do_wild: match() succeeds\n")); 144 if (have_dirname) { 145 /* strcpy(matchname, dirname); */ 146 strcpy(matchname+dirnamelen, file->d_name); 147 } else 148 strcpy(matchname, file->d_name); 149 return matchname; 150 } 151 } 152 153 closedir(wild_dir); /* have read at least one entry; nothing left */ 154 wild_dir = (DIR *)NULL; 155 notfirstcall = FALSE; /* reset for new wildspec */ 156 if (have_dirname) 157 free(dirname); 158 return (char *)NULL; 159 160} /* end function do_wild() */ 161 162#endif /* !SFX */ 163 164 165 166 167 168/**********************/ 169/* Function mapattr() */ 170/**********************/ 171 172int mapattr(__G) 173 __GDEF 174{ 175 int r; 176 ulg tmp = G.crec.external_file_attributes; 177 178 G.pInfo->file_attr = 0; 179 /* initialized to 0 for check in "default" branch below... */ 180 181 switch (G.pInfo->hostnum) { 182 case AMIGA_: 183 tmp = (unsigned)(tmp>>17 & 7); /* Amiga RWE bits */ 184 G.pInfo->file_attr = (unsigned)(tmp<<6 | tmp<<3 | tmp); 185 break; 186 case THEOS_: 187 tmp &= 0xF1FFFFFFL; 188 if ((tmp & 0xF0000000L) != 0x40000000L) 189 tmp &= 0x01FFFFFFL; /* not a dir, mask all ftype bits */ 190 else 191 tmp &= 0x41FFFFFFL; /* leave directory bit as set */ 192 /* fall through! */ 193 case UNIX_: 194 case VMS_: 195 case ACORN_: 196 case ATARI_: 197 case ATHEOS_: 198 case BEOS_: 199 case QDOS_: 200 case TANDEM_: 201 r = FALSE; 202 G.pInfo->file_attr = (unsigned)(tmp >> 16); 203 if (G.pInfo->file_attr == 0 && G.extra_field) { 204 /* Some (non-Info-ZIP) implementations of Zip for Unix and 205 * VMS (and probably others ??) leave 0 in the upper 16-bit 206 * part of the external_file_attributes field. Instead, they 207 * store file permission attributes in some extra field. 208 * As a work-around, we search for the presence of one of 209 * these extra fields and fall back to the MSDOS compatible 210 * part of external_file_attributes if one of the known 211 * e.f. types has been detected. 212 * Later, we might implement extraction of the permission 213 * bits from the VMS extra field. But for now, the work-around 214 * should be sufficient to provide "readable" extracted files. 215 * (For ASI Unix e.f., an experimental remap of the e.f. 216 * mode value IS already provided!) 217 */ 218 ush ebID; 219 unsigned ebLen; 220 uch *ef = G.extra_field; 221 unsigned ef_len = G.crec.extra_field_length; 222 223 while (!r && ef_len >= EB_HEADSIZE) { 224 ebID = makeword(ef); 225 ebLen = (unsigned)makeword(ef+EB_LEN); 226 if (ebLen > (ef_len - EB_HEADSIZE)) 227 /* discoverd some e.f. inconsistency! */ 228 break; 229 switch (ebID) { 230 case EF_ASIUNIX: 231 if (ebLen >= (EB_ASI_MODE+2)) { 232 G.pInfo->file_attr = 233 (unsigned)makeword(ef+(EB_HEADSIZE+EB_ASI_MODE)); 234 /* force stop of loop: */ 235 ef_len = (ebLen + EB_HEADSIZE); 236 break; 237 } 238 /* else: fall through! */ 239 case EF_PKVMS: 240 /* "found nondecypherable e.f. with perm. attr" */ 241 r = TRUE; 242 default: 243 break; 244 } 245 ef_len -= (ebLen + EB_HEADSIZE); 246 ef += (ebLen + EB_HEADSIZE); 247 } 248 } 249 if (!r) { 250#ifdef SYMLINKS 251 /* Check if the file is a (POSIX-compatible) symbolic link. 252 * We restrict symlink support to those "made-by" hosts that 253 * are known to support symbolic links. 254 */ 255 G.pInfo->symlink = S_ISLNK(G.pInfo->file_attr) && 256 SYMLINK_HOST(G.pInfo->hostnum); 257#endif 258 return 0; 259 } 260 /* fall through! */ 261 /* all remaining cases: expand MSDOS read-only bit into write perms */ 262 case FS_FAT_: 263 /* PKWARE's PKZip for Unix marks entries as FS_FAT_, but stores the 264 * Unix attributes in the upper 16 bits of the external attributes 265 * field, just like Info-ZIP's Zip for Unix. We try to use that 266 * value, after a check for consistency with the MSDOS attribute 267 * bits (see below). 268 */ 269 G.pInfo->file_attr = (unsigned)(tmp >> 16); 270 /* fall through! */ 271 case FS_HPFS_: 272 case FS_NTFS_: 273 case MAC_: 274 case TOPS20_: 275 default: 276 /* Ensure that DOS subdir bit is set when the entry's name ends 277 * in a '/'. Some third-party Zip programs fail to set the subdir 278 * bit for directory entries. 279 */ 280 if ((tmp & 0x10) == 0) { 281 extent fnlen = strlen(G.filename); 282 if (fnlen > 0 && G.filename[fnlen-1] == '/') 283 tmp |= 0x10; 284 } 285 /* read-only bit --> write perms; subdir bit --> dir exec bit */ 286 tmp = !(tmp & 1) << 1 | (tmp & 0x10) >> 4; 287 if ((G.pInfo->file_attr & 0700) == (unsigned)(0400 | tmp<<6)) { 288 /* keep previous G.pInfo->file_attr setting, when its "owner" 289 * part appears to be consistent with DOS attribute flags! 290 */ 291#ifdef SYMLINKS 292 /* Entries "made by FS_FAT_" could have been zipped on a 293 * system that supports POSIX-style symbolic links. 294 */ 295 G.pInfo->symlink = S_ISLNK(G.pInfo->file_attr) && 296 (G.pInfo->hostnum == FS_FAT_); 297#endif 298 return 0; 299 } 300 G.pInfo->file_attr = (unsigned)(0444 | tmp<<6 | tmp<<3 | tmp); 301 break; 302 } /* end switch (host-OS-created-by) */ 303 304 /* for originating systems with no concept of "group," "other," "system": */ 305 umask( (int)(tmp=umask(0)) ); /* apply mask to expanded r/w(/x) perms */ 306 G.pInfo->file_attr &= ~tmp; 307 308 return 0; 309 310} /* end function mapattr() */ 311 312 313 314 315 316/************************/ 317/* Function mapname() */ 318/************************/ 319 320int mapname(__G__ renamed) 321 __GDEF 322 int renamed; 323/* 324 * returns: 325 * MPN_OK - no problem detected 326 * MPN_INF_TRUNC - caution (truncated filename) 327 * MPN_INF_SKIP - info "skip entry" (dir doesn't exist) 328 * MPN_ERR_SKIP - error -> skip entry 329 * MPN_ERR_TOOLONG - error -> path is too long 330 * MPN_NOMEM - error (memory allocation failed) -> skip entry 331 * [also MPN_VOL_LABEL, MPN_CREATED_DIR] 332 */ 333{ 334 char pathcomp[FILNAMSIZ]; /* path-component buffer */ 335 char *pp, *cp=(char *)NULL; /* character pointers */ 336 char *lastsemi=(char *)NULL; /* pointer to last semi-colon in pathcomp */ 337#ifdef ACORN_FTYPE_NFS 338 char *lastcomma=(char *)NULL; /* pointer to last comma in pathcomp */ 339 RO_extra_block *ef_spark; /* pointer Acorn FTYPE ef block */ 340#endif 341 int killed_ddot = FALSE; /* is set when skipping "../" pathcomp */ 342 int error = MPN_OK; 343 register unsigned workch; /* hold the character being tested */ 344 345 346/*--------------------------------------------------------------------------- 347 Initialize various pointers and counters and stuff. 348 ---------------------------------------------------------------------------*/ 349 350 if (G.pInfo->vollabel) 351 return MPN_VOL_LABEL; /* can't set disk volume labels on Atari */ 352 353 /* can create path as long as not just freshening, or if user told us */ 354 G.create_dirs = (!uO.fflag || renamed); 355 356 created_dir = FALSE; /* not yet */ 357 358 /* user gave full pathname: don't prepend rootpath */ 359 renamed_fullpath = (renamed && (*G.filename == '/')); 360 361 if (checkdir(__G__ (char *)NULL, INIT) == MPN_NOMEM) 362 return MPN_NOMEM; /* initialize path buffer, unless no memory */ 363 364 *pathcomp = '\0'; /* initialize translation buffer */ 365 pp = pathcomp; /* point to translation buffer */ 366 if (uO.jflag) /* junking directories */ 367 cp = (char *)strrchr(G.filename, '/'); 368 if (cp == (char *)NULL) /* no '/' or not junking dirs */ 369 cp = G.filename; /* point to internal zipfile-member pathname */ 370 else 371 ++cp; /* point to start of last component of path */ 372 373/*--------------------------------------------------------------------------- 374 Begin main loop through characters in filename. 375 ---------------------------------------------------------------------------*/ 376 377 while ((workch = (uch)*cp++) != 0) { 378 379 switch (workch) { 380 case '/': /* can assume -j flag not given */ 381 *pp = '\0'; 382 if (strcmp(pathcomp, ".") == 0) { 383 /* don't bother appending "./" to the path */ 384 *pathcomp = '\0'; 385 } else if (!uO.ddotflag && strcmp(pathcomp, "..") == 0) { 386 /* "../" dir traversal detected, skip over it */ 387 *pathcomp = '\0'; 388 killed_ddot = TRUE; /* set "show message" flag */ 389 } 390 /* when path component is not empty, append it now */ 391 if (*pathcomp != '\0' && 392 ((error = checkdir(__G__ pathcomp, APPEND_DIR)) 393 & MPN_MASK) > MPN_INF_TRUNC) 394 return error; 395 pp = pathcomp; /* reset conversion buffer for next piece */ 396 lastsemi = (char *)NULL; /* leave direct. semi-colons alone */ 397 break; 398 399 case ';': /* VMS version (or DEC-20 attrib?) */ 400 lastsemi = pp; 401 *pp++ = ';'; /* keep for now; remove VMS ";##" */ 402 break; /* later, if requested */ 403 404#ifdef ACORN_FTYPE_NFS 405 case ',': /* NFS filetype extension */ 406 lastcomma = pp; 407 *pp++ = ','; /* keep for now; may need to remove */ 408 break; /* later, if requested */ 409#endif 410 411#ifdef MTS 412 case ' ': /* change spaces to underscore under */ 413 *pp++ = '_'; /* MTS; leave as spaces under Unix */ 414 break; 415#endif 416 417 default: 418 /* allow European characters in filenames: */ 419 if (isprint(workch) || (128 <= workch && workch <= 254)) 420 *pp++ = (char)workch; 421 } /* end switch */ 422 423 } /* end while loop */ 424 425 /* Show warning when stripping insecure "parent dir" path components */ 426 if (killed_ddot && QCOND2) { 427 Info(slide, 0, ((char *)slide, 428 "warning: skipped \"../\" path component(s) in %s\n", 429 FnFilter1(G.filename))); 430 if (!(error & ~MPN_MASK)) 431 error = (error & MPN_MASK) | PK_WARN; 432 } 433 434/*--------------------------------------------------------------------------- 435 Report if directory was created (and no file to create: filename ended 436 in '/'), check name to be sure it exists, and combine path and name be- 437 fore exiting. 438 ---------------------------------------------------------------------------*/ 439 440 if (G.filename[strlen(G.filename) - 1] == '/') { 441 checkdir(__G__ G.filename, GETPATH); 442 if (created_dir) { 443 if (QCOND2) { 444 Info(slide, 0, ((char *)slide, " creating: %s\n", 445 FnFilter1(G.filename))); 446 } 447 /* set dir time (note trailing '/') */ 448 return (error & ~MPN_MASK) | MPN_CREATED_DIR; 449 } 450 /* dir existed already; don't look for data to extract */ 451 return (error & ~MPN_MASK) | MPN_INF_SKIP; 452 } 453 454 *pp = '\0'; /* done with pathcomp: terminate it */ 455 456 /* if not saving them, remove VMS version numbers (appended ";###") */ 457 if (!uO.V_flag && lastsemi) { 458 pp = lastsemi + 1; 459 while (isdigit((uch)(*pp))) 460 ++pp; 461 if (*pp == '\0') /* only digits between ';' and end: nuke */ 462 *lastsemi = '\0'; 463 } 464 465 /* On UNIX (and compatible systems), "." and ".." are reserved for 466 * directory navigation and cannot be used as regular file names. 467 * These reserved one-dot and two-dot names are mapped to "_" and "__". 468 */ 469 if (strcmp(pathcomp, ".") == 0) 470 *pathcomp = '_'; 471 else if (strcmp(pathcomp, "..") == 0) 472 strcpy(pathcomp, "__"); 473 474#ifdef ACORN_FTYPE_NFS 475 /* translate Acorn filetype information if asked to do so */ 476 if (uO.acorn_nfs_ext && 477 (ef_spark = (RO_extra_block *) 478 getRISCOSexfield(G.extra_field, G.lrec.extra_field_length)) 479 != (RO_extra_block *)NULL) 480 { 481 /* file *must* have a RISC OS extra field */ 482 long ft = (long)makelong(ef_spark->loadaddr); 483 /*32-bit*/ 484 if (lastcomma) { 485 pp = lastcomma + 1; 486 while (isxdigit((uch)(*pp))) ++pp; 487 if (pp == lastcomma+4 && *pp == '\0') *lastcomma='\0'; /* nuke */ 488 } 489 if ((ft & 1<<31)==0) ft=0x000FFD00; 490 sprintf(pathcomp+strlen(pathcomp), ",%03x", (int)(ft>>8) & 0xFFF); 491 } 492#endif /* ACORN_FTYPE_NFS */ 493 494 if (*pathcomp == '\0') { 495 Info(slide, 1, ((char *)slide, "mapname: conversion of %s failed\n", 496 FnFilter1(G.filename))); 497 return (error & ~MPN_MASK) | MPN_ERR_SKIP; 498 } 499 500 checkdir(__G__ pathcomp, APPEND_NAME); /* returns 1 if truncated: care? */ 501 checkdir(__G__ G.filename, GETPATH); 502 503 return error; 504 505} /* end function mapname() */ 506 507 508 509 510#if 0 /*========== NOTES ==========*/ 511 512 extract-to dir: a:path/ 513 buildpath: path1/path2/ ... (NULL-terminated) 514 pathcomp: filename 515 516 mapname(): 517 loop over chars in zipfile member name 518 checkdir(path component, COMPONENT | CREATEDIR) --> map as required? 519 (d:/tmp/unzip/) (disk:[tmp.unzip.) 520 (d:/tmp/unzip/jj/) (disk:[tmp.unzip.jj.) 521 (d:/tmp/unzip/jj/temp/) (disk:[tmp.unzip.jj.temp.) 522 finally add filename itself and check for existence? (could use with rename) 523 (d:/tmp/unzip/jj/temp/msg.outdir) (disk:[tmp.unzip.jj.temp]msg.outdir) 524 checkdir(name, GETPATH) --> copy path to name and free space 525 526#endif /* 0 */ 527 528 529 530 531/***********************/ 532/* Function checkdir() */ 533/***********************/ 534 535int checkdir(__G__ pathcomp, flag) 536 __GDEF 537 char *pathcomp; 538 int flag; 539/* 540 * returns: 541 * MPN_OK - no problem detected 542 * MPN_INF_TRUNC - (on APPEND_NAME) truncated filename 543 * MPN_INF_SKIP - path doesn't exist, not allowed to create 544 * MPN_ERR_SKIP - path doesn't exist, tried to create and failed; or path 545 * exists and is not a directory, but is supposed to be 546 * MPN_ERR_TOOLONG - path is too long 547 * MPN_NOMEM - can't allocate memory for filename buffers 548 */ 549{ 550 static int rootlen = 0; /* length of rootpath */ 551 static char *rootpath; /* user's "extract-to" directory */ 552 static char *buildpath; /* full path (so far) to extracted file */ 553 static char *end; /* pointer to end of buildpath ('\0') */ 554 555# define FN_MASK 7 556# define FUNCTION (flag & FN_MASK) 557 558 559/*--------------------------------------------------------------------------- 560 APPEND_DIR: append the path component to the path being built and check 561 for its existence. If doesn't exist and we are creating directories, do 562 so for this one; else signal success or error as appropriate. 563 ---------------------------------------------------------------------------*/ 564 565 if (FUNCTION == APPEND_DIR) { 566 int too_long = FALSE; 567/* SHORT_NAMES required for TOS, but it has to co-exist for minix fs... [cjh] */ 568#ifdef SHORT_NAMES 569 char *old_end = end; 570#endif 571 572 Trace((stderr, "appending dir segment [%s]\n", FnFilter1(pathcomp))); 573 while ((*end = *pathcomp++) != '\0') 574 ++end; 575/* SHORT_NAMES required for TOS, but it has to co-exist for minix fs... [cjh] */ 576#ifdef SHORT_NAMES /* path components restricted to 14 chars, typically */ 577 if ((end-old_end) > FILENAME_MAX) /* GRR: proper constant? */ 578 *(end = old_end + FILENAME_MAX) = '\0'; 579#endif 580 581 /* GRR: could do better check, see if overrunning buffer as we go: 582 * check end-buildpath after each append, set warning variable if 583 * within 20 of FILNAMSIZ; then if var set, do careful check when 584 * appending. Clear variable when begin new path. */ 585 586 if ((end-buildpath) > FILNAMSIZ-3) /* need '/', one-char name, '\0' */ 587 too_long = TRUE; /* check if extracting directory? */ 588 if (stat(buildpath, &G.statbuf)) { /* path doesn't exist */ 589 if (!G.create_dirs) { /* told not to create (freshening) */ 590 free(buildpath); 591 return MPN_INF_SKIP; /* path doesn't exist: nothing to do */ 592 } 593 if (too_long) { 594 Info(slide, 1, ((char *)slide, 595 "checkdir error: path too long: %s\n", 596 FnFilter1(buildpath))); 597 free(buildpath); 598 /* no room for filenames: fatal */ 599 return MPN_ERR_TOOLONG; 600 } 601 if (mkdir(buildpath, 0777) == -1) { /* create the directory */ 602 Info(slide, 1, ((char *)slide, 603 "checkdir error: cannot create %s\n\ 604 unable to process %s.\n", 605 FnFilter2(buildpath), FnFilter1(G.filename))); 606 free(buildpath); 607 /* path didn't exist, tried to create, failed */ 608 return MPN_ERR_SKIP; 609 } 610 created_dir = TRUE; 611 } else if (!S_ISDIR(G.statbuf.st_mode)) { 612 Info(slide, 1, ((char *)slide, 613 "checkdir error: %s exists but is not directory\n\ 614 unable to process %s.\n", 615 FnFilter2(buildpath), FnFilter(G.filename))); 616 free(buildpath); 617 /* path existed but wasn't dir */ 618 return MPN_ERR_SKIP; 619 } 620 if (too_long) { 621 Info(slide, 1, ((char *)slide, 622 "checkdir error: path too long: %s\n", FnFilter1(buildpath))); 623 free(buildpath); 624 /* no room for filenames: fatal */ 625 return MPN_ERR_TOOLONG; 626 } 627 *end++ = '/'; 628 *end = '\0'; 629 Trace((stderr, "buildpath now = [%s]\n", FnFilter1(buildpath))); 630 return MPN_OK; 631 632 } /* end if (FUNCTION == APPEND_DIR) */ 633 634/*--------------------------------------------------------------------------- 635 GETPATH: copy full path to the string pointed at by pathcomp, and free 636 buildpath. 637 ---------------------------------------------------------------------------*/ 638 639 if (FUNCTION == GETPATH) { 640 strcpy(pathcomp, buildpath); 641 Trace((stderr, "getting and freeing path [%s]\n", 642 FnFilter1(pathcomp))); 643 free(buildpath); 644 buildpath = end = (char *)NULL; 645 return MPN_OK; 646 } 647 648/*--------------------------------------------------------------------------- 649 APPEND_NAME: assume the path component is the filename; append it and 650 return without checking for existence. 651 ---------------------------------------------------------------------------*/ 652 653 if (FUNCTION == APPEND_NAME) { 654/* SHORT_NAMES required for TOS, but it has to co-exist for minix fs... [cjh] */ 655#ifdef SHORT_NAMES 656 char *old_end = end; 657#endif 658 659 Trace((stderr, "appending filename [%s]\n", FnFilter1(pathcomp))); 660 while ((*end = *pathcomp++) != '\0') { 661 ++end; 662/* SHORT_NAMES required for TOS, but it has to co-exist for minix fs... [cjh] */ 663#ifdef SHORT_NAMES /* truncate name at 14 characters, typically */ 664 if ((end-old_end) > FILENAME_MAX) /* GRR: proper constant? */ 665 *(end = old_end + FILENAME_MAX) = '\0'; 666#endif 667 if ((end-buildpath) >= FILNAMSIZ) { 668 *--end = '\0'; 669 Info(slide, 0x201, ((char *)slide, 670 "checkdir warning: path too long; truncating\n\ 671 %s\n -> %s\n", 672 FnFilter1(G.filename), FnFilter2(buildpath))); 673 return MPN_INF_TRUNC; /* filename truncated */ 674 } 675 } 676 Trace((stderr, "buildpath now = [%s]\n", FnFilter1(buildpath))); 677 /* could check for existence here, prompt for new name... */ 678 return MPN_OK; 679 } 680 681/*--------------------------------------------------------------------------- 682 INIT: allocate and initialize buffer space for the file currently being 683 extracted. If file was renamed with an absolute path, don't prepend the 684 extract-to path. 685 ---------------------------------------------------------------------------*/ 686 687/* GRR: for VMS and TOPS-20, add up to 13 to strlen */ 688 689 if (FUNCTION == INIT) { 690 Trace((stderr, "initializing buildpath to ")); 691#ifdef ACORN_FTYPE_NFS 692 if ((buildpath = (char *)malloc(strlen(G.filename)+rootlen+ 693 (uO.acorn_nfs_ext ? 5 : 1))) 694#else 695 if ((buildpath = (char *)malloc(strlen(G.filename)+rootlen+1)) 696#endif 697 == (char *)NULL) 698 return MPN_NOMEM; 699 if ((rootlen > 0) && !renamed_fullpath) { 700 strcpy(buildpath, rootpath); 701 end = buildpath + rootlen; 702 } else { 703 *buildpath = '\0'; 704 end = buildpath; 705 } 706 Trace((stderr, "[%s]\n", FnFilter1(buildpath))); 707 return MPN_OK; 708 } 709 710/*--------------------------------------------------------------------------- 711 ROOT: if appropriate, store the path in rootpath and create it if 712 necessary; else assume it's a zipfile member and return. This path 713 segment gets used in extracting all members from every zipfile specified 714 on the command line. 715 ---------------------------------------------------------------------------*/ 716 717#if (!defined(SFX) || defined(SFX_EXDIR)) 718 if (FUNCTION == ROOT) { 719 Trace((stderr, "initializing root path to [%s]\n", pathcomp)); 720 if (pathcomp == (char *)NULL) { 721 rootlen = 0; 722 return MPN_OK; 723 } 724 if (rootlen > 0) /* rootpath was already set, nothing to do */ 725 return MPN_OK; 726 if ((rootlen = strlen(pathcomp)) > 0) { 727 char *tmproot; 728 729 if ((tmproot = (char *)malloc(rootlen+2)) == (char *)NULL) { 730 rootlen = 0; 731 return MPN_NOMEM; 732 } 733 strcpy(tmproot, pathcomp); 734 if (tmproot[rootlen-1] == '/') { 735 tmproot[--rootlen] = '\0'; 736 } 737 if (rootlen > 0 && (stat(tmproot, &G.statbuf) || 738 !S_ISDIR(G.statbuf.st_mode))) 739 { /* path does not exist */ 740 if (!G.create_dirs /* || iswild(tmproot) */ ) { 741 free(tmproot); 742 rootlen = 0; 743 /* skip (or treat as stored file) */ 744 return MPN_INF_SKIP; 745 } 746 /* create the directory (could add loop here scanning tmproot 747 * to create more than one level, but why really necessary?) */ 748 if (mkdir(tmproot, 0777) == -1) { 749 Info(slide, 1, ((char *)slide, 750 "checkdir: cannot create extraction directory: %s\n", 751 FnFilter1(tmproot))); 752 free(tmproot); 753 rootlen = 0; 754 /* path didn't exist, tried to create, and failed: */ 755 /* file exists, or 2+ subdir levels required */ 756 return MPN_ERR_SKIP; 757 } 758 } 759 tmproot[rootlen++] = '/'; 760 tmproot[rootlen] = '\0'; 761 if ((rootpath = (char *)realloc(tmproot, rootlen+1)) == NULL) { 762 free(tmproot); 763 rootlen = 0; 764 return MPN_NOMEM; 765 } 766 Trace((stderr, "rootpath now = [%s]\n", FnFilter1(rootpath))); 767 } 768 return MPN_OK; 769 } 770#endif /* !SFX || SFX_EXDIR */ 771 772/*--------------------------------------------------------------------------- 773 END: free rootpath, immediately prior to program exit. 774 ---------------------------------------------------------------------------*/ 775 776 if (FUNCTION == END) { 777 Trace((stderr, "freeing rootpath\n")); 778 if (rootlen > 0) { 779 free(rootpath); 780 rootlen = 0; 781 } 782 return MPN_OK; 783 } 784 785 return MPN_INVALID; /* should never reach */ 786 787} /* end function checkdir() */ 788 789 790 791 792 793/****************************/ 794/* Function close_outfile() */ 795/****************************/ 796 797void close_outfile(__G) /* GRR: change to return PK-style warning level */ 798 __GDEF 799{ 800#ifdef USE_EF_UT_TIME 801 unsigned eb_izux_flg; 802 iztimes zt; 803#endif 804 ztimbuf tp; 805 806/*--------------------------------------------------------------------------- 807 If symbolic links are supported, allocate storage for a symlink control 808 structure, put the uncompressed "data" and other required info in it, and 809 add the structure to the "deferred symlinks" chain. Since we know it's a 810 symbolic link to start with, we shouldn't have to worry about overflowing 811 unsigned ints with unsigned longs. 812 ---------------------------------------------------------------------------*/ 813 814 /* symlinks allowed on minix filesystems [cjh] 815 * Hopefully this will work properly... We won't bother to try if 816 * MiNT isn't present; the symlink should fail if we're on a TOS 817 * filesystem. 818 * BUG: should we copy the original file to the "symlink" if the 819 * link fails? 820 */ 821 if (G.symlnk) { 822 extent ucsize = (extent)G.lrec.ucsize; 823 /* size of the symlink entry is the sum of 824 * (struct size (includes 1st '\0') + 1 additional trailing '\0'), 825 * system specific attribute data size (might be 0), 826 * and the lengths of name and link target. 827 */ 828 extent slnk_entrysize = (sizeof(slinkentry) + 1) + 829 ucsize + strlen(G.filename); 830 slinkentry *slnk_entry; 831 832 if (slnk_entrysize < ucsize) { 833 Info(slide, 0x201, ((char *)slide, 834 "warning: symbolic link (%s) failed: mem alloc overflow\n", 835 FnFilter1(G.filename))); 836 fclose(G.outfile); 837 return; 838 } 839 840 if ((slnk_entry = (slinkentry *)malloc(slnk_entrysize)) == NULL) { 841 Info(slide, 0x201, ((char *)slide, 842 "warning: symbolic link (%s) failed: no mem\n", 843 FnFilter1(G.filename))); 844 fclose(G.outfile); 845 return; 846 } 847 slnk_entry->next = NULL; 848 slnk_entry->targetlen = ucsize; 849 slnk_entry->attriblen = 0; /* don't set attributes for symlinks */ 850 slnk_entry->target = slnk_entry->buf; 851 slnk_entry->fname = slnk_entry->target + ucsize + 1; 852 strcpy(slnk_entry->fname, G.filename); 853 854 /* move back to the start of the file to re-read the "link data" */ 855 rewind(G.outfile); 856 857 if (fread(slnk_entry->target, 1, ucsize, G.outfile) != ucsize) 858 { 859 Info(slide, 0x201, ((char *)slide, 860 "warning: symbolic link (%s) failed\n", 861 FnFilter1(G.filename))); 862 free(slnk_entry); 863 fclose(G.outfile); 864 return; 865 } 866 fclose(G.outfile); /* close "link" file for good... */ 867 slnk_entry->target[ucsize] = '\0'; 868 if (QCOND2) 869 Info(slide, 0, ((char *)slide, "-> %s ", 870 FnFilter1(slnk_entry->target))); 871 /* add this symlink record to the list of deferred symlinks */ 872 if (G.slink_last != NULL) 873 G.slink_last->next = slnk_entry; 874 else 875 G.slink_head = slnk_entry; 876 G.slink_last = slnk_entry; 877 return; 878 } 879 880 fclose(G.outfile); 881 882/*--------------------------------------------------------------------------- 883 Convert from MSDOS-format local time and date to Unix-format 32-bit GMT 884 time: adjust base year from 1980 to 1970, do usual conversions from 885 yy/mm/dd hh:mm:ss to elapsed seconds, and account for timezone and day- 886 light savings time differences. 887 ---------------------------------------------------------------------------*/ 888 889 /* skip restoring time stamps on user's request */ 890 if (uO.D_flag <= 1) { 891#ifdef USE_EF_UT_TIME 892 eb_izux_flg = (G.extra_field 893#ifdef IZ_CHECK_TZ 894 && G.tz_is_valid 895#endif 896 ? ef_scan_for_izux(G.extra_field, 897 G.lrec.extra_field_length, 0, 898 G.lrec.last_mod_dos_datetime, &zt, NULL) 899 : 0); 900 if (eb_izux_flg & EB_UT_FL_MTIME) { 901 tp.modtime = zt.mtime; 902 TTrace((stderr, 903 "\nclose_outfile: Unix e.f. modif. time = %ld\n", 904 tp.modtime)); 905 } else { 906 tp.modtime = dos_to_unix_time(G.lrec.last_mod_dos_datetime); 907 } 908 if (eb_izux_flg & EB_UT_FL_ATIME) { 909 tp.actime = zt.atime; 910 TTrace((stderr, 911 "close_outfile: Unix e.f. access time = %ld\n", 912 tp.actime)); 913 } else { 914 tp.actime = tp.modtime; 915 TTrace((stderr, 916 "\nclose_outfile: modification/access times = %ld\n", 917 tp.modtime)); 918 } 919#else /* !USE_EF_UT_TIME */ 920 tp.actime = tp.modtime 921 = dos_to_unix_time(G.lrec.last_mod_dos_datetime); 922 923 TTrace((stderr, "\nclose_outfile: modification/access times = %ld\n", 924 tp.modtime)); 925#endif /* ?USE_EF_UT_TIME */ 926 927 /* set the file's access and modification times */ 928 if (utime(G.filename, &tp)) 929 Info(slide, 0x201, ((char *)slide, 930 "warning: cannot set the time for %s\n", 931 FnFilter1(G.filename))); 932 } 933 934/*--------------------------------------------------------------------------- 935 Change the file permissions from default ones to those stored in the 936 zipfile. 937 ---------------------------------------------------------------------------*/ 938 939#ifndef NO_CHMOD 940 if (chmod(G.filename, 0xffff & G.pInfo->file_attr)) 941 perror("chmod (file attributes) error"); 942#endif 943 944} /* end function close_outfile() */ 945 946 947 948 949#ifdef TIMESTAMP 950 951/***************************/ 952/* Function stamp_file() */ 953/***************************/ 954 955int stamp_file(fname, modtime) 956 ZCONST char *fname; 957 time_t modtime; 958{ 959 ztimbuf tp; 960 961 tp.modtime = tp.actime = modtime; 962 return (utime(fname, &tp)); 963 964} /* end function stamp_file() */ 965 966#endif /* TIMESTAMP */ 967 968 969 970 971#ifndef SFX 972 973/************************/ 974/* Function version() */ 975/************************/ 976 977void version(__G) 978 __GDEF 979{ 980#ifdef __TURBOC__ 981 char buf[40]; 982#endif 983 984 sprintf((char *)slide, LoadFarString(CompiledWith), 985 986#ifdef __GNUC__ 987 "gcc ", __VERSION__, 988#else 989# if 0 990 "cc ", (sprintf(buf, " version %d", _RELEASE), buf), 991# else 992# ifdef __TURBOC__ 993 "Turbo C", (sprintf(buf, " (0x%04x = %d)", __TURBOC__, __TURBOC__), buf), 994# else 995 "unknown compiler", "", 996# endif 997# endif 998#endif 999 1000#ifdef __MINT__ 1001 "Atari TOS/MiNT", 1002#else 1003 "Atari TOS", 1004#endif 1005 1006 " (Atari ST/TT/Falcon030)", 1007 1008#ifdef __DATE__ 1009 " on ", __DATE__ 1010#else 1011 "", "" 1012#endif 1013 ); 1014 1015 (*G.message)((zvoid *)&G, slide, (ulg)strlen((char *)slide), 0); 1016 1017} /* end function version() */ 1018 1019#endif /* !SFX */ 1020