home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 9 Archive / 09-Archive.zip / unzip512.zip / msdos / msdos.c < prev    next >
C/C++ Source or Header  |  1994-08-16  |  46KB  |  1,338 lines

  1. /*---------------------------------------------------------------------------
  2.  
  3.   msdos.c
  4.  
  5.   MSDOS-specific routines for use with Info-ZIP's UnZip 5.1 and later.
  6.  
  7.   Contains:  Opendir()           (from zip)
  8.              Readdir()           (from zip)
  9.              do_wild()
  10.              mapattr()
  11.              mapname()
  12.              checkdir()
  13.              isfloppy()
  14.              volumelabel()       (non-djgpp, non-emx)
  15.              close_outfile()
  16.              dateformat()
  17.              version()
  18.              _dos_getcountryinfo() (djgpp, emx)
  19.              _dos_setftime()     (djgpp, emx)
  20.              _dos_setfileattr()  (djgpp, emx)
  21.              _dos_getdrive()     (djgpp, emx)
  22.              _dos_creat()        (djgpp, emx)
  23.              _dos_close()        (djgpp, emx)
  24.              volumelabel()       (djgpp, emx)
  25.  
  26.   ---------------------------------------------------------------------------*/
  27.  
  28.  
  29.  
  30. #include "unzip.h"
  31. #undef FILENAME    /* BC++ 3.1 and djgpp 1.11 define FILENAME in <dir.h> */
  32.  
  33. static int isfloppy OF((int nDrive));
  34. static int volumelabel OF((char *newlabel));
  35.  
  36. static int created_dir;        /* used by mapname(), checkdir() */
  37. static int renamed_fullpath;   /* ditto */
  38. static unsigned nLabelDrive;   /* ditto, plus volumelabel() */
  39.  
  40. #if (defined(__GO32__) || defined(__EMX__))
  41. #  define MKDIR(path,mode)   mkdir(path,mode)
  42. #  include <dirent.h>        /* use readdir() */
  43. #  define direct dirent
  44. #  define Opendir opendir
  45. #  define Readdir readdir
  46. #  ifdef __EMX__
  47. #    include <dos.h>
  48. #    define GETDRIVE(d)      d = _getdrive()
  49. #    define FA_LABEL         A_LABEL
  50. #  else
  51. #    define GETDRIVE(d)      _dos_getdrive(&d)
  52. #  endif
  53. #else /* !(__GO32__ || __EMX__) */
  54. #  define MKDIR(path,mode)   mkdir(path)
  55. #  ifdef __TURBOC__
  56. #    define FATTR            FA_HIDDEN+FA_SYSTEM+FA_DIREC
  57. #    define FVOLID           FA_VOLID
  58. #    define FFIRST(n,d,a)    findfirst(n,(struct ffblk *)d,a)
  59. #    define FNEXT(d)         findnext((struct ffblk *)d)
  60. #    define GETDRIVE(d)      d=getdisk()+1
  61. #    include <dir.h>
  62. #  else /* !__TURBOC__ */
  63. #    define FATTR            _A_HIDDEN+_A_SYSTEM+_A_SUBDIR
  64. #    define FVOLID           _A_VOLID
  65. #    define FFIRST(n,d,a)    _dos_findfirst(n,a,(struct find_t *)d)
  66. #    define FNEXT(d)         _dos_findnext((struct find_t *)d)
  67. #    define GETDRIVE(d)      _dos_getdrive(&d)
  68. #    include <direct.h>
  69. #  endif /* ?__TURBOC__ */
  70.    typedef struct direct {
  71.        char d_reserved[30];
  72.        char d_name[13];
  73.        int d_first;
  74.    } DIR;
  75. #  define closedir free
  76.    DIR *Opendir OF((const char *));
  77.    struct direct *Readdir OF((DIR *));
  78.  
  79.  
  80.  
  81.  
  82. #ifndef SFX
  83.  
  84. /**********************/   /* Borland C++ 3.x has its own opendir/readdir */
  85. /* Function Opendir() */   /*  library routines, but earlier versions don't, */
  86. /**********************/   /*  so use ours regardless */
  87.  
  88. DIR *Opendir(name)
  89.     const char *name;        /* name of directory to open */
  90. {
  91.     DIR *dirp;               /* malloc'd return value */
  92.     char *nbuf;              /* malloc'd temporary string */
  93.     int len = strlen(name);  /* path length to avoid strlens and strcats */
  94.  
  95.  
  96.     if ((dirp = (DIR *)malloc(sizeof(DIR))) == (DIR *)NULL)
  97.         return (DIR *)NULL;
  98.     if ((nbuf = malloc(len + 5)) == (char *)NULL) {
  99.         free(dirp);
  100.         return (DIR *)NULL;
  101.     }
  102.     strcpy(nbuf, name);
  103.     if (nbuf[len-1] == ':') {
  104.         nbuf[len++] = '.';
  105.     } else if (nbuf[len-1] == '/' || nbuf[len-1] == '\\')
  106.         --len;
  107.     strcpy(nbuf+len, "/*.*");
  108.     Trace((stderr, "opendir:  nbuf = [%s]\n", nbuf));
  109.  
  110.     if (FFIRST(nbuf, dirp, FATTR)) {
  111.         free((voidp *)nbuf);
  112.         return (DIR *)NULL;
  113.     }
  114.     free((voidp *)nbuf);
  115.     dirp->d_first = 1;
  116.     return dirp;
  117. }
  118.  
  119.  
  120.  
  121.  
  122.  
  123. /**********************/
  124. /* Function Readdir() */
  125. /**********************/
  126.  
  127. struct direct *Readdir(d)
  128.     DIR *d;         /* directory stream from which to read */
  129. {
  130.     /* Return pointer to first or next directory entry, or NULL if end. */
  131.  
  132.     if (d->d_first)
  133.         d->d_first = 0;
  134.     else
  135.         if (FNEXT(d))
  136.             return (struct direct *)NULL;
  137.     return (struct direct *)d;
  138. }
  139.  
  140. #endif /* !SFX */
  141. #endif /* ?(__GO32__ || __EMX__) */
  142.  
  143.  
  144.  
  145.  
  146.  
  147. #ifndef SFX
  148.  
  149. /************************/
  150. /*  Function do_wild()  */   /* identical to OS/2 version */
  151. /************************/
  152.  
  153. char *do_wild(wildspec)
  154.     char *wildspec;          /* only used first time on a given dir */
  155. {
  156.     static DIR *dir = (DIR *)NULL;
  157.     static char *dirname, *wildname, matchname[FILNAMSIZ];
  158.     static char Far CantAllocateWildcard[] =
  159.       "warning:  can't allocate wildcard buffers\n";
  160.     static int firstcall=TRUE, have_dirname, dirnamelen;
  161.     struct direct *file;
  162.  
  163.  
  164.     /* Even when we're just returning wildspec, we *always* do so in
  165.      * matchname[]--calling routine is allowed to append four characters
  166.      * to the returned string, and wildspec may be a pointer to argv[].
  167.      */
  168.     if (firstcall) {        /* first call:  must initialize everything */
  169.         firstcall = FALSE;
  170.  
  171.         /* break the wildspec into a directory part and a wildcard filename */
  172.         if ((wildname = strrchr(wildspec, '/')) == (char *)NULL &&
  173.             (wildname = strrchr(wildspec, ':')) == (char *)NULL) {
  174.             dirname = ".";
  175.             dirnamelen = 1;
  176.             have_dirname = FALSE;
  177.             wildname = wildspec;
  178.         } else {
  179.             ++wildname;     /* point at character after '/' or ':' */
  180.             dirnamelen = wildname - wildspec;
  181.             if ((dirname = (char *)malloc(dirnamelen+1)) == (char *)NULL) {
  182.                 FPRINTF(stderr, LoadFarString(CantAllocateWildcard));
  183.                 strcpy(matchname, wildspec);
  184.                 return matchname;   /* but maybe filespec was not a wildcard */
  185.             }
  186. /* GRR:  can't strip trailing char for opendir since might be "d:/" or "d:"
  187.  *       (would have to check for "./" at end--let opendir handle it instead) */
  188.             strncpy(dirname, wildspec, dirnamelen);
  189.             dirname[dirnamelen] = '\0';   /* terminate for strcpy below */
  190.             have_dirname = TRUE;
  191.         }
  192.         Trace((stderr, "do_wild:  dirname = [%s]\n", dirname));
  193.  
  194.         if ((dir = Opendir(dirname)) != (DIR *)NULL) {
  195.             while ((file = Readdir(dir)) != (struct direct *)NULL) {
  196.                 Trace((stderr, "do_wild:  readdir returns %s\n", file->d_name));
  197.                 if (match(file->d_name, wildname, 1)) {  /* 1 == ignore case */
  198.                     Trace((stderr, "do_wild:  match() succeeds\n"));
  199.                     if (have_dirname) {
  200.                         strcpy(matchname, dirname);
  201.                         strcpy(matchname+dirnamelen, file->d_name);
  202.                     } else
  203.                         strcpy(matchname, file->d_name);
  204.                     return matchname;
  205.                 }
  206.             }
  207.             /* if we get to here directory is exhausted, so close it */
  208.             closedir(dir);
  209.             dir = (DIR *)NULL;
  210.         }
  211.         Trace((stderr, "do_wild:  opendir(%s) returns NULL\n", dirname));
  212.  
  213.         /* return the raw wildspec in case that works (e.g., directory not
  214.          * searchable, but filespec was not wild and file is readable) */
  215.         strcpy(matchname, wildspec);
  216.         return matchname;
  217.     }
  218.  
  219.     /* last time through, might have failed opendir but returned raw wildspec */
  220.     if (dir == (DIR *)NULL) {
  221.         firstcall = TRUE;  /* nothing left to try--reset for new wildspec */
  222.         if (have_dirname)
  223.             free(dirname);
  224.         return (char *)NULL;
  225.     }
  226.  
  227.     /* If we've gotten this far, we've read and matched at least one entry
  228.      * successfully (in a previous call), so dirname has been copied into
  229.      * matchname already.
  230.      */
  231.     while ((file = Readdir(dir)) != (struct direct *)NULL)
  232.         if (match(file->d_name, wildname, 1)) {   /* 1 == ignore case */
  233.             if (have_dirname) {
  234.                 /* strcpy(matchname, dirname); */
  235.                 strcpy(matchname+dirnamelen, file->d_name);
  236.             } else
  237.                 strcpy(matchname, file->d_name);
  238.             return matchname;
  239.         }
  240.  
  241.     closedir(dir);     /* have read at least one dir entry; nothing left */
  242.     dir = (DIR *)NULL;
  243.     firstcall = TRUE;  /* reset for new wildspec */
  244.     if (have_dirname)
  245.         free(dirname);
  246.     return (char *)NULL;
  247.  
  248. } /* end function do_wild() */
  249.  
  250. #endif /* !SFX */
  251.  
  252.  
  253.  
  254.  
  255.  
  256. /**********************/
  257. /* Function mapattr() */
  258. /**********************/
  259.  
  260. int mapattr()
  261. {
  262.     /* set archive bit (file is not backed up): */
  263.     pInfo->file_attr = (unsigned)(crec.external_file_attributes | 32) & 0xff;
  264.     return 0;
  265.  
  266. } /* end function mapattr() */
  267.  
  268.  
  269.  
  270.  
  271.  
  272. /*****************************/
  273. /*  Strings used in msdos.c  */
  274. /*****************************/
  275.  
  276. static char Far ConversionFailed[] = "mapname:  conversion of %s failed\n";
  277. static char Far ErrSetVolLabel[] = "mapname:  error setting volume label\n";
  278. static char Far PathTooLong[] = "checkdir error:  path too long: %s\n";
  279. static char Far CantCreateDir[] = "checkdir error:  can't create %s\n\
  280.                  unable to process %s.\n";
  281. static char Far DirIsntDirectory[] =
  282.   "checkdir error:  %s exists but is not directory\n\
  283.                  unable to process %s.\n";
  284. static char Far PathTooLongTrunc[] =
  285.   "checkdir warning:  path too long; truncating\n                   %s\n\
  286.                 -> %s\n";
  287. #if (!defined(SFX) || defined(SFX_EXDIR))
  288.    static char Far CantCreateExtractDir[] =
  289.      "checkdir:  can't create extraction directory: %s\n";
  290. #endif
  291.  
  292.  
  293.  
  294.  
  295.  
  296. /************************/
  297. /*  Function mapname()  */
  298. /************************/
  299.  
  300. int mapname(renamed)  /* return 0 if no error, 1 if caution (filename trunc), */
  301.     int renamed;      /* 2 if warning (skip file because dir doesn't exist), */
  302. {                     /* 3 if error (skip file), 10 if no memory (skip file) */
  303.     char pathcomp[FILNAMSIZ];    /* path-component buffer */
  304.     char *pp, *cp=(char *)NULL;  /* character pointers */
  305.     char *lastsemi=(char *)NULL; /* pointer to last semi-colon in pathcomp */
  306.     char *last_dot=(char *)NULL; /* last dot not converted to underscore */
  307.     int quote = FALSE;           /* flag:  next char is literal */
  308.     int dotname = FALSE;         /* flag:  path component begins with dot */
  309.     int error = 0;
  310.     register unsigned workch;    /* hold the character being tested */
  311.  
  312.  
  313. /*---------------------------------------------------------------------------
  314.     Initialize various pointers and counters and stuff.
  315.   ---------------------------------------------------------------------------*/
  316.  
  317.     /* can create path as long as not just freshening, or if user told us */
  318.     create_dirs = (!fflag || renamed);
  319.  
  320.     created_dir = FALSE;        /* not yet */
  321.     renamed_fullpath = FALSE;
  322.  
  323. /* GRR:  for VMS, convert to internal format now or later? or never? */
  324.     if (renamed) {
  325.         cp = filename - 1;      /* point to beginning of renamed name... */
  326.         while (*++cp)
  327.             if (*cp == '\\')    /* convert backslashes to forward */
  328.                 *cp = '/';
  329.         cp = filename;
  330.         /* use temporary rootpath if user gave full pathname */
  331.         if (filename[0] == '/') {
  332.             renamed_fullpath = TRUE;
  333.             pathcomp[0] = '/';  /* copy the '/' and terminate */
  334.             pathcomp[1] = '\0';
  335.             ++cp;
  336.         } else if (isalpha(filename[0]) && filename[1] == ':') {
  337.             renamed_fullpath = TRUE;
  338.             pp = pathcomp;
  339.             *pp++ = *cp++;      /* copy the "d:" (+ '/', possibly) */
  340.             *pp++ = *cp++;
  341.             if (*cp == '/')
  342.                 *pp++ = *cp++;  /* otherwise add "./"? */
  343.             *pp = '\0';
  344.         }
  345.     }
  346.  
  347.     /* pathcomp is ignored unless renamed_fullpath is TRUE: */
  348.     if ((error = checkdir(pathcomp, INIT)) != 0)    /* initialize path buffer */
  349.         return error;           /* ...unless no mem or vol label on hard disk */
  350.  
  351.     *pathcomp = '\0';           /* initialize translation buffer */
  352.     pp = pathcomp;              /* point to translation buffer */
  353.     if (!renamed) {             /* cp already set if renamed */
  354.         if (jflag)              /* junking directories */
  355.             cp = (char *)strrchr(filename, '/');
  356.         if (cp == (char *)NULL) /* no '/' or not junking dirs */
  357.             cp = filename;      /* point to internal zipfile-member pathname */
  358.         else
  359.             ++cp;               /* point to start of last component of path */
  360.     }
  361.  
  362. /*---------------------------------------------------------------------------
  363.     Begin main loop through characters in filename.
  364.   ---------------------------------------------------------------------------*/
  365.  
  366.     while ((workch = (uch)*cp++) != 0) {
  367.  
  368.         if (quote) {              /* if character quoted, */
  369.             *pp++ = (char)workch; /*  include it literally */
  370.             quote = FALSE;
  371.         } else
  372.             switch (workch) {
  373.             case '/':             /* can assume -j flag not given */
  374.                 *pp = '\0';
  375. /* GRR:  can add 8.3 truncation here */
  376.                 if (last_dot) {   /* one dot in directory name is legal */
  377.                     *last_dot = '.';
  378.                     last_dot = (char *)NULL;
  379.                 }
  380.                 if ((error = checkdir(pathcomp, APPEND_DIR)) > 1)
  381.                     return error;
  382.                 pp = pathcomp;    /* reset conversion buffer for next piece */
  383.                 lastsemi = (char *)NULL; /* leave directory semi-colons alone */
  384.                 break;
  385.  
  386.             case ':':             /* drive names not stored in zipfile, */
  387.             case '[':             /*  so no colons allowed; no brackets, */
  388.             case ']':             /*  either */
  389.                 *pp++ = '_';
  390.                 break;
  391.  
  392.             case '.':
  393.                 if (pp == pathcomp) {     /* nothing appended yet... */
  394.                     if (*cp == '/') {     /* don't bother appending a "./" */
  395.                         ++cp;             /*  component to the path:  skip */
  396.                         break;            /*  to next char after the '/' */
  397.                     } else if (*cp == '.' && cp[1] == '/') {   /* "../" */
  398.                         *pp++ = '.';      /* add first dot, unchanged... */
  399.                         ++cp;             /* skip second dot, since it will */
  400.                     } else {              /*  be "added" at end of if-block */
  401.                         *pp++ = '_';      /* FAT doesn't allow null filename */
  402.                         dotname = TRUE;   /*  bodies, so map .exrc -> _.exrc */
  403.                     }                     /*  (extra '_' now, "dot" below) */
  404.                 } else if (dotname) {     /* found a second dot, but still */
  405.                     dotname = FALSE;      /*  have extra leading underscore: */
  406.                     *pp = '\0';           /*  remove it by shifting chars */
  407.                     pp = pathcomp + 1;    /*  left one space (e.g., .p1.p2: */
  408.                     while (pp[1]) {       /*  __p1 -> _p1_p2 -> _p1.p2 when */
  409.                         *pp = pp[1];      /*  finished) [opt.:  since first */
  410.                         ++pp;             /*  two chars are same, can start */
  411.                     }                     /*  shifting at second position] */
  412.                 }
  413.                 last_dot = pp;    /* point at last dot so far... */
  414.                 *pp++ = '_';      /* convert dot to underscore for now */
  415.                 break;
  416.  
  417.             case ';':             /* start of VMS version? */
  418.                 lastsemi = pp;    /* omit for now; remove VMS vers. later */
  419.                 break;
  420.  
  421.             case '\026':          /* control-V quote for special chars */
  422.                 quote = TRUE;     /* set flag for next character */
  423.                 break;
  424.  
  425.             case ' ':             /* change spaces to underscore only */
  426.                 if (sflag)        /*  if specifically requested */
  427.                     *pp++ = '_';
  428.                 else
  429.                     *pp++ = (char)workch;
  430.                 break;
  431.  
  432.             default:
  433.                 /* allow European characters in filenames: */
  434.                 if (isprint(workch) || (128 <= workch && workch <= 254))
  435.                     *pp++ = (char)workch;
  436.             } /* end switch */
  437.  
  438.     } /* end while loop */
  439.  
  440.     *pp = '\0';                   /* done with pathcomp:  terminate it */
  441.  
  442.     /* if not saving them, remove VMS version numbers (appended "###") */
  443.     if (!V_flag && lastsemi) {
  444.         pp = lastsemi;            /* semi-colon was omitted:  expect all #'s */
  445.         while (isdigit((uch)(*pp)))
  446.             ++pp;
  447.         if (*pp == '\0')          /* only digits between ';' and end:  nuke */
  448.             *lastsemi = '\0';
  449.     }
  450.  
  451. /* GRR:  can add 8.3 truncation here */
  452.     if (last_dot != (char *)NULL) /* one dot is OK:  put it back in */
  453.         *last_dot = '.';          /* (already done for directories) */
  454.  
  455. /*---------------------------------------------------------------------------
  456.     Report if directory was created (and no file to create:  filename ended
  457.     in '/'), check name to be sure it exists, and combine path and name be-
  458.     fore exiting.
  459.   ---------------------------------------------------------------------------*/
  460.  
  461.     if (filename[strlen(filename) - 1] == '/') {
  462.         checkdir(filename, GETPATH);
  463.         if (created_dir && QCOND2) {
  464.             FPRINTF(stdout, "   creating: %s\n", filename);
  465.             return IZ_CREATED_DIR;   /* set dir time (note trailing '/') */
  466.         }
  467.         return 2;   /* dir existed already; don't look for data to extract */
  468.     }
  469.  
  470.     if (*pathcomp == '\0') {
  471.         FPRINTF(stderr, LoadFarString(ConversionFailed), filename);
  472.         return 3;
  473.     }
  474.  
  475.     checkdir(pathcomp, APPEND_NAME);   /* returns 1 if truncated:  care? */
  476.     checkdir(filename, GETPATH);
  477.  
  478.     if (pInfo->vollabel) {   /* set the volume label now */
  479.         if (QCOND2)
  480.             FPRINTF(stdout, "labelling %c: %-22s\n", (nLabelDrive + 'a' - 1),
  481.               filename);
  482.         if (volumelabel(filename)) {
  483.             FPRINTF(stderr, LoadFarString(ErrSetVolLabel));
  484.             return 3;
  485.         }
  486.         return 2;   /* success:  skip the "extraction" quietly */
  487.     }
  488.  
  489.     return error;
  490.  
  491. } /* end function mapname() */
  492.  
  493.  
  494.  
  495.  
  496.  
  497. /***********************/
  498. /* Function checkdir() */
  499. /***********************/
  500.  
  501. int checkdir(pathcomp, flag)
  502.     char *pathcomp;
  503.     int flag;
  504. /*
  505.  * returns:  1 - (on APPEND_NAME) truncated filename
  506.  *           2 - path doesn't exist, not allowed to create
  507.  *           3 - path doesn't exist, tried to create and failed; or
  508.  *               path exists and is not a directory, but is supposed to be
  509.  *           4 - path is too long
  510.  *          10 - can't allocate memory for filename buffers
  511.  */
  512. {
  513.     static int rootlen = 0;   /* length of rootpath */
  514.     static char *rootpath;    /* user's "extract-to" directory */
  515.     static char *buildpath;   /* full path (so far) to extracted file */
  516.     static char *end;         /* pointer to end of buildpath ('\0') */
  517. #ifdef MSC
  518.     int attrs;                /* work around MSC stat() bug */
  519. #endif
  520.  
  521. #   define FN_MASK   7
  522. #   define FUNCTION  (flag & FN_MASK)
  523.  
  524.  
  525.  
  526. /*---------------------------------------------------------------------------
  527.     APPEND_DIR:  append the path component to the path being built and check
  528.     for its existence.  If doesn't exist and we are creating directories, do
  529.     so for this one; else signal success or error as appropriate.
  530.   ---------------------------------------------------------------------------*/
  531.  
  532.     if (FUNCTION == APPEND_DIR) {
  533.         int too_long = FALSE;
  534.  
  535.         Trace((stderr, "appending dir segment [%s]\n", pathcomp));
  536.         while ((*end = *pathcomp++) != '\0')
  537.             ++end;
  538.  
  539.         /* GRR:  could do better check, see if overrunning buffer as we go:
  540.          * check end-buildpath after each append, set warning variable if
  541.          * within 20 of FILNAMSIZ; then if var set, do careful check when
  542.          * appending.  Clear variable when begin new path. */
  543.  
  544.         if ((end-buildpath) > FILNAMSIZ-3)  /* need '/', one-char name, '\0' */
  545.             too_long = TRUE;                /* check if extracting directory? */
  546. #ifdef MSC /* MSC 6.00 bug:  stat(non-existent-dir) == 0 [exists!] */
  547.         if (_dos_getfileattr(buildpath, &attrs) || stat(buildpath, &statbuf))  
  548. #else
  549.         if (stat(buildpath, &statbuf))      /* path doesn't exist */
  550. #endif
  551.         {
  552.             if (!create_dirs) {   /* told not to create (freshening) */
  553.                 free(buildpath);
  554.                 return 2;         /* path doesn't exist:  nothing to do */
  555.             }
  556.             if (too_long) {
  557.                 FPRINTF(stderr, LoadFarString(PathTooLong), buildpath);
  558.                 fflush(stderr);
  559.                 free(buildpath);
  560.                 return 4;         /* no room for filenames:  fatal */
  561.             }
  562.             if (MKDIR(buildpath, 0777) == -1) {   /* create the directory */
  563.                 FPRINTF(stderr, LoadFarString(CantCreateDir), buildpath,
  564.                         filename);
  565.                 fflush(stderr);
  566.                 free(buildpath);
  567.                 return 3;      /* path didn't exist, tried to create, failed */
  568.             }
  569.             created_dir = TRUE;
  570.         } else if (!S_ISDIR(statbuf.st_mode)) {
  571.             FPRINTF(stderr, LoadFarString(DirIsntDirectory), buildpath,
  572.                     filename);
  573.             fflush(stderr);
  574.             free(buildpath);
  575.             return 3;          /* path existed but wasn't dir */
  576.         }
  577.         if (too_long) {
  578.             FPRINTF(stderr, LoadFarString(PathTooLong), buildpath);
  579.             fflush(stderr);
  580.             free(buildpath);
  581.             return 4;         /* no room for filenames:  fatal */
  582.         }
  583.         *end++ = '/';
  584.         *end = '\0';
  585.         Trace((stderr, "buildpath now = [%s]\n", buildpath));
  586.         return 0;
  587.  
  588.     } /* end if (FUNCTION == APPEND_DIR) */
  589.  
  590. /*---------------------------------------------------------------------------
  591.     GETPATH:  copy full path to the string pointed at by pathcomp, and free
  592.     buildpath.
  593.   ---------------------------------------------------------------------------*/
  594.  
  595.     if (FUNCTION == GETPATH) {
  596.         strcpy(pathcomp, buildpath);
  597.         Trace((stderr, "getting and freeing path [%s]\n", pathcomp));
  598.         free(buildpath);
  599.         buildpath = end = (char *)NULL;
  600.         return 0;
  601.     }
  602.  
  603. /*---------------------------------------------------------------------------
  604.     APPEND_NAME:  assume the path component is the filename; append it and
  605.     return without checking for existence.
  606.   ---------------------------------------------------------------------------*/
  607.  
  608.     if (FUNCTION == APPEND_NAME) {
  609.         Trace((stderr, "appending filename [%s]\n", pathcomp));
  610.         while ((*end = *pathcomp++) != '\0') {
  611.             ++end;
  612.             if ((end-buildpath) >= FILNAMSIZ) {
  613.                 *--end = '\0';
  614.                 FPRINTF(stderr, LoadFarString(PathTooLongTrunc),
  615.                         filename, buildpath);
  616.                 fflush(stderr);
  617.                 return 1;   /* filename truncated */
  618.             }
  619.         }
  620.         Trace((stderr, "buildpath now = [%s]\n", buildpath));
  621.         return 0;  /* could check for existence here, prompt for new name... */
  622.     }
  623.  
  624. /*---------------------------------------------------------------------------
  625.     INIT:  allocate and initialize buffer space for the file currently being
  626.     extracted.  If file was renamed with an absolute path, don't prepend the
  627.     extract-to path.
  628.   ---------------------------------------------------------------------------*/
  629.  
  630.     if (FUNCTION == INIT) {
  631.         Trace((stderr, "initializing buildpath to "));
  632.         if ((buildpath = (char *)malloc(strlen(filename)+rootlen+1)) ==
  633.             (char *)NULL)
  634.             return 10;
  635.         if (pInfo->vollabel) {
  636. /* GRR:  for network drives, do strchr() and return IZ_VOL_LABEL if not [1] */
  637.             if (renamed_fullpath && pathcomp[1] == ':')
  638.                 *buildpath = ToLower(*pathcomp);
  639.             else if (!renamed_fullpath && rootpath && rootpath[1] == ':')
  640.                 *buildpath = ToLower(*rootpath);
  641.             else {
  642.                 GETDRIVE(nLabelDrive);   /* assumed that a == 1, b == 2, etc. */
  643.                 *buildpath = (char)(nLabelDrive - 1 + 'a');
  644.             }
  645.             nLabelDrive = *buildpath - 'a' + 1;       /* save for mapname() */
  646.             if (volflag == 0 || *buildpath < 'a' ||   /* no labels/bogus disk */
  647.                 (volflag == 1 && !isfloppy(nLabelDrive))) {  /* -$:  no fixed */
  648.                 free(buildpath);
  649.                 return IZ_VOL_LABEL;     /* skipping with message */
  650.             }
  651.             *buildpath = '\0';
  652.             end = buildpath;
  653.         } else if (renamed_fullpath) {   /* pathcomp = valid data */
  654.             end = buildpath;
  655.             while ((*end = *pathcomp++) != '\0')
  656.                 ++end;
  657.         } else if (rootlen > 0) {
  658.             strcpy(buildpath, rootpath);
  659.             end = buildpath + rootlen;
  660.         } else {
  661.             *buildpath = '\0';
  662.             end = buildpath;
  663.         }
  664.         Trace((stderr, "[%s]\n", buildpath));
  665.         return 0;
  666.     }
  667.  
  668. /*---------------------------------------------------------------------------
  669.     ROOT:  if appropriate, store the path in rootpath and create it if neces-
  670.     sary; else assume it's a zipfile member and return.  This path segment
  671.     gets used in extracting all members from every zipfile specified on the
  672.     command line.  Note that under OS/2 and MS-DOS, if a candidate extract-to
  673.     directory specification includes a drive letter (leading "x:"), it is
  674.     treated just as if it had a trailing '/'--that is, one directory level
  675.     will be created if the path doesn't exist, unless this is otherwise pro-
  676.     hibited (e.g., freshening).
  677.   ---------------------------------------------------------------------------*/
  678.  
  679. #if (!defined(SFX) || defined(SFX_EXDIR))
  680.     if (FUNCTION == ROOT) {
  681.         Trace((stderr, "initializing root path to [%s]\n", pathcomp));
  682.         if (pathcomp == (char *)NULL) {
  683.             rootlen = 0;
  684.             return 0;
  685.         }
  686.         if ((rootlen = strlen(pathcomp)) > 0) {
  687.             int had_trailing_pathsep=FALSE, has_drive=FALSE, xtra=2;
  688.  
  689.             if (isalpha(pathcomp[0]) && pathcomp[1] == ':')
  690.                 has_drive = TRUE;   /* drive designator */
  691.             if (pathcomp[rootlen-1] == '/') {
  692.                 pathcomp[--rootlen] = '\0';
  693.                 had_trailing_pathsep = TRUE;
  694.             }
  695.             if (has_drive && (rootlen == 2)) {
  696.                 if (!had_trailing_pathsep)   /* i.e., original wasn't "x:/" */
  697.                     xtra = 3;      /* room for '.' + '/' + 0 at end of "x:" */
  698.             } else if (rootlen > 0) {     /* need not check "x:." and "x:/" */
  699. #ifdef MSC
  700.                 /* MSC 6.00 bug:  stat(non-existent-dir) == 0 [exists!] */
  701.                 if (_dos_getfileattr(pathcomp, &attrs) ||
  702.                     SSTAT(pathcomp, &statbuf) || !S_ISDIR(statbuf.st_mode))
  703. #else
  704.                 if (SSTAT(pathcomp, &statbuf) || !S_ISDIR(statbuf.st_mode))
  705. #endif
  706.                 {
  707.                     /* path does not exist */
  708.                     if (!create_dirs                 /* || iswild(pathcomp) */
  709. #ifdef OLD_EXDIR
  710.                                      || (!has_drive && !had_trailing_pathsep)
  711. #endif
  712.                                                                              ) {
  713.                         rootlen = 0;
  714.                         return 2;   /* treat as stored file */
  715.                     }
  716. /* GRR:  scan for wildcard characters?  OS-dependent...  if find any, return 2:
  717.  * treat as stored file(s) */
  718.                     /* create directory (could add loop here to scan pathcomp
  719.                      * and create more than one level, but really necessary?) */
  720.                     if (MKDIR(pathcomp, 0777) == -1) {
  721.                         FPRINTF(stderr, LoadFarString(CantCreateExtractDir),
  722.                                 pathcomp);
  723.                         fflush(stderr);
  724.                         rootlen = 0;   /* path didn't exist, tried to create, */
  725.                         return 3;  /* failed:  file exists, or need 2+ levels */
  726.                     }
  727.                 }
  728.             }
  729.             if ((rootpath = (char *)malloc(rootlen+xtra)) == (char *)NULL) {
  730.                 rootlen = 0;
  731.                 return 10;
  732.             }
  733.             strcpy(rootpath, pathcomp);
  734.             if (xtra == 3)                  /* had just "x:", make "x:." */
  735.                 rootpath[rootlen++] = '.';
  736.             rootpath[rootlen++] = '/';
  737.             rootpath[rootlen] = '\0';
  738.         }
  739.         Trace((stderr, "rootpath now = [%s]\n", rootpath));
  740.         return 0;
  741.     }
  742. #endif /* !SFX || SFX_EXDIR */
  743.  
  744. /*---------------------------------------------------------------------------
  745.     END:  free rootpath, immediately prior to program exit.
  746.   ---------------------------------------------------------------------------*/
  747.  
  748.     if (FUNCTION == END) {
  749.         Trace((stderr, "freeing rootpath\n"));
  750.         if (rootlen > 0)
  751.             free(rootpath);
  752.         return 0;
  753.     }
  754.  
  755.     return 99;  /* should never reach */
  756.  
  757. } /* end function checkdir() */
  758.  
  759.  
  760.  
  761.  
  762.  
  763.  
  764. /***********************/
  765. /* Function isfloppy() */
  766. /***********************/
  767.  
  768. static int isfloppy(nDrive)  /* more precisely, is it removable? */
  769.     int nDrive;
  770. {
  771.     union REGS regs;
  772.  
  773.     regs.h.ah = 0x44;
  774.     regs.h.al = 0x08;
  775.     regs.h.bl = (uch)nDrive;
  776. #ifdef __EMX__
  777.     _int86(0x21, ®s, ®s);
  778.     if (regs.x.flags & 1)
  779. #else
  780.     intdos(®s, ®s);
  781.     if (regs.x.cflag)        /* error:  do default a/b check instead */
  782. #endif
  783.     {
  784.         Trace((stderr,
  785.           "error in DOS function 0x44 (AX = 0x%04x):  guessing instead...\n",
  786.           regs.x.ax));
  787.         return (nDrive == 1 || nDrive == 2)? TRUE : FALSE;
  788.     } else
  789.         return regs.x.ax? FALSE : TRUE;
  790. }
  791.  
  792.  
  793.  
  794.  
  795. #if (!defined(__GO32__) && !defined(__EMX__))
  796.  
  797. typedef struct dosfcb {
  798.     uch  flag;        /* ff to indicate extended FCB */
  799.     char res[5];      /* reserved */
  800.     uch  vattr;       /* attribute */
  801.     uch  drive;       /* drive (1=A, 2=B, ...) */
  802.     uch  vn[11];      /* file or volume name */
  803.     char dmmy[5];
  804.     uch  nn[11];      /* holds new name if renaming (else reserved) */
  805.     char dmmy2[9];
  806. } dos_fcb;
  807.  
  808. /**************************/
  809. /* Function volumelabel() */
  810. /**************************/
  811.  
  812. static int volumelabel(newlabel)
  813.     char *newlabel;
  814. {
  815. #ifdef DEBUG
  816.     char *p;
  817. #endif
  818.     int len = strlen(newlabel);
  819.     dos_fcb  fcb, dta, far *pfcb=&fcb, far *pdta=&dta;
  820.     struct SREGS sregs;
  821.     union REGS regs;
  822.  
  823.  
  824. /*---------------------------------------------------------------------------
  825.     Label the diskette specified by nLabelDrive using FCB calls.  (Old ver-
  826.     sions of MS-DOS and OS/2 DOS boxes can't use DOS function 3Ch to create
  827.     labels.)  Must use far pointers for MSC FP_* macros to work; must pad
  828.     FCB filenames with spaces; and cannot include dot in 8th position.  May
  829.     or may not need to zero out FCBs before using; do so just in case.
  830.   ---------------------------------------------------------------------------*/
  831.  
  832.     memset((char *)&dta, 0, sizeof(dos_fcb));
  833.     memset((char *)&fcb, 0, sizeof(dos_fcb));
  834.  
  835. #ifdef DEBUG
  836.     for (p = (char *)&dta; (p - (char *)&dta) < sizeof(dos_fcb); ++p)
  837.         if (*p)
  838.             fprintf(stderr, "error:  dta[%d] = %x\n", (p - (char *)&dta), *p);
  839.     for (p = (char *)&fcb; (p - (char *)&fcb) < sizeof(dos_fcb); ++p)
  840.         if (*p)
  841.             fprintf(stderr, "error:  fcb[%d] = %x\n", (p - (char *)&fcb), *p);
  842.     printf("testing pointer macros:\n");
  843.     segread(&sregs);
  844.     printf("cs = %x, ds = %x, es = %x, ss = %x\n", sregs.cs, sregs.ds, sregs.es,
  845.       sregs.ss);
  846. #endif /* DEBUG */
  847.  
  848. #if 0
  849. #ifdef __TURBOC__
  850.     bdosptr(0x1a, dta, DO_NOT_CARE);
  851. #else
  852.     (intdosx method below)
  853. #endif
  854. #endif /* 0 */
  855.  
  856.     /* set the disk transfer address for subsequent FCB calls */
  857.     sregs.ds = FP_SEG(pdta);
  858.     regs.x.dx = FP_OFF(pdta);
  859.     Trace((stderr, "segment:offset of pdta = %x:%x\n", sregs.ds, regs.x.dx));
  860.     Trace((stderr, "&dta = %lx, pdta = %lx\n", (ulg)&dta, (ulg)pdta));
  861.     regs.h.ah = 0x1a;
  862.     intdosx(®s, ®s, &sregs);
  863.  
  864.     /* fill in the FCB */
  865.     sregs.ds = FP_SEG(pfcb);
  866.     regs.x.dx = FP_OFF(pfcb);
  867.     pfcb->flag = 0xff;          /* extended FCB */
  868.     pfcb->vattr = 0x08;         /* attribute:  disk volume label */
  869.     pfcb->drive = (uch)nLabelDrive;
  870.  
  871. #ifdef DEBUG
  872.     Trace((stderr, "segment:offset of pfcb = %x:%x\n", sregs.ds, regs.x.dx));
  873.     Trace((stderr, "&fcb = %lx, pfcb = %lx\n", (ulg)&fcb, (ulg)pfcb));
  874.     Trace((stderr, "(2nd check:  labelling drive %c:)\n", pfcb->drive-1+'A'));
  875.     if (pfcb->flag != fcb.flag)
  876.         fprintf(stderr, "error:  pfcb->flag = %d, fcb.flag = %d\n",
  877.           pfcb->flag, fcb.flag);
  878.     if (pfcb->drive != fcb.drive)
  879.         fprintf(stderr, "error:  pfcb->drive = %d, fcb.drive = %d\n",
  880.           pfcb->drive, fcb.drive);
  881.     if (pfcb->vattr != fcb.vattr)
  882.         fprintf(stderr, "error:  pfcb->vattr = %d, fcb.vattr = %d\n",
  883.           pfcb->vattr, fcb.vattr);
  884. #endif /* DEBUG */
  885.  
  886.     /* check for existing label */
  887.     Trace((stderr, "searching for existing label via FCBs\n"));
  888.     regs.h.ah = 0x11;      /* FCB find first */
  889. #if 0  /* THIS STRNCPY FAILS (MSC bug?): */
  890.     strncpy(pfcb->vn, "???????????", 11);   /* i.e., "*.*" */
  891.     Trace((stderr, "pfcb->vn = %lx\n", (ulg)pfcb->vn));
  892.     Trace((stderr, "flag = %x, drive = %d, vattr = %x, vn = %s = %s.\n",
  893.       fcb.flag, fcb.drive, fcb.vattr, fcb.vn, pfcb->vn));
  894. #endif
  895.     strncpy((char *)fcb.vn, "???????????", 11);   /* i.e., "*.*" */
  896.     Trace((stderr, "fcb.vn = %lx\n", (ulg)fcb.vn));
  897.     Trace((stderr, "regs.h.ah = %x, regs.x.dx = %04x, sregs.ds = %04x\n",
  898.       regs.h.ah, regs.x.dx, sregs.ds));
  899.     Trace((stderr, "flag = %x, drive = %d, vattr = %x, vn = %s = %s.\n",
  900.       fcb.flag, fcb.drive, fcb.vattr, fcb.vn, pfcb->vn));
  901.     intdosx(®s, ®s, &sregs);
  902.  
  903. /*---------------------------------------------------------------------------
  904.     If not previously labelled, write a new label.  Otherwise just rename,
  905.     since MS-DOS 2.x has a bug which damages the FAT when the old label is
  906.     deleted.
  907.   ---------------------------------------------------------------------------*/
  908.  
  909.     if (regs.h.al) {
  910.         Trace((stderr, "no label found\n\n"));
  911.         regs.h.ah = 0x16;                 /* FCB create file */
  912.         strncpy((char *)fcb.vn, newlabel, len);
  913.         if (len < 11)   /* fill with spaces */
  914.             strncpy((char *)(fcb.vn+len), "           ", 11-len);
  915.         Trace((stderr, "fcb.vn = %lx  pfcb->vn = %lx\n", (ulg)fcb.vn,
  916.           (ulg)pfcb->vn));
  917.         Trace((stderr, "flag = %x, drive = %d, vattr = %x\n", fcb.flag,
  918.           fcb.drive, fcb.vattr));
  919.         Trace((stderr, "vn = %s = %s.\n", fcb.vn, pfcb->vn));
  920.         intdosx(®s, ®s, &sregs);
  921.         regs.h.ah = 0x10;                 /* FCB close file */
  922.         if (regs.h.al) {
  923.             Trace((stderr, "unable to write volume name (AL = %x)\n",
  924.               regs.h.al));
  925.             intdosx(®s, ®s, &sregs);
  926.             return 1;
  927.         } else {
  928.             intdosx(®s, ®s, &sregs);
  929.             Trace((stderr, "new volume label [%s] written\n", newlabel));
  930.             return 0;
  931.         }
  932.     } else {
  933.         Trace((stderr, "found old label [%s]\n\n", dta.vn));  /* not term. */
  934.         regs.h.ah = 0x17;                 /* FCB rename */
  935.         strncpy((char *)fcb.vn, (char *)dta.vn, 11);
  936.         strncpy((char *)fcb.nn, newlabel, len);
  937.         if (len < 11)                     /* fill with spaces */
  938.             strncpy((char *)(fcb.nn+len), "           ", 11-len);
  939.         Trace((stderr, "fcb.vn = %lx  pfcb->vn = %lx\n", (ulg)fcb.vn,
  940.           (ulg)pfcb->vn));
  941.         Trace((stderr, "fcb.nn = %lx  pfcb->nn = %lx\n", (ulg)fcb.nn,
  942.           (ulg)pfcb->nn));
  943.         Trace((stderr, "flag = %x, drive = %d, vattr = %x\n", fcb.flag,
  944.           fcb.drive, fcb.vattr));
  945.         Trace((stderr, "vn = %s = %s.\n", fcb.vn, pfcb->vn));
  946.         Trace((stderr, "nn = %s = %s.\n", fcb.nn, pfcb->nn));
  947.         intdosx(®s, ®s, &sregs);
  948.         if (regs.h.al) {
  949.             Trace((stderr, "Unable to change volume name (AL = %x)\n",
  950.               regs.h.al));
  951.             return 1;
  952.         } else {
  953.             Trace((stderr, "volume label changed to [%s]\n", newlabel));
  954.             return 0;
  955.         }
  956.     }
  957. } /* end function volumelabel() */
  958.  
  959. #endif /* !__GO32__ && !__EMX__ */
  960.  
  961.  
  962.  
  963.  
  964.  
  965. /****************************/
  966. /* Function close_outfile() */
  967. /****************************/
  968.  
  969. void close_outfile()
  970.  /*
  971.   * MS-DOS VERSION
  972.   *
  973.   * Set the output file date/time stamp according to information from the
  974.   * zipfile directory record for this member, then close the file and set
  975.   * its permissions (archive, hidden, read-only, system).  Aside from closing
  976.   * the file, this routine is optional (but most compilers support it).
  977.   */
  978. {
  979. #ifdef __TURBOC__
  980.     union {
  981.         struct ftime ft;        /* system file time record */
  982.         struct {
  983.             ush ztime;          /* date and time words */
  984.             ush zdate;          /* .. same format as in .ZIP file */
  985.         } zt;
  986.     } td;
  987. #endif
  988.  
  989.  
  990. /*---------------------------------------------------------------------------
  991.     Copy and/or convert time and date variables, if necessary; then set the
  992.     file time/date.  WEIRD BORLAND "BUG":  if output is buffered, and if run
  993.     under at least some versions of DOS (e.g., 6.0), and if files are smaller
  994.     than DOS physical block size (i.e., 512 bytes) (?), then files MAY NOT
  995.     get timestamped correctly--apparently setftime() occurs before any data
  996.     are written to the file, and when file is closed and buffers are flushed,
  997.     timestamp is overwritten with current time.  Even with a 32K buffer, this
  998.     does not seem to occur with larger files.  UnZip output is now unbuffered,
  999.     but if it were not, could still avoid problem by adding "fflush(outfile)"
  1000.     just before setftime() call.  Weird, huh?
  1001.   ---------------------------------------------------------------------------*/
  1002.  
  1003. #ifdef __TURBOC__
  1004.     td.zt.ztime = lrec.last_mod_file_time;
  1005.     td.zt.zdate = lrec.last_mod_file_date;
  1006.     setftime(fileno(outfile), &td.ft);
  1007. #else
  1008.     _dos_setftime(fileno(outfile), lrec.last_mod_file_date,
  1009.                                    lrec.last_mod_file_time);
  1010. #endif
  1011.  
  1012. /*---------------------------------------------------------------------------
  1013.     And finally we can close the file...at least everybody agrees on how to
  1014.     do *this*.  I think...  Also change the mode according to the stored file
  1015.     attributes, since we didn't do that when we opened the dude.
  1016.   ---------------------------------------------------------------------------*/
  1017.  
  1018.     fclose(outfile);
  1019.  
  1020. #ifdef __TURBOC__
  1021. #   if (defined(__BORLANDC__) && (__BORLANDC__ >= 0x0452))
  1022. #     define Chmod  _rtl_chmod
  1023. #   else
  1024. #     define Chmod  _chmod
  1025. #   endif
  1026.     if (Chmod(filename, 1, pInfo->file_attr) != pInfo->file_attr)
  1027.         FPRINTF(stderr, "\nwarning:  file attributes may not be correct\n");
  1028. #else /* !__TURBOC__ */
  1029.     _dos_setfileattr(filename, pInfo->file_attr);
  1030. #endif /* ?__TURBOC__ */
  1031.  
  1032. } /* end function close_outfile() */
  1033.  
  1034.  
  1035.  
  1036.  
  1037.  
  1038. #ifndef SFX
  1039.  
  1040. /*************************/
  1041. /* Function dateformat() */
  1042. /*************************/
  1043.  
  1044. int dateformat()
  1045. {
  1046.  
  1047. /*---------------------------------------------------------------------------
  1048.     For those operating systems which support it, this function returns a
  1049.     value which tells how national convention says that numeric dates are
  1050.     displayed.  Return values are DF_YMD, DF_DMY and DF_MDY (the meanings
  1051.     should be fairly obvious).
  1052.   ---------------------------------------------------------------------------*/
  1053.  
  1054. #ifndef MSWIN
  1055. #if (!defined(__GO32__) && !defined(__EMX__))
  1056.     unsigned short _CountryInfo[18];
  1057.     unsigned short far *CountryInfo = _CountryInfo;
  1058.     struct SREGS sregs;
  1059.     union REGS regs;
  1060.  
  1061.     sregs.ds  = FP_SEG(CountryInfo);
  1062.     regs.x.dx = FP_OFF(CountryInfo);
  1063.     regs.x.ax = 0x3800;
  1064.     int86x(0x21, ®s, ®s, &sregs);
  1065.  
  1066. #else /* __GO32__ || __EMX__ */
  1067.     unsigned short CountryInfo[18];
  1068.  
  1069.     _dos_getcountryinfo(CountryInfo);
  1070. #endif
  1071.  
  1072.     switch(CountryInfo[0]) {
  1073.         case 0:
  1074.             return DF_MDY;
  1075.         case 1:
  1076.             return DF_DMY;
  1077.         case 2:
  1078.             return DF_YMD;
  1079.     }
  1080. #endif /* !MSWIN */
  1081.  
  1082.     return DF_MDY;   /* default for systems without locale info */
  1083.  
  1084. } /* end function dateformat() */
  1085.  
  1086.  
  1087.  
  1088.  
  1089.  
  1090. /************************/
  1091. /*  Function version()  */
  1092. /************************/
  1093.  
  1094. void version()
  1095. {
  1096.     extern char Far  CompiledWith[];
  1097. #if defined(__WATCOMC__) || defined(__TURBOC__) || defined(_MSC_VER)
  1098.     char buf[80];
  1099. #endif
  1100.  
  1101.     PRINTF(LoadFarString(CompiledWith),
  1102.  
  1103. #ifdef __GNUC__
  1104. #  ifdef __EMX__      /* __EMX__ is defined as "1" only (sigh) */
  1105.       "emx+gcc ",
  1106. #  else
  1107. #  ifdef __GO32__     /* ...so is __GO32__ (double sigh) */
  1108.       "djgpp gcc ",
  1109. #  else
  1110.       "gcc ",
  1111. #  endif
  1112. #  endif
  1113.       __VERSION__,
  1114. #else
  1115. #ifdef __WATCOMC__
  1116.       "Watcom C", (sprintf(buf, " (__WATCOMC__ = %d)", __WATCOMC__), buf),
  1117. #else
  1118. #ifdef __TURBOC__
  1119. #  ifdef __BORLANDC__
  1120.       "Borland C++",
  1121. #    if (__BORLANDC__ < 0x0200)
  1122.         " 1.0",
  1123. #    else
  1124. #    if (__BORLANDC__ == 0x0200)   /* James:  __TURBOC__ = 0x0297 */
  1125.         " 2.0",
  1126. #    else
  1127. #    if (__BORLANDC__ == 0x0400)
  1128.         " 3.0",
  1129. #    else
  1130. #    if (__BORLANDC__ == 0x0410)   /* __BCPLUSPLUS__ = 0x0310 */
  1131.         " 3.1",
  1132. #    else
  1133. #    if (__BORLANDC__ == 0x0452)   /* __BCPLUSPLUS__ = 0x0320 */
  1134.         " 4.0 or 4.02",
  1135. #    else
  1136.         " later than 4.1",
  1137. #    endif
  1138. #    endif
  1139. #    endif
  1140. #    endif
  1141. #    endif
  1142. #  else
  1143.       "Turbo C",
  1144. #    if (__TURBOC__ >= 0x0400)     /* Kevin:  3.0 -> 0x0401 */
  1145.         "++ 3.0 or later",
  1146. #    else
  1147. #    if (__TURBOC__ == 0x0295)     /* [661] vfy'd by Kevin */
  1148.         "++ 1.0",
  1149. #    else
  1150. #    if ((__TURBOC__ >= 0x018d) && (__TURBOC__ <= 0x0200))  /* James: 0x0200 */
  1151.         " 2.0",
  1152. #    else
  1153. #    if (__TURBOC__ > 0x0100)
  1154.         " 1.5",                    /* James:  0x0105? */
  1155. #    else
  1156.         " 1.0",                    /* James:  0x0100 */
  1157. #    endif
  1158. #    endif
  1159. #    endif
  1160. #    endif
  1161. #  endif
  1162. #else
  1163. #ifdef MSC
  1164.       "Microsoft C ",
  1165. #  ifdef _MSC_VER
  1166. #    if (_MSC_VER == 800)
  1167.         "8.0/8.0c (Visual C++ 1.0/1.5)",
  1168. #    else
  1169.         (sprintf(buf, "%d.%02d", _MSC_VER/100, _MSC_VER%100), buf),
  1170. #    endif
  1171. #  else
  1172.       "5.1 or earlier",
  1173. #  endif
  1174. #else
  1175.       "unknown compiler", "",
  1176. #endif /* MSC */
  1177. #endif /* __TURBOC__ */
  1178. #endif /* __WATCOMC__ */
  1179. #endif /* __GNUC__ */
  1180.  
  1181.       "MS-DOS",
  1182.  
  1183. #if (defined(__GNUC__) || (defined(__WATCOMC__) && defined(__386__)))
  1184.       " (32-bit)",
  1185. #else
  1186. #  if defined(M_I86HM) || defined(__HUGE__)
  1187.       " (16-bit, huge)",
  1188. #  else
  1189. #  if defined(M_I86LM) || defined(__LARGE__)
  1190.       " (16-bit, large)",
  1191. #  else
  1192. #  if defined(M_I86MM) || defined(__MEDIUM__)
  1193.       " (16-bit, medium)",
  1194. #  else
  1195. #  if defined(M_I86CM) || defined(__COMPACT__)
  1196.       " (16-bit, compact)",
  1197. #  else
  1198. #  if defined(M_I86SM) || defined(__SMALL__)
  1199.       " (16-bit, small)",
  1200. #  else
  1201. #  if defined(M_I86TM) || defined(__TINY__)
  1202.       " (16-bit, tiny)",
  1203. #  else
  1204.       " (16-bit)",
  1205. #  endif
  1206. #  endif
  1207. #  endif
  1208. #  endif
  1209. #  endif
  1210. #  endif
  1211. #endif
  1212.  
  1213. #ifdef __DATE__
  1214.       " on ", __DATE__
  1215. #else
  1216.       "", ""
  1217. #endif
  1218.       );
  1219.  
  1220.     /* temporary debugging code for Borland compilers only */
  1221. #ifdef __TURBOC__
  1222.     PRINTF("\tdebug(__TURBOC__ = 0x%04x = %d)\n", __TURBOC__, __TURBOC__);
  1223. #ifdef __BORLANDC__
  1224.     PRINTF("\tdebug(__BORLANDC__ = 0x%04x)\n", __BORLANDC__);
  1225. #else
  1226.     PRINTF("\tdebug(__BORLANDC__ not defined)\n");
  1227. #endif
  1228. #ifdef __TCPLUSPLUS__
  1229.     PRINTF("\tdebug(__TCPLUSPLUS__ = 0x%04x)\n", __TCPLUSPLUS__);
  1230. #else
  1231.     PRINTF("\tdebug(__TCPLUSPLUS__ not defined)\n");
  1232. #endif
  1233. #ifdef __BCPLUSPLUS__
  1234.     PRINTF("\tdebug(__BCPLUSPLUS__ = 0x%04x)\n\n", __BCPLUSPLUS__);
  1235. #else
  1236.     PRINTF("\tdebug(__BCPLUSPLUS__ not defined)\n\n");
  1237. #endif
  1238. #endif
  1239.  
  1240. } /* end function version() */
  1241.  
  1242. #endif /* !SFX */
  1243.  
  1244.  
  1245.  
  1246.  
  1247.  
  1248. #if (defined(__GO32__) || defined(__EMX__))
  1249.  
  1250. int volatile _doserrno;
  1251.  
  1252. unsigned _dos_getcountryinfo(void *countrybuffer)
  1253. {
  1254.     asm("movl %0, %%edx": : "g" (countrybuffer));
  1255.     asm("movl $0x3800, %eax");
  1256.     asm("int $0x21": : : "%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi");
  1257.     _doserrno = 0;
  1258.     asm("jnc 1f");
  1259.     asm("movl %%eax, %0": "=m" (_doserrno));
  1260.     asm("1:");
  1261.     return _doserrno;
  1262. }
  1263.  
  1264. void _dos_setftime(int fd, ush dosdate, ush dostime)
  1265. {
  1266.     asm("movl %0, %%ebx": : "g" (fd));
  1267.     asm("movl %0, %%ecx": : "g" (dostime));
  1268.     asm("movl %0, %%edx": : "g" (dosdate));
  1269.     asm("movl $0x5701, %eax");
  1270.     asm("int $0x21": : : "%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi");
  1271. }
  1272.  
  1273. void _dos_setfileattr(char *name, int attr)
  1274. {
  1275.     asm("movl %0, %%edx": : "g" (name));
  1276.     asm("movl %0, %%ecx": : "g" (attr));
  1277.     asm("movl $0x4301, %eax");
  1278.     asm("int $0x21": : : "%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi");
  1279. }
  1280.  
  1281. void _dos_getdrive(unsigned *d)
  1282. {
  1283.     asm("movl $0x1900, %eax");
  1284.     asm("int $0x21": : : "%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi");
  1285.     asm("xorb %ah, %ah");
  1286.     asm("incb %al");
  1287.     asm("movl %%eax, %0": "=a" (*d));
  1288. }
  1289.  
  1290. unsigned _dos_creat(char *path, unsigned attr, int *fd)
  1291. {
  1292.     asm("movl $0x3c00, %eax");
  1293.     asm("movl %0, %%edx": :"g" (path));
  1294.     asm("movl %0, %%ecx": :"g" (attr));
  1295.     asm("int $0x21": : : "%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi");
  1296.     asm("movl %%eax, %0": "=a" (*fd));
  1297.     _doserrno = 0;
  1298.     asm("jnc 1f");
  1299.     _doserrno = *fd;
  1300.     switch (_doserrno) {
  1301.     case 3:
  1302.            errno = ENOENT;
  1303.            break;
  1304.     case 4:
  1305.            errno = EMFILE;
  1306.            break;
  1307.     case 5:
  1308.            errno = EACCES;
  1309.            break;
  1310.     }
  1311.     asm("1:");
  1312.     return _doserrno;
  1313. }
  1314.  
  1315. unsigned _dos_close(int fd)
  1316. {
  1317.     asm("movl %0, %%ebx": : "g" (fd));
  1318.     asm("movl $0x3e00, %eax");
  1319.     asm("int $0x21": : : "%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi");
  1320.     _doserrno = 0;
  1321.     asm("jnc 1f");
  1322.     asm ("movl %%eax, %0": "=m" (_doserrno));
  1323.     if (_doserrno == 6) {
  1324.           errno = EBADF;
  1325.     }
  1326.     asm("1:");
  1327.     return _doserrno;
  1328. }
  1329.  
  1330. static int volumelabel(char *name)
  1331. {
  1332.     int fd;
  1333.  
  1334.     return _dos_creat(name, FA_LABEL, &fd) ? fd : _dos_close(fd);
  1335. }
  1336.  
  1337. #endif /* __GO32__ || __EMX__ */
  1338.