msdos.c
上传用户:andy_li
上传日期:2007-01-06
资源大小:1019k
文件大小:69k
源码类别:

压缩解压

开发平台:

MultiPlatform

  1. /*---------------------------------------------------------------------------
  2.   msdos.c
  3.   MSDOS-specific routines for use with Info-ZIP's UnZip 5.3 and later.
  4.   Contains:  Opendir()                      (from zip)
  5.              Readdir()                      (from zip)
  6.              do_wild()
  7.              mapattr()
  8.              mapname()
  9.              map2fat()
  10.              checkdir()
  11.              isfloppy()
  12.              z_dos_chmod()
  13.              volumelabel()                  (non-djgpp, non-emx)
  14.              close_outfile()
  15.              stamp_file()                   (TIMESTAMP only)
  16.              dateformat()
  17.              version()
  18.              _dos_getcountryinfo()          (djgpp 1.x, emx)
  19.             [_dos_getftime()                (djgpp 1.x, emx)   to be added]
  20.              _dos_setftime()                (djgpp 1.x, emx)
  21.              _dos_setfileattr()             (djgpp 1.x, emx)
  22.              _dos_getdrive()                (djgpp 1.x, emx)
  23.              _dos_creat()                   (djgpp 1.x, emx)
  24.              _dos_close()                   (djgpp 1.x, emx)
  25.              volumelabel()                  (djgpp, emx)
  26.              _dos_getcountryinfo()          (djgpp 2.x)
  27.              _is_executable()               (djgpp 2.x)
  28.              __crt0_glob_function()         (djgpp 2.x)
  29.              __crt0_load_environment_file() (djgpp 2.x)
  30.              screenlines()                  (emx)
  31.              screencolumns()                (emx)
  32.              int86x_realmode()              (Watcom 32-bit)
  33.              stat_bandaid()                 (Watcom)
  34.   ---------------------------------------------------------------------------*/
  35. #define UNZIP_INTERNAL
  36. #include "unzip.h"
  37. #ifdef MAYBE_PLAIN_FAT
  38.    static void map2fat OF((char *pathcomp, char *last_dot));
  39. #endif
  40. static int isfloppy OF((int nDrive));
  41. static int z_dos_chmod OF((__GPRO__ ZCONST char *fname, int attributes));
  42. static int volumelabel OF((char *newlabel));
  43. static int created_dir;        /* used by mapname(), checkdir() */
  44. static int renamed_fullpath;   /* ditto */
  45. static unsigned nLabelDrive;   /* ditto, plus volumelabel() */
  46. /*****************************/
  47. /*  Strings used in msdos.c  */
  48. /*****************************/
  49. #ifndef SFX
  50.   static ZCONST char Far CantAllocateWildcard[] =
  51.     "warning:  cannot allocate wildcard buffersn";
  52. #endif
  53. static ZCONST char Far Creating[] = "   creating: %sn";
  54. static ZCONST char Far ConversionFailed[] =
  55.   "mapname:  conversion of %s failedn";
  56. static ZCONST char Far Labelling[] = "labelling %c: %-22sn";
  57. static ZCONST char Far ErrSetVolLabel[] =
  58.   "mapname:  error setting volume labeln";
  59. static ZCONST char Far PathTooLong[] = "checkdir error:  path too long: %sn";
  60. static ZCONST char Far CantCreateDir[] = "checkdir error:  cannot create %sn
  61.                  unable to process %s.n";
  62. static ZCONST char Far DirIsntDirectory[] =
  63.   "checkdir error:  %s exists but is not directoryn
  64.                  unable to process %s.n";
  65. static ZCONST char Far PathTooLongTrunc[] =
  66.   "checkdir warning:  path too long; truncatingn                   %sn
  67.                 -> %sn";
  68. #if (!defined(SFX) || defined(SFX_EXDIR))
  69.    static ZCONST char Far CantCreateExtractDir[] =
  70.      "checkdir:  cannot create extraction directory: %sn";
  71. #endif
  72. static ZCONST char Far AttribsMayBeWrong[] =
  73.   "nwarning:  file attributes may not be correctn";
  74. /****************************/
  75. /*  Macros used in msdos.c  */
  76. /****************************/
  77. #ifdef WATCOMC_386
  78. #  define WREGS(v,r) (v##.w.##r)
  79. #  define int86x int386x
  80.    static int int86x_realmode(int inter_no, union REGS *in,
  81.                               union REGS *out, struct SREGS *seg);
  82. #  define F_intdosx(ir,or,sr) int86x_realmode(0x21, ir, or, sr)
  83. #  define XXX__MK_FP_IS_BROKEN
  84. #else
  85. #  define WREGS(v,r) (v##.x.##r)
  86. #  define F_intdosx(ir,or,sr) intdosx(ir, or, sr)
  87. #endif
  88. #if (defined(__GO32__) || defined(__EMX__))
  89. #  include <dirent.h>        /* use readdir() */
  90. #  define MKDIR(path,mode)   mkdir(path,mode)
  91. #  define Opendir  opendir
  92. #  define Readdir  readdir
  93. #  define Closedir closedir
  94. #  define zdirent  dirent
  95. #  define zDIR     DIR
  96. #  ifdef __EMX__
  97. #    include <dos.h>
  98. #    define GETDRIVE(d)      d = _getdrive()
  99. #    define FA_LABEL         A_LABEL
  100. #  else
  101. #    define GETDRIVE(d)      _dos_getdrive(&d)
  102. #  endif
  103. #  if defined(_A_SUBDIR)     /* MSC dos.h and compatibles */
  104. #    define FSUBDIR          _A_SUBDIR
  105. #  elif defined(FA_DIREC)    /* Borland dos.h and compatible variants */
  106. #    define FSUBDIR          FA_DIREC
  107. #  elif defined(A_DIR)       /* EMX dir.h (and dirent.h) */
  108. #    define FSUBDIR          A_DIR
  109. #  else                      /* fallback definition */
  110. #    define FSUBDIR          0x10
  111. #  endif
  112. #  if defined(_A_VOLID)      /* MSC dos.h and compatibles */
  113. #    define FVOLID           _A_VOLID
  114. #  elif defined(FA_LABEL)    /* Borland dos.h and compatible variants */
  115. #    define FVOLID           FA_LABEL
  116. #  elif defined(A_LABEL)     /* EMX dir.h (and dirent.h) */
  117. #    define FVOLID           A_LABEL
  118. #  else
  119. #    define FVOLID           0x08
  120. #  endif
  121. #else /* !(__GO32__ || __EMX__) */
  122. #  define MKDIR(path,mode)   mkdir(path)
  123. #  ifdef __TURBOC__
  124. #    define FATTR            FA_HIDDEN+FA_SYSTEM+FA_DIREC
  125. #    define FVOLID           FA_LABEL
  126. #    define FSUBDIR          FA_DIREC
  127. #    define FFIRST(n,d,a)    findfirst(n,(struct ffblk *)d,a)
  128. #    define FNEXT(d)         findnext((struct ffblk *)d)
  129. #    define GETDRIVE(d)      d=getdisk()+1
  130. #    include <dir.h>
  131. #  else /* !__TURBOC__ */
  132. #    define FATTR            _A_HIDDEN+_A_SYSTEM+_A_SUBDIR
  133. #    define FVOLID           _A_VOLID
  134. #    define FSUBDIR          _A_SUBDIR
  135. #    define FFIRST(n,d,a)    _dos_findfirst(n,a,(struct find_t *)d)
  136. #    define FNEXT(d)         _dos_findnext((struct find_t *)d)
  137. #    define GETDRIVE(d)      _dos_getdrive(&d)
  138. #    include <direct.h>
  139. #  endif /* ?__TURBOC__ */
  140.    typedef struct zdirent {
  141.        char d_reserved[30];
  142.        char d_name[13];
  143.        int d_first;
  144.    } zDIR;
  145.    zDIR *Opendir OF((const char *));
  146.    struct zdirent *Readdir OF((zDIR *));
  147. #  define Closedir free
  148. #ifndef SFX
  149. /**********************/   /* Borland C++ 3.x has its own opendir/readdir */
  150. /* Function Opendir() */   /*  library routines, but earlier versions don't, */
  151. /**********************/   /*  so use ours regardless */
  152. zDIR *Opendir(name)
  153.     const char *name;           /* name of directory to open */
  154. {
  155.     zDIR *dirp;                 /* malloc'd return value */
  156.     char *nbuf;                 /* malloc'd temporary string */
  157.     extent len = strlen(name);  /* path length to avoid strlens and strcats */
  158.     if ((dirp = (zDIR *)malloc(sizeof(zDIR))) == (zDIR *)NULL)
  159.         return (zDIR *)NULL;
  160.     if ((nbuf = malloc(len + 6)) == (char *)NULL) {
  161.         free(dirp);
  162.         return (zDIR *)NULL;
  163.     }
  164.     strcpy(nbuf, name);
  165.     if (len > 0) {
  166.         if (nbuf[len-1] == ':') {
  167.             nbuf[len++] = '.';
  168.         } else if (nbuf[len-1] == '/' || nbuf[len-1] == '\')
  169.             --len;
  170.     }
  171.     strcpy(nbuf+len, "/*.*");
  172.     Trace((stderr, "Opendir:  nbuf = [%s]n", nbuf));
  173.     if (FFIRST(nbuf, dirp, FATTR)) {
  174.         free((zvoid *)nbuf);
  175.         return (zDIR *)NULL;
  176.     }
  177.     free((zvoid *)nbuf);
  178.     dirp->d_first = 1;
  179.     return dirp;
  180. }
  181. /**********************/
  182. /* Function Readdir() */
  183. /**********************/
  184. struct zdirent *Readdir(d)
  185.     zDIR *d;        /* directory stream from which to read */
  186. {
  187.     /* Return pointer to first or next directory entry, or NULL if end. */
  188.     if (d->d_first)
  189.         d->d_first = 0;
  190.     else
  191.         if (FNEXT(d))
  192.             return (struct zdirent *)NULL;
  193.     return (struct zdirent *)d;
  194. }
  195. #endif /* !SFX */
  196. #endif /* ?(__GO32__ || __EMX__) */
  197. #ifndef SFX
  198. /************************/
  199. /*  Function do_wild()  */   /* identical to OS/2 version */
  200. /************************/
  201. char *do_wild(__G__ wildspec)
  202.     __GDEF
  203.     char *wildspec;          /* only used first time on a given dir */
  204. {
  205.     static zDIR *dir = (zDIR *)NULL;
  206.     static char *dirname, *wildname, matchname[FILNAMSIZ];
  207.     static int firstcall=TRUE, have_dirname, dirnamelen;
  208.     char *fnamestart;
  209.     struct zdirent *file;
  210.     /* Even when we're just returning wildspec, we *always* do so in
  211.      * matchname[]--calling routine is allowed to append four characters
  212.      * to the returned string, and wildspec may be a pointer to argv[].
  213.      */
  214.     if (firstcall) {        /* first call:  must initialize everything */
  215.         firstcall = FALSE;
  216.         if (!iswild(wildspec)) {
  217.             strcpy(matchname, wildspec);
  218.             have_dirname = FALSE;
  219.             dir = NULL;
  220.             return matchname;
  221.         }
  222.         /* break the wildspec into a directory part and a wildcard filename */
  223.         if ((wildname = strrchr(wildspec, '/')) == (char *)NULL &&
  224.             (wildname = strrchr(wildspec, ':')) == (char *)NULL) {
  225.             dirname = ".";
  226.             dirnamelen = 1;
  227.             have_dirname = FALSE;
  228.             wildname = wildspec;
  229.         } else {
  230.             ++wildname;     /* point at character after '/' or ':' */
  231.             dirnamelen = (int)(wildname - wildspec);
  232.             if ((dirname = (char *)malloc(dirnamelen+1)) == (char *)NULL) {
  233.                 Info(slide, 1, ((char *)slide,
  234.                   LoadFarString(CantAllocateWildcard)));
  235.                 strcpy(matchname, wildspec);
  236.                 return matchname;   /* but maybe filespec was not a wildcard */
  237.             }
  238. /* GRR:  can't strip trailing char for opendir since might be "d:/" or "d:"
  239.  *       (would have to check for "./" at end--let opendir handle it instead) */
  240.             strncpy(dirname, wildspec, dirnamelen);
  241.             dirname[dirnamelen] = '';       /* terminate for strcpy below */
  242.             have_dirname = TRUE;
  243.         }
  244.         Trace((stderr, "do_wild:  dirname = [%s]n", dirname));
  245.         if ((dir = Opendir(dirname)) != (zDIR *)NULL) {
  246.             if (have_dirname) {
  247.                 strcpy(matchname, dirname);
  248.                 fnamestart = matchname + dirnamelen;
  249.             } else
  250.                 fnamestart = matchname;
  251.             while ((file = Readdir(dir)) != (struct zdirent *)NULL) {
  252.                 Trace((stderr, "do_wild:  readdir returns %sn", file->d_name));
  253.                 strcpy(fnamestart, file->d_name);
  254.                 if (strrchr(fnamestart, '.') == (char *)NULL)
  255.                     strcat(fnamestart, ".");
  256.                 if (match(fnamestart, wildname, 1) &&  /* 1 == ignore case */
  257.                     /* skip "." and ".." directory entries */
  258.                     strcmp(fnamestart, ".") && strcmp(fnamestart, "..")) {
  259.                     Trace((stderr, "do_wild:  match() succeedsn"));
  260.                     /* remove trailing dot */
  261.                     fnamestart += strlen(fnamestart) - 1;
  262.                     if (*fnamestart == '.')
  263.                         *fnamestart = '';
  264.                     return matchname;
  265.                 }
  266.             }
  267.             /* if we get to here directory is exhausted, so close it */
  268.             Closedir(dir);
  269.             dir = (zDIR *)NULL;
  270.         }
  271. #ifdef DEBUG
  272.         else {
  273.             Trace((stderr, "do_wild:  Opendir(%s) returns NULLn", dirname));
  274.         }
  275. #endif /* DEBUG */
  276.         /* return the raw wildspec in case that works (e.g., directory not
  277.          * searchable, but filespec was not wild and file is readable) */
  278.         strcpy(matchname, wildspec);
  279.         return matchname;
  280.     }
  281.     /* last time through, might have failed opendir but returned raw wildspec */
  282.     if (dir == (zDIR *)NULL) {
  283.         firstcall = TRUE;  /* nothing left to try--reset for new wildspec */
  284.         if (have_dirname)
  285.             free(dirname);
  286.         return (char *)NULL;
  287.     }
  288.     /* If we've gotten this far, we've read and matched at least one entry
  289.      * successfully (in a previous call), so dirname has been copied into
  290.      * matchname already.
  291.      */
  292.     if (have_dirname) {
  293.         /* strcpy(matchname, dirname); */
  294.         fnamestart = matchname + dirnamelen;
  295.     } else
  296.         fnamestart = matchname;
  297.     while ((file = Readdir(dir)) != (struct zdirent *)NULL) {
  298.         Trace((stderr, "do_wild:  readdir returns %sn", file->d_name));
  299.         strcpy(fnamestart, file->d_name);
  300.         if (strrchr(fnamestart, '.') == (char *)NULL)
  301.             strcat(fnamestart, ".");
  302.         if (match(fnamestart, wildname, 1)) {   /* 1 == ignore case */
  303.             Trace((stderr, "do_wild:  match() succeedsn"));
  304.             /* remove trailing dot */
  305.             fnamestart += strlen(fnamestart) - 1;
  306.             if (*fnamestart == '.')
  307.                 *fnamestart = '';
  308.             return matchname;
  309.         }
  310.     }
  311.     Closedir(dir);     /* have read at least one dir entry; nothing left */
  312.     dir = (zDIR *)NULL;
  313.     firstcall = TRUE;  /* reset for new wildspec */
  314.     if (have_dirname)
  315.         free(dirname);
  316.     return (char *)NULL;
  317. } /* end function do_wild() */
  318. #endif /* !SFX */
  319. /**********************/
  320. /* Function mapattr() */
  321. /**********************/
  322. int mapattr(__G)
  323.     __GDEF
  324. {
  325.     /* set archive bit for file entries (file is not backed up): */
  326.     G.pInfo->file_attr = ((unsigned)G.crec.external_file_attributes |
  327.       (G.crec.external_file_attributes & FSUBDIR ? 0 : 32)) & 0xff;
  328.     return 0;
  329. } /* end function mapattr() */
  330. /************************/
  331. /*  Function mapname()  */
  332. /************************/
  333.                              /* return 0 if no error, 1 if caution (filename */
  334. int mapname(__G__ renamed)   /*  truncated), 2 if warning (skip file because */
  335.     __GDEF                   /*  dir doesn't exist), 3 if error (skip file), */
  336.     int renamed;             /*  or 10 if out of memory (skip file) */
  337. {                            /*  [also IZ_VOL_LABEL, IZ_CREATED_DIR] */
  338.     char pathcomp[FILNAMSIZ];      /* path-component buffer */
  339.     char *pp, *cp=(char *)NULL;    /* character pointers */
  340.     char *lastsemi=(char *)NULL;   /* pointer to last semi-colon in pathcomp */
  341. #ifdef MAYBE_PLAIN_FAT
  342.     char *last_dot=(char *)NULL;   /* last dot not converted to underscore */
  343.     int dotname = FALSE;           /* path component begins with dot? */
  344. # ifdef USE_LFN
  345.     int use_lfn = USE_LFN;         /* file system supports long filenames? */
  346. # endif
  347. #endif
  348.     int error = 0;
  349.     register unsigned workch;      /* hold the character being tested */
  350. /*---------------------------------------------------------------------------
  351.     Initialize various pointers and counters and stuff.
  352.   ---------------------------------------------------------------------------*/
  353.     /* can create path as long as not just freshening, or if user told us */
  354.     G.create_dirs = (!uO.fflag || renamed);
  355.     created_dir = FALSE;        /* not yet */
  356.     renamed_fullpath = FALSE;
  357.     if (renamed) {
  358.         cp = G.filename - 1;    /* point to beginning of renamed name... */
  359.         while (*++cp)
  360.             if (*cp == '\')    /* convert backslashes to forward */
  361.                 *cp = '/';
  362.         cp = G.filename;
  363.         /* use temporary rootpath if user gave full pathname */
  364.         if (G.filename[0] == '/') {
  365.             renamed_fullpath = TRUE;
  366.             pathcomp[0] = '/';  /* copy the '/' and terminate */
  367.             pathcomp[1] = '';
  368.             ++cp;
  369.         } else if (isalpha(G.filename[0]) && G.filename[1] == ':') {
  370.             renamed_fullpath = TRUE;
  371.             pp = pathcomp;
  372.             *pp++ = *cp++;      /* copy the "d:" (+ '/', possibly) */
  373.             *pp++ = *cp++;
  374.             if (*cp == '/')
  375.                 *pp++ = *cp++;  /* otherwise add "./"? */
  376.             *pp = '';
  377.         }
  378.     }
  379.     /* pathcomp is ignored unless renamed_fullpath is TRUE: */
  380.     if ((error = checkdir(__G__ pathcomp, INIT)) != 0) /* initialize path buf */
  381.         return error;           /* ...unless no mem or vol label on hard disk */
  382.     *pathcomp = '';           /* initialize translation buffer */
  383.     pp = pathcomp;              /* point to translation buffer */
  384.     if (!renamed) {             /* cp already set if renamed */
  385.         if (uO.jflag)           /* junking directories */
  386.             cp = (char *)strrchr(G.filename, '/');
  387.         if (cp == (char *)NULL) /* no '/' or not junking dirs */
  388.             cp = G.filename;    /* point to internal zipfile-member pathname */
  389.         else
  390.             ++cp;               /* point to start of last component of path */
  391.     }
  392. /*---------------------------------------------------------------------------
  393.     Begin main loop through characters in filename.
  394.   ---------------------------------------------------------------------------*/
  395.     while ((workch = (uch)*cp++) != 0) {
  396.         switch (workch) {
  397.             case '/':             /* can assume -j flag not given */
  398.                 *pp = '';
  399. #ifdef MAYBE_PLAIN_FAT
  400. # ifdef USE_LFN
  401.                 if (!use_lfn)
  402. # endif
  403.                 {
  404.                     map2fat(pathcomp, last_dot);   /* 8.3 trunc. (in place) */
  405.                     last_dot = (char *)NULL;
  406.                 }
  407. #endif
  408.                 if ((error = checkdir(__G__ pathcomp, APPEND_DIR)) > 1)
  409.                     return error;
  410.                 pp = pathcomp;    /* reset conversion buffer for next piece */
  411.                 lastsemi = (char *)NULL; /* leave directory semi-colons alone */
  412.                 break;
  413.             /* drive names are not stored in zipfile, so no colons allowed;
  414.              *  no brackets or most other punctuation either (all of which
  415.              *  can appear in Unix-created archives; backslash is particularly
  416.              *  bad unless all necessary directories exist) */
  417. #ifdef MAYBE_PLAIN_FAT
  418.             case '[':          /* these punctuation characters forbidden */
  419.             case ']':          /*  only on plain FAT file systems */
  420.             case '+':
  421.             case ',':
  422.             case '=':
  423. # ifdef USE_LFN
  424.                 if (use_lfn)
  425.                     *pp++ = (char)workch;
  426.                 else
  427.                     *pp++ = '_';
  428.                 break;
  429. # endif
  430. #endif
  431.             case ':':           /* special shell characters of command.com */
  432.             case '\':          /*  (device and directory limiters, wildcard */
  433.             case '"':           /*  characters, stdin/stdout redirection and */
  434.             case '<':           /*  pipe indicators and the quote sign) are */
  435.             case '>':           /*  never allowed in filenames on (V)FAT */
  436.             case '|':
  437.             case '*':
  438.             case '?':
  439.                 *pp++ = '_';
  440.                 break;
  441. #ifdef MAYBE_PLAIN_FAT
  442.             case '.':
  443. # ifdef USE_LFN
  444.                 if (use_lfn) {
  445.                     *pp++ = (char)workch;
  446.                     break;
  447.                 }
  448. # endif
  449.                 if (pp == pathcomp) {     /* nothing appended yet... */
  450.                     if (*cp == '/') {     /* don't bother appending a "./" */
  451.                         ++cp;             /*  component to the path:  skip */
  452.                         break;            /*  to next char after the '/' */
  453.                     } else if (*cp == '.' && cp[1] == '/') {   /* "../" */
  454.                         *pp++ = '.';      /* add first dot, unchanged... */
  455.                         ++cp;             /* skip second dot, since it will */
  456.                     } else {              /*  be "added" at end of if-block */
  457.                         *pp++ = '_';      /* FAT doesn't allow null filename */
  458.                         dotname = TRUE;   /*  bodies, so map .exrc -> _.exrc */
  459.                     }                     /*  (extra '_' now, "dot" below) */
  460.                 } else if (dotname) {     /* found a second dot, but still */
  461.                     dotname = FALSE;      /*  have extra leading underscore: */
  462.                     *pp = '';           /*  remove it by shifting chars */
  463.                     pp = pathcomp + 1;    /*  left one space (e.g., .p1.p2: */
  464.                     while (pp[1]) {       /*  __p1 -> _p1_p2 -> _p1.p2 when */
  465.                         *pp = pp[1];      /*  finished) [opt.:  since first */
  466.                         ++pp;             /*  two chars are same, can start */
  467.                     }                     /*  shifting at second position] */
  468.                 }
  469.                 last_dot = pp;    /* point at last dot so far... */
  470.                 *pp++ = '_';      /* convert dot to underscore for now */
  471.                 break;
  472. #endif /* MAYBE_PLAIN_FAT */
  473.             case ';':             /* start of VMS version? */
  474.                 lastsemi = pp;
  475. #ifdef MAYBE_PLAIN_FAT
  476. # ifdef USE_LFN
  477.                 if (use_lfn)
  478.                     *pp++ = ';';  /* keep for now; remove VMS ";##" later */
  479. # endif
  480. #else
  481.                 *pp++ = ';';      /* keep for now; remove VMS ";##" later */
  482. #endif
  483.                 break;
  484. #ifdef MAYBE_PLAIN_FAT
  485.             case ' ':                      /* change spaces to underscores */
  486. # ifdef USE_LFN
  487.                 if (!use_lfn && uO.sflag)  /*  only if requested and NO lfn! */
  488. # else
  489.                 if (uO.sflag)              /*  only if requested */
  490. # endif
  491.                     *pp++ = '_';
  492.                 else
  493.                     *pp++ = (char)workch;
  494.                 break;
  495. #endif /* MAYBE_PLAIN_FAT */
  496.             default:
  497.                 /* allow ASCII 255 and European characters in filenames: */
  498.                 if (isprint(workch) || workch >= 127)
  499.                     *pp++ = (char)workch;
  500.         } /* end switch */
  501.     } /* end while loop */
  502.     *pp = '';                   /* done with pathcomp:  terminate it */
  503.     /* if not saving them, remove VMS version numbers (appended ";###") */
  504.     if (!uO.V_flag && lastsemi) {
  505. #ifndef MAYBE_PLAIN_FAT
  506.         pp = lastsemi + 1;
  507. #else
  508. # ifdef USE_LFN
  509.         if (use_lfn)
  510.             pp = lastsemi + 1;
  511.         else
  512.             pp = lastsemi;        /* semi-colon was omitted:  expect all #'s */
  513. # else
  514.         pp = lastsemi;            /* semi-colon was omitted:  expect all #'s */
  515. # endif
  516. #endif
  517.         while (isdigit((uch)(*pp)))
  518.             ++pp;
  519.         if (*pp == '')          /* only digits between ';' and end:  nuke */
  520.             *lastsemi = '';
  521.     }
  522.     if (G.pInfo->vollabel) {
  523.         if (strlen(pathcomp) > 11)
  524.             pathcomp[11] = '';
  525.     }
  526. #ifdef MAYBE_PLAIN_FAT
  527. # ifdef USE_LFN
  528.     if (!use_lfn)
  529.         map2fat(pathcomp, last_dot);  /* 8.3 truncation (in place) */
  530. # else
  531.     map2fat(pathcomp, last_dot);  /* 8.3 truncation (in place) */
  532. # endif
  533. #endif
  534. /*---------------------------------------------------------------------------
  535.     Report if directory was created (and no file to create:  filename ended
  536.     in '/'), check name to be sure it exists, and combine path and name be-
  537.     fore exiting.
  538.   ---------------------------------------------------------------------------*/
  539.     if (G.filename[strlen(G.filename) - 1] == '/') {
  540.         checkdir(__G__ G.filename, GETPATH);
  541.         if (created_dir) {
  542.             if (QCOND2) {
  543.                 Info(slide, 0, ((char *)slide, LoadFarString(Creating),
  544.                   FnFilter1(G.filename)));
  545.             }
  546.             /* set file attributes: */
  547.             z_dos_chmod(__G__ G.filename, G.pInfo->file_attr);
  548.             return IZ_CREATED_DIR;   /* set dir time (note trailing '/') */
  549.         } else if (uO.overwrite_all) {
  550.             /* overwrite attributes of existing directory on user's request */
  551.             /* set file attributes: */
  552.             z_dos_chmod(__G__ G.filename, G.pInfo->file_attr);
  553.         }
  554.         return 2;   /* dir existed already; don't look for data to extract */
  555.     }
  556.     if (*pathcomp == '') {
  557.         Info(slide, 1, ((char *)slide, LoadFarString(ConversionFailed),
  558.           FnFilter1(G.filename)));
  559.         return 3;
  560.     }
  561.     checkdir(__G__ pathcomp, APPEND_NAME);  /* returns 1 if truncated: care? */
  562.     checkdir(__G__ G.filename, GETPATH);
  563.     if (G.pInfo->vollabel) {    /* set the volume label now */
  564.         if (QCOND2)
  565.             Info(slide, 0, ((char *)slide, LoadFarString(Labelling),
  566.               (nLabelDrive + 'a' - 1),
  567.               FnFilter1(G.filename)));
  568.         if (volumelabel(G.filename)) {
  569.             Info(slide, 1, ((char *)slide, LoadFarString(ErrSetVolLabel)));
  570.             return 3;
  571.         }
  572.         return 2;   /* success:  skip the "extraction" quietly */
  573.     }
  574.     return error;
  575. } /* end function mapname() */
  576. #ifdef MAYBE_PLAIN_FAT
  577. /**********************/
  578. /* Function map2fat() */
  579. /**********************/
  580. static void map2fat(pathcomp, last_dot)
  581.     char *pathcomp, *last_dot;
  582. {
  583.     char *pEnd = pathcomp + strlen(pathcomp);
  584. /*---------------------------------------------------------------------------
  585.     Case 1:  filename has no dot, so figure out if we should add one.  Note
  586.     that the algorithm does not try to get too fancy:  if there are no dots
  587.     already, the name either gets truncated at 8 characters or the last un-
  588.     derscore is converted to a dot (only if more characters are saved that
  589.     way).  In no case is a dot inserted between existing characters.
  590.               GRR:  have problem if filename is volume label??
  591.   ---------------------------------------------------------------------------*/
  592.     if (last_dot == (char *)NULL) {   /* no dots:  check for underscores... */
  593.         char *plu = strrchr(pathcomp, '_');   /* pointer to last underscore */
  594.         if (plu == (char *)NULL) {  /* no dots, no underscores:  truncate at */
  595.             if (pEnd > pathcomp+8)  /* 8 chars (could insert '.' and keep 11) */
  596.                 *(pEnd = pathcomp+8) = '';
  597.         } else if (MIN(plu - pathcomp, 8) + MIN(pEnd - plu - 1, 3) > 8) {
  598.             last_dot = plu;       /* be lazy:  drop through to next if-block */
  599.         } else if ((pEnd - pathcomp) > 8)    /* more fits into just basename */
  600.             pathcomp[8] = '';    /* than if convert last underscore to dot */
  601.         /* else whole thing fits into 8 chars or less:  no change */
  602.     }
  603. /*---------------------------------------------------------------------------
  604.     Case 2:  filename has dot in it, so truncate first half at 8 chars (shift
  605.     extension if necessary) and second half at three.
  606.   ---------------------------------------------------------------------------*/
  607.     if (last_dot != (char *)NULL) {   /* one dot (or two, in the case of */
  608.         *last_dot = '.';              /*  "..") is OK:  put it back in */
  609.         if ((last_dot - pathcomp) > 8) {
  610.             char *p, *q;
  611.             int i;
  612.             p = last_dot;
  613.             q = last_dot = pathcomp + 8;
  614.             for (i = 0;  (i < 4) && *p;  ++i)  /* too many chars in basename: */
  615.                 *q++ = *p++;                   /*  shift extension left and */
  616.             *q = '';                         /*  truncate/terminate it */
  617.         } else if ((pEnd - last_dot) > 4)
  618.             last_dot[4] = '';                /* too many chars in extension */
  619.         /* else filename is fine as is:  no change */
  620.         if ((last_dot - pathcomp) > 0 && last_dot[-1] == ' ')
  621.             last_dot[-1] = '_';                /* NO blank in front of '.'! */
  622.     }
  623. } /* end function map2fat() */
  624. #endif /* MAYBE_PLAIN_FAT */
  625. /***********************/
  626. /* Function checkdir() */
  627. /***********************/
  628. int checkdir(__G__ pathcomp, flag)
  629.     __GDEF
  630.     char *pathcomp;
  631.     int flag;
  632. /*
  633.  * returns:  1 - (on APPEND_NAME) truncated filename
  634.  *           2 - path doesn't exist, not allowed to create
  635.  *           3 - path doesn't exist, tried to create and failed; or
  636.  *               path exists and is not a directory, but is supposed to be
  637.  *           4 - path is too long
  638.  *          10 - can't allocate memory for filename buffers
  639.  */
  640. {
  641.     static int rootlen = 0;   /* length of rootpath */
  642.     static char *rootpath;    /* user's "extract-to" directory */
  643.     static char *buildpath;   /* full path (so far) to extracted file */
  644.     static char *end;         /* pointer to end of buildpath ('') */
  645. #ifdef MSC
  646.     int attrs;                /* work around MSC stat() bug */
  647. #endif
  648. #   define FN_MASK   7
  649. #   define FUNCTION  (flag & FN_MASK)
  650. /*---------------------------------------------------------------------------
  651.     APPEND_DIR:  append the path component to the path being built and check
  652.     for its existence.  If doesn't exist and we are creating directories, do
  653.     so for this one; else signal success or error as appropriate.
  654.   ---------------------------------------------------------------------------*/
  655.     if (FUNCTION == APPEND_DIR) {
  656.         int too_long = FALSE;
  657.         Trace((stderr, "appending dir segment [%s]n", pathcomp));
  658.         while ((*end = *pathcomp++) != '')
  659.             ++end;
  660.         /* GRR:  could do better check, see if overrunning buffer as we go:
  661.          * check end-buildpath after each append, set warning variable if
  662.          * within 20 of FILNAMSIZ; then if var set, do careful check when
  663.          * appending.  Clear variable when begin new path. */
  664.         if ((end-buildpath) > FILNAMSIZ-3)  /* need '/', one-char name, '' */
  665.             too_long = TRUE;                /* check if extracting directory? */
  666. #ifdef MSC /* MSC 6.00 bug:  stat(non-existent-dir) == 0 [exists!] */
  667.         if (_dos_getfileattr(buildpath, &attrs) || stat(buildpath, &G.statbuf))
  668. #else
  669.         if (SSTAT(buildpath, &G.statbuf))    /* path doesn't exist */
  670. #endif
  671.         {
  672.             if (!G.create_dirs) { /* told not to create (freshening) */
  673.                 free(buildpath);
  674.                 return 2;         /* path doesn't exist:  nothing to do */
  675.             }
  676.             if (too_long) {
  677.                 Info(slide, 1, ((char *)slide, LoadFarString(PathTooLong),
  678.                   FnFilter1(buildpath)));
  679.                 free(buildpath);
  680.                 return 4;         /* no room for filenames:  fatal */
  681.             }
  682.             if (MKDIR(buildpath, 0777) == -1) {   /* create the directory */
  683.                 Info(slide, 1, ((char *)slide, LoadFarString(CantCreateDir),
  684.                   FnFilter2(buildpath), FnFilter1(G.filename)));
  685.                 free(buildpath);
  686.                 return 3;      /* path didn't exist, tried to create, failed */
  687.             }
  688.             created_dir = TRUE;
  689.         } else if (!S_ISDIR(G.statbuf.st_mode)) {
  690.             Info(slide, 1, ((char *)slide, LoadFarString(DirIsntDirectory),
  691.               FnFilter2(buildpath), FnFilter1(G.filename)));
  692.             free(buildpath);
  693.             return 3;          /* path existed but wasn't dir */
  694.         }
  695.         if (too_long) {
  696.             Info(slide, 1, ((char *)slide, LoadFarString(PathTooLong),
  697.               FnFilter1(buildpath)));
  698.             free(buildpath);
  699.             return 4;         /* no room for filenames:  fatal */
  700.         }
  701.         *end++ = '/';
  702.         *end = '';
  703.         Trace((stderr, "buildpath now = [%s]n", FnFilter1(buildpath)));
  704.         return 0;
  705.     } /* end if (FUNCTION == APPEND_DIR) */
  706. /*---------------------------------------------------------------------------
  707.     GETPATH:  copy full path to the string pointed at by pathcomp, and free
  708.     buildpath.
  709.   ---------------------------------------------------------------------------*/
  710.     if (FUNCTION == GETPATH) {
  711.         strcpy(pathcomp, buildpath);
  712.         Trace((stderr, "getting and freeing path [%s]n",
  713.           FnFilter1(pathcomp)));
  714.         free(buildpath);
  715.         buildpath = end = (char *)NULL;
  716.         return 0;
  717.     }
  718. /*---------------------------------------------------------------------------
  719.     APPEND_NAME:  assume the path component is the filename; append it and
  720.     return without checking for existence.
  721.   ---------------------------------------------------------------------------*/
  722.     if (FUNCTION == APPEND_NAME) {
  723. #ifdef NOVELL_BUG_WORKAROUND
  724.         if (end == buildpath && !G.pInfo->vollabel) {
  725.             /* work-around for Novell's "overwriting executables" bug:
  726.                prepend "./" to name when no path component is specified */
  727.             *end++ = '.';
  728.             *end++ = '/';
  729.         }
  730. #endif /* NOVELL_BUG_WORKAROUND */
  731.         Trace((stderr, "appending filename [%s]n", FnFilter1(pathcomp)));
  732.         while ((*end = *pathcomp++) != '') {
  733.             ++end;
  734.             if ((end-buildpath) >= FILNAMSIZ) {
  735.                 *--end = '';
  736.                 Info(slide, 1, ((char *)slide, LoadFarString(PathTooLongTrunc),
  737.                   FnFilter1(G.filename), FnFilter2(buildpath)));
  738.                 return 1;   /* filename truncated */
  739.             }
  740.         }
  741.         Trace((stderr, "buildpath now = [%s]n", FnFilter1(buildpath)));
  742.         return 0;  /* could check for existence here, prompt for new name... */
  743.     }
  744. /*---------------------------------------------------------------------------
  745.     INIT:  allocate and initialize buffer space for the file currently being
  746.     extracted.  If file was renamed with an absolute path, don't prepend the
  747.     extract-to path.
  748.   ---------------------------------------------------------------------------*/
  749.     if (FUNCTION == INIT) {
  750.         Trace((stderr, "initializing buildpath to "));
  751.         /* allocate space for full filename, root path, and maybe "./" */
  752.         if ((buildpath = (char *)malloc(strlen(G.filename)+rootlen+3)) ==
  753.             (char *)NULL)
  754.             return 10;
  755.         if (G.pInfo->vollabel) {
  756. /* GRR:  for network drives, do strchr() and return IZ_VOL_LABEL if not [1] */
  757.             if (renamed_fullpath && pathcomp[1] == ':')
  758.                 *buildpath = (char)ToLower(*pathcomp);
  759.             else if (!renamed_fullpath && rootlen > 1 && rootpath[1] == ':')
  760.                 *buildpath = (char)ToLower(*rootpath);
  761.             else {
  762.                 GETDRIVE(nLabelDrive);   /* assumed that a == 1, b == 2, etc. */
  763.                 *buildpath = (char)(nLabelDrive - 1 + 'a');
  764.             }
  765.             nLabelDrive = *buildpath - 'a' + 1;        /* save for mapname() */
  766.             if (uO.volflag == 0 || *buildpath < 'a' || /* no label/bogus disk */
  767.                (uO.volflag == 1 && !isfloppy(nLabelDrive))) /* -$:  no fixed */
  768.             {
  769.                 free(buildpath);
  770.                 return IZ_VOL_LABEL;     /* skipping with message */
  771.             }
  772.             *buildpath = '';
  773.             end = buildpath;
  774.         } else if (renamed_fullpath) {   /* pathcomp = valid data */
  775.             end = buildpath;
  776.             while ((*end = *pathcomp++) != '')
  777.                 ++end;
  778.         } else if (rootlen > 0) {
  779.             strcpy(buildpath, rootpath);
  780.             end = buildpath + rootlen;
  781.         } else {
  782.             *buildpath = '';
  783.             end = buildpath;
  784.         }
  785.         Trace((stderr, "[%s]n", FnFilter1(buildpath)));
  786.         return 0;
  787.     }
  788. /*---------------------------------------------------------------------------
  789.     ROOT:  if appropriate, store the path in rootpath and create it if neces-
  790.     sary; else assume it's a zipfile member and return.  This path segment
  791.     gets used in extracting all members from every zipfile specified on the
  792.     command line.  Note that under OS/2 and MS-DOS, if a candidate extract-to
  793.     directory specification includes a drive letter (leading "x:"), it is
  794.     treated just as if it had a trailing '/'--that is, one directory level
  795.     will be created if the path doesn't exist, unless this is otherwise pro-
  796.     hibited (e.g., freshening).
  797.   ---------------------------------------------------------------------------*/
  798. #if (!defined(SFX) || defined(SFX_EXDIR))
  799.     if (FUNCTION == ROOT) {
  800.         Trace((stderr, "initializing root path to [%s]n",
  801.           FnFilter1(pathcomp)));
  802.         if (pathcomp == (char *)NULL) {
  803.             rootlen = 0;
  804.             return 0;
  805.         }
  806.         if ((rootlen = strlen(pathcomp)) > 0) {
  807.             int had_trailing_pathsep=FALSE, has_drive=FALSE, xtra=2;
  808.             if (isalpha(pathcomp[0]) && pathcomp[1] == ':')
  809.                 has_drive = TRUE;   /* drive designator */
  810.             if (pathcomp[rootlen-1] == '/' || pathcomp[rootlen-1] == '\') {
  811.                 pathcomp[--rootlen] = '';
  812.                 had_trailing_pathsep = TRUE;
  813.             }
  814.             if (has_drive && (rootlen == 2)) {
  815.                 if (!had_trailing_pathsep)   /* i.e., original wasn't "x:/" */
  816.                     xtra = 3;      /* room for '.' + '/' + 0 at end of "x:" */
  817.             } else if (rootlen > 0) {     /* need not check "x:." and "x:/" */
  818. #ifdef MSC
  819.                 /* MSC 6.00 bug:  stat(non-existent-dir) == 0 [exists!] */
  820.                 if (_dos_getfileattr(pathcomp, &attrs) ||
  821.                     SSTAT(pathcomp,&G.statbuf) || !S_ISDIR(G.statbuf.st_mode))
  822. #else
  823.                 if (SSTAT(pathcomp,&G.statbuf) || !S_ISDIR(G.statbuf.st_mode))
  824. #endif
  825.                 {
  826.                     /* path does not exist */
  827.                     if (!G.create_dirs /* || iswild(pathcomp) */ ) {
  828.                         rootlen = 0;
  829.                         return 2;   /* treat as stored file */
  830.                     }
  831. /* GRR:  scan for wildcard characters?  OS-dependent...  if find any, return 2:
  832.  * treat as stored file(s) */
  833.                     /* create directory (could add loop here to scan pathcomp
  834.                      * and create more than one level, but really necessary?) */
  835.                     if (MKDIR(pathcomp, 0777) == -1) {
  836.                         Info(slide, 1, ((char *)slide,
  837.                           LoadFarString(CantCreateExtractDir),
  838.                           FnFilter1(pathcomp)));
  839.                         rootlen = 0;   /* path didn't exist, tried to create, */
  840.                         return 3;  /* failed:  file exists, or need 2+ levels */
  841.                     }
  842.                 }
  843.             }
  844.             if ((rootpath = (char *)malloc(rootlen+xtra)) == (char *)NULL) {
  845.                 rootlen = 0;
  846.                 return 10;
  847.             }
  848.             strcpy(rootpath, pathcomp);
  849.             if (xtra == 3)                  /* had just "x:", make "x:." */
  850.                 rootpath[rootlen++] = '.';
  851.             rootpath[rootlen++] = '/';
  852.             rootpath[rootlen] = '';
  853.             Trace((stderr, "rootpath now = [%s]n", FnFilter1(rootpath)));
  854.         }
  855.         return 0;
  856.     }
  857. #endif /* !SFX || SFX_EXDIR */
  858. /*---------------------------------------------------------------------------
  859.     END:  free rootpath, immediately prior to program exit.
  860.   ---------------------------------------------------------------------------*/
  861.     if (FUNCTION == END) {
  862.         Trace((stderr, "freeing rootpathn"));
  863.         if (rootlen > 0) {
  864.             free(rootpath);
  865.             rootlen = 0;
  866.         }
  867.         return 0;
  868.     }
  869.     return 99;  /* should never reach */
  870. } /* end function checkdir() */
  871. /***********************/
  872. /* Function isfloppy() */
  873. /***********************/
  874. static int isfloppy(nDrive)  /* more precisely, is it removable? */
  875.     int nDrive;
  876. {
  877.     union REGS regs;
  878.     regs.h.ah = 0x44;
  879.     regs.h.al = 0x08;
  880.     regs.h.bl = (uch)nDrive;
  881. #ifdef __EMX__
  882.     _int86(0x21, &regs, &regs);
  883.     if (WREGS(regs,flags) & 1)
  884. #else
  885.     intdos(&regs, &regs);
  886.     if (WREGS(regs,cflag))        /* error:  do default a/b check instead */
  887. #endif
  888.     {
  889.         Trace((stderr,
  890.           "error in DOS function 0x44 (AX = 0x%04x):  guessing instead...n",
  891.           WREGS(regs,ax)));
  892.         return (nDrive == 1 || nDrive == 2)? TRUE : FALSE;
  893.     } else
  894.         return WREGS(regs,ax)? FALSE : TRUE;
  895. }
  896. /**************************/
  897. /* Function z_dos_chmod() */
  898. /**************************/
  899. static int z_dos_chmod(__G__ fname, attributes)
  900.     __GDEF
  901.     ZCONST char *fname;
  902.     int attributes;
  903. {
  904.     char *name;
  905.     unsigned fnamelength;
  906.     int errv;
  907.     /* set file attributes:
  908.        The DOS `chmod' system call requires to mask out the
  909.        directory and volume_label attribute bits.
  910.        And, a trailing '/' has to be removed from the directory name,
  911.        the DOS `chmod' system call does not accept it. */
  912.     fnamelength = strlen(fname);
  913.     if (fnamelength > 1 && fname[fnamelength-1] == '/' &&
  914.         fname[fnamelength-2] != ':' &&
  915.         (name = (char *)malloc(fnamelength)) != (char *)NULL) {
  916.         strncpy(name, fname, fnamelength-1);
  917.         name[fnamelength-1] = '';
  918.     } else {
  919.         name = (char *)fname;
  920.         fnamelength = 0;
  921.     }
  922. #if defined(__TURBOC__) || (defined(__DJGPP__) && (__DJGPP__ >= 2))
  923. #   if (defined(__BORLANDC__) && (__BORLANDC__ >= 0x0452))
  924. #     define Chmod  _rtl_chmod
  925. #   else
  926. #     define Chmod  _chmod
  927. #   endif
  928.     errv = (Chmod(name, 1, attributes & (~FSUBDIR & ~FVOLID)) !=
  929.             (attributes & (~FSUBDIR & ~FVOLID)));
  930. #   undef Chmod
  931. #else /* !(__TURBOC__ || (__DJGPP__ && __DJGPP__ >= 2)) */
  932.     errv = (_dos_setfileattr(name, attributes & (~FSUBDIR & ~FVOLID)) != 0);
  933. #endif /* ?(__TURBOC__ || (__DJGPP__ && __DJGPP__ >= 2)) */
  934.     if (errv)
  935.         Info(slide, 1, ((char *)slide, LoadFarString(AttribsMayBeWrong)));
  936.     if (fnamelength > 0)
  937.         free(name);
  938.     return errv;
  939. } /* end function z_dos_chmod() */
  940. #if (!defined(__GO32__) && !defined(__EMX__))
  941. typedef struct dosfcb {
  942.     uch  flag;        /* ff to indicate extended FCB */
  943.     char res[5];      /* reserved */
  944.     uch  vattr;       /* attribute */
  945.     uch  drive;       /* drive (1=A, 2=B, ...) */
  946.     uch  vn[11];      /* file or volume name */
  947.     char dmmy[5];
  948.     uch  nn[11];      /* holds new name if renaming (else reserved) */
  949.     char dmmy2[9];
  950. } dos_fcb;
  951. /**************************/
  952. /* Function volumelabel() */
  953. /**************************/
  954. static int volumelabel(newlabel)
  955.     char *newlabel;
  956. {
  957. #ifdef DEBUG
  958.     char *p;
  959. #endif
  960.     int len = strlen(newlabel);
  961.     int fcbseg, dtaseg, fcboff, dtaoff, retv;
  962.     dos_fcb  fcb, dta, far *pfcb=&fcb, far *pdta=&dta;
  963.     struct SREGS sregs;
  964.     union REGS regs;
  965. /*---------------------------------------------------------------------------
  966.     Label the diskette specified by nLabelDrive using FCB calls.  (Old ver-
  967.     sions of MS-DOS and OS/2 DOS boxes can't use DOS function 3Ch to create
  968.     labels.)  Must use far pointers for MSC FP_* macros to work; must pad
  969.     FCB filenames with spaces; and cannot include dot in 8th position.  May
  970.     or may not need to zero out FCBs before using; do so just in case.
  971.   ---------------------------------------------------------------------------*/
  972. #ifdef WATCOMC_386
  973.     int truseg;
  974.     memset(&sregs, 0, sizeof(sregs));
  975.     memset(&regs, 0, sizeof(regs));
  976.     /* PMODE/W does not support extended versions of any dos FCB functions, */
  977.     /* so we have to use brute force, allocating real mode memory for them. */
  978.     regs.w.ax = 0x0100;
  979.     regs.w.bx = (2 * sizeof(dos_fcb) + 15) >> 4;   /* size in paragraphs */
  980.     int386(0x31, &regs, &regs);            /* DPMI allocate DOS memory */
  981.     if (regs.w.cflag)
  982.         return DF_MDY;                     /* no memory, return default */
  983.     truseg = regs.w.dx;                    /* protected mode selector */
  984.     dtaseg = regs.w.ax;                    /* real mode paragraph */
  985.     fcboff = 0;
  986.     dtaoff = sizeof(dos_fcb);
  987. #ifdef XXX__MK_FP_IS_BROKEN
  988.     /* XXX  This code may not be trustworthy in general, though it is   */
  989.     /* valid with DOS/4GW and PMODE/w, which is all we support for now. */
  990.     regs.w.ax = 6;
  991.     regs.w.bx = truseg;
  992.     int386(0x31, &regs, &regs);            /* convert seg to linear address */
  993.     pfcb = (dos_fcb far *) (((ulg) regs.w.cx << 16) | regs.w.dx);
  994.     /* pfcb = (dos_fcb far *) ((ulg) dtaseg << 4); */
  995.     pdta = pfcb + 1;
  996. #else
  997.     pfcb = MK_FP(truseg, fcboff);
  998.     pdta = MK_FP(truseg, dtaoff);
  999. #endif
  1000.     _fmemset((char far *)pfcb, 0, 2 * sizeof(dos_fcb));
  1001.     /* we pass the REAL MODE paragraph to the dos interrupts: */
  1002.     fcbseg = dtaseg;
  1003. #else /* !WATCOMC_386 */
  1004.     memset((char *)&dta, 0, sizeof(dos_fcb));
  1005.     memset((char *)&fcb, 0, sizeof(dos_fcb));
  1006.     fcbseg = FP_SEG(pfcb);
  1007.     fcboff = FP_OFF(pfcb);
  1008.     dtaseg = FP_SEG(pdta);
  1009.     dtaoff = FP_OFF(pdta);
  1010. #endif /* ?WATCOMC_386 */
  1011. #ifdef DEBUG
  1012.     for (p = (char *)&dta; (p - (char *)&dta) < sizeof(dos_fcb); ++p)
  1013.         if (*p)
  1014.             fprintf(stderr, "error:  dta[%d] = %xn", (p - (char *)&dta), *p);
  1015.     for (p = (char *)&fcb; (p - (char *)&fcb) < sizeof(dos_fcb); ++p)
  1016.         if (*p)
  1017.             fprintf(stderr, "error:  fcb[%d] = %xn", (p - (char *)&fcb), *p);
  1018.     printf("testing pointer macros:n");
  1019.     segread(&sregs);
  1020.     printf("cs = %x, ds = %x, es = %x, ss = %xn", sregs.cs, sregs.ds, sregs.es,
  1021.       sregs.ss);
  1022. #endif /* DEBUG */
  1023. #if 0
  1024. #ifdef __TURBOC__
  1025.     bdosptr(0x1a, dta, DO_NOT_CARE);
  1026. #else
  1027.     (intdosx method below)
  1028. #endif
  1029. #endif /* 0 */
  1030.     /* set the disk transfer address for subsequent FCB calls */
  1031.     sregs.ds = dtaseg;
  1032.     WREGS(regs,dx) = dtaoff;
  1033.     Trace((stderr, "segment:offset of pdta = %x:%xn", dtaseg, dtaoff));
  1034.     Trace((stderr, "&dta = %lx, pdta = %lxn", (ulg)&dta, (ulg)pdta));
  1035.     regs.h.ah = 0x1a;
  1036.     F_intdosx(&regs, &regs, &sregs);
  1037.     /* fill in the FCB */
  1038.     sregs.ds = fcbseg;
  1039.     WREGS(regs,dx) = fcboff;
  1040.     pfcb->flag = 0xff;          /* extended FCB */
  1041.     pfcb->vattr = 0x08;         /* attribute:  disk volume label */
  1042.     pfcb->drive = (uch)nLabelDrive;
  1043. #ifdef DEBUG
  1044.     Trace((stderr, "segment:offset of pfcb = %x:%xn", sregs.ds, WREGS(regs,dx)));
  1045.     Trace((stderr, "&fcb = %lx, pfcb = %lxn", (ulg)&fcb, (ulg)pfcb));
  1046.     Trace((stderr, "(2nd check:  labelling drive %c:)n", pfcb->drive-1+'A'));
  1047.     if (pfcb->flag != fcb.flag)
  1048.         fprintf(stderr, "error:  pfcb->flag = %d, fcb.flag = %dn",
  1049.           pfcb->flag, fcb.flag);
  1050.     if (pfcb->drive != fcb.drive)
  1051.         fprintf(stderr, "error:  pfcb->drive = %d, fcb.drive = %dn",
  1052.           pfcb->drive, fcb.drive);
  1053.     if (pfcb->vattr != fcb.vattr)
  1054.         fprintf(stderr, "error:  pfcb->vattr = %d, fcb.vattr = %dn",
  1055.           pfcb->vattr, fcb.vattr);
  1056. #endif /* DEBUG */
  1057.     /* check for existing label */
  1058.     Trace((stderr, "searching for existing label via FCBsn"));
  1059.     regs.h.ah = 0x11;      /* FCB find first */
  1060. #ifdef WATCOMC_386
  1061.     _fstrncpy((char far *)&pfcb->vn, "???????????", 11);
  1062. #else
  1063.     strncpy((char *)fcb.vn, "???????????", 11);   /* i.e., "*.*" */
  1064. #endif /* ?WATCOMC_386 */
  1065.     Trace((stderr, "fcb.vn = %lxn", (ulg)fcb.vn));
  1066.     Trace((stderr, "regs.h.ah = %x, regs.x.dx = %04x, sregs.ds = %04xn",
  1067.       regs.h.ah, WREGS(regs,dx), sregs.ds));
  1068.     Trace((stderr, "flag = %x, drive = %d, vattr = %x, vn = %s = %s.n",
  1069.       fcb.flag, fcb.drive, fcb.vattr, fcb.vn, pfcb->vn));
  1070.     F_intdosx(&regs, &regs, &sregs);
  1071. /*---------------------------------------------------------------------------
  1072.     If not previously labelled, write a new label.  Otherwise just rename,
  1073.     since MS-DOS 2.x has a bug that damages the FAT when the old label is
  1074.     deleted.
  1075.   ---------------------------------------------------------------------------*/
  1076.     if (regs.h.al) {
  1077.         Trace((stderr, "no label foundnn"));
  1078.         regs.h.ah = 0x16;                 /* FCB create file */
  1079. #ifdef WATCOMC_386
  1080.         _fstrncpy((char far *)pfcb->vn, newlabel, len);
  1081.         if (len < 11)
  1082.             _fstrncpy((char far *)(pfcb->vn+len), "           ", 11-len);
  1083. #else
  1084.         strncpy((char *)fcb.vn, newlabel, len);
  1085.         if (len < 11)   /* fill with spaces */
  1086.             strncpy((char *)(fcb.vn+len), "           ", 11-len);
  1087. #endif
  1088.         Trace((stderr, "fcb.vn = %lx  pfcb->vn = %lxn", (ulg)fcb.vn,
  1089.           (ulg)pfcb->vn));
  1090.         Trace((stderr, "flag = %x, drive = %d, vattr = %xn", fcb.flag,
  1091.           fcb.drive, fcb.vattr));
  1092.         Trace((stderr, "vn = %s = %s.n", fcb.vn, pfcb->vn));
  1093.         F_intdosx(&regs, &regs, &sregs);
  1094.         regs.h.ah = 0x10;                 /* FCB close file */
  1095.         if (regs.h.al) {
  1096.             Trace((stderr, "unable to write volume name (AL = %x)n",
  1097.               regs.h.al));
  1098.             F_intdosx(&regs, &regs, &sregs);
  1099.             retv = 1;
  1100.         } else {
  1101.             F_intdosx(&regs, &regs, &sregs);
  1102.             Trace((stderr, "new volume label [%s] writtenn", newlabel));
  1103.             retv = 0;
  1104.         }
  1105.     } else {
  1106.         Trace((stderr, "found old label [%s]nn", dta.vn));  /* not term. */
  1107.         regs.h.ah = 0x17;                 /* FCB rename */
  1108. #ifdef WATCOMC_386
  1109.         _fstrncpy((char far *)pfcb->vn, (char far *)pdta->vn, 11);
  1110.         _fstrncpy((char far *)pfcb->nn, newlabel, len);
  1111.         if (len < 11)
  1112.             _fstrncpy((char far *)(pfcb->nn+len), "           ", 11-len);
  1113. #else
  1114.         strncpy((char *)fcb.vn, (char *)dta.vn, 11);
  1115.         strncpy((char *)fcb.nn, newlabel, len);
  1116.         if (len < 11)                     /* fill with spaces */
  1117.             strncpy((char *)(fcb.nn+len), "           ", 11-len);
  1118. #endif
  1119.         Trace((stderr, "fcb.vn = %lx  pfcb->vn = %lxn", (ulg)fcb.vn,
  1120.           (ulg)pfcb->vn));
  1121.         Trace((stderr, "fcb.nn = %lx  pfcb->nn = %lxn", (ulg)fcb.nn,
  1122.           (ulg)pfcb->nn));
  1123.         Trace((stderr, "flag = %x, drive = %d, vattr = %xn", fcb.flag,
  1124.           fcb.drive, fcb.vattr));
  1125.         Trace((stderr, "vn = %s = %s.n", fcb.vn, pfcb->vn));
  1126.         Trace((stderr, "nn = %s = %s.n", fcb.nn, pfcb->nn));
  1127.         F_intdosx(&regs, &regs, &sregs);
  1128.         if (regs.h.al) {
  1129.             Trace((stderr, "Unable to change volume name (AL = %x)n",
  1130.               regs.h.al));
  1131.             retv = 1;
  1132.         } else {
  1133.             Trace((stderr, "volume label changed to [%s]n", newlabel));
  1134.             retv = 0;
  1135.         }
  1136.     }
  1137. #ifdef WATCOMC_386
  1138.     regs.w.ax = 0x0101;                    /* free dos memory */
  1139.     regs.w.dx = truseg;
  1140.     int386(0x31, &regs, &regs);
  1141. #endif
  1142.     return retv;
  1143. } /* end function volumelabel() */
  1144. #endif /* !__GO32__ && !__EMX__ */
  1145. #if defined(USE_EF_UT_TIME) || defined(TIMESTAMP)
  1146. /* The following DOS date/time structure is machine-dependent as it
  1147.  * assumes "little-endian" byte order.  For MSDOS-specific code, which
  1148.  * is run on x86 CPUs (or emulators), this assumption is valid; but
  1149.  * care should be taken when using this code as template for other ports.
  1150.  */
  1151. typedef union {
  1152.     ulg z_dostime;
  1153. # ifdef __TURBOC__
  1154.     struct ftime ft;            /* system file time record */
  1155. # endif
  1156.     struct {                    /* date and time words */
  1157.         ush ztime;              /* DOS file modification time word */
  1158.         ush zdate;              /* DOS file modification date word */
  1159.     } zft;
  1160.     struct {                    /* DOS date/time components bitfield */
  1161.         unsigned zt_se : 5;
  1162.         unsigned zt_mi : 6;
  1163.         unsigned zt_hr : 5;
  1164.         unsigned zd_dy : 5;
  1165.         unsigned zd_mo : 4;
  1166.         unsigned zd_yr : 7;
  1167.     } z_dtf;
  1168. } dos_fdatetime;
  1169. #endif /* USE_EF_UT_TIME || TIMESTAMP */
  1170. /****************************/
  1171. /* Function close_outfile() */
  1172. /****************************/
  1173. void close_outfile(__G)
  1174.     __GDEF
  1175.  /*
  1176.   * MS-DOS VERSION
  1177.   *
  1178.   * Set the output file date/time stamp according to information from the
  1179.   * zipfile directory record for this member, then close the file and set
  1180.   * its permissions (archive, hidden, read-only, system).  Aside from closing
  1181.   * the file, this routine is optional (but most compilers support it).
  1182.   */
  1183. {
  1184. #ifdef USE_EF_UT_TIME
  1185.     dos_fdatetime dos_dt;
  1186.     iztimes z_utime;
  1187.     struct tm *t;
  1188. #endif /* USE_EF_UT_TIME */
  1189. /*---------------------------------------------------------------------------
  1190.     Copy and/or convert time and date variables, if necessary; then set the
  1191.     file time/date.  WEIRD BORLAND "BUG":  if output is buffered, and if run
  1192.     under at least some versions of DOS (e.g., 6.0), and if files are smaller
  1193.     than DOS physical block size (i.e., 512 bytes) (?), then files MAY NOT
  1194.     get timestamped correctly--apparently setftime() occurs before any data
  1195.     are written to the file, and when file is closed and buffers are flushed,
  1196.     timestamp is overwritten with current time.  Even with a 32K buffer, this
  1197.     does not seem to occur with larger files.  UnZip output is now unbuffered,
  1198.     but if it were not, could still avoid problem by adding "fflush(outfile)"
  1199.     just before setftime() call.  Weird, huh?
  1200.   ---------------------------------------------------------------------------*/
  1201. #ifdef USE_EF_UT_TIME
  1202.     if (G.extra_field &&
  1203. #ifdef IZ_CHECK_TZ
  1204.         G.tz_is_valid &&
  1205. #endif
  1206.         (ef_scan_for_izux(G.extra_field, G.lrec.extra_field_length, 0,
  1207.                           G.lrec.last_mod_dos_datetime, &z_utime, NULL)
  1208.          & EB_UT_FL_MTIME))
  1209.     {
  1210.         TTrace((stderr, "close_outfile:  Unix e.f. modif. time = %ldn",
  1211.           z_utime.mtime));
  1212.         /* round up (down if "up" overflows) to even seconds */
  1213.         if (z_utime.mtime & 1)
  1214.             z_utime.mtime = (z_utime.mtime + 1 > z_utime.mtime) ?
  1215.                              z_utime.mtime + 1 : z_utime.mtime - 1;
  1216.         TIMET_TO_NATIVE(z_utime.mtime)   /* NOP unless MSC 7.0 or Macintosh */
  1217.         t = localtime(&(z_utime.mtime));
  1218.     } else
  1219.         t = (struct tm *)NULL;
  1220.     if (t != (struct tm *)NULL) {
  1221.         if (t->tm_year < 80) {
  1222.             dos_dt.z_dtf.zt_se = 0;
  1223.             dos_dt.z_dtf.zt_mi = 0;
  1224.             dos_dt.z_dtf.zt_hr = 0;
  1225.             dos_dt.z_dtf.zd_dy = 1;
  1226.             dos_dt.z_dtf.zd_mo = 1;
  1227.             dos_dt.z_dtf.zd_yr = 0;
  1228.         } else {
  1229.             dos_dt.z_dtf.zt_se = t->tm_sec >> 1;
  1230.             dos_dt.z_dtf.zt_mi = t->tm_min;
  1231.             dos_dt.z_dtf.zt_hr = t->tm_hour;
  1232.             dos_dt.z_dtf.zd_dy = t->tm_mday;
  1233.             dos_dt.z_dtf.zd_mo = t->tm_mon + 1;
  1234.             dos_dt.z_dtf.zd_yr = t->tm_year - 80;
  1235.         }
  1236.     } else {
  1237.         dos_dt.z_dostime = G.lrec.last_mod_dos_datetime;
  1238.     }
  1239. # ifdef __TURBOC__
  1240.     setftime(fileno(G.outfile), &dos_dt.ft);
  1241. # else
  1242.     _dos_setftime(fileno(G.outfile), dos_dt.zft.zdate, dos_dt.zft.ztime);
  1243. # endif
  1244. #else /* !USE_EF_UT_TIME */
  1245. # ifdef __TURBOC__
  1246.     setftime(fileno(G.outfile),
  1247.              (struct ftime *)(&(G.lrec.last_mod_dos_datetime)));
  1248. # else
  1249.     _dos_setftime(fileno(G.outfile), (ush)(G.lrec.last_mod_dos_datetime >> 16),
  1250.                                      (ush)(G.lrec.last_mod_dos_datetime));
  1251. # endif
  1252. #endif /* ?USE_EF_UT_TIME */
  1253. /*---------------------------------------------------------------------------
  1254.     And finally we can close the file...at least everybody agrees on how to
  1255.     do *this*.  I think...  Also change the mode according to the stored file
  1256.     attributes, since we didn't do that when we opened the dude.
  1257.   ---------------------------------------------------------------------------*/
  1258.     fclose(G.outfile);
  1259.     z_dos_chmod(__G__ G.filename, G.pInfo->file_attr);
  1260. } /* end function close_outfile() */
  1261. #ifdef TIMESTAMP
  1262. /*************************/
  1263. /* Function stamp_file() */
  1264. /*************************/
  1265. int stamp_file(fname, modtime)
  1266.     ZCONST char *fname;
  1267.     time_t modtime;
  1268. {
  1269.     dos_fdatetime dos_dt;
  1270.     time_t t_even;
  1271.     struct tm *t;
  1272.     int fd;                             /* file handle */
  1273.     /* round up (down if "up" overflows) to even seconds */
  1274.     t_even = ((modtime + 1 > modtime) ? modtime + 1 : modtime) & (~1);
  1275.     TIMET_TO_NATIVE(t_even)             /* NOP unless MSC 7.0 or Macintosh */
  1276.     t = localtime(&t_even);
  1277.     if (t == (struct tm *)NULL)
  1278.         return -1;                      /* time conversion error */
  1279.     if (t->tm_year < 80) {
  1280.         dos_dt.z_dtf.zt_se = 0;
  1281.         dos_dt.z_dtf.zt_mi = 0;
  1282.         dos_dt.z_dtf.zt_hr = 0;
  1283.         dos_dt.z_dtf.zd_dy = 1;
  1284.         dos_dt.z_dtf.zd_mo = 1;
  1285.         dos_dt.z_dtf.zd_yr = 0;
  1286.     } else {
  1287.         dos_dt.z_dtf.zt_se = t->tm_sec >> 1;
  1288.         dos_dt.z_dtf.zt_mi = t->tm_min;
  1289.         dos_dt.z_dtf.zt_hr = t->tm_hour;
  1290.         dos_dt.z_dtf.zd_dy = t->tm_mday;
  1291.         dos_dt.z_dtf.zd_mo = t->tm_mon + 1;
  1292.         dos_dt.z_dtf.zd_yr = t->tm_year - 80;
  1293.     }
  1294.     if (((fd = open((char *)fname, 0)) == -1) ||
  1295. # ifdef __TURBOC__
  1296.         (setftime(fd, &dos_dt.ft)))
  1297. # else
  1298.         (_dos_setftime(fd, dos_dt.zft.zdate, dos_dt.zft.ztime)))
  1299. # endif
  1300.     {
  1301.         if (fd != -1)
  1302.             close(fd);
  1303.         return -1;
  1304.     }
  1305.     close(fd);
  1306.     return 0;
  1307. } /* end function stamp_file() */
  1308. #endif /* TIMESTAMP */
  1309. #ifndef SFX
  1310. /*************************/
  1311. /* Function dateformat() */
  1312. /*************************/
  1313. int dateformat()
  1314. {
  1315. /*---------------------------------------------------------------------------
  1316.     For those operating systems that support it, this function returns a
  1317.     value that tells how national convention says that numeric dates are
  1318.     displayed.  Return values are DF_YMD, DF_DMY and DF_MDY (the meanings
  1319.     should be fairly obvious).
  1320.   ---------------------------------------------------------------------------*/
  1321. #ifndef WINDLL
  1322.     ush CountryInfo[18];
  1323. #if (!defined(__GO32__) && !defined(__EMX__))
  1324.     ush far *_CountryInfo = CountryInfo;
  1325.     struct SREGS sregs;
  1326.     union REGS regs;
  1327. #ifdef WATCOMC_386
  1328.     ush seg, para;
  1329.     memset(&sregs, 0, sizeof(sregs));
  1330.     memset(&regs, 0, sizeof(regs));
  1331.     /* PMODE/W does not support an extended version of dos function 38,   */
  1332.     /* so we have to use brute force, allocating real mode memory for it. */
  1333.     regs.w.ax = 0x0100;
  1334.     regs.w.bx = 3;                         /* 36 bytes rounds up to 48 */
  1335.     int386(0x31, &regs, &regs);            /* DPMI allocate DOS memory */
  1336.     if (regs.w.cflag)
  1337.         return DF_MDY;                     /* no memory, return default */
  1338.     seg = regs.w.dx;
  1339.     para = regs.w.ax;
  1340. #ifdef XXX__MK_FP_IS_BROKEN
  1341.     /* XXX  This code may not be trustworthy in general, though it is
  1342.      * valid with DOS/4GW and PMODE/w, which is all we support for now. */
  1343.  /* _CountryInfo = (ush far *) (para << 4); */ /* works for some extenders */
  1344.     regs.w.ax = 6;
  1345.     regs.w.bx = seg;
  1346.     int386(0x31, &regs, &regs);            /* convert seg to linear address */
  1347.     _CountryInfo = (ush far *) (((ulg) regs.w.cx << 16) | regs.w.dx);
  1348. #else
  1349.     _CountryInfo = (ush far *) MK_FP(seg, 0);
  1350. #endif
  1351.     sregs.ds = para;                       /* real mode paragraph */
  1352.     regs.w.dx = 0;                         /* no offset from segment */
  1353.     regs.w.ax = 0x3800;
  1354.     int86x_realmode(0x21, &regs, &regs, &sregs);
  1355.     CountryInfo[0] = regs.w.cflag ? 0 : _CountryInfo[0];
  1356.     regs.w.ax = 0x0101;
  1357.     regs.w.dx = seg;
  1358.     int386(0x31, &regs, &regs);              /* DPMI free DOS memory */
  1359. #else /* !WATCOMC_386 */
  1360.     sregs.ds  = FP_SEG(_CountryInfo);
  1361.     regs.x.dx = FP_OFF(_CountryInfo);
  1362.     regs.x.ax = 0x3800;
  1363.     intdosx(&regs, &regs, &sregs);
  1364. #endif /* ?WATCOMC_386 */
  1365. #else /* __GO32__ || __EMX__ */
  1366.     _dos_getcountryinfo(CountryInfo);
  1367. #endif /* ?(__GO32__ || __EMX__) */
  1368.     switch(CountryInfo[0]) {
  1369.         case 0:
  1370.             return DF_MDY;
  1371.         case 1:
  1372.             return DF_DMY;
  1373.         case 2:
  1374.             return DF_YMD;
  1375.     }
  1376. #endif /* !WINDLL && !WATCOMC_386 */
  1377.     return DF_MDY;   /* default for systems without locale info */
  1378. } /* end function dateformat() */
  1379. #ifndef WINDLL
  1380. /************************/
  1381. /*  Function version()  */
  1382. /************************/
  1383. void version(__G)
  1384.     __GDEF
  1385. {
  1386.     int len;
  1387. #if defined(__DJGPP__) || defined(__WATCOMC__) || 
  1388.     (defined(_MSC_VER) && (_MSC_VER != 800))
  1389.     char buf[80];
  1390. #endif
  1391.     len = sprintf((char *)slide, LoadFarString(CompiledWith),
  1392. #if defined(__GNUC__)
  1393. #  if defined(__DJGPP__)
  1394.       (sprintf(buf, "djgpp v%d.%02d / gcc ", __DJGPP__, __DJGPP_MINOR__), buf),
  1395. #  elif defined(__GO32__)         /* __GO32__ is defined as "1" only (sigh) */
  1396.       "djgpp v1.x / gcc ",
  1397. #  elif defined(__EMX__)          /* ...so is __EMX__ (double sigh) */
  1398.       "emx+gcc ",
  1399. #  else
  1400.       "gcc ",
  1401. #  endif
  1402.       __VERSION__,
  1403. #elif defined(__WATCOMC__)
  1404. #  if (__WATCOMC__ % 10 != 0)
  1405.       "Watcom C/C++", (sprintf(buf, " %d.%02d", __WATCOMC__ / 100,
  1406.                                __WATCOMC__ % 100), buf),
  1407. #  else
  1408.       "Watcom C/C++", (sprintf(buf, " %d.%d", __WATCOMC__ / 100,
  1409.                                (__WATCOMC__ % 100) / 10), buf),
  1410. #  endif
  1411. #elif defined(__TURBOC__)
  1412. #  ifdef __BORLANDC__
  1413.       "Borland C++",
  1414. #    if (__BORLANDC__ < 0x0200)
  1415.         " 1.0",
  1416. #    elif (__BORLANDC__ == 0x0200)   /* James:  __TURBOC__ = 0x0297 */
  1417.         " 2.0",
  1418. #    elif (__BORLANDC__ == 0x0400)
  1419.         " 3.0",
  1420. #    elif (__BORLANDC__ == 0x0410)   /* __BCPLUSPLUS__ = 0x0310 */
  1421.         " 3.1",
  1422. #    elif (__BORLANDC__ == 0x0452)   /* __BCPLUSPLUS__ = 0x0320 */
  1423.         " 4.0 or 4.02",
  1424. #    elif (__BORLANDC__ == 0x0460)   /* __BCPLUSPLUS__ = 0x0340 */
  1425.         " 4.5",
  1426. #    elif (__BORLANDC__ == 0x0500)
  1427.         " 5.0",
  1428. #    else
  1429.         " later than 5.0",
  1430. #    endif
  1431. #  else
  1432.       "Turbo C",
  1433. #    if (__TURBOC__ > 0x0401)        /* Kevin:  3.0 -> 0x0401 */
  1434.         "++ later than 3.0",
  1435. #    elif (__TURBOC__ >= 0x0400)
  1436.         "++ 3.0",
  1437. #    elif (__TURBOC__ == 0x0295)     /* [661] vfy'd by Kevin */
  1438.         "++ 1.0",
  1439. #    elif ((__TURBOC__ >= 0x018d) && (__TURBOC__ <= 0x0200)) /* James: 0x0200 */
  1440.         " 2.0",
  1441. #    elif (__TURBOC__ > 0x0100)
  1442.         " 1.5",                      /* James:  0x0105? */
  1443. #    else
  1444.         " 1.0",                      /* James:  0x0100 */
  1445. #    endif
  1446. #  endif
  1447. #elif defined(MSC)
  1448. #  if defined(_QC) && !defined(_MSC_VER)
  1449.       "MS Quick C ", "2.0 or earlier",      /* _QC is defined as 1 */
  1450. #  elif defined(_QC) && (_MSC_VER == 600)
  1451.       "MS Quick C ", "2.5 (MSC 6.00)",
  1452. #  else
  1453.       "Microsoft C ",
  1454. #    ifdef _MSC_VER
  1455. #      if (_MSC_VER == 800)
  1456.         "8.0/8.0c (Visual C++ 1.0/1.5)",
  1457. #      else
  1458.         (sprintf(buf, "%d.%02d", _MSC_VER/100, _MSC_VER%100), buf),
  1459. #      endif
  1460. #    else
  1461.       "5.1 or earlier",
  1462. #    endif
  1463. #  endif
  1464. #else
  1465.       "unknown compiler", "",
  1466. #endif /* ?compilers */
  1467.       "MS-DOS",
  1468. #if (defined(__GNUC__) || defined(WATCOMC_386))
  1469.       " (32-bit)",
  1470. #else
  1471. #  if defined(M_I86HM) || defined(__HUGE__)
  1472.       " (16-bit, huge)",
  1473. #  elif defined(M_I86LM) || defined(__LARGE__)
  1474.       " (16-bit, large)",
  1475. #  elif defined(M_I86MM) || defined(__MEDIUM__)
  1476.       " (16-bit, medium)",
  1477. #  elif defined(M_I86CM) || defined(__COMPACT__)
  1478.       " (16-bit, compact)",
  1479. #  elif defined(M_I86SM) || defined(__SMALL__)
  1480.       " (16-bit, small)",
  1481. #  elif defined(M_I86TM) || defined(__TINY__)
  1482.       " (16-bit, tiny)",
  1483. #  else
  1484.       " (16-bit)",
  1485. #  endif
  1486. #endif
  1487. #ifdef __DATE__
  1488.       " on ", __DATE__
  1489. #else
  1490.       "", ""
  1491. #endif
  1492.     );
  1493.     (*G.message)((zvoid *)&G, slide, (ulg)len, 0);
  1494.                                 /* MSC can't handle huge macro expansion */
  1495.     /* temporary debugging code for Borland compilers only */
  1496. #if (defined(__TURBOC__) && defined(DEBUG))
  1497.     Info(slide, 0, ((char *)slide, "tdebug(__TURBOC__ = 0x%04x = %d)n",
  1498.       __TURBOC__, __TURBOC__));
  1499. #ifdef __BORLANDC__
  1500.     Info(slide, 0, ((char *)slide, "tdebug(__BORLANDC__ = 0x%04x)n",
  1501.       __BORLANDC__));
  1502. #else
  1503.     Info(slide, 0, ((char *)slide, "tdebug(__BORLANDC__ not defined)n"));
  1504. #endif
  1505. #ifdef __TCPLUSPLUS__
  1506.     Info(slide, 0, ((char *)slide, "tdebug(__TCPLUSPLUS__ = 0x%04x)n",
  1507.       __TCPLUSPLUS__));
  1508. #else
  1509.     Info(slide, 0, ((char *)slide, "tdebug(__TCPLUSPLUS__ not defined)n"));
  1510. #endif
  1511. #ifdef __BCPLUSPLUS__
  1512.     Info(slide, 0, ((char *)slide, "tdebug(__BCPLUSPLUS__ = 0x%04x)nn",
  1513.       __BCPLUSPLUS__));
  1514. #else
  1515.     Info(slide, 0, ((char *)slide, "tdebug(__BCPLUSPLUS__ not defined)nn"));
  1516. #endif
  1517. #endif /* __TURBOC__ && DEBUG */
  1518. } /* end function version() */
  1519. #endif /* !WINDLL */
  1520. #endif /* !SFX */
  1521. #if (defined(__GO32__) || defined(__EMX__))
  1522. #if (!defined(__DJGPP__) || (__DJGPP__ < 2) || 
  1523.      ((__DJGPP__ == 2) && (__DJGPP_MINOR__ < 2)))
  1524. int volatile _doserrno;
  1525. #endif /* not "djgpp v2.02 or newer" */
  1526. #if (!defined(__DJGPP__) || (__DJGPP__ < 2))
  1527. unsigned _dos_getcountryinfo(void *countrybuffer)
  1528. {
  1529.     asm("movl %0, %%edx": : "g" (countrybuffer));
  1530.     asm("movl $0x3800, %eax");
  1531.     asm("int $0x21": : : "%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi");
  1532.     _doserrno = 0;
  1533.     asm("jnc 1f");
  1534.     asm("movl %%eax, %0": "=m" (_doserrno));
  1535.     asm("1:");
  1536.     return (unsigned)_doserrno;
  1537. }
  1538. unsigned _dos_setftime(int fd, ush dosdate, ush dostime)
  1539. {
  1540.     asm("movl %0, %%ebx": : "g" (fd));
  1541.     asm("movl %0, %%ecx": : "g" (dostime));
  1542.     asm("movl %0, %%edx": : "g" (dosdate));
  1543.     asm("movl $0x5701, %eax");
  1544.     asm("int $0x21": : : "%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi");
  1545.     _doserrno = 0;
  1546.     asm("jnc 1f");
  1547.     asm("movl %%eax, %0": "=m" (_doserrno));
  1548.     errno = EBADF;
  1549.     asm("1:");
  1550.     return (unsigned)_doserrno;
  1551. }
  1552. unsigned _dos_setfileattr(char *name, unsigned attr)
  1553. {
  1554. #if 0   /* stripping of trailing '/' is not needed for unzip-internal use */
  1555.     unsigned namlen = strlen(name);
  1556.     char *i_name = alloca(namlen + 1);
  1557.     strcpy(i_name, name);
  1558.     if (namlen > 1 && i_name[namlen-1] == '/' && i_name[namlen-2] != ':')
  1559.         i_name[namlen-1] = '';
  1560.     asm("movl %0, %%edx": : "g" (i_name));
  1561. #else
  1562.     asm("movl %0, %%edx": : "g" (name));
  1563. #endif
  1564.     asm("movl %0, %%ecx": : "g" (attr));
  1565.     asm("movl $0x4301, %eax");
  1566.     asm("int $0x21": : : "%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi");
  1567.     _doserrno = 0;
  1568.     asm("jnc 1f");
  1569.     asm("movl %%eax, %0": "=m" (_doserrno));
  1570.     switch (_doserrno) {
  1571.     case 2:
  1572.     case 3:
  1573.            errno = ENOENT;
  1574.            break;
  1575.     case 5:
  1576.            errno = EACCES;
  1577.            break;
  1578.     }
  1579.     asm("1:");
  1580.     return (unsigned)_doserrno;
  1581. }
  1582. void _dos_getdrive(unsigned *d)
  1583. {
  1584.     asm("movl $0x1900, %eax");
  1585.     asm("int $0x21": : : "%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi");
  1586.     asm("xorb %ah, %ah");
  1587.     asm("incb %al");
  1588.     asm("movl %%eax, %0": "=a" (*d));
  1589. }
  1590. unsigned _dos_creat(char *path, unsigned attr, int *fd)
  1591. {
  1592.     asm("movl $0x3c00, %eax");
  1593.     asm("movl %0, %%edx": :"g" (path));
  1594.     asm("movl %0, %%ecx": :"g" (attr));
  1595.     asm("int $0x21": : : "%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi");
  1596.     asm("movl %%eax, %0": "=a" (*fd));
  1597.     _doserrno = 0;
  1598.     asm("jnc 1f");
  1599.     _doserrno = *fd;
  1600.     switch (_doserrno) {
  1601.     case 3:
  1602.            errno = ENOENT;
  1603.            break;
  1604.     case 4:
  1605.            errno = EMFILE;
  1606.            break;
  1607.     case 5:
  1608.            errno = EACCES;
  1609.            break;
  1610.     }
  1611.     asm("1:");
  1612.     return (unsigned)_doserrno;
  1613. }
  1614. unsigned _dos_close(int fd)
  1615. {
  1616.     asm("movl %0, %%ebx": : "g" (fd));
  1617.     asm("movl $0x3e00, %eax");
  1618.     asm("int $0x21": : : "%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi");
  1619.     _doserrno = 0;
  1620.     asm("jnc 1f");
  1621.     asm ("movl %%eax, %0": "=m" (_doserrno));
  1622.     if (_doserrno == 6) {
  1623.           errno = EBADF;
  1624.     }
  1625.     asm("1:");
  1626.     return (unsigned)_doserrno;
  1627. }
  1628. #endif /* !__DJGPP__ || (__DJGPP__ < 2) */
  1629. static int volumelabel(char *name)
  1630. {
  1631.     int fd;
  1632.     return _dos_creat(name, FA_LABEL, &fd) ? fd : _dos_close(fd);
  1633. }
  1634. #if (defined(__DJGPP__) && (__DJGPP__ >= 2))
  1635. #include <dpmi.h>               /* These includes for the country info */
  1636. #include <go32.h>
  1637. #include <sys/farptr.h>
  1638. /* The above _dos_getcountryinfo function doesn't work with djgpp v2, presumably
  1639.  * because ds is not set correctly (does it really work at all?). Note that
  1640.  * this version only sets the date (ie. CountryInfo[0]).
  1641.  */
  1642. unsigned _dos_getcountryinfo(void *countrybuffer)
  1643. {
  1644.    __dpmi_regs regs;
  1645.    regs.x.ax = 0x3800;
  1646.    regs.x.dx = __tb & 0x0f;
  1647.    regs.x.ds = (__tb >> 4) & 0xffff;
  1648.    _doserrno = __dpmi_int(0x21, &regs);
  1649.    *(ush*)countrybuffer = _farpeekw(_dos_ds, __tb & 0xfffff);
  1650.    return (unsigned)_doserrno;
  1651. }
  1652. /* Disable determination of "x" bit in st_mode field for [f]stat() calls. */
  1653. int _is_executable (const char *path, int fhandle, const char *ext)
  1654. {
  1655.     return 0;
  1656. }
  1657. /* Prevent globbing of filenames.  This gives the same functionality as
  1658.  * "stubedit <program> globbing=no" did with DJGPP v1.
  1659.  */
  1660. char **__crt0_glob_function(char *_arg)
  1661. {
  1662.     return NULL;
  1663. }
  1664. /* Reduce the size of the executable and remove the functionality to read
  1665.  * the program's environment from whatever $DJGPP points to.
  1666.  */
  1667. void __crt0_load_environment_file(char *_app_name)
  1668. {
  1669. }
  1670. #endif /* __DJGPP__ >= 2 */
  1671. #endif /* __GO32__ || __EMX__ */
  1672. #ifdef __EMX__
  1673. #ifdef MORE
  1674. /**************************/
  1675. /* Function screenlines() */
  1676. /**************************/
  1677. int screenlines()
  1678. {
  1679.     int scr_dimen[2];           /* scr_dimen[0]: columns, src_dimen[1]: rows */
  1680.     _scrsize(scr_dimen);
  1681.     return (scr_dimen[1]);
  1682. }
  1683. /****************************/
  1684. /* Function screencolumns() */
  1685. /****************************/
  1686. int screencolumns()
  1687. {
  1688.     int scr_dimen[2];           /* scr_dimen[0]: columns, src_dimen[1]: rows */
  1689.     _scrsize(scr_dimen);
  1690.     return (scr_dimen[0]);
  1691. }
  1692. #endif /* MORE */
  1693. #endif /* __EMX__ */
  1694. #ifdef WATCOMC_386
  1695. static struct RMINFO {
  1696.     ulg edi, esi, ebp;
  1697.     ulg reserved;
  1698.     ulg ebx, edx, ecx, eax;
  1699.     ush flags;
  1700.     ush es,ds,fs,gs;
  1701.     ush ip_ignored,cs_ignored;
  1702.     ush sp,ss;
  1703. };
  1704. /* This function is used to call dos interrupts that may not be supported
  1705.  * by some particular 32-bit DOS extender.  It uses DPMI function 300h to
  1706.  * simulate a real mode call of the interrupt.  The caller is responsible
  1707.  * for providing real mode addresses of any buffer areas used.  The docs
  1708.  * for PMODE/W imply that this should not be necessary for calling the DOS
  1709.  * interrupts that it doesn't extend, but it crashes when this isn't used. */
  1710. static int int86x_realmode(int inter_no, union REGS *in,
  1711.                             union REGS *out, struct SREGS *seg)
  1712. {
  1713.     union REGS local;
  1714.     struct SREGS localseg;
  1715.     struct RMINFO rmi;
  1716.     int r;
  1717.     rmi.eax = in->x.eax;
  1718.     rmi.ebx = in->x.ebx;
  1719.     rmi.ecx = in->x.ecx;
  1720.     rmi.edx = in->x.edx;
  1721.     rmi.edi = in->x.edi;
  1722.     rmi.esi = in->x.esi;
  1723.     rmi.ebp = rmi.reserved = 0L;
  1724.     rmi.es = seg->es;
  1725.     rmi.ds = seg->ds;
  1726.     rmi.fs = seg->fs;
  1727.     rmi.gs = seg->gs;
  1728.     rmi.sp = rmi.ss = rmi.ip_ignored = rmi.cs_ignored = rmi.flags = 0;
  1729.     memset(&local, 0, sizeof(local));
  1730.     memset(&localseg, 0, sizeof(localseg));
  1731.     local.w.ax = 0x0300;
  1732.     local.h.bl = inter_no;
  1733.     local.h.bh = 0;
  1734.     local.w.cx = 0;
  1735.     localseg.es = FP_SEG(&rmi);
  1736.     local.x.edi = FP_OFF(&rmi);
  1737.     r = int386x(0x31, &local, &local, &localseg);
  1738.     out->x.eax = rmi.eax;
  1739.     out->x.ebx = rmi.ebx;
  1740.     out->x.ecx = rmi.ecx;
  1741.     out->x.edx = rmi.edx;
  1742.     out->x.edi = rmi.edi;
  1743.     out->x.esi = rmi.esi;
  1744.     out->x.cflag = rmi.flags & INTR_CF;
  1745.     return r;
  1746. }
  1747. #endif /* WATCOMC_386 */
  1748. #ifdef DOS_STAT_BANDAID
  1749. /* This papers over a bug in Watcom 10.6's standard library...sigh.
  1750.  * Apparently it applies to both the DOS and Win32 stat()s. */
  1751. int stat_bandaid(const char *path, struct stat *buf)
  1752. {
  1753.     char newname[4];
  1754.     if (!stat(path, buf))
  1755.         return 0;
  1756.     else if (!strcmp(path, ".") || (path[0] && !strcmp(path + 1, ":."))) {
  1757.         strcpy(newname, path);
  1758.         newname[strlen(path) - 1] = '\';   /* stat(".") fails for root! */
  1759.         return stat(newname, buf);
  1760.     } else
  1761.         return -1;
  1762. }
  1763. #endif /* DOS_STAT_BANDAID */