home *** CD-ROM | disk | FTP | other *** search
/ Monster Media 1994 #1 / monster.zip / monster / WIN_NT / NTUNZ2.ZIP / NT.C < prev    next >
C/C++ Source or Header  |  1993-12-19  |  41KB  |  1,097 lines

  1. /*---------------------------------------------------------------------------
  2.  
  3.   nt.c
  4.  
  5.   WinNT-specific routines for use with Info-ZIP's UnZip 5.1 and later.
  6.  
  7.  
  8.   1993-09-28 Henry Gessau <henryg@kullmar.kullmar.se>
  9.   Started hacking at this. Borrowed, pilfered and plundered code from OS/2
  10.   and MS-DOS versions and from ZIP, modifying as necessary.
  11.  
  12.   Last updated: 19 Dec 1993
  13.  
  14.   ---------------------------------------------------------------------------*/
  15.  
  16. #include <windows.h>
  17. #include "unzip.h"
  18.  
  19.  
  20. #define MKDIR(path,mode)   mkdir(path)
  21.  
  22. struct direct
  23. {
  24.     char    reserved [21];
  25.     char    ff_attrib;
  26.     short   ff_ftime;
  27.     short   ff_fdate;
  28.     long    size;
  29.     char    d_name[MAX_PATH];
  30.     int     d_first;
  31.     HANDLE  d_hFindFile;
  32. };
  33.  
  34.  
  35. static int created_dir;         /* used by mapname(), checkdir() */
  36. static int renamed_fullpath;    /* ditto */
  37. static int fnlen;               /* ditto */
  38. static unsigned nLabelDrive;    /* ditto */
  39.  
  40.  
  41.  
  42. /**********************/        /* Borrowed from ZIP 2.0 sources             */
  43. /* Function opendir() */        /* Difference: no special handling for       */
  44. /**********************/        /*             hidden or system files.       */
  45.  
  46. static struct direct *opendir(n)
  47.     const char *n;  /* directory to open */
  48. {
  49.     struct direct *d;       /* malloc'd return value */
  50.     char *p;                /* malloc'd temporary string */
  51.     WIN32_FIND_DATA fd;
  52.  
  53.     /* Start searching for files in the MSDOS directory n */
  54.  
  55.     if ( (d = (struct direct *)malloc(sizeof(struct direct))) == NULL ||
  56.          (p = malloc(strlen(n) + 5)) == NULL                             )
  57.     {
  58.         if ( d != NULL )
  59.             free((void *) d);
  60.         return NULL;
  61.     }
  62.     strcat(strcpy(p, n), "/*.*");
  63.  
  64.     if ( INVALID_HANDLE_VALUE == (d->d_hFindFile = FindFirstFile(p, &fd)) )
  65.     {
  66.         free((voidp *)d);
  67.         free((voidp *)p);
  68.         return NULL;
  69.     }
  70.     strcpy(d->d_name, fd.cFileName);
  71.  
  72.     free((voidp *)p);
  73.     d->d_first = 1;
  74.     return d;
  75.  
  76. } /* end of function opendir() */
  77.  
  78.  
  79.  
  80.  
  81. /**********************/        /* Borrowed from ZIP 2.0 sources             */
  82. /* Function readdir() */        /* Difference: no special handling for       */
  83. /**********************/        /*             hidden or system files.       */
  84.  
  85. static struct direct *readdir(d)
  86.     struct direct *d;         /* directory stream from which to read */
  87. {
  88.     /* Return pointer to first or next directory entry, or NULL if end. */
  89.  
  90.     if ( d->d_first )
  91.         d->d_first = 0;
  92.     else
  93.     {
  94.         WIN32_FIND_DATA fd;
  95.  
  96.         if ( !FindNextFile(d->d_hFindFile, &fd) )
  97.             return NULL;
  98.  
  99.         strcpy(d->d_name, fd.cFileName);
  100.     }
  101.     return (struct direct *)d;
  102.  
  103. } /* end of function readdir() */
  104.  
  105.  
  106.  
  107.  
  108. /***********************/
  109. /* Function closedir() */       /* Borrowed from ZIP 2.0 sources */
  110. /***********************/
  111.  
  112. static void closedir(d)
  113.     struct direct *d;         /* directory stream to close */
  114. {
  115.     FindClose(d->d_hFindFile);
  116.     free(d);
  117. }
  118.  
  119.  
  120.  
  121.  
  122. /**********************/
  123. /* Function mapattr() */
  124. /**********************/
  125.  
  126. /* Identical to MS-DOS, OS/2 versions.                                       */
  127. /* However, NT has a lot of extra permission stuff, so this function should  */
  128. /*  probably be extended in the future.                                      */
  129.  
  130. int mapattr()
  131. {
  132.     /* set archive bit (file is not backed up): */
  133.     pInfo->file_attr = (unsigned)(crec.external_file_attributes | 32) & 0xff;
  134.     return 0;
  135.  
  136. } /* end function mapattr() */
  137.  
  138.  
  139.  
  140.  
  141. /****************************/      /* Get the file time in a format that */
  142. /* Function getNTfiletime() */      /* can be used by SetFileTime() in NT */
  143. /****************************/
  144.  
  145. int getNTfiletime(FILETIME *ft)
  146. {
  147.     FILETIME lft;      /* 64-bit value made up of two 32 bit [low & high] */
  148.     WORD wDOSDate;     /* for converting from DOS date to Windows NT      */
  149.     WORD wDOSTime;
  150.  
  151.     /* Copy and/or convert time and date variables, if necessary;   */
  152.     /* then set the file time/date.                                 */
  153.     wDOSTime = (WORD)lrec.last_mod_file_time;
  154.     wDOSDate = (WORD)lrec.last_mod_file_date;
  155.  
  156.     /* The DosDateTimeToFileTime() function converts a DOS date/time    */
  157.     /* into a 64 bit Windows NT file time                               */
  158.     if (!DosDateTimeToFileTime(wDOSDate, wDOSTime, &lft))
  159.     {
  160.         printf("DosDateTime failed: %d\n", GetLastError());
  161.         return FALSE;
  162.     }
  163.     if (!LocalFileTimeToFileTime( &lft, ft))
  164.     {
  165.         printf("LocalFileTime failed: %d\n", GetLastError());
  166.         *ft = lft;
  167.     }
  168.     return TRUE;
  169. }
  170.  
  171.  
  172.  
  173.  
  174. /****************************/
  175. /* Function close_outfile() */
  176. /****************************/
  177.  
  178. void close_outfile()
  179. {
  180.     FILETIME ft;       /* File time type defined in NT */
  181.     HANDLE hFile;      /* File handle defined in NT    */
  182.     int gotTime;
  183.  
  184.     /* don't set the time stamp on standard output */
  185.     if (cflag) {
  186.         fclose(outfile);
  187.         return;
  188.     }
  189.  
  190.     gotTime = getNTfiletime(&ft);
  191.  
  192.     /* Close the file and then re-open it using the Win32
  193.      * CreateFile call, so that the file can be created
  194.      * with GENERIC_WRITE access, otherwise the SetFileTime
  195.      * call will fail. */
  196.     fclose(outfile);
  197.  
  198.     hFile = CreateFile(filename, GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
  199.          FILE_ATTRIBUTE_NORMAL, NULL);
  200.     if ( hFile == INVALID_HANDLE_VALUE ) {
  201.         fprintf(stderr, "\nCreateFile error %d when trying set file time\n",
  202.                 GetLastError());
  203.     }
  204.     else {
  205.         if (gotTime)
  206.             if (!SetFileTime(hFile, NULL, NULL, &ft))
  207.                 printf("\nSetFileTime failed: %d\n", GetLastError());
  208.         CloseHandle(hFile);
  209.     }
  210.  
  211.     /* HG: I think this could be done in the CreateFile call above - just  */
  212.     /*     replace 'FILE_ATTRIBUTE_NORMAL' with 'pInfo->file_attr & 0x7F'  */
  213.     if (!SetFileAttributes(filename, pInfo->file_attr & 0x7F))
  214.         fprintf(stderr, "\nwarning (%d): could not set file attributes\n",
  215.                 GetLastError());
  216.  
  217.     return;
  218.  
  219. } /* end function close_outfile() */
  220.  
  221.  
  222.  
  223. /* ============================================================================
  224. */
  225.  
  226.  
  227.  
  228. /***********************/
  229. /* Function isfloppy() */   /* more precisely, is it removable? */
  230. /***********************/
  231.  
  232. static int isfloppy(int nDrive)   /* 1 == A:, 2 == B:, etc. */
  233. {
  234.     char rootPathName[4];
  235.  
  236.     rootPathName[0] = 'A' + nDrive - 1;   /* Build the root path name, */
  237.     rootPathName[1] = ':';                /* e.g. "A:/"                */
  238.     rootPathName[2] = '/';
  239.     rootPathName[3] = '\0';
  240.  
  241.     if ( GetDriveType(rootPathName) == DRIVE_REMOVABLE )
  242.         return TRUE;
  243.     else
  244.         return FALSE;
  245.  
  246. } /* end function isfloppy() */
  247.  
  248.  
  249.  
  250.  
  251. /**************************/
  252. /* Function IsVolumeFAT() */
  253. /**************************/
  254.  
  255. static int IsVolumeFAT(char *name)
  256. {
  257.     char     *tmp0;
  258.     char      rootPathName[4];
  259.     char      tmp1[MAX_PATH], tmp2[MAX_PATH];
  260.     unsigned  volSerNo, maxCompLen, fileSysFlags;
  261.  
  262.     if (isalpha(name[0]) && (name[1] == ':'))
  263.         tmp0 = name;
  264.     else
  265.     {
  266.         GetFullPathName(name, MAX_PATH, tmp1, &tmp0);
  267.         tmp0 = &tmp1[0];
  268.     }
  269.     strncpy(rootPathName, tmp0, 3);   /* Build the root path name, */
  270.     rootPathName[3] = '\0';           /* e.g. "A:/"                */
  271.  
  272.     GetVolumeInformation(rootPathName, tmp1, MAX_PATH, &volSerNo,
  273.                          &maxCompLen, &fileSysFlags, tmp2, MAX_PATH);
  274.  
  275.     if ( !strncmp(strupr(tmp2), "FAT", 3) )
  276.         return TRUE;
  277.     else
  278.         return FALSE;
  279. }
  280.  
  281.  
  282.  
  283.  
  284. /******************************/
  285. /* Function IsFileNameValid() */
  286. /******************************/
  287.  
  288. static int IsFileNameValid(char *name)
  289. {
  290.     HFILE    hf;
  291.     OFSTRUCT of;
  292.  
  293.     hf = OpenFile(name, &of, OF_READ | OF_SHARE_DENY_NONE);
  294.     if (hf == HFILE_ERROR)
  295.         switch (GetLastError())
  296.         {
  297.             case ERROR_INVALID_NAME:
  298.             case ERROR_FILENAME_EXCED_RANGE:
  299.                 return FALSE;
  300.             default:
  301.                 return TRUE;
  302.         }
  303.     else
  304.         _lclose(hf);
  305.     return TRUE;
  306. }
  307.  
  308.  
  309.  
  310.  
  311. /**********************/
  312. /* Function map2fat() */        /* Identical to OS/2 version */
  313. /**********************/
  314.  
  315. void map2fat(pathcomp, pEndFAT)
  316.     char *pathcomp, **pEndFAT;
  317. {
  318.     char *ppc = pathcomp;       /* variable pointer to pathcomp */
  319.     char *pEnd = *pEndFAT;      /* variable pointer to buildpathFAT */
  320.     char *pBegin = *pEndFAT;    /* constant pointer to start of this comp. */
  321.     char *last_dot = NULL;      /* last dot not converted to underscore */
  322.     int dotname = FALSE;        /* flag:  path component begins with dot */
  323.                                 /*  ("." and ".." don't count) */
  324.     register unsigned workch;   /* hold the character being tested */
  325.  
  326.  
  327.     /* Only need check those characters which are legal in HPFS but not
  328.      * in FAT:  to get here, must already have passed through mapname.
  329.      * (GRR:  oops, small bug--if char was quoted, no longer have any
  330.      * knowledge of that.)  Also must truncate path component to ensure
  331.      * 8.3 compliance...
  332.      */
  333.     while ((workch = (uch)*ppc++) != 0) {
  334.         switch (workch) {
  335.             case '[':
  336.             case ']':
  337.                 *pEnd++ = '_';      /* convert brackets to underscores */
  338.                 break;
  339.  
  340.             case '.':
  341.                 if (pEnd == *pEndFAT) {   /* nothing appended yet... */
  342.                     if (*ppc == '\0')     /* don't bother appending a */
  343.                         break;            /*  "./" component to the path */
  344.                     else if (*ppc == '.' && ppc[1] == '\0') {   /* "../" */
  345.                         *pEnd++ = '.';    /* add first dot, unchanged... */
  346.                         ++ppc;            /* skip second dot, since it will */
  347.                     } else {              /*  be "added" at end of if-block */
  348.                         *pEnd++ = '_';    /* FAT doesn't allow null filename */
  349.                         dotname = TRUE;   /*  bodies, so map .exrc -> _.exrc */
  350.                     }                     /*  (extra '_' now, "dot" below) */
  351.                 } else if (dotname) {     /* found a second dot, but still */
  352.                     dotname = FALSE;      /*  have extra leading underscore: */
  353.                     *pEnd = '\0';         /*  remove it by shifting chars */
  354.                     pEnd = *pEndFAT + 1;  /*  left one space (e.g., .p1.p2: */
  355.                     while (pEnd[1]) {     /*  __p1 -> _p1_p2 -> _p1.p2 when */
  356.                         *pEnd = pEnd[1];  /*  finished) [opt.:  since first */
  357.                         ++pEnd;           /*  two chars are same, can start */
  358.                     }                     /*  shifting at second position] */
  359.                 }
  360.                 last_dot = pEnd;    /* point at last dot so far... */
  361.                 *pEnd++ = '_';      /* convert dot to underscore for now */
  362.                 break;
  363.  
  364.             default:
  365.                 *pEnd++ = (char)workch;
  366.  
  367.         } /* end switch */
  368.     } /* end while loop */
  369.  
  370.     *pEnd = '\0';                 /* terminate buildpathFAT */
  371.  
  372.     /* NOTE:  keep in mind that pEnd points to the end of the path
  373.      * component, and *pEndFAT still points to the *beginning* of it...
  374.      * Also note that the algorithm does not try to get too fancy:
  375.      * if there are no dots already, the name either gets truncated
  376.      * at 8 characters or the last underscore is converted to a dot
  377.      * (only if more characters are saved that way).  In no case is
  378.      * a dot inserted between existing characters.
  379.      */
  380.     if (last_dot == NULL) {       /* no dots:  check for underscores... */
  381.         char *plu = strrchr(pBegin, '_');   /* pointer to last underscore */
  382.  
  383.         if (plu == NULL) {   /* no dots, no underscores:  truncate at 8 chars */
  384.             *pEndFAT += 8;        /* (or could insert '.' and keep 11...?) */
  385.             if (*pEndFAT > pEnd)
  386.                 *pEndFAT = pEnd;  /* oops...didn't have 8 chars to truncate */
  387.             else
  388.                 **pEndFAT = '\0';
  389.         } else if (MIN(plu - pBegin, 8) + MIN(pEnd - plu - 1, 3) > 8) {
  390.             last_dot = plu;       /* be lazy:  drop through to next if-blk */
  391.         } else if ((pEnd - *pEndFAT) > 8) {
  392.             *pEndFAT += 8;        /* more fits into just basename than if */
  393.             **pEndFAT = '\0';     /*  convert last underscore to dot */
  394.         } else
  395.             *pEndFAT = pEnd;      /* whole thing fits into 8 chars or less */
  396.     }
  397.  
  398.     if (last_dot != NULL) {       /* one dot (or two, in the case of */
  399.         *last_dot = '.';          /*  "..") is OK:  put it back in */
  400.  
  401.         if ((last_dot - pBegin) > 8) {
  402.             char *p=last_dot, *q=pBegin+8;
  403.             int i;
  404.  
  405.             for (i = 0;  (i < 4) && *p;  ++i)  /* too many chars in basename: */
  406.                 *q++ = *p++;                   /*  shift .ext left and trun- */
  407.             *q = '\0';                         /*  cate/terminate it */
  408.             *pEndFAT = q;
  409.         } else if ((pEnd - last_dot) > 4) {    /* too many chars in extension */
  410.             *pEndFAT = last_dot + 4;
  411.             **pEndFAT = '\0';
  412.         } else
  413.             *pEndFAT = pEnd;   /* filename is fine; point at terminating zero */
  414.     }
  415. } /* end function map2fat() */
  416.  
  417.  
  418.  
  419.  
  420. /***********************/       /* Borrowed from OS2.C for UNZIP 5.1.        */
  421. /* Function checkdir() */       /* Difference: no EA stuff                   */
  422. /***********************/       /*             HPFS stuff works on NTFS too  */
  423.  
  424. int checkdir(pathcomp, flag)
  425.     char *pathcomp;
  426.     int flag;
  427. /*
  428.  * returns:  1 - (on APPEND_NAME) truncated filename
  429.  *           2 - path doesn't exist, not allowed to create
  430.  *           3 - path doesn't exist, tried to create and failed; or
  431.  *               path exists and is not a directory, but is supposed to be
  432.  *           4 - path is too long
  433.  *          10 - can't allocate memory for filename buffers
  434.  */
  435. {
  436.     static int rootlen = 0;      /* length of rootpath */
  437.     static char *rootpath;       /* user's "extract-to" directory */
  438.     static char *buildpathHPFS;  /* full path (so far) to extracted file, */
  439.     static char *buildpathFAT;   /*  both HPFS/EA (main) and FAT versions */
  440.     static char *endHPFS;        /* corresponding pointers to end of */
  441.     static char *endFAT;         /*  buildpath ('\0') */
  442.  
  443. #   define FN_MASK   7
  444. #   define FUNCTION  (flag & FN_MASK)
  445.  
  446.  
  447.  
  448. /*---------------------------------------------------------------------------
  449.     APPEND_DIR:  append the path component to the path being built and check
  450.     for its existence.  If doesn't exist and we are creating directories, do
  451.     so for this one; else signal success or error as appropriate.
  452.   ---------------------------------------------------------------------------*/
  453.  
  454.     if (FUNCTION == APPEND_DIR) {
  455.         char *p = pathcomp;
  456. #ifdef OS2
  457.         int longdirEA;
  458. #endif
  459.         int too_long=FALSE;
  460.  
  461.         Trace((stderr, "appending dir segment [%s]\n", pathcomp));
  462.         while ((*endHPFS = *p++) != '\0')     /* copy to HPFS filename */
  463.             ++endHPFS;
  464.         if (IsFileNameValid(buildpathHPFS)) {
  465. #ifdef OS2
  466.             longdirEA = FALSE;
  467. #endif
  468.             p = pathcomp;
  469.             while ((*endFAT = *p++) != '\0')  /* copy to FAT filename, too */
  470.                 ++endFAT;
  471.         } else {
  472. #ifdef OS2
  473.             longdirEA = TRUE;
  474. #endif
  475. /* GRR:  check error return? */
  476.             map2fat(pathcomp, &endFAT);  /* map, put in FAT fn, update endFAT */
  477.         }
  478.  
  479.         /* GRR:  could do better check, see if overrunning buffer as we go:
  480.          * check endHPFS-buildpathHPFS after each append, set warning variable
  481.          * if within 20 of FILNAMSIZ; then if var set, do careful check when
  482.          * appending.  Clear variable when begin new path. */
  483.  
  484.         /* next check:  need to append '/', at least one-char name, '\0' */
  485.         if ((endHPFS-buildpathHPFS) > FILNAMSIZ-3)
  486.             too_long = TRUE;                 /* check if extracting dir? */
  487.         if (stat(buildpathFAT, &statbuf))    /* path doesn't exist */
  488.         {
  489.             if (!create_dirs) {   /* told not to create (freshening) */
  490.                 free(buildpathHPFS);
  491.                 free(buildpathFAT);
  492.                 return 2;         /* path doesn't exist:  nothing to do */
  493.             }
  494.             if (too_long) {   /* GRR:  should allow FAT extraction w/o EAs */
  495.                 fprintf(stderr, "checkdir error:  path too long: %s\n",
  496.                   buildpathHPFS);
  497.                 fflush(stderr);
  498.                 free(buildpathHPFS);
  499.                 free(buildpathFAT);
  500.                 return 4;         /* no room for filenames:  fatal */
  501.             }
  502.             if (MKDIR(buildpathFAT, 0777) == -1) {   /* create the directory */
  503.                 fprintf(stderr, "checkdir error:  can't create %s\n\
  504.                  unable to process %s.\n", buildpathFAT, filename);
  505.                 fflush(stderr);
  506.                 free(buildpathHPFS);
  507.                 free(buildpathFAT);
  508.                 return 3;      /* path didn't exist, tried to create, failed */
  509.             }
  510.             created_dir = TRUE;
  511. #ifdef OS2
  512.             /* only set EA if creating directory */
  513. /* GRR:  need trailing '/' before function call? */
  514.             if (longdirEA) {
  515.                 int e = SetLongNameEA(buildpathFAT, pathcomp);
  516.                 Trace((stderr, "APPEND_DIR:  SetLongNameEA() returns %d\n", e));
  517.             }
  518. #endif
  519.         } else if (!S_ISDIR(statbuf.st_mode)) {
  520.             fprintf(stderr, "checkdir error:  %s exists but is not directory\n\
  521.                  unable to process %s.\n", buildpathFAT, filename);
  522.             fflush(stderr);
  523.             free(buildpathHPFS);
  524.             free(buildpathFAT);
  525.             return 3;          /* path existed but wasn't dir */
  526.         }
  527.         if (too_long) {
  528.             fprintf(stderr, "checkdir error:  path too long: %s\n",
  529.               buildpathHPFS);
  530.             fflush(stderr);
  531.             free(buildpathHPFS);
  532.             free(buildpathFAT);
  533.             return 4;         /* no room for filenames:  fatal */
  534.         }
  535.         *endHPFS++ = '/';
  536.         *endFAT++ = '/';
  537.         *endHPFS = *endFAT = '\0';
  538.         Trace((stderr, "buildpathHPFS now = [%s]\n", buildpathHPFS));
  539.         Trace((stderr, "buildpathFAT now =  [%s]\n", buildpathFAT));
  540.         return 0;
  541.  
  542.     } /* end if (FUNCTION == APPEND_DIR) */
  543.  
  544. /*---------------------------------------------------------------------------
  545.     GETPATH:  copy full FAT path to the string pointed at by pathcomp (want
  546.     filename to reflect name used on disk, not EAs; if full path is HPFS,
  547.     buildpathFAT and buildpathHPFS will be identical).  Also free both paths.
  548.   ---------------------------------------------------------------------------*/
  549.  
  550.     if (FUNCTION == GETPATH) {
  551.         Trace((stderr, "getting and freeing FAT path [%s]\n", buildpathFAT));
  552.         Trace((stderr, "freeing HPFS path [%s]\n", buildpathHPFS));
  553.         strcpy(pathcomp, buildpathFAT);
  554.         free(buildpathFAT);
  555.         free(buildpathHPFS);
  556.         buildpathHPFS = buildpathFAT = endHPFS = endFAT = NULL;
  557.         return 0;
  558.     }
  559.  
  560. /*---------------------------------------------------------------------------
  561.     APPEND_NAME:  assume the path component is the filename; append it and
  562.     return without checking for existence.
  563.   ---------------------------------------------------------------------------*/
  564.  
  565.     if (FUNCTION == APPEND_NAME) {
  566.         char *p = pathcomp;
  567.         int error = 0;
  568.  
  569.         Trace((stderr, "appending filename [%s]\n", pathcomp));
  570.         while ((*endHPFS = *p++) != '\0') {    /* copy to HPFS filename */
  571.             ++endHPFS;
  572.             if ((endHPFS-buildpathHPFS) >= FILNAMSIZ) {
  573.                 *--endHPFS = '\0';
  574.                 fprintf(stderr, "checkdir warning:  path too long; truncating\n\
  575.                    %s\n                -> %s\n", filename, buildpathHPFS);
  576.                 fflush(stderr);
  577.                 error = 1;   /* filename truncated */
  578.             }
  579.         }
  580.  
  581. #ifdef OS2
  582. /* GRR:  how can longnameEA ever be set before this point???  we don't want
  583.  * to save the original name to EAs if user renamed it, do we?
  584.  *
  585.  * if (!longnameEA && ((longnameEA = !IsFileNameValid(name)) != 0))
  586.  */
  587. #endif
  588.         if ( pInfo->vollabel || IsFileNameValid(buildpathHPFS)) {
  589. #ifdef OS2
  590.             longnameEA = FALSE;
  591. #endif
  592.             p = pathcomp;
  593.             while ((*endFAT = *p++) != '\0')   /* copy to FAT filename, too */
  594.                 ++endFAT;
  595.         } else {
  596. #ifdef OS2
  597.             longnameEA = TRUE;
  598.             if ((lastpathcomp = (char *)malloc(strlen(pathcomp)+1)) == NULL) {
  599.                 fprintf(stderr,
  600.                   "checkdir warning:  can't save longname EA: out of memory\n");
  601.                 longnameEA = FALSE;
  602.                 error = 1;   /* can't set .LONGNAME extended attribute */
  603.             } else           /* used and freed in close_outfile() */
  604.                 strcpy(lastpathcomp, pathcomp);
  605. #endif
  606.             map2fat(pathcomp, &endFAT);  /* map, put in FAT fn, update endFAT */
  607.         }
  608.         Trace((stderr, "buildpathHPFS: %s\nbuildpathFAT:  %s\n",
  609.           buildpathHPFS, buildpathFAT));
  610.  
  611.         return error;  /* could check for existence, prompt for new name... */
  612.  
  613.     } /* end if (FUNCTION == APPEND_NAME) */
  614.  
  615. /*---------------------------------------------------------------------------
  616.     INIT:  allocate and initialize buffer space for the file currently being
  617.     extracted.  If file was renamed with an absolute path, don't prepend the
  618.     extract-to path.
  619.   ---------------------------------------------------------------------------*/
  620.  
  621.     if (FUNCTION == INIT) {
  622.  
  623. /* HG: variable not used here */
  624. /*      char *p;              */
  625.  
  626.         Trace((stderr, "initializing buildpathHPFS and buildpathFAT to "));
  627.         if ((buildpathHPFS = (char *)malloc(fnlen+rootlen+1)) == NULL)
  628.             return 10;
  629.         if ((buildpathFAT = (char *)malloc(fnlen+rootlen+1)) == NULL) {
  630.             free(buildpathHPFS);
  631.             return 10;
  632.         }
  633.         if (pInfo->vollabel) {  /* use root or renamed path, but don't store */
  634. /* GRR:  for network drives, do strchr() and return IZ_VOL_LABEL if not [1] */
  635.             if (renamed_fullpath && pathcomp[1] == ':')
  636.                 *buildpathHPFS = ToLower(*pathcomp);
  637.             else if (!renamed_fullpath && rootpath && rootpath[1] == ':')
  638.                 *buildpathHPFS = ToLower(*rootpath);
  639.             else {
  640. #ifdef OS2
  641.                 ULONG lMap;
  642.                 DosQueryCurrentDisk(&nLabelDrive, &lMap);
  643. #else /* NT */
  644.                 char tmpN[MAX_PATH], *tmpP;
  645.                 if (GetFullPathName(".", MAX_PATH, tmpN, &tmpP) > MAX_PATH)
  646.                 { /* by definition of MAX_PATH we should never get here */
  647.                     fprintf(stderr,
  648.                             "checkdir warning: current dir path too long\n");
  649.                     return 1;   /* can't get drive letter */
  650.                 }
  651.                 nLabelDrive = *tmpN - 'a' + 1;
  652. #endif
  653.                 *buildpathHPFS = (char)(nLabelDrive - 1 + 'a');
  654.             }
  655.             nLabelDrive = *buildpathHPFS - 'a' + 1;     /* save for mapname() */
  656.             if (volflag == 0 || *buildpathHPFS < 'a' ||   /* no labels/bogus? */
  657.                 (volflag == 1 && !isfloppy(nLabelDrive))) {  /* -$:  no fixed */
  658.                 free(buildpathHPFS);
  659.                 free(buildpathFAT);
  660.                 return IZ_VOL_LABEL;   /* skipping with message */
  661.             }
  662.             *buildpathHPFS = '\0';
  663.         } else if (renamed_fullpath)   /* pathcomp = valid data */
  664.             strcpy(buildpathHPFS, pathcomp);
  665.         else if (rootlen > 0)
  666.             strcpy(buildpathHPFS, rootpath);
  667.         else
  668.             *buildpathHPFS = '\0';
  669.         endHPFS = buildpathHPFS;
  670.         endFAT = buildpathFAT;
  671.         while ((*endFAT = *endHPFS) != '\0') {
  672.             ++endFAT;
  673.             ++endHPFS;
  674.         }
  675.         Trace((stderr, "[%s]\n", buildpathHPFS));
  676.         return 0;
  677.     }
  678.  
  679. /*---------------------------------------------------------------------------
  680.     ROOT:  if appropriate, store the path in rootpath and create it if neces-
  681.     sary; else assume it's a zipfile member and return.  This path segment
  682.     gets used in extracting all members from every zipfile specified on the
  683.     command line.  Note that under OS/2 and MS-DOS, if a candidate extract-to
  684.     directory specification includes a drive letter (leading "x:"), it is
  685.     treated just as if it had a trailing '/'--that is, one directory level
  686.     will be created if the path doesn't exist, unless this is otherwise pro-
  687.     hibited (e.g., freshening).
  688.   ---------------------------------------------------------------------------*/
  689.  
  690. /* GRR:  for VMS and TOPS-20, allow either y]z.dir or y.z] forms; fix as
  691.  * appropriate before stat call */
  692.  
  693. /* GRR:  for MS-DOS and OS/2, necessary to append '.' to path of form "x:"? */
  694.  
  695.     if (FUNCTION == ROOT) {
  696.         Trace((stderr, "initializing root path to [%s]\n", pathcomp));
  697.         if (pathcomp == NULL) {
  698.             rootlen = 0;
  699.             return 0;
  700.         }
  701.         if ((rootlen = strlen(pathcomp)) > 0) {
  702.             int had_trailing_pathsep=FALSE, has_drive=FALSE, xtra=2;
  703.  
  704.             if (isalpha(pathcomp[0]) && pathcomp[1] == ':')
  705.                 has_drive = TRUE;   /* drive designator */
  706.             if (pathcomp[rootlen-1] == '/') {
  707.                 pathcomp[--rootlen] = '\0';
  708.                 had_trailing_pathsep = TRUE;
  709.             }
  710.             if (has_drive && (rootlen == 2)) {
  711.                 if (!had_trailing_pathsep)   /* i.e., original wasn't "x:/" */
  712.                     xtra = 3;      /* room for '.' + '/' + 0 at end of "x:" */
  713.             } else {               /* don't bother checking "x:." and "x:/" */
  714.                 if (SSTAT(pathcomp, &statbuf) || !S_ISDIR(statbuf.st_mode))
  715.                 {   /* path does not exist */
  716.                     if (!create_dirs
  717. #ifdef OLD_EXDIR
  718.                                      || (!has_drive && !had_trailing_pathsep)
  719. #endif
  720.                                                                              ) {
  721.                         rootlen = 0;
  722.                         return 2;   /* treat as stored file */
  723.                     }
  724. /* GRR:  scan for wildcard characters?  OS-dependent...  if find any, return 2:
  725.  * treat as stored file(s) */
  726.                     /* create directory (could add loop here to scan pathcomp
  727.                      * and create more than one level, but really necessary?) */
  728.                     if (MKDIR(pathcomp, 0777) == -1) {
  729.                         fprintf(stderr,
  730.                           "checkdir:  can't create extraction directory: %s\n",
  731.                           pathcomp);
  732.                         fflush(stderr);
  733.                         rootlen = 0;   /* path didn't exist, tried to create, */
  734.                         return 3;  /* failed:  file exists, or need 2+ levels */
  735.                     }
  736.                 }
  737.             }
  738.             if ((rootpath = (char *)malloc(rootlen+xtra)) == NULL) {
  739.                 rootlen = 0;
  740.                 return 10;
  741.             }
  742.             strcpy(rootpath, pathcomp);
  743.             if (xtra == 3)                  /* had just "x:", make "x:." */
  744.                 rootpath[rootlen++] = '.';
  745.             rootpath[rootlen++] = '/';
  746.             rootpath[rootlen] = '\0';
  747.         }
  748.         Trace((stderr, "rootpath now = [%s]\n", rootpath));
  749.         return 0;
  750.     }
  751.  
  752. /*---------------------------------------------------------------------------
  753.     END:  free rootpath, immediately prior to program exit.
  754.   ---------------------------------------------------------------------------*/
  755.  
  756.     if (FUNCTION == END) {
  757.         Trace((stderr, "freeing rootpath\n"));
  758.         if (rootlen > 0)
  759.             free(rootpath);
  760.         return 0;
  761.     }
  762.  
  763.     return 99;  /* should never reach */
  764.  
  765. } /* end function checkdir() */
  766.  
  767.  
  768.  
  769.  
  770. /************************/
  771. /*  Function do_wild()  */   /* identical to OS/2 version */
  772. /************************/
  773.  
  774. char *do_wild(wildspec)
  775.     char *wildspec;         /* only used first time on a given dir */
  776. {
  777.     static struct direct *dir = NULL;
  778.     static char *dirname, *wildname, matchname[FILNAMSIZ];
  779.     static int firstcall=TRUE, have_dirname, dirnamelen;
  780.     struct direct *file;
  781.  
  782.  
  783.     /* Even when we're just returning wildspec, we *always* do so in
  784.      * matchname[]--calling routine is allowed to append four characters
  785.      * to the returned string, and wildspec may be a pointer to argv[].
  786.      */
  787.     if (firstcall) {        /* first call:  must initialize everything */
  788.         firstcall = FALSE;
  789.  
  790.         /* break the wildspec into a directory part and a wildcard filename */
  791.         if ((wildname = strrchr(wildspec, '/')) == NULL &&
  792.             (wildname = strrchr(wildspec, ':')) == NULL) {
  793.             dirname = ".";
  794.             dirnamelen = 1;
  795.             have_dirname = FALSE;
  796.             wildname = wildspec;
  797.         } else {
  798.             ++wildname;     /* point at character after '/' or ':' */
  799.             dirnamelen = wildname - wildspec;
  800.             if ((dirname = (char *)malloc(dirnamelen+1)) == NULL) {
  801.                 fprintf(stderr, "warning:  can't allocate wildcard buffers\n");
  802.                 strcpy(matchname, wildspec);
  803.                 return matchname;   /* but maybe filespec was not a wildcard */
  804.             }
  805.             strncpy(dirname, wildspec, dirnamelen);
  806.             dirname[dirnamelen] = '\0';   /* terminate for strcpy below */
  807.             have_dirname = TRUE;
  808.         }
  809.         Trace((stderr, "do_wild:  dirname = [%s]\n", dirname));
  810.  
  811.         if ((dir = opendir(dirname)) != NULL) {
  812.             while ((file = readdir(dir)) != NULL) {
  813.                 Trace((stderr, "do_wild:  readdir returns %s\n", file->d_name));
  814.                 if (match(file->d_name, wildname, 1)) {  /* 1 == ignore case */
  815.                     Trace((stderr, "do_wild:  match() succeeds\n"));
  816.                     if (have_dirname) {
  817.                         strcpy(matchname, dirname);
  818.                         strcpy(matchname+dirnamelen, file->d_name);
  819.                     } else
  820.                         strcpy(matchname, file->d_name);
  821.                     return matchname;
  822.                 }
  823.             }
  824.             /* if we get to here directory is exhausted, so close it */
  825.             closedir(dir);
  826.             dir = NULL;
  827.         }
  828.         Trace((stderr, "do_wild:  opendir(%s) returns NULL\n", dirname));
  829.  
  830.         /* return the raw wildspec in case that works (e.g., directory not
  831.          * searchable, but filespec was not wild and file is readable) */
  832.         strcpy(matchname, wildspec);
  833.         return matchname;
  834.     }
  835.  
  836.     /* last time through, might have failed opendir but returned raw wildspec */
  837.     if (dir == NULL) {
  838.         firstcall = TRUE;  /* nothing left to try--reset for new wildspec */
  839.         if (have_dirname)
  840.             free(dirname);
  841.         return (char *)NULL;
  842.     }
  843.  
  844.     /* If we've gotten this far, we've read and matched at least one entry
  845.      * successfully (in a previous call), so dirname has been copied into
  846.      * matchname already.
  847.      */
  848.     while ((file = readdir(dir)) != NULL)
  849.         if (match(file->d_name, wildname, 1)) {   /* 1 == ignore case */
  850.             if (have_dirname) {
  851.                 /* strcpy(matchname, dirname); */
  852.                 strcpy(matchname+dirnamelen, file->d_name);
  853.             } else
  854.                 strcpy(matchname, file->d_name);
  855.             return matchname;
  856.         }
  857.  
  858.     closedir(dir);     /* have read at least one dir entry; nothing left */
  859.     dir = NULL;
  860.     firstcall = TRUE;  /* reset for new wildspec */
  861.     if (have_dirname)
  862.         free(dirname);
  863.     return (char *)NULL;
  864.  
  865. } /* end function do_wild() */
  866.  
  867.  
  868.  
  869.  
  870. /************************/
  871. /*  Function mapname()  */
  872. /************************/
  873.  
  874. /*
  875.  * There are presently two possibilities in OS/2:  the output filesystem is
  876.  * FAT, or it is HPFS.  If the former, we need to map to FAT, obviously, but
  877.  * we *also* must map to HPFS and store that version of the name in extended
  878.  * attributes.  Either way, we need to map to HPFS, so the main mapname
  879.  * routine does that.  In the case that the output file system is FAT, an
  880.  * extra filename-mapping routine is called in checkdir().  While it should
  881.  * be possible to determine the filesystem immediately upon entry to mapname(),
  882.  * it is conceivable that the DOS APPEND utility could be added to OS/2 some-
  883.  * day, allowing a FAT directory to be APPENDed to an HPFS drive/path.  There-
  884.  * fore we simply check the filesystem at each path component.
  885.  *
  886.  * Note that when alternative IFS's become available/popular, everything will
  887.  * become immensely more complicated.  For example, a Minix filesystem would
  888.  * have limited filename lengths like FAT but no extended attributes in which
  889.  * to store the longer versions of the names.  A BSD Unix filesystem would
  890.  * support paths of length 1024 bytes or more, but it is not clear that FAT
  891.  * EAs would allow such long .LONGNAME fields or that OS/2 would properly
  892.  * restore such fields when moving files from FAT to the new filesystem.
  893.  *
  894.  * GRR:  some or all of the following chars should be checked in either
  895.  *       mapname (HPFS) or map2fat (FAT), depending:  ,=^+'"[]<>|\t&
  896.  */
  897.  
  898. int mapname(renamed)  /* return 0 if no error, 1 if caution (filename trunc), */
  899.     int renamed;      /* 2 if warning (skip file because dir doesn't exist), */
  900. {                     /* 3 if error (skip file), 10 if no memory (skip file), */
  901.                       /* IZ_VOL_LABEL if can't do vol label, IZ_CREATED_DIR */
  902.     char pathcomp[FILNAMSIZ];   /* path-component buffer */
  903.     char *pp, *cp=NULL;         /* character pointers */
  904.     char *lastsemi = NULL;      /* pointer to last semi-colon in pathcomp */
  905.     int quote = FALSE;          /* flag:  next char is literal */
  906.     int error = 0;
  907.     register unsigned workch;   /* hold the character being tested */
  908.  
  909.  
  910. /*---------------------------------------------------------------------------
  911.     Initialize various pointers and counters and stuff.
  912.   ---------------------------------------------------------------------------*/
  913.  
  914.     /* can create path as long as not just freshening, or if user told us */
  915.     create_dirs = (!fflag || renamed);
  916.  
  917.     created_dir = FALSE;        /* not yet */
  918.     renamed_fullpath = FALSE;
  919.     fnlen = strlen(filename);
  920.  
  921. /* GRR:  for VMS, convert to internal format now or later? or never? */
  922.     if (renamed) {
  923.         cp = filename - 1;      /* point to beginning of renamed name... */
  924.         while (*++cp)
  925.             if (*cp == '\\')    /* convert backslashes to forward */
  926.                 *cp = '/';
  927.         cp = filename;
  928.         /* use temporary rootpath if user gave full pathname */
  929.         if (filename[0] == '/') {
  930.             renamed_fullpath = TRUE;
  931.             pathcomp[0] = '/';  /* copy the '/' and terminate */
  932.             pathcomp[1] = '\0';
  933.             ++cp;
  934.         } else if (isalpha(filename[0]) && filename[1] == ':') {
  935.             renamed_fullpath = TRUE;
  936.             pp = pathcomp;
  937.             *pp++ = *cp++;      /* copy the "d:" (+ '/', possibly) */
  938.             *pp++ = *cp++;
  939.             if (*cp == '/')
  940.                 *pp++ = *cp++;  /* otherwise add "./"? */
  941.             *pp = '\0';
  942.         }
  943.     }
  944.  
  945.     /* pathcomp is ignored unless renamed_fullpath is TRUE: */
  946.     if ((error = checkdir(pathcomp, INIT)) != 0)    /* initialize path buffer */
  947.         return error;           /* ...unless no mem or vol label on hard disk */
  948.  
  949.     *pathcomp = '\0';           /* initialize translation buffer */
  950.     pp = pathcomp;              /* point to translation buffer */
  951.     if (!renamed) {             /* cp already set if renamed */
  952.         if (jflag)              /* junking directories */
  953. /* GRR:  watch out for VMS version... */
  954.             cp = (char *)strrchr(filename, '/');
  955.         if (cp == NULL)             /* no '/' or not junking dirs */
  956.             cp = filename;          /* point to internal zipfile-member pathname */
  957.         else
  958.             ++cp;                   /* point to start of last component of path */
  959.     }
  960.  
  961. /*---------------------------------------------------------------------------
  962.     Begin main loop through characters in filename.
  963.   ---------------------------------------------------------------------------*/
  964.  
  965.     while ((workch = (uch)*cp++) != 0) {
  966.  
  967.         if (quote) {              /* if character quoted, */
  968.             *pp++ = (char)workch; /*  include it literally */
  969.             quote = FALSE;
  970.         } else
  971.             switch (workch) {
  972.             case '/':             /* can assume -j flag not given */
  973.                 *pp = '\0';
  974.                 if ((error = checkdir(pathcomp, APPEND_DIR)) > 1)
  975.                     return error;
  976.                 pp = pathcomp;    /* reset conversion buffer for next piece */
  977.                 lastsemi = NULL;  /* leave directory semi-colons alone */
  978.                 break;
  979.  
  980.             case ':':
  981.                 *pp++ = '_';      /* drive names not stored in zipfile, */
  982.                 break;            /*  so no colons allowed */
  983.  
  984.             case ';':             /* start of VMS version? */
  985.                 lastsemi = pp;    /* remove VMS version later... */
  986.                 *pp++ = ';';      /*  but keep semicolon for now */
  987.                 break;
  988.  
  989.             case '\026':          /* control-V quote for special chars */
  990.                 quote = TRUE;     /* set flag for next character */
  991.                 break;
  992.  
  993.             case ' ':             /* keep spaces unless specifically */
  994. #ifdef OS2
  995.                 if (sflag)        /*  requested to change to underscore */
  996. #else /* NT */
  997.                 /* NT cannot create filenames with spaces on FAT volumes */
  998.                 if (sflag || IsVolumeFAT(filename))
  999. #endif
  1000.                     *pp++ = '_';
  1001.                 else
  1002.                     *pp++ = ' ';
  1003.                 break;
  1004.  
  1005.             default:
  1006.                 /* allow European characters in filenames: */
  1007.                 if (isprint(workch) || (128 <= workch && workch <= 254))
  1008.                     *pp++ = (char)workch;
  1009.             } /* end switch */
  1010.  
  1011.     } /* end while loop */
  1012.  
  1013.     *pp = '\0';                   /* done with pathcomp:  terminate it */
  1014.  
  1015.     /* if not saving them, remove VMS version numbers (appended "###") */
  1016.     if (!V_flag && lastsemi) {
  1017.         pp = lastsemi + 1;        /* semi-colon was kept:  expect #'s after */
  1018.         while (isdigit((uch)(*pp)))
  1019.             ++pp;
  1020.         if (*pp == '\0')          /* only digits between ';' and end:  nuke */
  1021.             *lastsemi = '\0';
  1022.     }
  1023.  
  1024. /*---------------------------------------------------------------------------
  1025.     Report if directory was created (and no file to create:  filename ended
  1026.     in '/'), check name to be sure it exists, and combine path and name be-
  1027.     fore exiting.
  1028.   ---------------------------------------------------------------------------*/
  1029.  
  1030.     if (filename[fnlen-1] == '/') {
  1031.         checkdir(filename, GETPATH);
  1032.         if (created_dir && QCOND2) {
  1033. /* GRR:  trailing '/'?  need to strip or not? */
  1034.             fprintf(stdout, "   creating: %-22s\n", filename);
  1035. #ifdef OS2
  1036.             SetPathInfo(filename, lrec.last_mod_file_date,
  1037.                                   lrec.last_mod_file_time, -1);
  1038.             if (extra_field)  /* zipfile extra field has extended attributes */
  1039.                 SetEAs(filename, extra_field);
  1040. #else /* NT */
  1041.             /* HG: are we setting the date&time on a newly created dir?  */
  1042.             /*     Not quite sure how to do this. It does not seem to    */
  1043.             /*     be done in the MS-DOS version of mapname().           */
  1044. #endif
  1045.             return IZ_CREATED_DIR;   /* dir time already set */
  1046.         }
  1047.         return 2;   /* dir existed already; don't look for data to extract */
  1048.     }
  1049.  
  1050.     if (*pathcomp == '\0') {
  1051.         fprintf(stderr, "mapname:  conversion of %s failed\n", filename);
  1052.         return 3;
  1053.     }
  1054.  
  1055.     checkdir(pathcomp, APPEND_NAME);   /* returns 1 if truncated:  care? */
  1056.     checkdir(filename, GETPATH);
  1057.     Trace((stderr, "mapname returns with filename = [%s] (error = %d)\n\n",
  1058.       filename, error));
  1059.  
  1060.     if (pInfo->vollabel) {   /* set the volume label now */
  1061.  
  1062. #ifdef OS2
  1063.  
  1064.         VOLUMELABEL FSInfoBuf;
  1065. /* GRR:  "VOLUMELABEL" defined for IBM C and emx, but haven't checked MSC... */
  1066.  
  1067.         strcpy(FSInfoBuf.szVolLabel, filename);
  1068.         FSInfoBuf.cch = (BYTE)strlen(FSInfoBuf.szVolLabel);
  1069.  
  1070.         if (QCOND2)
  1071.             fprintf(stdout, "labelling %c: %-22s\n", (nLabelDrive + 'a' - 1),
  1072.               filename);
  1073.         if (DosSetFSInfo(nLabelDrive, FSIL_VOLSER, &FSInfoBuf,
  1074.                          sizeof(VOLUMELABEL)))
  1075. #else /* NT */
  1076.  
  1077.         char drive[3];
  1078.  
  1079.         /* Build a drive string, e.g. "b:" */
  1080.         drive[0] = 'a' + nLabelDrive - 1;
  1081.         drive[1] = ':';
  1082.         drive[2] = '\0';
  1083.         if (QCOND2)
  1084.             fprintf(stdout, "labelling %s %-22s\n", drive, filename);
  1085.         if (!SetVolumeLabel(drive, filename))
  1086. #endif
  1087.         {
  1088.             fprintf(stderr, "mapname:  error setting volume label\n");
  1089.             return 3;
  1090.         }
  1091.         return 2;   /* success:  skip the "extraction" quietly */
  1092.     }
  1093.  
  1094.     return error;
  1095.  
  1096. } /* end function mapname() */
  1097.