Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | Download | RSS feed

  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.  
  62. static 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
  66. static int isfloppy OF((int nDrive));
  67. static int z_dos_chmod OF((__GPRO__ ZCONST char *fname, int attributes));
  68. static int volumelabel OF((ZCONST char *newlabel));
  69. #if (!defined(SFX) && !defined(WINDLL))
  70.    static int is_running_on_windows OF((void));
  71. #endif
  72. static int getdoscodepage OF((void));
  73.  
  74. static int created_dir;        /* used by mapname(), checkdir() */
  75. static int renamed_fullpath;   /* ditto */
  76. static 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
  88. static ZCONST char Far WarnDirTraversSkip[] =
  89.   "warning:  skipped \"../\" path component(s) in %s\n";
  90. static ZCONST char Far Creating[] = "   creating: %s\n";
  91. static ZCONST char Far ConversionFailed[] =
  92.   "mapname:  conversion of %s failed\n";
  93. static ZCONST char Far Labelling[] = "labelling %c: %-22s\n";
  94. static ZCONST char Far ErrSetVolLabel[] =
  95.   "mapname:  error setting volume label\n";
  96. static ZCONST char Far PathTooLong[] = "checkdir error:  path too long: %s\n";
  97. static ZCONST char Far CantCreateDir[] = "checkdir error:  cannot create %s\n\
  98.                 unable to process %s.\n";
  99. static ZCONST char Far DirIsntDirectory[] =
  100.   "checkdir error:  %s exists but is not directory\n\
  101.                 unable to process %s.\n";
  102. static 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
  109. static 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.  
  209. zDIR *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.  
  250. struct 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.  
  276. char *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.  
  420. int 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.  
  438. int 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.  
  748. static 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.  
  803. static 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.  
  868. int 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.  
  1164. static 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, &regs, &regs);
  1174.     if (WREGS(regs,flags) & 1)
  1175. #else
  1176.     intdos(&regs, &regs);
  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.  
  1195. static 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.  
  1245. typedef 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.  
  1260. static 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(&regs, 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, &regs, &regs);            /* 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, &regs, &regs);            /* 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(&regs, &regs, &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(&regs, &regs, &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(&regs, &regs, &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(&regs, &regs, &sregs);
  1422.             retv = 1;
  1423.         } else {
  1424.             F_intdosx(&regs, &regs, &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(&regs, &regs, &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, &regs, &regs);
  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.  */
  1481. typedef 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.  
  1506. void 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.  
  1617. int 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.  
  1668. void 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.  
  1714. int 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(&regs, 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, &regs, &regs);            /* 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, &regs, &regs);            /* 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, &regs, &regs, &sregs);
  1761.     CountryInfo[0] = regs.w.cflag ? 0 : _CountryInfo[0];
  1762.     regs.w.ax = 0x0101;
  1763.     regs.w.dx = seg;
  1764.     int386(0x31, &regs, &regs);              /* 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(&regs, &regs, &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.  
  1801. static 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.  
  1826. void 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.  
  1847. void 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.  
  2020. static int next_ptr = 0;
  2021.  
  2022. typedef struct ptr_table_s {
  2023.     zvoid far *org_ptr;
  2024.     zvoid far *new_ptr;
  2025. } ptr_table;
  2026.  
  2027. static 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.  
  2035. zvoid 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.  
  2056. zvoid 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.  
  2084. zvoid far *zcalloc(unsigned items, unsigned size)
  2085. {
  2086.     return (zvoid far *)_halloc((long)items, size);
  2087. }
  2088.  
  2089. zvoid 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)))
  2104. int volatile _doserrno;
  2105. #endif /* not "djgpp v2.02 or newer" */
  2106.  
  2107. #if (!defined(__DJGPP__) || (__DJGPP__ < 2))
  2108.  
  2109. unsigned _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.  
  2121. unsigned _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.  
  2136. unsigned _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.  
  2168. void _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.  
  2177. unsigned _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.  
  2202. unsigned _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.  
  2220. static 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.  */
  2238. unsigned _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, &regs);
  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. */
  2254. int _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.  */
  2263. char **__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.  */
  2273. void __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.  
  2283. static int getdoscodepage(void)
  2284. {
  2285.     union REGS regs;
  2286.  
  2287.     WREGS(regs,ax) = 0x6601;
  2288. #ifdef __EMX__
  2289.     _int86(0x21, &regs, &regs);
  2290.     if (WREGS(regs,flags) & 1)
  2291. #else
  2292.     intdos(&regs, &regs);
  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.  
  2313. int 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.  
  2336. int 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.  
  2349. static 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.  
  2366. static 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.  
  2415. int 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.