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 msdos.c 12 13 MSDOS-specific routines for use with Info-ZIP's UnZip 5.3 and later. 14 15 Contains: Opendir() (from zip) 16 Readdir() (from zip) 17 do_wild() 18 mapattr() 19 mapname() 20 maskDOSdevice() 21 map2fat() 22 checkdir() 23 isfloppy() 24 z_dos_chmod() 25 volumelabel() (non-djgpp, non-emx) 26 close_outfile() 27 stamp_file() (TIMESTAMP only) 28 prepare_ISO_OEM_translat() 29 dateformat() 30 version() 31 zcalloc() (16-bit, only) 32 zcfree() (16-bit, only) 33 _dos_getcountryinfo() (djgpp 1.x, emx) 34 [_dos_getftime() (djgpp 1.x, emx) to be added] 35 _dos_setftime() (djgpp 1.x, emx) 36 _dos_setfileattr() (djgpp 1.x, emx) 37 _dos_getdrive() (djgpp 1.x, emx) 38 _dos_creat() (djgpp 1.x, emx) 39 _dos_close() (djgpp 1.x, emx) 40 volumelabel() (djgpp, emx) 41 _dos_getcountryinfo() (djgpp 2.x) 42 _is_executable() (djgpp 2.x) 43 __crt0_glob_function() (djgpp 2.x) 44 __crt0_load_environment_file() (djgpp 2.x) 45 dos_getcodepage() (all, ASM system call) 46 screensize() (emx, Watcom 32-bit) 47 int86x_realmode() (Watcom 32-bit) 48 stat_bandaid() (Watcom) 49 50 ---------------------------------------------------------------------------*/ 51 52 53 54#define UNZIP_INTERNAL 55#include "unzip.h" 56 57/* fUnZip does not need anything from here except the zcalloc() & zcfree() 58 * function pair (when Deflate64 support is enabled in 16-bit environment). 59 */ 60#ifndef FUNZIP 61 62static void maskDOSdevice(__GPRO__ char *pathcomp, char *last_dot); 63#ifdef MAYBE_PLAIN_FAT 64 static void map2fat OF((char *pathcomp, char *last_dot)); 65#endif 66static int isfloppy OF((int nDrive)); 67static int z_dos_chmod OF((__GPRO__ ZCONST char *fname, int attributes)); 68static int volumelabel OF((ZCONST char *newlabel)); 69#if (!defined(SFX) && !defined(WINDLL)) 70 static int is_running_on_windows OF((void)); 71#endif 72static int getdoscodepage OF((void)); 73 74static int created_dir; /* used by mapname(), checkdir() */ 75static int renamed_fullpath; /* ditto */ 76static unsigned nLabelDrive; /* ditto, plus volumelabel() */ 77 78 79 80/*****************************/ 81/* Strings used in msdos.c */ 82/*****************************/ 83 84#ifndef SFX 85 static ZCONST char Far CantAllocateWildcard[] = 86 "warning: cannot allocate wildcard buffers\n"; 87#endif 88static ZCONST char Far WarnDirTraversSkip[] = 89 "warning: skipped \"../\" path component(s) in %s\n"; 90static ZCONST char Far Creating[] = " creating: %s\n"; 91static ZCONST char Far ConversionFailed[] = 92 "mapname: conversion of %s failed\n"; 93static ZCONST char Far Labelling[] = "labelling %c: %-22s\n"; 94static ZCONST char Far ErrSetVolLabel[] = 95 "mapname: error setting volume label\n"; 96static ZCONST char Far PathTooLong[] = "checkdir error: path too long: %s\n"; 97static ZCONST char Far CantCreateDir[] = "checkdir error: cannot create %s\n\ 98 unable to process %s.\n"; 99static ZCONST char Far DirIsntDirectory[] = 100 "checkdir error: %s exists but is not directory\n\ 101 unable to process %s.\n"; 102static ZCONST char Far PathTooLongTrunc[] = 103 "checkdir warning: path too long; truncating\n %s\n\ 104 -> %s\n"; 105#if (!defined(SFX) || defined(SFX_EXDIR)) 106 static ZCONST char Far CantCreateExtractDir[] = 107 "checkdir: cannot create extraction directory: %s\n"; 108#endif 109static ZCONST char Far AttribsMayBeWrong[] = 110 "\nwarning: file attributes may not be correct\n"; 111#if (!defined(SFX) && !defined(WINDLL)) 112 static ZCONST char Far WarnUsedOnWindows[] = 113 "\n%s warning: You are using the MSDOS version on Windows.\n" 114 "Please try the native Windows version before reporting any problems.\n"; 115#endif 116 117 118 119/****************************/ 120/* Macros used in msdos.c */ 121/****************************/ 122 123#ifdef WATCOMC_386 124# define WREGS(v,r) (v.w.r) 125# define int86x int386x 126 static int int86x_realmode(int inter_no, union REGS *in, 127 union REGS *out, struct SREGS *seg); 128# define F_intdosx(ir,or,sr) int86x_realmode(0x21, ir, or, sr) 129# define XXX__MK_FP_IS_BROKEN 130#else 131# if (defined(__DJGPP__) && (__DJGPP__ >= 2)) 132# define WREGS(v,r) (v.w.r) 133# else 134# define WREGS(v,r) (v.x.r) 135# endif 136# define F_intdosx(ir,or,sr) intdosx(ir, or, sr) 137#endif 138 139#if (defined(__GO32__) || defined(__EMX__)) 140# include <dirent.h> /* use readdir() */ 141# define MKDIR(path,mode) mkdir(path,mode) 142# define Opendir opendir 143# define Readdir readdir 144# define Closedir closedir 145# define zdirent dirent 146# define zDIR DIR 147# ifdef __EMX__ 148# include <dos.h> 149# define GETDRIVE(d) d = _getdrive() 150# define FA_LABEL A_LABEL 151# else 152# define GETDRIVE(d) _dos_getdrive(&d) 153# endif 154# if defined(_A_SUBDIR) /* MSC dos.h and compatibles */ 155# define FSUBDIR _A_SUBDIR 156# elif defined(FA_DIREC) /* Borland dos.h and compatible variants */ 157# define FSUBDIR FA_DIREC 158# elif defined(A_DIR) /* EMX dir.h (and dirent.h) */ 159# define FSUBDIR A_DIR 160# else /* fallback definition */ 161# define FSUBDIR 0x10 162# endif 163# if defined(_A_VOLID) /* MSC dos.h and compatibles */ 164# define FVOLID _A_VOLID 165# elif defined(FA_LABEL) /* Borland dos.h and compatible variants */ 166# define FVOLID FA_LABEL 167# elif defined(A_LABEL) /* EMX dir.h (and dirent.h) */ 168# define FVOLID A_LABEL 169# else 170# define FVOLID 0x08 171# endif 172#else /* !(__GO32__ || __EMX__) */ 173# define MKDIR(path,mode) mkdir(path) 174# ifdef __TURBOC__ 175# define FATTR FA_HIDDEN+FA_SYSTEM+FA_DIREC 176# define FVOLID FA_LABEL 177# define FSUBDIR FA_DIREC 178# define FFIRST(n,d,a) findfirst(n,(struct ffblk *)d,a) 179# define FNEXT(d) findnext((struct ffblk *)d) 180# define GETDRIVE(d) d=getdisk()+1 181# include <dir.h> 182# else /* !__TURBOC__ */ 183# define FATTR _A_HIDDEN+_A_SYSTEM+_A_SUBDIR 184# define FVOLID _A_VOLID 185# define FSUBDIR _A_SUBDIR 186# define FFIRST(n,d,a) _dos_findfirst(n,a,(struct find_t *)d) 187# define FNEXT(d) _dos_findnext((struct find_t *)d) 188# define GETDRIVE(d) _dos_getdrive(&d) 189# include <direct.h> 190# endif /* ?__TURBOC__ */ 191 typedef struct zdirent { 192 char d_reserved[30]; 193 char d_name[13]; 194 int d_first; 195 } zDIR; 196 zDIR *Opendir OF((const char *)); 197 struct zdirent *Readdir OF((zDIR *)); 198# define Closedir free 199 200 201 202 203#ifndef SFX 204 205/**********************/ /* Borland C++ 3.x has its own opendir/readdir */ 206/* Function Opendir() */ /* library routines, but earlier versions don't, */ 207/**********************/ /* so use ours regardless */ 208 209zDIR *Opendir(name) 210 const char *name; /* name of directory to open */ 211{ 212 zDIR *dirp; /* malloc'd return value */ 213 char *nbuf; /* malloc'd temporary string */ 214 extent len = strlen(name); /* path length to avoid strlens and strcats */ 215 216 217 if ((dirp = (zDIR *)malloc(sizeof(zDIR))) == (zDIR *)NULL) 218 return (zDIR *)NULL; 219 if ((nbuf = malloc(len + 6)) == (char *)NULL) { 220 free(dirp); 221 return (zDIR *)NULL; 222 } 223 strcpy(nbuf, name); 224 if (len > 0) { 225 if (nbuf[len-1] == ':') { 226 nbuf[len++] = '.'; 227 } else if (nbuf[len-1] == '/' || nbuf[len-1] == '\\') 228 --len; 229 } 230 strcpy(nbuf+len, "/*.*"); 231 Trace((stderr, "Opendir: nbuf = [%s]\n", FnFilter1(nbuf))); 232 233 if (FFIRST(nbuf, dirp, FATTR)) { 234 free((zvoid *)nbuf); 235 return (zDIR *)NULL; 236 } 237 free((zvoid *)nbuf); 238 dirp->d_first = 1; 239 return dirp; 240} 241 242 243 244 245 246/**********************/ 247/* Function Readdir() */ 248/**********************/ 249 250struct zdirent *Readdir(d) 251 zDIR *d; /* directory stream from which to read */ 252{ 253 /* Return pointer to first or next directory entry, or NULL if end. */ 254 255 if (d->d_first) 256 d->d_first = 0; 257 else 258 if (FNEXT(d)) 259 return (struct zdirent *)NULL; 260 return (struct zdirent *)d; 261} 262 263#endif /* !SFX */ 264#endif /* ?(__GO32__ || __EMX__) */ 265 266 267 268 269 270#ifndef SFX 271 272/************************/ 273/* Function do_wild() */ /* identical to OS/2 version */ 274/************************/ 275 276char *do_wild(__G__ wildspec) 277 __GDEF 278 ZCONST char *wildspec; /* only used first time on a given dir */ 279{ 280 static zDIR *wild_dir = (zDIR *)NULL; 281 static ZCONST char *wildname; 282 static char *dirname, matchname[FILNAMSIZ]; 283 static int notfirstcall=FALSE, have_dirname, dirnamelen; 284 char *fnamestart; 285 struct zdirent *file; 286 287 /* Even when we're just returning wildspec, we *always* do so in 288 * matchname[]--calling routine is allowed to append four characters 289 * to the returned string, and wildspec may be a pointer to argv[]. 290 */ 291 if (!notfirstcall) { /* first call: must initialize everything */ 292 notfirstcall = TRUE; 293 294 if (!iswild(wildspec)) { 295 strncpy(matchname, wildspec, FILNAMSIZ); 296 matchname[FILNAMSIZ-1] = '\0'; 297 have_dirname = FALSE; 298 wild_dir = NULL; 299 return matchname; 300 } 301 302 /* break the wildspec into a directory part and a wildcard filename */ 303 if ((wildname = strrchr(wildspec, '/')) == (ZCONST char *)NULL && 304 (wildname = strrchr(wildspec, ':')) == (ZCONST char *)NULL) { 305 dirname = "."; 306 dirnamelen = 1; 307 have_dirname = FALSE; 308 wildname = wildspec; 309 } else { 310 ++wildname; /* point at character after '/' or ':' */ 311 dirnamelen = (int)(wildname - wildspec); 312 if ((dirname = (char *)malloc(dirnamelen+1)) == (char *)NULL) { 313 Info(slide, 1, ((char *)slide, 314 LoadFarString(CantAllocateWildcard))); 315 strncpy(matchname, wildspec, FILNAMSIZ); 316 matchname[FILNAMSIZ-1] = '\0'; 317 return matchname; /* but maybe filespec was not a wildcard */ 318 } 319/* GRR: can't strip trailing char for opendir since might be "d:/" or "d:" 320 * (would have to check for "./" at end--let opendir handle it instead) */ 321 strncpy(dirname, wildspec, dirnamelen); 322 dirname[dirnamelen] = '\0'; /* terminate for strcpy below */ 323 have_dirname = TRUE; 324 } 325 Trace((stderr, "do_wild: dirname = [%s]\n", FnFilter1(dirname))); 326 327 if ((wild_dir = Opendir(dirname)) != (zDIR *)NULL) { 328 if (have_dirname) { 329 strcpy(matchname, dirname); 330 fnamestart = matchname + dirnamelen; 331 } else 332 fnamestart = matchname; 333 while ((file = Readdir(wild_dir)) != (struct zdirent *)NULL) { 334 Trace((stderr, "do_wild: readdir returns %s\n", 335 FnFilter1(file->d_name))); 336 strcpy(fnamestart, file->d_name); 337 if (strrchr(fnamestart, '.') == (char *)NULL) 338 strcat(fnamestart, "."); 339 /* 1 == ignore case (for case-insensitive DOS-FS) */ 340 if (match(fnamestart, wildname, 1 WISEP) && 341 /* skip "." and ".." directory entries */ 342 strcmp(fnamestart, ".") && strcmp(fnamestart, "..")) { 343 Trace((stderr, "do_wild: match() succeeds\n")); 344 /* remove trailing dot */ 345 fnamestart += strlen(fnamestart) - 1; 346 if (*fnamestart == '.') 347 *fnamestart = '\0'; 348 return matchname; 349 } 350 } 351 /* if we get to here directory is exhausted, so close it */ 352 Closedir(wild_dir); 353 wild_dir = (zDIR *)NULL; 354 } 355#ifdef DEBUG 356 else { 357 Trace((stderr, "do_wild: Opendir(%s) returns NULL\n", 358 FnFilter1(dirname))); 359 } 360#endif /* DEBUG */ 361 362 /* return the raw wildspec in case that works (e.g., directory not 363 * searchable, but filespec was not wild and file is readable) */ 364 strncpy(matchname, wildspec, FILNAMSIZ); 365 matchname[FILNAMSIZ-1] = '\0'; 366 return matchname; 367 } 368 369 /* last time through, might have failed opendir but returned raw wildspec */ 370 if (wild_dir == (zDIR *)NULL) { 371 notfirstcall = FALSE; /* nothing left to try--reset for new wildspec */ 372 if (have_dirname) 373 free(dirname); 374 return (char *)NULL; 375 } 376 377 /* If we've gotten this far, we've read and matched at least one entry 378 * successfully (in a previous call), so dirname has been copied into 379 * matchname already. 380 */ 381 if (have_dirname) { 382 /* strcpy(matchname, dirname); */ 383 fnamestart = matchname + dirnamelen; 384 } else 385 fnamestart = matchname; 386 while ((file = Readdir(wild_dir)) != (struct zdirent *)NULL) { 387 Trace((stderr, "do_wild: readdir returns %s\n", 388 FnFilter1(file->d_name))); 389 strcpy(fnamestart, file->d_name); 390 if (strrchr(fnamestart, '.') == (char *)NULL) 391 strcat(fnamestart, "."); 392 if (match(fnamestart, wildname, 1 WISEP)) { /* 1 == ignore case */ 393 Trace((stderr, "do_wild: match() succeeds\n")); 394 /* remove trailing dot */ 395 fnamestart += strlen(fnamestart) - 1; 396 if (*fnamestart == '.') 397 *fnamestart = '\0'; 398 return matchname; 399 } 400 } 401 402 Closedir(wild_dir); /* have read at least one entry; nothing left */ 403 wild_dir = (zDIR *)NULL; 404 notfirstcall = FALSE; /* reset for new wildspec */ 405 if (have_dirname) 406 free(dirname); 407 return (char *)NULL; 408 409} /* end function do_wild() */ 410 411#endif /* !SFX */ 412 413 414 415 416/**********************/ 417/* Function mapattr() */ 418/**********************/ 419 420int mapattr(__G) 421 __GDEF 422{ 423 /* set archive bit for file entries (file is not backed up): */ 424 G.pInfo->file_attr = ((unsigned)G.crec.external_file_attributes | 425 (G.crec.external_file_attributes & FSUBDIR ? 0 : 32)) & 0xff; 426 return 0; 427 428} /* end function mapattr() */ 429 430 431 432 433 434/************************/ 435/* Function mapname() */ 436/************************/ 437 438int mapname(__G__ renamed) 439 __GDEF 440 int renamed; 441/* 442 * returns: 443 * MPN_OK - no problem detected 444 * MPN_INF_TRUNC - caution (truncated filename) 445 * MPN_INF_SKIP - info "skip entry" (dir doesn't exist) 446 * MPN_ERR_SKIP - error -> skip entry 447 * MPN_ERR_TOOLONG - error -> path is too long 448 * MPN_NOMEM - error (memory allocation failed) -> skip entry 449 * [also MPN_VOL_LABEL, MPN_CREATED_DIR] 450 */ 451{ 452 char pathcomp[FILNAMSIZ]; /* path-component buffer */ 453 char *pp, *cp=(char *)NULL; /* character pointers */ 454 char *lastsemi=(char *)NULL; /* pointer to last semi-colon in pathcomp */ 455#ifdef MAYBE_PLAIN_FAT 456 char *last_dot=(char *)NULL; /* last dot not converted to underscore */ 457# ifdef USE_LFN 458 int use_lfn = USE_LFN; /* file system supports long filenames? */ 459# endif 460#endif 461 int killed_ddot = FALSE; /* is set when skipping "../" pathcomp */ 462 int error = MPN_OK; 463 register unsigned workch; /* hold the character being tested */ 464 465 466/*--------------------------------------------------------------------------- 467 Initialize various pointers and counters and stuff. 468 ---------------------------------------------------------------------------*/ 469 470 /* can create path as long as not just freshening, or if user told us */ 471 G.create_dirs = (!uO.fflag || renamed); 472 473 created_dir = FALSE; /* not yet */ 474 renamed_fullpath = FALSE; 475 476 if (renamed) { 477 cp = G.filename - 1; /* point to beginning of renamed name... */ 478 while (*++cp) 479 if (*cp == '\\') /* convert backslashes to forward */ 480 *cp = '/'; 481 cp = G.filename; 482 /* use temporary rootpath if user gave full pathname */ 483 if (G.filename[0] == '/') { 484 renamed_fullpath = TRUE; 485 pathcomp[0] = '/'; /* copy the '/' and terminate */ 486 pathcomp[1] = '\0'; 487 ++cp; 488 } else if (isalpha((uch)G.filename[0]) && G.filename[1] == ':') { 489 renamed_fullpath = TRUE; 490 pp = pathcomp; 491 *pp++ = *cp++; /* copy the "d:" (+ '/', possibly) */ 492 *pp++ = *cp++; 493 if (*cp == '/') 494 *pp++ = *cp++; /* otherwise add "./"? */ 495 *pp = '\0'; 496 } 497 } 498 499 /* pathcomp is ignored unless renamed_fullpath is TRUE: */ 500 if ((error = checkdir(__G__ pathcomp, INIT)) != 0) /* initialize path buf */ 501 return error; /* ...unless no mem or vol label on hard disk */ 502 503 *pathcomp = '\0'; /* initialize translation buffer */ 504 pp = pathcomp; /* point to translation buffer */ 505 if (!renamed) { /* cp already set if renamed */ 506 if (uO.jflag) /* junking directories */ 507 cp = (char *)strrchr(G.filename, '/'); 508 if (cp == (char *)NULL) /* no '/' or not junking dirs */ 509 cp = G.filename; /* point to internal zipfile-member pathname */ 510 else 511 ++cp; /* point to start of last component of path */ 512 } 513 514/*--------------------------------------------------------------------------- 515 Begin main loop through characters in filename. 516 ---------------------------------------------------------------------------*/ 517 518 while ((workch = (uch)*cp++) != 0) { 519 520 switch (workch) { 521 case '/': /* can assume -j flag not given */ 522 *pp = '\0'; 523#ifdef MAYBE_PLAIN_FAT 524 maskDOSdevice(__G__ pathcomp, last_dot); 525#else 526 maskDOSdevice(__G__ pathcomp, NULL); 527#endif 528#ifdef MAYBE_PLAIN_FAT 529# ifdef USE_LFN 530 if (!use_lfn) 531# endif 532 { 533 map2fat(pathcomp, last_dot); /* 8.3 trunc. (in place) */ 534 last_dot = (char *)NULL; 535 } 536#endif 537 if (strcmp(pathcomp, ".") == 0) { 538 /* don't bother appending "./" to the path */ 539 *pathcomp = '\0'; 540 } else if (!uO.ddotflag && strcmp(pathcomp, "..") == 0) { 541 /* "../" dir traversal detected, skip over it */ 542 *pathcomp = '\0'; 543 killed_ddot = TRUE; /* set "show message" flag */ 544 } 545 /* when path component is not empty, append it now */ 546 if (*pathcomp != '\0' && 547 ((error = checkdir(__G__ pathcomp, APPEND_DIR)) 548 & MPN_MASK) > MPN_INF_TRUNC) 549 return error; 550 pp = pathcomp; /* reset conversion buffer for next piece */ 551 lastsemi = (char *)NULL; /* leave direct. semi-colons alone */ 552 break; 553 554#ifdef MAYBE_PLAIN_FAT 555 case '.': 556# ifdef USE_LFN 557 if (use_lfn) { /* LFN filenames may contain many */ 558 *pp++ = '.'; /* dots, so simply copy it ... */ 559 } else 560# endif 561 if (pp == pathcomp && *cp == '.' && cp[1] == '/') { 562 /* nothing appended yet.., and found "../" */ 563 *pp++ = '.'; /* add first dot, */ 564 *pp++ = '.'; /* second dot, and */ 565 ++cp; /* skip over to the '/' */ 566 } else { /* found dot within path component */ 567 last_dot = pp; /* point at last dot so far... */ 568 *pp++ = '_'; /* convert to underscore for now */ 569 } 570 break; 571#endif /* MAYBE_PLAIN_FAT */ 572 573 /* drive names are not stored in zipfile, so no colons allowed; 574 * no brackets or most other punctuation either (all of which 575 * can appear in Unix-created archives; backslash is particularly 576 * bad unless all necessary directories exist) */ 577#ifdef MAYBE_PLAIN_FAT 578 case '[': /* these punctuation characters forbidden */ 579 case ']': /* only on plain FAT file systems */ 580 case '+': 581 case ',': 582 case '=': 583# ifdef USE_LFN 584 if (use_lfn) 585 *pp++ = (char)workch; 586 else 587 *pp++ = '_'; 588 break; 589# endif 590#endif 591 case ':': /* special shell characters of command.com */ 592 case '\\': /* (device and directory limiters, wildcard */ 593 case '"': /* characters, stdin/stdout redirection and */ 594 case '<': /* pipe indicators and the quote sign) are */ 595 case '>': /* never allowed in filenames on (V)FAT */ 596 case '|': 597 case '*': 598 case '?': 599 *pp++ = '_'; 600 break; 601 602 case ';': /* start of VMS version? */ 603 lastsemi = pp; 604#ifdef MAYBE_PLAIN_FAT 605# ifdef USE_LFN 606 if (use_lfn) 607 *pp++ = ';'; /* keep for now; remove VMS ";##" later */ 608# endif 609#else 610 *pp++ = ';'; /* keep for now; remove VMS ";##" later */ 611#endif 612 break; 613 614#ifdef MAYBE_PLAIN_FAT 615 case ' ': /* change spaces to underscores */ 616# ifdef USE_LFN 617 if (!use_lfn && uO.sflag) /* only if requested and NO lfn! */ 618# else 619 if (uO.sflag) /* only if requested */ 620# endif 621 *pp++ = '_'; 622 else 623 *pp++ = (char)workch; 624 break; 625#endif /* MAYBE_PLAIN_FAT */ 626 627 default: 628 /* allow ASCII 255 and European characters in filenames: */ 629 if (isprint(workch) || workch >= 127) 630 *pp++ = (char)workch; 631 632 } /* end switch */ 633 } /* end while loop */ 634 635 /* Show warning when stripping insecure "parent dir" path components */ 636 if (killed_ddot && QCOND2) { 637 Info(slide, 0, ((char *)slide, LoadFarString(WarnDirTraversSkip), 638 FnFilter1(G.filename))); 639 if (!(error & ~MPN_MASK)) 640 error = (error & MPN_MASK) | PK_WARN; 641 } 642 643/*--------------------------------------------------------------------------- 644 Report if directory was created (and no file to create: filename ended 645 in '/'), check name to be sure it exists, and combine path and name be- 646 fore exiting. 647 ---------------------------------------------------------------------------*/ 648 649 if (G.filename[strlen(G.filename) - 1] == '/') { 650 checkdir(__G__ G.filename, GETPATH); 651 if (created_dir) { 652 if (QCOND2) { 653 Info(slide, 0, ((char *)slide, LoadFarString(Creating), 654 FnFilter1(G.filename))); 655 } 656 657 /* set file attributes: */ 658 z_dos_chmod(__G__ G.filename, G.pInfo->file_attr); 659 660 /* set dir time (note trailing '/') */ 661 return (error & ~MPN_MASK) | MPN_CREATED_DIR; 662 } else if (IS_OVERWRT_ALL) { 663 /* overwrite attributes of existing directory on user's request */ 664 665 /* set file attributes: */ 666 z_dos_chmod(__G__ G.filename, G.pInfo->file_attr); 667 } 668 /* dir existed already; don't look for data to extract */ 669 return (error & ~MPN_MASK) | MPN_INF_SKIP; 670 } 671 672 *pp = '\0'; /* done with pathcomp: terminate it */ 673 674 /* if not saving them, remove VMS version numbers (appended ";###") */ 675 if (!uO.V_flag && lastsemi) { 676#ifndef MAYBE_PLAIN_FAT 677 pp = lastsemi + 1; 678#else 679# ifdef USE_LFN 680 if (use_lfn) 681 pp = lastsemi + 1; 682 else 683 pp = lastsemi; /* semi-colon was omitted: expect all #'s */ 684# else 685 pp = lastsemi; /* semi-colon was omitted: expect all #'s */ 686# endif 687#endif 688 while (isdigit((uch)(*pp))) 689 ++pp; 690 if (*pp == '\0') /* only digits between ';' and end: nuke */ 691 *lastsemi = '\0'; 692 } 693 694#ifdef MAYBE_PLAIN_FAT 695 maskDOSdevice(__G__ pathcomp, last_dot); 696#else 697 maskDOSdevice(__G__ pathcomp, NULL); 698#endif 699 700 if (G.pInfo->vollabel) { 701 if (strlen(pathcomp) > 11) 702 pathcomp[11] = '\0'; 703 } else { 704#ifdef MAYBE_PLAIN_FAT 705# ifdef USE_LFN 706 if (!use_lfn) 707 map2fat(pathcomp, last_dot); /* 8.3 truncation (in place) */ 708# else 709 map2fat(pathcomp, last_dot); /* 8.3 truncation (in place) */ 710# endif 711#endif 712 } 713 714 if (*pathcomp == '\0') { 715 Info(slide, 1, ((char *)slide, LoadFarString(ConversionFailed), 716 FnFilter1(G.filename))); 717 return (error & ~MPN_MASK) | MPN_ERR_SKIP; 718 } 719 720 checkdir(__G__ pathcomp, APPEND_NAME); /* returns 1 if truncated: care? */ 721 checkdir(__G__ G.filename, GETPATH); 722 723 if (G.pInfo->vollabel) { /* set the volume label now */ 724 if (QCOND2) 725 Info(slide, 0, ((char *)slide, LoadFarString(Labelling), 726 (nLabelDrive + 'a' - 1), 727 FnFilter1(G.filename))); 728 if (volumelabel(G.filename)) { 729 Info(slide, 1, ((char *)slide, LoadFarString(ErrSetVolLabel))); 730 return (error & ~MPN_MASK) | MPN_ERR_SKIP; 731 } 732 /* success: skip the "extraction" quietly */ 733 return (error & ~MPN_MASK) | MPN_INF_SKIP; 734 } 735 736 return error; 737 738} /* end function mapname() */ 739 740 741 742 743 744/****************************/ 745/* Function maskDOSdevice() */ 746/****************************/ 747 748static void maskDOSdevice(__G__ pathcomp, last_dot) 749 __GDEF 750 char *pathcomp, *last_dot; 751{ 752/*--------------------------------------------------------------------------- 753 Put an underscore in front of the file name if the file name is a 754 DOS/WINDOWS device name like CON.*, AUX.*, PRN.*, etc. Trying to 755 extract such a file would fail at best and wedge us at worst. 756 ---------------------------------------------------------------------------*/ 757#if !defined(S_IFCHR) && defined(_S_IFCHR) 758# define S_IFCHR _S_IFCHR 759#endif 760#if !defined(S_ISCHR) 761# if defined(_S_ISCHR) 762# define S_ISCHR(m) _S_ISCHR(m) 763# elif defined(S_IFCHR) 764# define S_ISCHR(m) ((m) & S_IFCHR) 765# endif 766#endif 767 768#ifdef DEBUG 769 if (stat(pathcomp, &G.statbuf) == 0) { 770 Trace((stderr, 771 "maskDOSdevice() stat(\"%s\", buf) st_mode result: %X, %o\n", 772 FnFilter1(pathcomp), G.statbuf.st_mode, G.statbuf.st_mode)); 773 } else { 774 Trace((stderr, "maskDOSdevice() stat(\"%s\", buf) failed\n", 775 FnFilter1(pathcomp))); 776 } 777#endif 778 if (stat(pathcomp, &G.statbuf) == 0 && S_ISCHR(G.statbuf.st_mode)) { 779 extent i; 780 781 /* pathcomp contains a name of a DOS character device (builtin or 782 * installed device driver). 783 * Prepend a '_' to allow creation of the item in the file system. 784 */ 785 for (i = strlen(pathcomp) + 1; i > 0; --i) 786 pathcomp[i] = pathcomp[i - 1]; 787 pathcomp[0] = '_'; 788 if (last_dot != (char *)NULL) 789 last_dot++; 790 } 791} /* end function maskDOSdevice() */ 792 793 794 795 796 797#ifdef MAYBE_PLAIN_FAT 798 799/**********************/ 800/* Function map2fat() */ 801/**********************/ 802 803static void map2fat(pathcomp, last_dot) 804 char *pathcomp, *last_dot; 805{ 806 char *pEnd = pathcomp + strlen(pathcomp); 807 808/*--------------------------------------------------------------------------- 809 Case 1: filename has no dot, so figure out if we should add one. Note 810 that the algorithm does not try to get too fancy: if there are no dots 811 already, the name either gets truncated at 8 characters or the last un- 812 derscore is converted to a dot (only if more characters are saved that 813 way). In no case is a dot inserted between existing characters. 814 815 GRR: have problem if filename is volume label?? 816 817 ---------------------------------------------------------------------------*/ 818 819 if (last_dot == (char *)NULL) { /* no dots: check for underscores... */ 820 char *plu = strrchr(pathcomp, '_'); /* pointer to last underscore */ 821 822 if ((plu != (char *)NULL) && /* found underscore: convert to dot? */ 823 (MIN(plu - pathcomp, 8) + MIN(pEnd - plu - 1, 3) > 8)) { 824 last_dot = plu; /* be lazy: drop through to next if-block */ 825 } else if ((pEnd - pathcomp) > 8) 826 /* no underscore; or converting underscore to dot would save less 827 chars than leaving everything in the basename */ 828 pathcomp[8] = '\0'; /* truncate at 8 chars */ 829 /* else whole thing fits into 8 chars or less: no change */ 830 } 831 832/*--------------------------------------------------------------------------- 833 Case 2: filename has dot in it, so truncate first half at 8 chars (shift 834 extension if necessary) and second half at three. 835 ---------------------------------------------------------------------------*/ 836 837 if (last_dot != (char *)NULL) { /* one dot is OK: */ 838 *last_dot = '.'; /* put the last back in */ 839 840 if ((last_dot - pathcomp) > 8) { 841 char *p, *q; 842 int i; 843 844 p = last_dot; 845 q = last_dot = pathcomp + 8; 846 for (i = 0; (i < 4) && *p; ++i) /* too many chars in basename: */ 847 *q++ = *p++; /* shift extension left and */ 848 *q = '\0'; /* truncate/terminate it */ 849 } else if ((pEnd - last_dot) > 4) 850 last_dot[4] = '\0'; /* too many chars in extension */ 851 /* else filename is fine as is: no change */ 852 853 if ((last_dot - pathcomp) > 0 && last_dot[-1] == ' ') 854 last_dot[-1] = '_'; /* NO blank in front of '.'! */ 855 } 856} /* end function map2fat() */ 857 858#endif /* MAYBE_PLAIN_FAT */ 859 860 861 862 863 864/***********************/ 865/* Function checkdir() */ 866/***********************/ 867 868int checkdir(__G__ pathcomp, flag) 869 __GDEF 870 char *pathcomp; 871 int flag; 872/* 873 * returns: 874 * MPN_OK - no problem detected 875 * MPN_INF_TRUNC - (on APPEND_NAME) truncated filename 876 * MPN_INF_SKIP - path doesn't exist, not allowed to create 877 * MPN_ERR_SKIP - path doesn't exist, tried to create and failed; or path 878 * exists and is not a directory, but is supposed to be 879 * MPN_ERR_TOOLONG - path is too long 880 * MPN_NOMEM - can't allocate memory for filename buffers 881 */ 882{ 883 static int rootlen = 0; /* length of rootpath */ 884 static char *rootpath; /* user's "extract-to" directory */ 885 static char *buildpath; /* full path (so far) to extracted file */ 886 static char *end; /* pointer to end of buildpath ('\0') */ 887#ifdef MSC 888 int attrs; /* work around MSC stat() bug */ 889#endif 890 891# define FN_MASK 7 892# define FUNCTION (flag & FN_MASK) 893 894 895 896/*--------------------------------------------------------------------------- 897 APPEND_DIR: append the path component to the path being built and check 898 for its existence. If doesn't exist and we are creating directories, do 899 so for this one; else signal success or error as appropriate. 900 ---------------------------------------------------------------------------*/ 901 902 if (FUNCTION == APPEND_DIR) { 903 int too_long = FALSE; 904 905 Trace((stderr, "appending dir segment [%s]\n", FnFilter1(pathcomp))); 906 while ((*end = *pathcomp++) != '\0') 907 ++end; 908 909 /* GRR: could do better check, see if overrunning buffer as we go: 910 * check end-buildpath after each append, set warning variable if 911 * within 20 of FILNAMSIZ; then if var set, do careful check when 912 * appending. Clear variable when begin new path. */ 913 914 if ((end-buildpath) > FILNAMSIZ-3) /* need '/', one-char name, '\0' */ 915 too_long = TRUE; /* check if extracting directory? */ 916#ifdef MSC /* MSC 6.00 bug: stat(non-existent-dir) == 0 [exists!] */ 917 if (_dos_getfileattr(buildpath, &attrs) || stat(buildpath, &G.statbuf)) 918#else 919 if (SSTAT(buildpath, &G.statbuf)) /* path doesn't exist */ 920#endif 921 { 922 if (!G.create_dirs) { /* told not to create (freshening) */ 923 free(buildpath); 924 /* path doesn't exist: nothing to do */ 925 return MPN_INF_SKIP; 926 } 927 if (too_long) { 928 Info(slide, 1, ((char *)slide, LoadFarString(PathTooLong), 929 FnFilter1(buildpath))); 930 free(buildpath); 931 /* no room for filenames: fatal */ 932 return MPN_ERR_TOOLONG; 933 } 934 if (MKDIR(buildpath, 0777) == -1) { /* create the directory */ 935 Info(slide, 1, ((char *)slide, LoadFarString(CantCreateDir), 936 FnFilter2(buildpath), FnFilter1(G.filename))); 937 free(buildpath); 938 /* path didn't exist, tried to create, failed */ 939 return MPN_ERR_SKIP; 940 } 941 created_dir = TRUE; 942 } else if (!S_ISDIR(G.statbuf.st_mode)) { 943 Info(slide, 1, ((char *)slide, LoadFarString(DirIsntDirectory), 944 FnFilter2(buildpath), FnFilter1(G.filename))); 945 free(buildpath); 946 /* path existed but wasn't dir */ 947 return MPN_ERR_SKIP; 948 } 949 if (too_long) { 950 Info(slide, 1, ((char *)slide, LoadFarString(PathTooLong), 951 FnFilter1(buildpath))); 952 free(buildpath); 953 /* no room for filenames: fatal */ 954 return MPN_ERR_TOOLONG; 955 } 956 *end++ = '/'; 957 *end = '\0'; 958 Trace((stderr, "buildpath now = [%s]\n", FnFilter1(buildpath))); 959 return MPN_OK; 960 961 } /* end if (FUNCTION == APPEND_DIR) */ 962 963/*--------------------------------------------------------------------------- 964 GETPATH: copy full path to the string pointed at by pathcomp, and free 965 buildpath. 966 ---------------------------------------------------------------------------*/ 967 968 if (FUNCTION == GETPATH) { 969 strcpy(pathcomp, buildpath); 970 Trace((stderr, "getting and freeing path [%s]\n", 971 FnFilter1(pathcomp))); 972 free(buildpath); 973 buildpath = end = (char *)NULL; 974 return MPN_OK; 975 } 976 977/*--------------------------------------------------------------------------- 978 APPEND_NAME: assume the path component is the filename; append it and 979 return without checking for existence. 980 ---------------------------------------------------------------------------*/ 981 982 if (FUNCTION == APPEND_NAME) { 983#ifdef NOVELL_BUG_WORKAROUND 984 if (end == buildpath && !G.pInfo->vollabel) { 985 /* work-around for Novell's "overwriting executables" bug: 986 prepend "./" to name when no path component is specified */ 987 *end++ = '.'; 988 *end++ = '/'; 989 } 990#endif /* NOVELL_BUG_WORKAROUND */ 991 Trace((stderr, "appending filename [%s]\n", FnFilter1(pathcomp))); 992 while ((*end = *pathcomp++) != '\0') { 993 ++end; 994 if ((end-buildpath) >= FILNAMSIZ) { 995 *--end = '\0'; 996 Info(slide, 1, ((char *)slide, LoadFarString(PathTooLongTrunc), 997 FnFilter1(G.filename), FnFilter2(buildpath))); 998 return MPN_INF_TRUNC; /* filename truncated */ 999 } 1000 } 1001 Trace((stderr, "buildpath now = [%s]\n", FnFilter1(buildpath))); 1002 /* could check for existence here, prompt for new name... */ 1003 return MPN_OK; 1004 } 1005 1006/*--------------------------------------------------------------------------- 1007 INIT: allocate and initialize buffer space for the file currently being 1008 extracted. If file was renamed with an absolute path, don't prepend the 1009 extract-to path. 1010 ---------------------------------------------------------------------------*/ 1011 1012 if (FUNCTION == INIT) { 1013 Trace((stderr, "initializing buildpath to ")); 1014 /* allocate space for full filename, root path, and maybe "./" */ 1015 if ((buildpath = (char *)malloc(strlen(G.filename)+rootlen+3)) == 1016 (char *)NULL) 1017 return MPN_NOMEM; 1018 if (G.pInfo->vollabel) { 1019/* GRR: for network drives, do strchr() and return IZ_VOL_LABEL if not [1] */ 1020 if (renamed_fullpath && pathcomp[1] == ':') 1021 *buildpath = (char)ToLower(*pathcomp); 1022 else if (!renamed_fullpath && rootlen > 1 && rootpath[1] == ':') 1023 *buildpath = (char)ToLower(*rootpath); 1024 else { 1025 GETDRIVE(nLabelDrive); /* assumed that a == 1, b == 2, etc. */ 1026 *buildpath = (char)(nLabelDrive - 1 + 'a'); 1027 } 1028 nLabelDrive = *buildpath - 'a' + 1; /* save for mapname() */ 1029 if (uO.volflag == 0 || *buildpath < 'a' || /* no label/bogus disk */ 1030 (uO.volflag == 1 && !isfloppy(nLabelDrive))) /* -$: no fixed */ 1031 { 1032 free(buildpath); 1033 return MPN_VOL_LABEL; /* skipping with message */ 1034 } 1035 *buildpath = '\0'; 1036 end = buildpath; 1037 } else if (renamed_fullpath) { /* pathcomp = valid data */ 1038 end = buildpath; 1039 while ((*end = *pathcomp++) != '\0') 1040 ++end; 1041 } else if (rootlen > 0) { 1042 strcpy(buildpath, rootpath); 1043 end = buildpath + rootlen; 1044 } else { 1045 *buildpath = '\0'; 1046 end = buildpath; 1047 } 1048 Trace((stderr, "[%s]\n", FnFilter1(buildpath))); 1049 return MPN_OK; 1050 } 1051 1052/*--------------------------------------------------------------------------- 1053 ROOT: if appropriate, store the path in rootpath and create it if neces- 1054 sary; else assume it's a zipfile member and return. This path segment 1055 gets used in extracting all members from every zipfile specified on the 1056 command line. Note that under OS/2 and MS-DOS, if a candidate extract-to 1057 directory specification includes a drive letter (leading "x:"), it is 1058 treated just as if it had a trailing '/'--that is, one directory level 1059 will be created if the path doesn't exist, unless this is otherwise pro- 1060 hibited (e.g., freshening). 1061 ---------------------------------------------------------------------------*/ 1062 1063#if (!defined(SFX) || defined(SFX_EXDIR)) 1064 if (FUNCTION == ROOT) { 1065 Trace((stderr, "initializing root path to [%s]\n", 1066 FnFilter1(pathcomp))); 1067 if (pathcomp == (char *)NULL) { 1068 rootlen = 0; 1069 return MPN_OK; 1070 } 1071 if (rootlen > 0) /* rootpath was already set, nothing to do */ 1072 return MPN_OK; 1073 if ((rootlen = strlen(pathcomp)) > 0) { 1074 int had_trailing_pathsep=FALSE, has_drive=FALSE, add_dot=FALSE; 1075 char *tmproot; 1076 1077 if ((tmproot = (char *)malloc(rootlen+3)) == (char *)NULL) { 1078 rootlen = 0; 1079 return MPN_NOMEM; 1080 } 1081 strcpy(tmproot, pathcomp); 1082 if (isalpha((uch)tmproot[0]) && tmproot[1] == ':') 1083 has_drive = TRUE; /* drive designator */ 1084 if (tmproot[rootlen-1] == '/' || tmproot[rootlen-1] == '\\') { 1085 tmproot[--rootlen] = '\0'; 1086 had_trailing_pathsep = TRUE; 1087 } 1088 if (has_drive && (rootlen == 2)) { 1089 if (!had_trailing_pathsep) /* i.e., original wasn't "x:/" */ 1090 add_dot = TRUE; /* relative path: add '.' before '/' */ 1091 } else if (rootlen > 0) { /* need not check "x:." and "x:/" */ 1092#ifdef MSC 1093 /* MSC 6.00 bug: stat(non-existent-dir) == 0 [exists!] */ 1094 if (_dos_getfileattr(tmproot, &attrs) || 1095 SSTAT(tmproot, &G.statbuf) || !S_ISDIR(G.statbuf.st_mode)) 1096#else 1097 if (SSTAT(tmproot, &G.statbuf) || !S_ISDIR(G.statbuf.st_mode)) 1098#endif 1099 { 1100 /* path does not exist */ 1101 if (!G.create_dirs /* || iswild(tmproot) */ ) { 1102 free(tmproot); 1103 rootlen = 0; 1104 /* treat as stored file */ 1105 return MPN_INF_SKIP; 1106 } 1107/* GRR: scan for wildcard characters? OS-dependent... if find any, return 2: 1108 * treat as stored file(s) */ 1109 /* create directory (could add loop here scanning tmproot 1110 * to create more than one level, but really necessary?) */ 1111 if (MKDIR(tmproot, 0777) == -1) { 1112 Info(slide, 1, ((char *)slide, 1113 LoadFarString(CantCreateExtractDir), 1114 FnFilter1(tmproot))); 1115 free(tmproot); 1116 rootlen = 0; 1117 /* path didn't exist, tried to create, failed: */ 1118 /* file exists, or need 2+ subdir levels */ 1119 return MPN_ERR_SKIP; 1120 } 1121 } 1122 } 1123 if (add_dot) /* had just "x:", make "x:." */ 1124 tmproot[rootlen++] = '.'; 1125 tmproot[rootlen++] = '/'; 1126 tmproot[rootlen] = '\0'; 1127 if ((rootpath = (char *)realloc(tmproot, rootlen+1)) == NULL) { 1128 free(tmproot); 1129 rootlen = 0; 1130 return MPN_NOMEM; 1131 } 1132 Trace((stderr, "rootpath now = [%s]\n", FnFilter1(rootpath))); 1133 } 1134 return MPN_OK; 1135 } 1136#endif /* !SFX || SFX_EXDIR */ 1137 1138/*--------------------------------------------------------------------------- 1139 END: free rootpath, immediately prior to program exit. 1140 ---------------------------------------------------------------------------*/ 1141 1142 if (FUNCTION == END) { 1143 Trace((stderr, "freeing rootpath\n")); 1144 if (rootlen > 0) { 1145 free(rootpath); 1146 rootlen = 0; 1147 } 1148 return MPN_OK; 1149 } 1150 1151 return MPN_INVALID; /* should never reach */ 1152 1153} /* end function checkdir() */ 1154 1155 1156 1157 1158 1159 1160/***********************/ 1161/* Function isfloppy() */ 1162/***********************/ 1163 1164static int isfloppy(nDrive) /* more precisely, is it removable? */ 1165 int nDrive; 1166{ 1167 union REGS regs; 1168 1169 regs.h.ah = 0x44; 1170 regs.h.al = 0x08; 1171 regs.h.bl = (uch)nDrive; 1172#ifdef __EMX__ 1173 _int86(0x21, ®s, ®s); 1174 if (WREGS(regs,flags) & 1) 1175#else 1176 intdos(®s, ®s); 1177 if (WREGS(regs,cflag)) /* error: do default a/b check instead */ 1178#endif 1179 { 1180 Trace((stderr, 1181 "error in DOS function 0x44 (AX = 0x%04x): guessing instead...\n", 1182 (unsigned int)(WREGS(regs,ax)))); 1183 return (nDrive == 1 || nDrive == 2)? TRUE : FALSE; 1184 } else 1185 return WREGS(regs,ax)? FALSE : TRUE; 1186} 1187 1188 1189 1190 1191/**************************/ 1192/* Function z_dos_chmod() */ 1193/**************************/ 1194 1195static int z_dos_chmod(__G__ fname, attributes) 1196 __GDEF 1197 ZCONST char *fname; 1198 int attributes; 1199{ 1200 char *name; 1201 unsigned fnamelength; 1202 int errv; 1203 1204 /* set file attributes: 1205 The DOS `chmod' system call requires to mask out the 1206 directory and volume_label attribute bits. 1207 And, a trailing '/' has to be removed from the directory name, 1208 the DOS `chmod' system call does not accept it. */ 1209 fnamelength = strlen(fname); 1210 if (fnamelength > 1 && fname[fnamelength-1] == '/' && 1211 fname[fnamelength-2] != ':' && 1212 (name = (char *)malloc(fnamelength)) != (char *)NULL) { 1213 strncpy(name, fname, fnamelength-1); 1214 name[fnamelength-1] = '\0'; 1215 } else { 1216 name = (char *)fname; 1217 fnamelength = 0; 1218 } 1219 1220#if defined(__TURBOC__) || (defined(__DJGPP__) && (__DJGPP__ >= 2)) 1221# if (defined(__BORLANDC__) && (__BORLANDC__ >= 0x0452)) 1222# define Chmod _rtl_chmod 1223# else 1224# define Chmod _chmod 1225# endif 1226 errv = (Chmod(name, 1, attributes & (~FSUBDIR & ~FVOLID)) != 1227 (attributes & (~FSUBDIR & ~FVOLID))); 1228# undef Chmod 1229#else /* !(__TURBOC__ || (__DJGPP__ && __DJGPP__ >= 2)) */ 1230 errv = (_dos_setfileattr(name, attributes & (~FSUBDIR & ~FVOLID)) != 0); 1231#endif /* ?(__TURBOC__ || (__DJGPP__ && __DJGPP__ >= 2)) */ 1232 if (errv) 1233 Info(slide, 1, ((char *)slide, LoadFarString(AttribsMayBeWrong))); 1234 1235 if (fnamelength > 0) 1236 free(name); 1237 return errv; 1238} /* end function z_dos_chmod() */ 1239 1240 1241 1242 1243#if (!defined(__GO32__) && !defined(__EMX__)) 1244 1245typedef struct dosfcb { 1246 uch flag; /* ff to indicate extended FCB */ 1247 char res[5]; /* reserved */ 1248 uch vattr; /* attribute */ 1249 uch drive; /* drive (1=A, 2=B, ...) */ 1250 uch vn[11]; /* file or volume name */ 1251 char dmmy[5]; 1252 uch nn[11]; /* holds new name if renaming (else reserved) */ 1253 char dmmy2[9]; 1254} dos_fcb; 1255 1256/**************************/ 1257/* Function volumelabel() */ 1258/**************************/ 1259 1260static int volumelabel(newlabel) 1261 ZCONST char *newlabel; 1262{ 1263#ifdef DEBUG 1264 char *p; 1265#endif 1266 int len = strlen(newlabel); 1267 int fcbseg, dtaseg, fcboff, dtaoff, retv; 1268 dos_fcb fcb, dta, far *pfcb=&fcb, far *pdta=&dta; 1269 struct SREGS sregs; 1270 union REGS regs; 1271 1272 1273/*--------------------------------------------------------------------------- 1274 Label the diskette specified by nLabelDrive using FCB calls. (Old ver- 1275 sions of MS-DOS and OS/2 DOS boxes can't use DOS function 3Ch to create 1276 labels.) Must use far pointers for MSC FP_* macros to work; must pad 1277 FCB filenames with spaces; and cannot include dot in 8th position. May 1278 or may not need to zero out FCBs before using; do so just in case. 1279 ---------------------------------------------------------------------------*/ 1280 1281#ifdef WATCOMC_386 1282 int truseg; 1283 1284 memset(&sregs, 0, sizeof(sregs)); 1285 memset(®s, 0, sizeof(regs)); 1286 /* PMODE/W does not support extended versions of any dos FCB functions, */ 1287 /* so we have to use brute force, allocating real mode memory for them. */ 1288 regs.w.ax = 0x0100; 1289 regs.w.bx = (2 * sizeof(dos_fcb) + 15) >> 4; /* size in paragraphs */ 1290 int386(0x31, ®s, ®s); /* DPMI allocate DOS memory */ 1291 if (regs.w.cflag) 1292 return DF_MDY; /* no memory, return default */ 1293 truseg = regs.w.dx; /* protected mode selector */ 1294 dtaseg = regs.w.ax; /* real mode paragraph */ 1295 fcboff = 0; 1296 dtaoff = sizeof(dos_fcb); 1297#ifdef XXX__MK_FP_IS_BROKEN 1298 /* XXX This code may not be trustworthy in general, though it is */ 1299 /* valid with DOS/4GW and PMODE/w, which is all we support for now. */ 1300 regs.w.ax = 6; 1301 regs.w.bx = truseg; 1302 int386(0x31, ®s, ®s); /* convert seg to linear address */ 1303 pfcb = (dos_fcb far *) (((ulg) regs.w.cx << 16) | regs.w.dx); 1304 /* pfcb = (dos_fcb far *) ((ulg) dtaseg << 4); */ 1305 pdta = pfcb + 1; 1306#else 1307 pfcb = MK_FP(truseg, fcboff); 1308 pdta = MK_FP(truseg, dtaoff); 1309#endif 1310 _fmemset((char far *)pfcb, 0, 2 * sizeof(dos_fcb)); 1311 /* we pass the REAL MODE paragraph to the dos interrupts: */ 1312 fcbseg = dtaseg; 1313 1314#else /* !WATCOMC_386 */ 1315 1316 memset((char *)&dta, 0, sizeof(dos_fcb)); 1317 memset((char *)&fcb, 0, sizeof(dos_fcb)); 1318 fcbseg = FP_SEG(pfcb); 1319 fcboff = FP_OFF(pfcb); 1320 dtaseg = FP_SEG(pdta); 1321 dtaoff = FP_OFF(pdta); 1322#endif /* ?WATCOMC_386 */ 1323 1324#ifdef DEBUG 1325 for (p = (char *)&dta; (p - (char *)&dta) < sizeof(dos_fcb); ++p) 1326 if (*p) 1327 fprintf(stderr, "error: dta[%d] = %x\n", (p - (char *)&dta), *p); 1328 for (p = (char *)&fcb; (p - (char *)&fcb) < sizeof(dos_fcb); ++p) 1329 if (*p) 1330 fprintf(stderr, "error: fcb[%d] = %x\n", (p - (char *)&fcb), *p); 1331 printf("testing pointer macros:\n"); 1332 segread(&sregs); 1333 printf("cs = %x, ds = %x, es = %x, ss = %x\n", sregs.cs, sregs.ds, sregs.es, 1334 sregs.ss); 1335#endif /* DEBUG */ 1336 1337#if 0 1338#ifdef __TURBOC__ 1339 bdosptr(0x1a, dta, DO_NOT_CARE); 1340#else 1341 (intdosx method below) 1342#endif 1343#endif /* 0 */ 1344 1345 /* set the disk transfer address for subsequent FCB calls */ 1346 sregs.ds = dtaseg; 1347 WREGS(regs,dx) = dtaoff; 1348 Trace((stderr, "segment:offset of pdta = %x:%x\n", dtaseg, dtaoff)); 1349 Trace((stderr, "&dta = %lx, pdta = %lx\n", (ulg)&dta, (ulg)pdta)); 1350 regs.h.ah = 0x1a; 1351 F_intdosx(®s, ®s, &sregs); 1352 1353 /* fill in the FCB */ 1354 sregs.ds = fcbseg; 1355 WREGS(regs,dx) = fcboff; 1356 pfcb->flag = 0xff; /* extended FCB */ 1357 pfcb->vattr = 0x08; /* attribute: disk volume label */ 1358 pfcb->drive = (uch)nLabelDrive; 1359 1360#ifdef DEBUG 1361 Trace((stderr, "segment:offset of pfcb = %x:%x\n", 1362 (unsigned int)(sregs.ds), 1363 (unsigned int)(WREGS(regs,dx)))); 1364 Trace((stderr, "&fcb = %lx, pfcb = %lx\n", (ulg)&fcb, (ulg)pfcb)); 1365 Trace((stderr, "(2nd check: labelling drive %c:)\n", pfcb->drive-1+'A')); 1366 if (pfcb->flag != fcb.flag) 1367 fprintf(stderr, "error: pfcb->flag = %d, fcb.flag = %d\n", 1368 pfcb->flag, fcb.flag); 1369 if (pfcb->drive != fcb.drive) 1370 fprintf(stderr, "error: pfcb->drive = %d, fcb.drive = %d\n", 1371 pfcb->drive, fcb.drive); 1372 if (pfcb->vattr != fcb.vattr) 1373 fprintf(stderr, "error: pfcb->vattr = %d, fcb.vattr = %d\n", 1374 pfcb->vattr, fcb.vattr); 1375#endif /* DEBUG */ 1376 1377 /* check for existing label */ 1378 Trace((stderr, "searching for existing label via FCBs\n")); 1379 regs.h.ah = 0x11; /* FCB find first */ 1380#ifdef WATCOMC_386 1381 _fstrncpy((char far *)&pfcb->vn, "???????????", 11); 1382#else 1383 strncpy((char *)fcb.vn, "???????????", 11); /* i.e., "*.*" */ 1384#endif /* ?WATCOMC_386 */ 1385 Trace((stderr, "fcb.vn = %lx\n", (ulg)fcb.vn)); 1386 Trace((stderr, "regs.h.ah = %x, regs.x.dx = %04x, sregs.ds = %04x\n", 1387 (unsigned int)(regs.h.ah), (unsigned int)(WREGS(regs,dx)), 1388 (unsigned int)(sregs.ds))); 1389 Trace((stderr, "flag = %x, drive = %d, vattr = %x, vn = %s = %s.\n", 1390 fcb.flag, fcb.drive, fcb.vattr, fcb.vn, pfcb->vn)); 1391 F_intdosx(®s, ®s, &sregs); 1392 1393/*--------------------------------------------------------------------------- 1394 If not previously labelled, write a new label. Otherwise just rename, 1395 since MS-DOS 2.x has a bug that damages the FAT when the old label is 1396 deleted. 1397 ---------------------------------------------------------------------------*/ 1398 1399 if (regs.h.al) { 1400 Trace((stderr, "no label found\n\n")); 1401 regs.h.ah = 0x16; /* FCB create file */ 1402#ifdef WATCOMC_386 1403 _fstrncpy((char far *)pfcb->vn, newlabel, len); 1404 if (len < 11) 1405 _fstrncpy((char far *)(pfcb->vn+len), " ", 11-len); 1406#else 1407 strncpy((char *)fcb.vn, newlabel, len); 1408 if (len < 11) /* fill with spaces */ 1409 strncpy((char *)(fcb.vn+len), " ", 11-len); 1410#endif 1411 Trace((stderr, "fcb.vn = %lx pfcb->vn = %lx\n", (ulg)fcb.vn, 1412 (ulg)pfcb->vn)); 1413 Trace((stderr, "flag = %x, drive = %d, vattr = %x\n", fcb.flag, 1414 fcb.drive, fcb.vattr)); 1415 Trace((stderr, "vn = %s = %s.\n", fcb.vn, pfcb->vn)); 1416 F_intdosx(®s, ®s, &sregs); 1417 regs.h.ah = 0x10; /* FCB close file */ 1418 if (regs.h.al) { 1419 Trace((stderr, "unable to write volume name (AL = %x)\n", 1420 (unsigned int)(regs.h.al))); 1421 F_intdosx(®s, ®s, &sregs); 1422 retv = 1; 1423 } else { 1424 F_intdosx(®s, ®s, &sregs); 1425 Trace((stderr, "new volume label [%s] written\n", newlabel)); 1426 retv = 0; 1427 } 1428 } else { 1429 Trace((stderr, "found old label [%s]\n\n", dta.vn)); /* not term. */ 1430 regs.h.ah = 0x17; /* FCB rename */ 1431#ifdef WATCOMC_386 1432 _fstrncpy((char far *)pfcb->vn, (char far *)pdta->vn, 11); 1433 _fstrncpy((char far *)pfcb->nn, newlabel, len); 1434 if (len < 11) 1435 _fstrncpy((char far *)(pfcb->nn+len), " ", 11-len); 1436#else 1437 strncpy((char *)fcb.vn, (char *)dta.vn, 11); 1438 strncpy((char *)fcb.nn, newlabel, len); 1439 if (len < 11) /* fill with spaces */ 1440 strncpy((char *)(fcb.nn+len), " ", 11-len); 1441#endif 1442 Trace((stderr, "fcb.vn = %lx pfcb->vn = %lx\n", (ulg)fcb.vn, 1443 (ulg)pfcb->vn)); 1444 Trace((stderr, "fcb.nn = %lx pfcb->nn = %lx\n", (ulg)fcb.nn, 1445 (ulg)pfcb->nn)); 1446 Trace((stderr, "flag = %x, drive = %d, vattr = %x\n", fcb.flag, 1447 fcb.drive, fcb.vattr)); 1448 Trace((stderr, "vn = %s = %s.\n", fcb.vn, pfcb->vn)); 1449 Trace((stderr, "nn = %s = %s.\n", fcb.nn, pfcb->nn)); 1450 F_intdosx(®s, ®s, &sregs); 1451 if (regs.h.al) { 1452 Trace((stderr, "Unable to change volume name (AL = %x)\n", 1453 (unsigned int)(regs.h.al))); 1454 retv = 1; 1455 } else { 1456 Trace((stderr, "volume label changed to [%s]\n", newlabel)); 1457 retv = 0; 1458 } 1459 } 1460#ifdef WATCOMC_386 1461 regs.w.ax = 0x0101; /* free dos memory */ 1462 regs.w.dx = truseg; 1463 int386(0x31, ®s, ®s); 1464#endif 1465 return retv; 1466 1467} /* end function volumelabel() */ 1468 1469#endif /* !__GO32__ && !__EMX__ */ 1470 1471 1472 1473 1474 1475#if (defined(USE_EF_UT_TIME) || defined(TIMESTAMP)) 1476/* The following DOS date/time structure is machine-dependent as it 1477 * assumes "little-endian" byte order. For MSDOS-specific code, which 1478 * is run on ix86 CPUs (or emulators), this assumption is valid; but 1479 * care should be taken when using this code as template for other ports. 1480 */ 1481typedef union { 1482 ulg z_dostime; 1483# ifdef __TURBOC__ 1484 struct ftime ft; /* system file time record */ 1485# endif 1486 struct { /* date and time words */ 1487 ush ztime; /* DOS file modification time word */ 1488 ush zdate; /* DOS file modification date word */ 1489 } zft; 1490 struct { /* DOS date/time components bitfield */ 1491 unsigned zt_se : 5; 1492 unsigned zt_mi : 6; 1493 unsigned zt_hr : 5; 1494 unsigned zd_dy : 5; 1495 unsigned zd_mo : 4; 1496 unsigned zd_yr : 7; 1497 } z_dtf; 1498} dos_fdatetime; 1499#endif /* USE_EF_UT_TIME || TIMESTAMP */ 1500 1501 1502/****************************/ 1503/* Function close_outfile() */ 1504/****************************/ 1505 1506void close_outfile(__G) 1507 __GDEF 1508 /* 1509 * MS-DOS VERSION 1510 * 1511 * Set the output file date/time stamp according to information from the 1512 * zipfile directory record for this member, then close the file and set 1513 * its permissions (archive, hidden, read-only, system). Aside from closing 1514 * the file, this routine is optional (but most compilers support it). 1515 */ 1516{ 1517 /* skip restoring time stamps on user's request */ 1518 if (uO.D_flag <= 1) { 1519#ifdef USE_EF_UT_TIME 1520 dos_fdatetime dos_dt; 1521 iztimes z_utime; 1522 struct tm *t; 1523#endif /* USE_EF_UT_TIME */ 1524 1525 1526/*--------------------------------------------------------------------------- 1527 Copy and/or convert time and date variables, if necessary; then set 1528 the file time/date. WEIRD BORLAND "BUG": if output is buffered, 1529 and if run under at least some versions of DOS (e.g., 6.0), and if 1530 files are smaller than DOS physical block size (i.e., 512 bytes) (?), 1531 then files MAY NOT get timestamped correctly--apparently setftime() 1532 occurs before any data are written to the file, and when file is 1533 closed and buffers are flushed, timestamp is overwritten with 1534 current time. Even with a 32K buffer, this does not seem to occur 1535 with larger files. UnZip output is now unbuffered, but if it were 1536 not, could still avoid problem by adding "fflush(outfile)" just 1537 before setftime() call. Weird, huh? 1538 ---------------------------------------------------------------------------*/ 1539 1540#ifdef USE_EF_UT_TIME 1541 if (G.extra_field && 1542#ifdef IZ_CHECK_TZ 1543 G.tz_is_valid && 1544#endif 1545 (ef_scan_for_izux(G.extra_field, G.lrec.extra_field_length, 0, 1546 G.lrec.last_mod_dos_datetime, &z_utime, NULL) 1547 & EB_UT_FL_MTIME)) 1548 { 1549 TTrace((stderr, "close_outfile: Unix e.f. modif. time = %ld\n", 1550 z_utime.mtime)); 1551 /* round up (down if "up" overflows) to even seconds */ 1552 if (z_utime.mtime & 1) 1553 z_utime.mtime = (z_utime.mtime + 1 > z_utime.mtime) ? 1554 z_utime.mtime + 1 : z_utime.mtime - 1; 1555 TIMET_TO_NATIVE(z_utime.mtime) /* NOP unless MSC 7 or Macintosh */ 1556 t = localtime(&(z_utime.mtime)); 1557 } else 1558 t = (struct tm *)NULL; 1559 if (t != (struct tm *)NULL) { 1560 if (t->tm_year < 80) { 1561 dos_dt.z_dtf.zt_se = 0; 1562 dos_dt.z_dtf.zt_mi = 0; 1563 dos_dt.z_dtf.zt_hr = 0; 1564 dos_dt.z_dtf.zd_dy = 1; 1565 dos_dt.z_dtf.zd_mo = 1; 1566 dos_dt.z_dtf.zd_yr = 0; 1567 } else { 1568 dos_dt.z_dtf.zt_se = t->tm_sec >> 1; 1569 dos_dt.z_dtf.zt_mi = t->tm_min; 1570 dos_dt.z_dtf.zt_hr = t->tm_hour; 1571 dos_dt.z_dtf.zd_dy = t->tm_mday; 1572 dos_dt.z_dtf.zd_mo = t->tm_mon + 1; 1573 dos_dt.z_dtf.zd_yr = t->tm_year - 80; 1574 } 1575 } else { 1576 dos_dt.z_dostime = G.lrec.last_mod_dos_datetime; 1577 } 1578# ifdef __TURBOC__ 1579 setftime(fileno(G.outfile), &dos_dt.ft); 1580# else 1581 _dos_setftime(fileno(G.outfile), dos_dt.zft.zdate, dos_dt.zft.ztime); 1582# endif 1583#else /* !USE_EF_UT_TIME */ 1584# ifdef __TURBOC__ 1585 setftime(fileno(G.outfile), 1586 (struct ftime *)(&(G.lrec.last_mod_dos_datetime))); 1587# else 1588 _dos_setftime(fileno(G.outfile), 1589 (ush)(G.lrec.last_mod_dos_datetime >> 16), 1590 (ush)(G.lrec.last_mod_dos_datetime)); 1591# endif 1592#endif /* ?USE_EF_UT_TIME */ 1593 } 1594 1595/*--------------------------------------------------------------------------- 1596 And finally we can close the file...at least everybody agrees on how to 1597 do *this*. I think... Also change the mode according to the stored file 1598 attributes, since we didn't do that when we opened the dude. 1599 ---------------------------------------------------------------------------*/ 1600 1601 fclose(G.outfile); 1602 1603 z_dos_chmod(__G__ G.filename, G.pInfo->file_attr); 1604 1605} /* end function close_outfile() */ 1606 1607 1608 1609 1610 1611#ifdef TIMESTAMP 1612 1613/*************************/ 1614/* Function stamp_file() */ 1615/*************************/ 1616 1617int stamp_file(fname, modtime) 1618 ZCONST char *fname; 1619 time_t modtime; 1620{ 1621 dos_fdatetime dos_dt; 1622 time_t t_even; 1623 struct tm *t; 1624 int fd; /* file handle */ 1625 1626 /* round up (down if "up" overflows) to even seconds */ 1627 t_even = ((modtime + 1 > modtime) ? modtime + 1 : modtime) & (~1); 1628 TIMET_TO_NATIVE(t_even) /* NOP unless MSC 7.0 or Macintosh */ 1629 t = localtime(&t_even); 1630 if (t == (struct tm *)NULL) 1631 return -1; /* time conversion error */ 1632 if (t->tm_year < 80) { 1633 dos_dt.z_dtf.zt_se = 0; 1634 dos_dt.z_dtf.zt_mi = 0; 1635 dos_dt.z_dtf.zt_hr = 0; 1636 dos_dt.z_dtf.zd_dy = 1; 1637 dos_dt.z_dtf.zd_mo = 1; 1638 dos_dt.z_dtf.zd_yr = 0; 1639 } else { 1640 dos_dt.z_dtf.zt_se = t->tm_sec >> 1; 1641 dos_dt.z_dtf.zt_mi = t->tm_min; 1642 dos_dt.z_dtf.zt_hr = t->tm_hour; 1643 dos_dt.z_dtf.zd_dy = t->tm_mday; 1644 dos_dt.z_dtf.zd_mo = t->tm_mon + 1; 1645 dos_dt.z_dtf.zd_yr = t->tm_year - 80; 1646 } 1647 if (((fd = open((char *)fname, 0)) == -1) || 1648# ifdef __TURBOC__ 1649 (setftime(fd, &dos_dt.ft))) 1650# else 1651 (_dos_setftime(fd, dos_dt.zft.zdate, dos_dt.zft.ztime))) 1652# endif 1653 { 1654 if (fd != -1) 1655 close(fd); 1656 return -1; 1657 } 1658 close(fd); 1659 return 0; 1660 1661} /* end function stamp_file() */ 1662 1663#endif /* TIMESTAMP */ 1664 1665 1666 1667 1668void prepare_ISO_OEM_translat(__G) 1669 __GDEF 1670{ 1671 switch (getdoscodepage()) { 1672 case 437: 1673 case 850: 1674 case 858: 1675#ifdef IZ_ISO2OEM_ARRAY 1676 iso2oem = iso2oem_850; 1677#endif 1678#ifdef IZ_OEM2ISO_ARRAY 1679 oem2iso = oem2iso_850; 1680#endif 1681 1682 case 932: /* Japanese */ 1683 case 949: /* Korean */ 1684 case 936: /* Chinese, simple */ 1685 case 950: /* Chinese, traditional */ 1686 case 874: /* Thai */ 1687 case 1258: /* Vietnamese */ 1688#ifdef IZ_ISO2OEM_ARRAY 1689 iso2oem = NULL; 1690#endif 1691#ifdef IZ_OEM2ISO_ARRAY 1692 oem2iso = NULL; 1693#endif 1694 1695 default: 1696#ifdef IZ_ISO2OEM_ARRAY 1697 iso2oem = NULL; 1698#endif 1699#ifdef IZ_OEM2ISO_ARRAY 1700 oem2iso = NULL; 1701#endif 1702 } 1703} /* end function prepare_ISO_OEM_translat() */ 1704 1705 1706 1707 1708#ifndef SFX 1709 1710/*************************/ 1711/* Function dateformat() */ 1712/*************************/ 1713 1714int dateformat() 1715{ 1716 1717/*--------------------------------------------------------------------------- 1718 For those operating systems that support it, this function returns a 1719 value that tells how national convention says that numeric dates are 1720 displayed. Return values are DF_YMD, DF_DMY and DF_MDY (the meanings 1721 should be fairly obvious). 1722 ---------------------------------------------------------------------------*/ 1723 1724#ifndef WINDLL 1725 ush CountryInfo[18]; 1726#if (!defined(__GO32__) && !defined(__EMX__)) 1727 ush far *_CountryInfo = CountryInfo; 1728 struct SREGS sregs; 1729 union REGS regs; 1730#ifdef WATCOMC_386 1731 ush seg, para; 1732 1733 memset(&sregs, 0, sizeof(sregs)); 1734 memset(®s, 0, sizeof(regs)); 1735 /* PMODE/W does not support an extended version of dos function 38, */ 1736 /* so we have to use brute force, allocating real mode memory for it. */ 1737 regs.w.ax = 0x0100; 1738 regs.w.bx = 3; /* 36 bytes rounds up to 48 */ 1739 int386(0x31, ®s, ®s); /* DPMI allocate DOS memory */ 1740 if (regs.w.cflag) 1741 return DF_MDY; /* no memory, return default */ 1742 seg = regs.w.dx; 1743 para = regs.w.ax; 1744 1745#ifdef XXX__MK_FP_IS_BROKEN 1746 /* XXX This code may not be trustworthy in general, though it is 1747 * valid with DOS/4GW and PMODE/w, which is all we support for now. */ 1748 /* _CountryInfo = (ush far *) (para << 4); */ /* works for some extenders */ 1749 regs.w.ax = 6; 1750 regs.w.bx = seg; 1751 int386(0x31, ®s, ®s); /* convert seg to linear address */ 1752 _CountryInfo = (ush far *) (((ulg) regs.w.cx << 16) | regs.w.dx); 1753#else 1754 _CountryInfo = (ush far *) MK_FP(seg, 0); 1755#endif 1756 1757 sregs.ds = para; /* real mode paragraph */ 1758 regs.w.dx = 0; /* no offset from segment */ 1759 regs.w.ax = 0x3800; 1760 int86x_realmode(0x21, ®s, ®s, &sregs); 1761 CountryInfo[0] = regs.w.cflag ? 0 : _CountryInfo[0]; 1762 regs.w.ax = 0x0101; 1763 regs.w.dx = seg; 1764 int386(0x31, ®s, ®s); /* DPMI free DOS memory */ 1765 1766#else /* !WATCOMC_386 */ 1767 1768 sregs.ds = FP_SEG(_CountryInfo); 1769 regs.x.dx = FP_OFF(_CountryInfo); 1770 regs.x.ax = 0x3800; 1771 intdosx(®s, ®s, &sregs); 1772#endif /* ?WATCOMC_386 */ 1773 1774#else /* __GO32__ || __EMX__ */ 1775 _dos_getcountryinfo(CountryInfo); 1776#endif /* ?(__GO32__ || __EMX__) */ 1777 1778 switch(CountryInfo[0]) { 1779 case 0: 1780 return DF_MDY; 1781 case 1: 1782 return DF_DMY; 1783 case 2: 1784 return DF_YMD; 1785 } 1786#endif /* !WINDLL */ 1787 1788 return DF_MDY; /* default for systems without locale info */ 1789 1790} /* end function dateformat() */ 1791 1792 1793 1794 1795#ifndef WINDLL 1796 1797/**************************************/ 1798/* Function is_running_on_windows() */ 1799/**************************************/ 1800 1801static int is_running_on_windows(void) 1802{ 1803 char *var = getenv("OS"); 1804 1805 /* if the OS env.var says 'Windows_NT' then */ 1806 /* we're likely running on a variant of WinNT */ 1807 if ((var != NULL) && (strcmp("Windows_NT", var) == 0)) 1808 return TRUE; 1809 1810 /* if the windir env.var is non-null then */ 1811 /* we're likely running on a variant of Win9x */ 1812 /* DOS mode of Win9x doesn't define windir, only winbootdir */ 1813 /* NT's command.com can't see lowercase env. vars */ 1814 var = getenv("windir"); 1815 if ((var != NULL) && (var[0] != '\0')) 1816 return TRUE; 1817 1818 return FALSE; 1819} 1820 1821 1822/**********************************/ 1823/* Function check_for_windows() */ 1824/**********************************/ 1825 1826void check_for_windows(ZCONST char *app) 1827{ 1828#ifdef SMALL_MEM 1829 char msg_str[160]; /* enough space for two 79-char-lines */ 1830 1831 (void)zfstrcpy(msg_buf, WarnUsedOnWindows) 1832#else 1833# define msg_str WarnUsedOnWindows 1834#endif 1835 /* Print a warning for users running under Windows */ 1836 /* to reduce bug reports due to running DOS version */ 1837 /* under Windows, when Windows version usually works correctly */ 1838 if (is_running_on_windows()) 1839 printf(msg_str, app); 1840} /* end function check_for_windows() */ 1841 1842 1843/************************/ 1844/* Function version() */ 1845/************************/ 1846 1847void version(__G) 1848 __GDEF 1849{ 1850 int len; 1851#if defined(__DJGPP__) || defined(__WATCOMC__) || \ 1852 (defined(_MSC_VER) && (_MSC_VER != 800)) 1853 char buf[80]; 1854#endif 1855 1856 len = sprintf((char *)slide, LoadFarString(CompiledWith), 1857 1858#if defined(__GNUC__) 1859# if defined(__DJGPP__) 1860 (sprintf(buf, "djgpp v%d.%02d / gcc ", __DJGPP__, __DJGPP_MINOR__), buf), 1861# elif defined(__GO32__) /* __GO32__ is defined as "1" only (sigh) */ 1862 "djgpp v1.x / gcc ", 1863# elif defined(__EMX__) /* ...so is __EMX__ (double sigh) */ 1864 "emx+gcc ", 1865# else 1866 "gcc ", 1867# endif 1868 __VERSION__, 1869#elif defined(__WATCOMC__) 1870# if (__WATCOMC__ % 10 != 0) 1871 "Watcom C/C++", (sprintf(buf, " %d.%02d", __WATCOMC__ / 100, 1872 __WATCOMC__ % 100), buf), 1873# else 1874 "Watcom C/C++", (sprintf(buf, " %d.%d", __WATCOMC__ / 100, 1875 (__WATCOMC__ % 100) / 10), buf), 1876# endif 1877#elif defined(__TURBOC__) 1878# ifdef __BORLANDC__ 1879 "Borland C++", 1880# if (__BORLANDC__ < 0x0200) 1881 " 1.0", 1882# elif (__BORLANDC__ == 0x0200) /* James: __TURBOC__ = 0x0297 */ 1883 " 2.0", 1884# elif (__BORLANDC__ == 0x0400) 1885 " 3.0", 1886# elif (__BORLANDC__ == 0x0410) /* __BCPLUSPLUS__ = 0x0310 */ 1887 " 3.1", 1888# elif (__BORLANDC__ == 0x0452) /* __BCPLUSPLUS__ = 0x0320 */ 1889 " 4.0 or 4.02", 1890# elif (__BORLANDC__ == 0x0460) /* __BCPLUSPLUS__ = 0x0340 */ 1891 " 4.5", 1892# elif (__BORLANDC__ == 0x0500) 1893 " 5.0", 1894# else 1895 " later than 5.0", 1896# endif 1897# else 1898 "Turbo C", 1899# if (__TURBOC__ > 0x0401) /* Kevin: 3.0 -> 0x0401 */ 1900 "++ later than 3.0", 1901# elif (__TURBOC__ >= 0x0400) 1902 "++ 3.0", 1903# elif (__TURBOC__ >= 0x0297) /* see remark for Borland C++ 2.0 */ 1904 "++ 2.0", 1905# elif (__TURBOC__ == 0x0296) /* [662] checked by SPC */ 1906 "++ 1.01", 1907# elif (__TURBOC__ == 0x0295) /* [661] vfy'd by Kevin */ 1908 "++ 1.0", 1909# elif (__TURBOC__ == 0x0201) /* Brian: 2.01 -> 0x0201 */ 1910 " 2.01", 1911# elif ((__TURBOC__ >= 0x018d) && (__TURBOC__ <= 0x0200)) /* James: 0x0200 */ 1912 " 2.0", 1913# elif (__TURBOC__ > 0x0100) 1914 " 1.5", /* James: 0x0105? */ 1915# else 1916 " 1.0", /* James: 0x0100 */ 1917# endif 1918# endif 1919#elif defined(MSC) 1920# if defined(_QC) && !defined(_MSC_VER) 1921 "MS Quick C ", "2.0 or earlier", /* _QC is defined as 1 */ 1922# elif defined(_QC) && (_MSC_VER == 600) 1923 "MS Quick C ", "2.5 (MSC 6.00)", 1924# else 1925 "Microsoft C ", 1926# ifdef _MSC_VER 1927# if (_MSC_VER == 800) 1928 "8.0/8.0c (Visual C++ 1.0/1.5)", 1929# else 1930 (sprintf(buf, "%d.%02d", _MSC_VER/100, _MSC_VER%100), buf), 1931# endif 1932# else 1933 "5.1 or earlier", 1934# endif 1935# endif 1936#else 1937 "unknown compiler", "", 1938#endif /* ?compilers */ 1939 1940 "\nMS-DOS", 1941 1942#if (defined(__GNUC__) || defined(WATCOMC_386)) 1943 " (32-bit)", 1944#else 1945# if defined(M_I86HM) || defined(__HUGE__) 1946 " (16-bit, huge)", 1947# elif defined(M_I86LM) || defined(__LARGE__) 1948 " (16-bit, large)", 1949# elif defined(M_I86MM) || defined(__MEDIUM__) 1950 " (16-bit, medium)", 1951# elif defined(M_I86CM) || defined(__COMPACT__) 1952 " (16-bit, compact)", 1953# elif defined(M_I86SM) || defined(__SMALL__) 1954 " (16-bit, small)", 1955# elif defined(M_I86TM) || defined(__TINY__) 1956 " (16-bit, tiny)", 1957# else 1958 " (16-bit)", 1959# endif 1960#endif 1961 1962#ifdef __DATE__ 1963 " on ", __DATE__ 1964#else 1965 "", "" 1966#endif 1967 ); 1968 1969 (*G.message)((zvoid *)&G, slide, (ulg)len, 0); 1970 /* MSC can't handle huge macro expansion */ 1971 1972 /* temporary debugging code for Borland compilers only */ 1973#if (defined(__TURBOC__) && defined(DEBUG)) 1974 Info(slide, 0, ((char *)slide, "\tdebug(__TURBOC__ = 0x%04x = %d)\n", 1975 __TURBOC__, __TURBOC__)); 1976#ifdef __BORLANDC__ 1977 Info(slide, 0, ((char *)slide, "\tdebug(__BORLANDC__ = 0x%04x)\n", 1978 __BORLANDC__)); 1979#else 1980 Info(slide, 0, ((char *)slide, "\tdebug(__BORLANDC__ not defined)\n")); 1981#endif 1982#ifdef __TCPLUSPLUS__ 1983 Info(slide, 0, ((char *)slide, "\tdebug(__TCPLUSPLUS__ = 0x%04x)\n", 1984 __TCPLUSPLUS__)); 1985#else 1986 Info(slide, 0, ((char *)slide, "\tdebug(__TCPLUSPLUS__ not defined)\n")); 1987#endif 1988#ifdef __BCPLUSPLUS__ 1989 Info(slide, 0, ((char *)slide, "\tdebug(__BCPLUSPLUS__ = 0x%04x)\n\n", 1990 __BCPLUSPLUS__)); 1991#else 1992 Info(slide, 0, ((char *)slide, "\tdebug(__BCPLUSPLUS__ not defined)\n\n")); 1993#endif 1994#endif /* __TURBOC__ && DEBUG */ 1995 1996} /* end function version() */ 1997 1998#endif /* !WINDLL */ 1999#endif /* !SFX */ 2000 2001#endif /* !FUNZIP */ 2002 2003 2004 2005 2006 2007#ifdef MY_ZCALLOC /* Special zcalloc function for MEMORY16 (MSDOS/OS2) */ 2008 2009#if defined(__TURBOC__) && !defined(OS2) 2010#include <alloc.h> 2011/* Turbo C malloc() does not allow dynamic allocation of 64K bytes 2012 * and farmalloc(64K) returns a pointer with an offset of 8, so we 2013 * must fix the pointer. Warning: the pointer must be put back to its 2014 * original form in order to free it, use zcfree(). 2015 */ 2016 2017#define MAX_PTR 2 /* reduced from 10 to save space */ 2018/* 10*64K = 640K */ 2019 2020static int next_ptr = 0; 2021 2022typedef struct ptr_table_s { 2023 zvoid far *org_ptr; 2024 zvoid far *new_ptr; 2025} ptr_table; 2026 2027static ptr_table table[MAX_PTR]; 2028/* This table is used to remember the original form of pointers 2029 * to large buffers (64K). Such pointers are normalized with a zero offset. 2030 * Since MSDOS is not a preemptive multitasking OS, this table is not 2031 * protected from concurrent access. This hack doesn't work anyway on 2032 * a protected system like OS/2. Use Microsoft C instead. 2033 */ 2034 2035zvoid far *zcalloc(unsigned items, unsigned size) 2036{ 2037 zvoid far *buf; 2038 ulg bsize = (ulg)items*size; 2039 2040 if (bsize < (65536L-16L)) { 2041 buf = farmalloc(bsize); 2042 if (*(ush*)&buf != 0) return buf; 2043 } else { 2044 buf = farmalloc(bsize + 16L); 2045 } 2046 if (buf == NULL || next_ptr >= MAX_PTR) return NULL; 2047 table[next_ptr].org_ptr = buf; 2048 2049 /* Normalize the pointer to seg:0 */ 2050 *((ush*)&buf+1) += ((ush)((uch*)buf-NULL) + 15) >> 4; 2051 *(ush*)&buf = 0; 2052 table[next_ptr++].new_ptr = buf; 2053 return buf; 2054} 2055 2056zvoid zcfree(zvoid far *ptr) 2057{ 2058 int n; 2059 if (*(ush*)&ptr != 0) { /* object < 64K */ 2060 farfree(ptr); 2061 return; 2062 } 2063 /* Find the original pointer */ 2064 for (n = next_ptr - 1; n >= 0; n--) { 2065 if (ptr != table[n].new_ptr) continue; 2066 2067 farfree(table[n].org_ptr); 2068 while (++n < next_ptr) { 2069 table[n-1] = table[n]; 2070 } 2071 next_ptr--; 2072 return; 2073 } 2074 Trace((stderr, "zcfree: ptr not found!\n")); 2075} 2076#endif /* __TURBOC__ */ 2077 2078#if defined(MSC) || defined(__WATCOMC__) 2079#if (!defined(_MSC_VER) || (_MSC_VER < 700)) 2080# define _halloc halloc 2081# define _hfree hfree 2082#endif 2083 2084zvoid far *zcalloc(unsigned items, unsigned size) 2085{ 2086 return (zvoid far *)_halloc((long)items, size); 2087} 2088 2089zvoid zcfree(zvoid far *ptr) 2090{ 2091 _hfree((void huge *)ptr); 2092} 2093#endif /* MSC || __WATCOMC__ */ 2094 2095#endif /* MY_ZCALLOC */ 2096 2097 2098#ifndef FUNZIP 2099 2100#if (defined(__GO32__) || defined(__EMX__)) 2101 2102#if (!defined(__DJGPP__) || (__DJGPP__ < 2) || \ 2103 ((__DJGPP__ == 2) && (__DJGPP_MINOR__ < 2))) 2104int volatile _doserrno; 2105#endif /* not "djgpp v2.02 or newer" */ 2106 2107#if (!defined(__DJGPP__) || (__DJGPP__ < 2)) 2108 2109unsigned _dos_getcountryinfo(void *countrybuffer) 2110{ 2111 asm("movl %0, %%edx": : "g" (countrybuffer)); 2112 asm("movl $0x3800, %eax"); 2113 asm("int $0x21": : : "%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi"); 2114 _doserrno = 0; 2115 asm("jnc 1f"); 2116 asm("movl %%eax, %0": "=m" (_doserrno)); 2117 asm("1:"); 2118 return (unsigned)_doserrno; 2119} 2120 2121unsigned _dos_setftime(int fd, unsigned dosdate, unsigned dostime) 2122{ 2123 asm("movl %0, %%ebx": : "g" (fd)); 2124 asm("movl %0, %%ecx": : "g" (dostime)); 2125 asm("movl %0, %%edx": : "g" (dosdate)); 2126 asm("movl $0x5701, %eax"); 2127 asm("int $0x21": : : "%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi"); 2128 _doserrno = 0; 2129 asm("jnc 1f"); 2130 asm("movl %%eax, %0": "=m" (_doserrno)); 2131 errno = EBADF; 2132 asm("1:"); 2133 return (unsigned)_doserrno; 2134} 2135 2136unsigned _dos_setfileattr(const char *name, unsigned attr) 2137{ 2138#if 0 /* stripping of trailing '/' is not needed for unzip-internal use */ 2139 unsigned namlen = strlen(name); 2140 char *i_name = alloca(namlen + 1); 2141 2142 strcpy(i_name, name); 2143 if (namlen > 1 && i_name[namlen-1] == '/' && i_name[namlen-2] != ':') 2144 i_name[namlen-1] = '\0'; 2145 asm("movl %0, %%edx": : "g" (i_name)); 2146#else 2147 asm("movl %0, %%edx": : "g" (name)); 2148#endif 2149 asm("movl %0, %%ecx": : "g" (attr)); 2150 asm("movl $0x4301, %eax"); 2151 asm("int $0x21": : : "%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi"); 2152 _doserrno = 0; 2153 asm("jnc 1f"); 2154 asm("movl %%eax, %0": "=m" (_doserrno)); 2155 switch (_doserrno) { 2156 case 2: 2157 case 3: 2158 errno = ENOENT; 2159 break; 2160 case 5: 2161 errno = EACCES; 2162 break; 2163 } 2164 asm("1:"); 2165 return (unsigned)_doserrno; 2166} 2167 2168void _dos_getdrive(unsigned *d) 2169{ 2170 asm("movl $0x1900, %eax"); 2171 asm("int $0x21": : : "%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi"); 2172 asm("xorb %ah, %ah"); 2173 asm("incb %al"); 2174 asm("movl %%eax, %0": "=a" (*d)); 2175} 2176 2177unsigned _dos_creat(const char *path, unsigned attr, int *fd) 2178{ 2179 asm("movl $0x3c00, %eax"); 2180 asm("movl %0, %%edx": :"g" (path)); 2181 asm("movl %0, %%ecx": :"g" (attr)); 2182 asm("int $0x21": : : "%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi"); 2183 asm("movl %%eax, %0": "=a" (*fd)); 2184 _doserrno = 0; 2185 asm("jnc 1f"); 2186 _doserrno = *fd; 2187 switch (_doserrno) { 2188 case 3: 2189 errno = ENOENT; 2190 break; 2191 case 4: 2192 errno = EMFILE; 2193 break; 2194 case 5: 2195 errno = EACCES; 2196 break; 2197 } 2198 asm("1:"); 2199 return (unsigned)_doserrno; 2200} 2201 2202unsigned _dos_close(int fd) 2203{ 2204 asm("movl %0, %%ebx": : "g" (fd)); 2205 asm("movl $0x3e00, %eax"); 2206 asm("int $0x21": : : "%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi"); 2207 _doserrno = 0; 2208 asm("jnc 1f"); 2209 asm ("movl %%eax, %0": "=m" (_doserrno)); 2210 if (_doserrno == 6) { 2211 errno = EBADF; 2212 } 2213 asm("1:"); 2214 return (unsigned)_doserrno; 2215} 2216 2217#endif /* !__DJGPP__ || (__DJGPP__ < 2) */ 2218 2219 2220static int volumelabel(ZCONST char *name) 2221{ 2222 int fd; 2223 2224 return _dos_creat(name, FA_LABEL, &fd) ? fd : _dos_close(fd); 2225} 2226 2227 2228#if (defined(__DJGPP__) && (__DJGPP__ >= 2)) 2229 2230#include <dpmi.h> /* These includes for the country info */ 2231#include <go32.h> 2232#include <sys/farptr.h> 2233 2234/* The above _dos_getcountryinfo function doesn't work with djgpp v2, presumably 2235 * because ds is not set correctly (does it really work at all?). Note that 2236 * this version only sets the date (ie. CountryInfo[0]). 2237 */ 2238unsigned _dos_getcountryinfo(void *countrybuffer) 2239{ 2240 __dpmi_regs regs; 2241 2242 regs.x.ax = 0x3800; 2243 regs.x.dx = __tb & 0x0f; 2244 regs.x.ds = (__tb >> 4) & 0xffff; 2245 _doserrno = __dpmi_int(0x21, ®s); 2246 2247 *(ush*)countrybuffer = _farpeekw(_dos_ds, __tb & 0xfffff); 2248 2249 return (unsigned)_doserrno; 2250} 2251 2252 2253/* Disable determination of "x" bit in st_mode field for [f]stat() calls. */ 2254int _is_executable (const char *path, int fhandle, const char *ext) 2255{ 2256 return 0; 2257} 2258 2259#ifndef USE_DJGPP_GLOB 2260/* Prevent globbing of filenames. This gives the same functionality as 2261 * "stubedit <program> globbing=no" did with DJGPP v1. 2262 */ 2263char **__crt0_glob_function(char *_arg) 2264{ 2265 return NULL; 2266} 2267#endif /* !USE_DJGPP_GLOB */ 2268 2269#ifndef USE_DJGPP_ENV 2270/* Reduce the size of the executable and remove the functionality to read 2271 * the program's environment from whatever $DJGPP points to. 2272 */ 2273void __crt0_load_environment_file(char *_app_name) 2274{ 2275} 2276#endif /* !USE_DJGPP_ENV */ 2277 2278#endif /* __DJGPP__ >= 2 */ 2279#endif /* __GO32__ || __EMX__ */ 2280 2281 2282 2283static int getdoscodepage(void) 2284{ 2285 union REGS regs; 2286 2287 WREGS(regs,ax) = 0x6601; 2288#ifdef __EMX__ 2289 _int86(0x21, ®s, ®s); 2290 if (WREGS(regs,flags) & 1) 2291#else 2292 intdos(®s, ®s); 2293 if (WREGS(regs,cflag)) 2294#endif 2295 { 2296 Trace((stderr, 2297 "error in DOS function 0x66 (AX = 0x%04x): default to 850...\n", 2298 (unsigned int)(WREGS(regs,ax)))); 2299 return 858; 2300 } else 2301 return WREGS(regs,bx); 2302} 2303 2304 2305 2306#ifdef __EMX__ 2307#ifdef MORE 2308 2309/*************************/ 2310/* Function screensize() */ 2311/*************************/ 2312 2313int screensize(int *tt_rows, int *tt_cols) 2314{ 2315 int scr_dimen[2]; /* scr_dimen[0]: columns, src_dimen[1]: rows */ 2316 2317 _scrsize(scr_dimen); 2318 if (tt_rows != NULL) *tt_rows = scr_dimen[1]; 2319 if (tt_cols != NULL) *tt_cols = scr_dimen[0]; 2320 return 0; 2321} 2322 2323#endif /* MORE */ 2324#endif /* __EMX__ */ 2325 2326 2327 2328#ifdef WATCOMC_386 2329#ifdef MORE 2330#include <graph.h> 2331 2332/*************************/ 2333/* Function screensize() */ 2334/*************************/ 2335 2336int screensize(int *tt_rows, int *tt_cols) 2337{ 2338 struct videoconfig vc; 2339 2340 _getvideoconfig(&vc); 2341 if (tt_rows != NULL) *tt_rows = (int)(vc.numtextrows); 2342 if (tt_cols != NULL) *tt_cols = (int)(vc.numtextcols); 2343 return 0; 2344} 2345 2346#endif /* MORE */ 2347 2348 2349static struct RMINFO { 2350 ulg edi, esi, ebp; 2351 ulg reserved; 2352 ulg ebx, edx, ecx, eax; 2353 ush flags; 2354 ush es,ds,fs,gs; 2355 ush ip_ignored,cs_ignored; 2356 ush sp,ss; 2357}; 2358 2359/* This function is used to call dos interrupts that may not be supported 2360 * by some particular 32-bit DOS extender. It uses DPMI function 300h to 2361 * simulate a real mode call of the interrupt. The caller is responsible 2362 * for providing real mode addresses of any buffer areas used. The docs 2363 * for PMODE/W imply that this should not be necessary for calling the DOS 2364 * interrupts that it doesn't extend, but it crashes when this isn't used. */ 2365 2366static int int86x_realmode(int inter_no, union REGS *in, 2367 union REGS *out, struct SREGS *seg) 2368{ 2369 union REGS local; 2370 struct SREGS localseg; 2371 struct RMINFO rmi; 2372 int r; 2373 2374 rmi.eax = in->x.eax; 2375 rmi.ebx = in->x.ebx; 2376 rmi.ecx = in->x.ecx; 2377 rmi.edx = in->x.edx; 2378 rmi.edi = in->x.edi; 2379 rmi.esi = in->x.esi; 2380 rmi.ebp = rmi.reserved = 0L; 2381 rmi.es = seg->es; 2382 rmi.ds = seg->ds; 2383 rmi.fs = seg->fs; 2384 rmi.gs = seg->gs; 2385 rmi.sp = rmi.ss = rmi.ip_ignored = rmi.cs_ignored = rmi.flags = 0; 2386 memset(&local, 0, sizeof(local)); 2387 memset(&localseg, 0, sizeof(localseg)); 2388 local.w.ax = 0x0300; 2389 local.h.bl = inter_no; 2390 local.h.bh = 0; 2391 local.w.cx = 0; 2392 localseg.es = FP_SEG(&rmi); 2393 local.x.edi = FP_OFF(&rmi); 2394 r = int386x(0x31, &local, &local, &localseg); 2395 out->x.eax = rmi.eax; 2396 out->x.ebx = rmi.ebx; 2397 out->x.ecx = rmi.ecx; 2398 out->x.edx = rmi.edx; 2399 out->x.edi = rmi.edi; 2400 out->x.esi = rmi.esi; 2401 out->x.cflag = rmi.flags & INTR_CF; 2402 return r; 2403} 2404 2405#endif /* WATCOMC_386 */ 2406 2407 2408 2409 2410#ifdef DOS_STAT_BANDAID 2411 2412/* This papers over a bug in Watcom 10.6's standard library...sigh. 2413 * Apparently it applies to both the DOS and Win32 stat()s. */ 2414 2415int stat_bandaid(const char *path, struct stat *buf) 2416{ 2417 char newname[4]; 2418 2419 if (!stat(path, buf)) 2420 return 0; 2421 else if (!strcmp(path, ".") || (path[0] && !strcmp(path + 1, ":."))) { 2422 strcpy(newname, path); 2423 newname[strlen(path) - 1] = '\\'; /* stat(".") fails for root! */ 2424 return stat(newname, buf); 2425 } else 2426 return -1; 2427} 2428 2429#endif /* DOS_STAT_BANDAID */ 2430 2431#endif /* !FUNZIP */ 2432