1/* 2 Copyright (c) 1990-2007 Info-ZIP. All rights reserved. 3 4 See the accompanying file LICENSE, version 2007-Mar-04 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 netware.c 12 13 This file implements these functions for a NetWare Loadable Module (NLM): 14 15 Contains: InitUnZipConsole() 16 do_wild() 17 mapattr() 18 mapname() 19 checkdir() 20 close_outfile() 21 stamp_file() 22 version() 23 screensize() 24 25 ---------------------------------------------------------------------------*/ 26 27 28#define UNZIP_INTERNAL 29#include "unzip.h" 30#include <dirent.h> 31#include <nwdir.h> 32#include <nwnamspc.h> 33#include <nwconio.h> 34#include <nwthread.h> 35#include <nwadv.h> 36 37#ifdef ACORN_FTYPE_NFS 38/* Acorn bits for NFS filetyping */ 39typedef struct { 40 uch ID[2]; 41 uch size[2]; 42 uch ID_2[4]; 43 uch loadaddr[4]; 44 uch execaddr[4]; 45 uch attr[4]; 46} RO_extra_block; 47 48#endif /* ACORN_FTYPE_NFS */ 49 50static int created_dir; /* used in mapname(), checkdir() */ 51static int renamed_fullpath; /* ditto */ 52 53 54/*********************************/ 55/* Function InitUnZipConsole() */ 56/*********************************/ 57 58void InitUnZipConsole() 59{ 60 unsigned int myHandle = GetNLMHandle(); 61 unsigned int *activeScreen = 62 ImportSymbol(myHandle, "activeScreen"); 63 unsigned int *systemConsoleScreen = 64 ImportSymbol(myHandle, "systemConsoleScreen"); 65 void (*pUseAccurateCaseForPaths)(int) = 66 ImportSymbol(myHandle, "UseAccurateCaseForPaths"); 67 68 if (!activeScreen || !systemConsoleScreen || 69 *activeScreen == *systemConsoleScreen) 70 CreateScreen("Info-ZIP UnZip Utility", 0); 71 else 72 CreateScreen("System Console", DONT_AUTO_ACTIVATE); 73 74 SetCurrentNameSpace(NW_NS_LONG); 75 if (pUseAccurateCaseForPaths) 76 pUseAccurateCaseForPaths(TRUE); 77 78 UnimportSymbol(myHandle, "activeScreen"); 79 UnimportSymbol(myHandle, "systemConsoleScreen"); 80 UnimportSymbol(myHandle, "UseAccurateCaseForPaths"); 81} 82 83 84/**********************/ 85/* Function do_wild() */ /* for porting: dir separator; match(ignore_case) */ 86/**********************/ 87 88char *do_wild(__G__ wildspec) 89 __GDEF 90 ZCONST char *wildspec; /* only used first time on a given dir */ 91{ 92 static DIR *wild_dir = (DIR *)NULL; 93 static ZCONST char *wildname; 94 static char *dirname, matchname[FILNAMSIZ]; 95 static int notfirstcall=FALSE, have_dirname, dirnamelen; 96 struct dirent *file; 97 98 /* Even when we're just returning wildspec, we *always* do so in 99 * matchname[]--calling routine is allowed to append four characters 100 * to the returned string, and wildspec may be a pointer to argv[]. 101 */ 102 if (!notfirstcall) { /* first call: must initialize everything */ 103 notfirstcall = TRUE; 104 105 if (!iswild(wildspec)) { 106 strncpy(matchname, wildspec, FILNAMSIZ); 107 matchname[FILNAMSIZ-1] = '\0'; 108 have_dirname = FALSE; 109 dir = NULL; 110 return matchname; 111 } 112 113 /* break the wildspec into a directory part and a wildcard filename */ 114 if ((wildname = strrchr(wildspec, '/')) == (ZCONST char *)NULL) { 115 dirname = "."; 116 dirnamelen = 1; 117 have_dirname = FALSE; 118 wildname = wildspec; 119 } else { 120 ++wildname; /* point at character after '/' */ 121 dirnamelen = wildname - wildspec; 122 if ((dirname = (char *)malloc(dirnamelen+1)) == (char *)NULL) { 123 Info(slide, 0x201, ((char *)slide, 124 "warning: cannot allocate wildcard buffers\n")); 125 strncpy(matchname, wildspec, FILNAMSIZ); 126 matchname[FILNAMSIZ-1] = '\0'; 127 return matchname; /* but maybe filespec was not a wildcard */ 128 } 129 strncpy(dirname, wildspec, dirnamelen); 130 dirname[dirnamelen] = '\0'; /* terminate for strcpy below */ 131 have_dirname = TRUE; 132 } 133 134 if ((wild_dir = opendir(dirname)) != (DIR *)NULL) { 135 while ((file = readdir(wild_dir)) != (struct dirent *)NULL) { 136 Trace((stderr, "do_wild: readdir returns %s\n", 137 FnFilter1(file->d_name))); 138 if (file->d_name[0] == '.' && wildname[0] != '.') 139 continue; /* Unix: '*' and '?' do not match leading dot */ 140 if (match(file->d_name, wildname, 0 WISEP) && /* 0=case sens.*/ 141 /* skip "." and ".." directory entries */ 142 strcmp(file->d_name, ".") && strcmp(file->d_name, "..")) { 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 /* if we get to here directory is exhausted, so close it */ 153 closedir(wild_dir); 154 wild_dir = (DIR *)NULL; 155 } 156 157 /* return the raw wildspec in case that works (e.g., directory not 158 * searchable, but filespec was not wild and file is readable) */ 159 strncpy(matchname, wildspec, FILNAMSIZ); 160 matchname[FILNAMSIZ-1] = '\0'; 161 return matchname; 162 } 163 164 /* last time through, might have failed opendir but returned raw wildspec */ 165 if (wild_dir == (DIR *)NULL) { 166 notfirstcall = FALSE; /* nothing left to try--reset for new wildspec */ 167 if (have_dirname) 168 free(dirname); 169 return (char *)NULL; 170 } 171 172 /* If we've gotten this far, we've read and matched at least one entry 173 * successfully (in a previous call), so dirname has been copied into 174 * matchname already. 175 */ 176 while ((file = readdir(wild_dir)) != (struct dirent *)NULL) { 177 Trace((stderr, "do_wild: readdir returns %s\n", 178 FnFilter1(file->d_name))); 179 if (file->d_name[0] == '.' && wildname[0] != '.') 180 continue; /* Unix: '*' and '?' do not match leading dot */ 181 if (match(file->d_name, wildname, 0 WISEP)) { /* 0 == case sens. */ 182 Trace((stderr, "do_wild: match() succeeds\n")); 183 if (have_dirname) { 184 /* strcpy(matchname, dirname); */ 185 strcpy(matchname+dirnamelen, file->d_name); 186 } else 187 strcpy(matchname, file->d_name); 188 return matchname; 189 } 190 } 191 192 closedir(wild_dir); /* have read at least one entry; nothing left */ 193 wild_dir = (DIR *)NULL; 194 notfirstcall = FALSE; /* reset for new wildspec */ 195 if (have_dirname) 196 free(dirname); 197 return (char *)NULL; 198 199} /* end function do_wild() */ 200 201 202/**********************/ 203/* Function mapattr() */ 204/**********************/ 205 206int mapattr(__G) 207 __GDEF 208{ 209 ulg tmp = G.crec.external_file_attributes; 210 211 G.pInfo->file_attr = 0; 212 /* initialized to 0 for check in "default" branch below... */ 213 214 switch (G.pInfo->hostnum) { 215 case AMIGA_: 216 tmp = (unsigned)(tmp>>17 & 7); /* Amiga RWE bits */ 217 G.pInfo->file_attr = (unsigned)(tmp<<6 | tmp<<3 | tmp); 218 break; 219 case UNIX_: 220 case VMS_: 221 case ACORN_: 222 case ATARI_: 223 case ATHEOS_: 224 case BEOS_: 225 case QDOS_: 226 case TANDEM_: 227 G.pInfo->file_attr = (unsigned)(tmp >> 16); 228 if (G.pInfo->file_attr != 0 || !G.extra_field) { 229 return 0; 230 } else { 231 /* Some (non-Info-ZIP) implementations of Zip for Unix and 232 * VMS (and probably others ??) leave 0 in the upper 16-bit 233 * part of the external_file_attributes field. Instead, they 234 * store file permission attributes in some extra field. 235 * As a work-around, we search for the presence of one of 236 * these extra fields and fall back to the MSDOS compatible 237 * part of external_file_attributes if one of the known 238 * e.f. types has been detected. 239 * Later, we might implement extraction of the permission 240 * bits from the VMS extra field. But for now, the work-around 241 * should be sufficient to provide "readable" extracted files. 242 * (For ASI Unix e.f., an experimental remap from the e.f. 243 * mode value IS already provided!) 244 */ 245 ush ebID; 246 unsigned ebLen; 247 uch *ef = G.extra_field; 248 unsigned ef_len = G.crec.extra_field_length; 249 int r = FALSE; 250 251 while (!r && ef_len >= EB_HEADSIZE) { 252 ebID = makeword(ef); 253 ebLen = (unsigned)makeword(ef+EB_LEN); 254 if (ebLen > (ef_len - EB_HEADSIZE)) 255 /* discoverd some e.f. inconsistency! */ 256 break; 257 switch (ebID) { 258 case EF_ASIUNIX: 259 if (ebLen >= (EB_ASI_MODE+2)) { 260 G.pInfo->file_attr = 261 (unsigned)makeword(ef+(EB_HEADSIZE+EB_ASI_MODE)); 262 /* force stop of loop: */ 263 ef_len = (ebLen + EB_HEADSIZE); 264 break; 265 } 266 /* else: fall through! */ 267 case EF_PKVMS: 268 /* "found nondecypherable e.f. with perm. attr" */ 269 r = TRUE; 270 default: 271 break; 272 } 273 ef_len -= (ebLen + EB_HEADSIZE); 274 ef += (ebLen + EB_HEADSIZE); 275 } 276 if (!r) 277 return 0; 278 } 279 /* fall through! */ 280 /* all remaining cases: expand MSDOS read-only bit into write perms */ 281 case FS_FAT_: 282 /* PKWARE's PKZip for Unix marks entries as FS_FAT_, but stores the 283 * Unix attributes in the upper 16 bits of the external attributes 284 * field, just like Info-ZIP's Zip for Unix. We try to use that 285 * value, after a check for consistency with the MSDOS attribute 286 * bits (see below). 287 */ 288 G.pInfo->file_attr = (unsigned)(tmp >> 16); 289 /* fall through! */ 290 case FS_HPFS_: 291 case FS_NTFS_: 292 case MAC_: 293 case TOPS20_: 294 default: 295 /* Ensure that DOS subdir bit is set when the entry's name ends 296 * in a '/'. Some third-party Zip programs fail to set the subdir 297 * bit for directory entries. 298 */ 299 if ((tmp & 0x10) == 0) { 300 extent fnlen = strlen(G.filename); 301 if (fnlen > 0 && G.filename[fnlen-1] == '/') 302 tmp |= 0x10; 303 } 304 /* read-only bit --> write perms; subdir bit --> dir exec bit */ 305 tmp = !(tmp & 1) << 1 | (tmp & 0x10) >> 4; 306 if ((G.pInfo->file_attr & 0700) == (unsigned)(0400 | tmp<<6)) 307 /* keep previous G.pInfo->file_attr setting, when its "owner" 308 * part appears to be consistent with DOS attribute flags! 309 */ 310 return 0; 311 G.pInfo->file_attr = (unsigned)(0444 | tmp<<6 | tmp<<3 | tmp); 312 break; 313 } /* end switch (host-OS-created-by) */ 314 315 /* for originating systems with no concept of "group," "other," "system": */ 316 umask( (int)(tmp=umask(0)) ); /* apply mask to expanded r/w(/x) perms */ 317 G.pInfo->file_attr &= ~tmp; 318 319 return 0; 320 321} /* end function mapattr() */ 322 323 324 325 326/**********************/ 327/* Function mapname() */ 328/**********************/ 329 330int mapname(__G__ renamed) 331 __GDEF 332 int renamed; 333/* 334 * returns: 335 * MPN_OK - no problem detected 336 * MPN_INF_TRUNC - caution (truncated filename) 337 * MPN_INF_SKIP - info "skip entry" (dir doesn't exist) 338 * MPN_ERR_SKIP - error -> skip entry 339 * MPN_ERR_TOOLONG - error -> path is too long 340 * MPN_NOMEM - error (memory allocation failed) -> skip entry 341 * [also MPN_VOL_LABEL, MPN_CREATED_DIR] 342 */ 343{ 344 char pathcomp[FILNAMSIZ]; /* path-component buffer */ 345 char *pp, *cp=(char *)NULL; /* character pointers */ 346 char *lastsemi=(char *)NULL; /* pointer to last semi-colon in pathcomp */ 347#ifdef ACORN_FTYPE_NFS 348 char *lastcomma=(char *)NULL; /* pointer to last comma in pathcomp */ 349 RO_extra_block *ef_spark; /* pointer Acorn FTYPE ef block */ 350#endif 351 int killed_ddot = FALSE; /* is set when skipping "../" pathcomp */ 352 int error = MPN_OK; 353 register unsigned workch; /* hold the character being tested */ 354 355 356/*--------------------------------------------------------------------------- 357 Initialize various pointers and counters and stuff. 358 ---------------------------------------------------------------------------*/ 359 360 if (G.pInfo->vollabel) 361 return MPN_VOL_LABEL; /* can't set disk volume labels in Netware */ 362 363 /* can create path as long as not just freshening, or if user told us */ 364 G.create_dirs = (!uO.fflag || renamed); 365 366 created_dir = FALSE; /* not yet */ 367 368 /* user gave full pathname: don't prepend rootpath */ 369 renamed_fullpath = (renamed && (*G.filename == '/')); 370 371 if (checkdir(__G__ (char *)NULL, INIT) == MPN_NOMEM) 372 return MPN_NOMEM; /* initialize path buffer, unless no memory */ 373 374 *pathcomp = '\0'; /* initialize translation buffer */ 375 pp = pathcomp; /* point to translation buffer */ 376 if (uO.jflag) /* junking directories */ 377 cp = (char *)strrchr(G.filename, '/'); 378 if (cp == (char *)NULL) /* no '/' or not junking dirs */ 379 cp = G.filename; /* point to internal zipfile-member pathname */ 380 else 381 ++cp; /* point to start of last component of path */ 382 383/*--------------------------------------------------------------------------- 384 Begin main loop through characters in filename. 385 ---------------------------------------------------------------------------*/ 386 387 while ((workch = (uch)*cp++) != 0) { 388 389 switch (workch) { 390 case '/': /* can assume -j flag not given */ 391 *pp = '\0'; 392 if (strcmp(pathcomp, ".") == 0) { 393 /* don't bother appending "./" to the path */ 394 *pathcomp = '\0'; 395 } else if (!uO.ddotflag && strcmp(pathcomp, "..") == 0) { 396 /* "../" dir traversal detected, skip over it */ 397 *pathcomp = '\0'; 398 killed_ddot = TRUE; /* set "show message" flag */ 399 } 400 /* when path component is not empty, append it now */ 401 if (*pathcomp != '\0' && 402 ((error = checkdir(__G__ pathcomp, APPEND_DIR)) 403 & MPN_MASK) > MPN_INF_TRUNC) 404 return error; 405 pp = pathcomp; /* reset conversion buffer for next piece */ 406 lastsemi = (char *)NULL; /* leave direct. semi-colons alone */ 407 break; 408 409 case ';': /* VMS version (or DEC-20 attrib?) */ 410 lastsemi = pp; 411 *pp++ = ';'; /* keep for now; remove VMS ";##" */ 412 break; /* later, if requested */ 413 414#ifdef ACORN_FTYPE_NFS 415 case ',': /* NFS filetype extension */ 416 lastcomma = pp; 417 *pp++ = ','; /* keep for now; may need to remove */ 418 break; /* later, if requested */ 419#endif 420 421 default: 422 /* allow European characters in filenames: */ 423 if (isprint(workch) || (128 <= workch && workch <= 254)) 424 *pp++ = (char)workch; 425 } /* end switch */ 426 427 } /* end while loop */ 428 429 /* Show warning when stripping insecure "parent dir" path components */ 430 if (killed_ddot && QCOND2) { 431 Info(slide, 0, ((char *)slide, 432 "warning: skipped \"../\" path component(s) in %s\n", 433 FnFilter1(G.filename))); 434 if (!(error & ~MPN_MASK)) 435 error = (error & MPN_MASK) | PK_WARN; 436 } 437 438/*--------------------------------------------------------------------------- 439 Report if directory was created (and no file to create: filename ended 440 in '/'), check name to be sure it exists, and combine path and name be- 441 fore exiting. 442 ---------------------------------------------------------------------------*/ 443 444 if (G.filename[strlen(G.filename) - 1] == '/') { 445 checkdir(__G__ G.filename, GETPATH); 446 if (created_dir) { 447 if (QCOND2) { 448 Info(slide, 0, ((char *)slide, " creating: %s\n", 449 FnFilter1(G.filename))); 450 } 451#if !defined(NO_CHMOD) && !defined(NLM) 452 /* In NetWare, chmod does not work on directories */ 453 /* set approx. dir perms (make sure can still read/write in dir) */ 454 if (chmod(G.filename, (0xffff & G.pInfo->file_attr) | 0700)) 455 perror("chmod (directory attributes) error"); 456#endif 457 /* set dir time (note trailing '/') */ 458 return (error & ~MPN_MASK) | MPN_CREATED_DIR; 459 } 460 /* dir existed already; don't look for data to extract */ 461 return (error & ~MPN_MASK) | MPN_INF_SKIP; 462 } 463 464 *pp = '\0'; /* done with pathcomp: terminate it */ 465 466 /* if not saving them, remove VMS version numbers (appended ";###") */ 467 if (!uO.V_flag && lastsemi) { 468 pp = lastsemi + 1; 469 while (isdigit((uch)(*pp))) 470 ++pp; 471 if (*pp == '\0') /* only digits between ';' and end: nuke */ 472 *lastsemi = '\0'; 473 } 474 475#ifdef ACORN_FTYPE_NFS 476 /* translate Acorn filetype information if asked to do so */ 477 if (uO.acorn_nfs_ext && 478 (ef_spark = (RO_extra_block *) 479 getRISCOSexfield(G.extra_field, G.lrec.extra_field_length)) 480 != (RO_extra_block *)NULL) 481 { 482 /* file *must* have a RISC OS extra field */ 483 long ft = (long)makelong(ef_spark->loadaddr); 484 /*32-bit*/ 485 if (lastcomma) { 486 pp = lastcomma + 1; 487 while (isxdigit((uch)(*pp))) ++pp; 488 if (pp == lastcomma+4 && *pp == '\0') *lastcomma='\0'; /* nuke */ 489 } 490 if ((ft & 1<<31)==0) ft=0x000FFD00; 491 sprintf(pathcomp+strlen(pathcomp), ",%03x", (int)(ft>>8) & 0xFFF); 492 } 493#endif /* ACORN_FTYPE_NFS */ 494 495 if (*pathcomp == '\0') { 496 Info(slide, 1, ((char *)slide, "mapname: conversion of %s failed\n", 497 FnFilter1(G.filename))); 498 return (error & ~MPN_MASK) | MPN_ERR_SKIP; 499 } 500 501 checkdir(__G__ pathcomp, APPEND_NAME); /* returns 1 if truncated: care? */ 502 checkdir(__G__ G.filename, GETPATH); 503 504 return error; 505 506} /* end function mapname() */ 507 508 509 510 511/***********************/ 512/* Function checkdir() */ 513/***********************/ 514 515int checkdir(__G__ pathcomp, flag) 516 __GDEF 517 char *pathcomp; 518 int flag; 519/* 520 * returns: 521 * MPN_OK - no problem detected 522 * MPN_INF_TRUNC - (on APPEND_NAME) truncated filename 523 * MPN_INF_SKIP - path doesn't exist, not allowed to create 524 * MPN_ERR_SKIP - path doesn't exist, tried to create and failed; or path 525 * exists and is not a directory, but is supposed to be 526 * MPN_ERR_TOOLONG - path is too long 527 * MPN_NOMEM - can't allocate memory for filename buffers 528 */ 529{ 530 static int rootlen = 0; /* length of rootpath */ 531 static char *rootpath; /* user's "extract-to" directory */ 532 static char *buildpath; /* full path (so far) to extracted file */ 533 static char *end; /* pointer to end of buildpath ('\0') */ 534 535# define FN_MASK 7 536# define FUNCTION (flag & FN_MASK) 537 538 539/*--------------------------------------------------------------------------- 540 APPEND_DIR: append the path component to the path being built and check 541 for its existence. If doesn't exist and we are creating directories, do 542 so for this one; else signal success or error as appropriate. 543 ---------------------------------------------------------------------------*/ 544 545 if (FUNCTION == APPEND_DIR) { 546 int too_long = FALSE; 547#ifdef SHORT_NAMES 548 char *old_end = end; 549#endif 550 551 Trace((stderr, "appending dir segment [%s]\n", FnFilter1(pathcomp))); 552 while ((*end = *pathcomp++) != '\0') 553 ++end; 554#ifdef SHORT_NAMES /* path components restricted to 14 chars, typically */ 555 if ((end-old_end) > FILENAME_MAX) /* GRR: proper constant? */ 556 *(end = old_end + FILENAME_MAX) = '\0'; 557#endif 558 559 /* GRR: could do better check, see if overrunning buffer as we go: 560 * check end-buildpath after each append, set warning variable if 561 * within 20 of FILNAMSIZ; then if var set, do careful check when 562 * appending. Clear variable when begin new path. */ 563 564 if ((end-buildpath) > FILNAMSIZ-3) /* need '/', one-char name, '\0' */ 565 too_long = TRUE; /* check if extracting directory? */ 566 if (stat(buildpath, &G.statbuf)) { /* path doesn't exist */ 567 if (!G.create_dirs) { /* told not to create (freshening) */ 568 free(buildpath); 569 /* path doesn't exist: nothing to do */ 570 return MPN_INF_SKIP; 571 } 572 if (too_long) { 573 Info(slide, 1, ((char *)slide, 574 "checkdir error: path too long: %s\n", 575 FnFilter1(buildpath))); 576 free(buildpath); 577 /* no room for filenames: fatal */ 578 return MPN_ERR_TOOLONG; 579 } 580 if (mkdir(buildpath) == -1) { /* create the directory */ 581 Info(slide, 1, ((char *)slide, 582 "checkdir error: cannot create %s\n\ 583 unable to process %s.\n", 584 FnFilter2(buildpath), FnFilter1(G.filename))); 585 free(buildpath); 586 /* path didn't exist, tried to create, failed */ 587 return MPN_ERR_SKIP; 588 } 589 created_dir = TRUE; 590 } else if (!S_ISDIR(G.statbuf.st_mode)) { 591 Info(slide, 1, ((char *)slide, 592 "checkdir error: %s exists but is not directory\n\ 593 unable to process %s.\n", 594 FnFilter2(buildpath), FnFilter1(G.filename))); 595 free(buildpath); 596 /* path existed but wasn't dir */ 597 return MPN_ERR_SKIP; 598 } 599 if (too_long) { 600 Info(slide, 1, ((char *)slide, 601 "checkdir error: path too long: %s\n", FnFilter1(buildpath))); 602 free(buildpath); 603 /* no room for filenames: fatal */ 604 return MPN_ERR_TOOLONG; 605 } 606 *end++ = '/'; 607 *end = '\0'; 608 Trace((stderr, "buildpath now = [%s]\n", FnFilter1(buildpath))); 609 return MPN_OK; 610 611 } /* end if (FUNCTION == APPEND_DIR) */ 612 613/*--------------------------------------------------------------------------- 614 GETPATH: copy full path to the string pointed at by pathcomp, and free 615 buildpath. 616 ---------------------------------------------------------------------------*/ 617 618 if (FUNCTION == GETPATH) { 619 strcpy(pathcomp, buildpath); 620 Trace((stderr, "getting and freeing path [%s]\n", 621 FnFilter1(pathcomp))); 622 free(buildpath); 623 buildpath = end = (char *)NULL; 624 return MPN_OK; 625 } 626 627/*--------------------------------------------------------------------------- 628 APPEND_NAME: assume the path component is the filename; append it and 629 return without checking for existence. 630 ---------------------------------------------------------------------------*/ 631 632 if (FUNCTION == APPEND_NAME) { 633#ifdef SHORT_NAMES 634 char *old_end = end; 635#endif 636 637 Trace((stderr, "appending filename [%s]\n", FnFilter1(pathcomp))); 638 while ((*end = *pathcomp++) != '\0') { 639 ++end; 640#ifdef SHORT_NAMES /* truncate name at 14 characters, typically */ 641 if ((end-old_end) > FILENAME_MAX) /* GRR: proper constant? */ 642 *(end = old_end + FILENAME_MAX) = '\0'; 643#endif 644 if ((end-buildpath) >= FILNAMSIZ) { 645 *--end = '\0'; 646 Info(slide, 0x201, ((char *)slide, 647 "checkdir warning: path too long; truncating\n\ 648 %s\n -> %s\n", 649 FnFilter1(G.filename), FnFilter2(buildpath))); 650 return MPN_INF_TRUNC; /* filename truncated */ 651 } 652 } 653 Trace((stderr, "buildpath now = [%s]\n", FnFilter1(buildpath))); 654 /* could check for existence here, prompt for new name... */ 655 return MPN_OK; 656 } 657 658/*--------------------------------------------------------------------------- 659 INIT: allocate and initialize buffer space for the file currently being 660 extracted. If file was renamed with an absolute path, don't prepend the 661 extract-to path. 662 ---------------------------------------------------------------------------*/ 663 664/* GRR: for VMS and TOPS-20, add up to 13 to strlen */ 665 666 if (FUNCTION == INIT) { 667 Trace((stderr, "initializing buildpath to ")); 668#ifdef ACORN_FTYPE_NFS 669 if ((buildpath = (char *)malloc(strlen(G.filename)+rootlen+ 670 (uO.acorn_nfs_ext ? 5 : 1))) 671#else 672 if ((buildpath = (char *)malloc(strlen(G.filename)+rootlen+1)) 673#endif 674 == (char *)NULL) 675 return MPN_NOMEM; 676 if ((rootlen > 0) && !renamed_fullpath) { 677 strcpy(buildpath, rootpath); 678 end = buildpath + rootlen; 679 } else { 680 *buildpath = '\0'; 681 end = buildpath; 682 } 683 Trace((stderr, "[%s]\n", FnFilter1(buildpath))); 684 return MPN_OK; 685 } 686 687/*--------------------------------------------------------------------------- 688 ROOT: if appropriate, store the path in rootpath and create it if neces- 689 sary; else assume it's a zipfile member and return. This path segment 690 gets used in extracting all members from every zipfile specified on the 691 command line. 692 ---------------------------------------------------------------------------*/ 693 694#if (!defined(SFX) || defined(SFX_EXDIR)) 695 if (FUNCTION == ROOT) { 696 Trace((stderr, "initializing root path to [%s]\n", 697 FnFilter1(pathcomp))); 698 if (pathcomp == (char *)NULL) { 699 rootlen = 0; 700 return MPN_OK; 701 } 702 if ((rootlen = strlen(pathcomp)) > 0) { 703 if (pathcomp[rootlen-1] == '/') { 704 pathcomp[--rootlen] = '\0'; 705 } 706 if (rootlen > 0 && (stat(pathcomp, &G.statbuf) || 707 !S_ISDIR(G.statbuf.st_mode))) /* path does not exist */ 708 { 709 if (!G.create_dirs /* || iswild(pathcomp) */ ) { 710 rootlen = 0; 711 /* skip (or treat as stored file) */ 712 return MPN_INF_SKIP; 713 } 714 /* create the directory (could add loop here to scan pathcomp 715 * and create more than one level, but why really necessary?) */ 716 if (mkdir(pathcomp) == -1) { 717 Info(slide, 1, ((char *)slide, 718 "checkdir: cannot create extraction directory: %s\n", 719 FnFilter1(pathcomp))); 720 rootlen = 0; 721 /* path didn't exist, tried to create, and failed: */ 722 /* file exists, or 2+ subdirectory levels required */ 723 return MPN_ERR_SKIP; 724 } 725 } 726 if ((rootpath = (char *)malloc(rootlen+2)) == (char *)NULL) { 727 rootlen = 0; 728 return MPN_NOMEM; 729 } 730 strcpy(rootpath, pathcomp); 731 rootpath[rootlen++] = '/'; 732 rootpath[rootlen] = '\0'; 733 Trace((stderr, "rootpath now = [%s]\n", FnFilter1(rootpath))); 734 } 735 return MPN_OK; 736 } 737#endif /* !SFX || SFX_EXDIR */ 738 739/*--------------------------------------------------------------------------- 740 END: free rootpath, immediately prior to program exit. 741 ---------------------------------------------------------------------------*/ 742 743 if (FUNCTION == END) { 744 Trace((stderr, "freeing rootpath\n")); 745 if (rootlen > 0) { 746 free(rootpath); 747 rootlen = 0; 748 } 749 return MPN_OK; 750 } 751 752 return MPN_INVALID; /* should never reach */ 753 754} /* end function checkdir() */ 755 756 757 758/****************************/ 759/* Function close_outfile() */ 760/****************************/ 761 762void close_outfile(__G) /* GRR: change to return PK-style warning level */ 763 __GDEF 764{ 765 fclose(G.outfile); 766 767 /* skip restoring time stamps on user's request */ 768 if (uO.D_flag <= 1) { 769 WORD date = G.lrec.last_mod_dos_datetime >> 16; 770 WORD time = G.lrec.last_mod_dos_datetime & 0xffff; 771 static struct ModifyStructure changeBuffer; 772 773 /* set the file's access and modification times */ 774 changeBuffer.MLastAccessedDate = date; 775 changeBuffer.MLastUpdatedDate = date; 776 changeBuffer.MLastUpdatedTime = time; 777 if (ChangeDirectoryEntry(G.filename, &changeBuffer, 778 MLastAccessedDateBit | MLastUpdatedDateBit | MLastUpdatedTimeBit, 779 0)) 780 { 781 if (uO.qflag) 782 Info(slide, 0x201, ((char *)slide, 783 "warning: cannot set times for %s\n", 784 FnFilter1(G.filename))); 785 else 786 Info(slide, 0x201, ((char *)slide, 787 " (warning) cannot set times")); 788 } 789 } 790 791/*--------------------------------------------------------------------------- 792 Change the file permissions from default ones to those stored in the 793 zipfile. 794 ---------------------------------------------------------------------------*/ 795 796 if (chmod(G.filename, 0xffff & G.pInfo->file_attr)) 797 perror("chmod (file attributes) error"); 798 799} /* end function close_outfile() */ 800 801 802#ifdef TIMESTAMP 803 804/***************************/ 805/* Function stamp_file() */ 806/***************************/ 807 808int stamp_file(fname, modtime) 809 ZCONST char *fname; 810 time_t modtime; 811{ 812 ztimbuf tp; 813 814 tp.modtime = tp.actime = modtime; 815 return (utime(fname, &tp)); 816 817} /* end function stamp_file() */ 818 819#endif /* TIMESTAMP */ 820 821 822/************************/ 823/* Function version() */ 824/************************/ 825 826void version(__G) 827 __GDEF 828{ 829 int len; 830#if defined(__IBMC__) || defined(__WATCOMC__) || defined(_MSC_VER) 831 char buf[80]; 832#endif 833 834 len = sprintf((char *)slide, LoadFarString(CompiledWith), 835 836#if defined(__GNUC__) 837# ifdef __EMX__ /* __EMX__ is defined as "1" only (sigh) */ 838 "emx+gcc ", __VERSION__, 839# else 840 "gcc/2 ", __VERSION__, 841# endif 842#elif defined(__WATCOMC__) 843 "Watcom C", (sprintf(buf, " (__WATCOMC__ = %d)", __WATCOMC__), buf), 844#elif defined(__TURBOC__) 845# ifdef __BORLANDC__ 846 "Borland C++", 847# if (__BORLANDC__ < 0x0460) 848 " 1.0", 849# elif (__BORLANDC__ == 0x0460) 850 " 1.5", /* from Kai Uwe: three less than DOS */ 851# else 852 " 2.0", /* (__BORLANDC__ == 0x0500)? */ 853# endif 854# else 855 "Turbo C", /* these are probably irrelevant */ 856# if (__TURBOC__ >= 661) 857 "++ 1.0 or later", 858# elif (__TURBOC__ == 661) 859 " 3.0?", 860# elif (__TURBOC__ == 397) 861 " 2.0", 862# else 863 " 1.0 or 1.5?", 864# endif 865# endif 866#elif defined(MSC) 867 "Microsoft C ", 868# ifdef _MSC_VER 869 (sprintf(buf, "%d.%02d", _MSC_VER/100, _MSC_VER%100), buf), 870# else 871 "5.1 or earlier", 872# endif 873#else 874 "unknown compiler", "", 875#endif /* ?compilers */ 876 877 "NetWare", 878 " (32-bit)", 879 880#ifdef __DATE__ 881 " on ", __DATE__ 882#else 883 "", "" 884#endif 885 ); 886 887 (*G.message)((zvoid *)&G, slide, (ulg)len, 0); 888 /* MSC can't handle huge macro expansions */ 889 890} /* end function version() */ 891 892 893#ifdef MORE 894 895/*************************/ 896/* Function screensize() */ 897/*************************/ 898 899int screensize(int *tt_rows, int *tt_cols) 900{ 901 WORD height; 902 WORD width; 903 904 if (GetSizeOfScreen(&height, &width) == 0) { 905 if (tt_rows != NULL) *tt_rows = height; 906 if (tt_cols != NULL) *tt_cols = width; 907 return 0; /* signal success */ 908 } else { 909 if (tt_rows != NULL) *tt_rows = 25; 910 if (tt_cols != NULL) *tt_cols = 80; 911 return 1; /* signal failure */ 912 } 913} 914 915#endif /* MORE */ 916