Subversion Repositories Kolibri OS

Rev

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

  1. /*
  2.   Copyright (c) 1990-2007 Info-ZIP.  All rights reserved.
  3.  
  4.   See the accompanying file LICENSE, version 2007-Mar-04 or later
  5.   (the contents of which are also included in unzip.h) for terms of use.
  6.   If, for some reason, all these files are missing, the Info-ZIP license
  7.   also may be found at:  ftp://ftp.info-zip.org/pub/infozip/license.html
  8. */
  9. /*---------------------------------------------------------------------------
  10.  
  11.   netware.c
  12.  
  13.   This file implements these functions for a NetWare Loadable Module (NLM):
  14.  
  15.   Contains:  InitUnZipConsole()
  16.              do_wild()
  17.              mapattr()
  18.              mapname()
  19.              checkdir()
  20.              close_outfile()
  21.              stamp_file()
  22.              version()
  23.              screensize()
  24.  
  25.   ---------------------------------------------------------------------------*/
  26.  
  27.  
  28. #define UNZIP_INTERNAL
  29. #include "unzip.h"
  30. #include <dirent.h>
  31. #include <nwdir.h>
  32. #include <nwnamspc.h>
  33. #include <nwconio.h>
  34. #include <nwthread.h>
  35. #include <nwadv.h>
  36.  
  37. #ifdef ACORN_FTYPE_NFS
  38. /* Acorn bits for NFS filetyping */
  39. typedef struct {
  40.   uch ID[2];
  41.   uch size[2];
  42.   uch ID_2[4];
  43.   uch loadaddr[4];
  44.   uch execaddr[4];
  45.   uch attr[4];
  46. } RO_extra_block;
  47.  
  48. #endif /* ACORN_FTYPE_NFS */
  49.  
  50. static int created_dir;        /* used in mapname(), checkdir() */
  51. static int renamed_fullpath;   /* ditto */
  52.  
  53.  
  54. /*********************************/
  55. /*  Function InitUnZipConsole()  */
  56. /*********************************/
  57.  
  58. void InitUnZipConsole()
  59. {
  60.     unsigned int myHandle = GetNLMHandle();
  61.     unsigned int *activeScreen =
  62.             ImportSymbol(myHandle, "activeScreen");
  63.     unsigned int *systemConsoleScreen =
  64.             ImportSymbol(myHandle, "systemConsoleScreen");
  65.     void (*pUseAccurateCaseForPaths)(int) =
  66.             ImportSymbol(myHandle, "UseAccurateCaseForPaths");
  67.  
  68.     if (!activeScreen || !systemConsoleScreen ||
  69.             *activeScreen == *systemConsoleScreen)
  70.         CreateScreen("Info-ZIP UnZip Utility", 0);
  71.     else
  72.         CreateScreen("System Console", DONT_AUTO_ACTIVATE);
  73.  
  74.     SetCurrentNameSpace(NW_NS_LONG);
  75.     if (pUseAccurateCaseForPaths)
  76.             pUseAccurateCaseForPaths(TRUE);
  77.  
  78.     UnimportSymbol(myHandle, "activeScreen");
  79.     UnimportSymbol(myHandle, "systemConsoleScreen");
  80.     UnimportSymbol(myHandle, "UseAccurateCaseForPaths");
  81. }
  82.  
  83.  
  84. /**********************/
  85. /* Function do_wild() */   /* for porting: dir separator; match(ignore_case) */
  86. /**********************/
  87.  
  88. char *do_wild(__G__ wildspec)
  89.     __GDEF
  90.     ZCONST char *wildspec;      /* only used first time on a given dir */
  91. {
  92.     static DIR *wild_dir = (DIR *)NULL;
  93.     static ZCONST char *wildname;
  94.     static char *dirname, matchname[FILNAMSIZ];
  95.     static int notfirstcall=FALSE, have_dirname, dirnamelen;
  96.     struct dirent *file;
  97.  
  98.     /* Even when we're just returning wildspec, we *always* do so in
  99.      * matchname[]--calling routine is allowed to append four characters
  100.      * to the returned string, and wildspec may be a pointer to argv[].
  101.      */
  102.     if (!notfirstcall) {    /* first call:  must initialize everything */
  103.         notfirstcall = TRUE;
  104.  
  105.         if (!iswild(wildspec)) {
  106.             strncpy(matchname, wildspec, FILNAMSIZ);
  107.             matchname[FILNAMSIZ-1] = '\0';
  108.             have_dirname = FALSE;
  109.             dir = NULL;
  110.             return matchname;
  111.         }
  112.  
  113.         /* break the wildspec into a directory part and a wildcard filename */
  114.         if ((wildname = strrchr(wildspec, '/')) == (ZCONST char *)NULL) {
  115.             dirname = ".";
  116.             dirnamelen = 1;
  117.             have_dirname = FALSE;
  118.             wildname = wildspec;
  119.         } else {
  120.             ++wildname;     /* point at character after '/' */
  121.             dirnamelen = wildname - wildspec;
  122.             if ((dirname = (char *)malloc(dirnamelen+1)) == (char *)NULL) {
  123.                 Info(slide, 0x201, ((char *)slide,
  124.                   "warning:  cannot allocate wildcard buffers\n"));
  125.                 strncpy(matchname, wildspec, FILNAMSIZ);
  126.                 matchname[FILNAMSIZ-1] = '\0';
  127.                 return matchname;   /* but maybe filespec was not a wildcard */
  128.             }
  129.             strncpy(dirname, wildspec, dirnamelen);
  130.             dirname[dirnamelen] = '\0';   /* terminate for strcpy below */
  131.             have_dirname = TRUE;
  132.         }
  133.  
  134.         if ((wild_dir = opendir(dirname)) != (DIR *)NULL) {
  135.             while ((file = readdir(wild_dir)) != (struct dirent *)NULL) {
  136.                 Trace((stderr, "do_wild:  readdir returns %s\n",
  137.                   FnFilter1(file->d_name)));
  138.                 if (file->d_name[0] == '.' && wildname[0] != '.')
  139.                     continue; /* Unix:  '*' and '?' do not match leading dot */
  140.                 if (match(file->d_name, wildname, 0 WISEP) && /* 0=case sens.*/
  141.                     /* skip "." and ".." directory entries */
  142.                     strcmp(file->d_name, ".") && strcmp(file->d_name, "..")) {
  143.                     Trace((stderr, "do_wild:  match() succeeds\n"));
  144.                     if (have_dirname) {
  145.                         strcpy(matchname, dirname);
  146.                         strcpy(matchname+dirnamelen, file->d_name);
  147.                     } else
  148.                         strcpy(matchname, file->d_name);
  149.                     return matchname;
  150.                 }
  151.             }
  152.             /* if we get to here directory is exhausted, so close it */
  153.             closedir(wild_dir);
  154.             wild_dir = (DIR *)NULL;
  155.         }
  156.  
  157.         /* return the raw wildspec in case that works (e.g., directory not
  158.          * searchable, but filespec was not wild and file is readable) */
  159.         strncpy(matchname, wildspec, FILNAMSIZ);
  160.         matchname[FILNAMSIZ-1] = '\0';
  161.         return matchname;
  162.     }
  163.  
  164.     /* last time through, might have failed opendir but returned raw wildspec */
  165.     if (wild_dir == (DIR *)NULL) {
  166.         notfirstcall = FALSE; /* nothing left to try--reset for new wildspec */
  167.         if (have_dirname)
  168.             free(dirname);
  169.         return (char *)NULL;
  170.     }
  171.  
  172.     /* If we've gotten this far, we've read and matched at least one entry
  173.      * successfully (in a previous call), so dirname has been copied into
  174.      * matchname already.
  175.      */
  176.     while ((file = readdir(wild_dir)) != (struct dirent *)NULL) {
  177.         Trace((stderr, "do_wild:  readdir returns %s\n",
  178.           FnFilter1(file->d_name)));
  179.         if (file->d_name[0] == '.' && wildname[0] != '.')
  180.             continue;   /* Unix:  '*' and '?' do not match leading dot */
  181.         if (match(file->d_name, wildname, 0 WISEP)) {   /* 0 == case sens. */
  182.             Trace((stderr, "do_wild:  match() succeeds\n"));
  183.             if (have_dirname) {
  184.                 /* strcpy(matchname, dirname); */
  185.                 strcpy(matchname+dirnamelen, file->d_name);
  186.             } else
  187.                 strcpy(matchname, file->d_name);
  188.             return matchname;
  189.         }
  190.     }
  191.  
  192.     closedir(wild_dir);     /* have read at least one entry; nothing left */
  193.     wild_dir = (DIR *)NULL;
  194.     notfirstcall = FALSE;  /* reset for new wildspec */
  195.     if (have_dirname)
  196.         free(dirname);
  197.     return (char *)NULL;
  198.  
  199. } /* end function do_wild() */
  200.  
  201.  
  202. /**********************/
  203. /* Function mapattr() */
  204. /**********************/
  205.  
  206. int mapattr(__G)
  207.     __GDEF
  208. {
  209.     ulg tmp = G.crec.external_file_attributes;
  210.  
  211.     G.pInfo->file_attr = 0;
  212.     /* initialized to 0 for check in "default" branch below... */
  213.  
  214.     switch (G.pInfo->hostnum) {
  215.         case AMIGA_:
  216.             tmp = (unsigned)(tmp>>17 & 7);   /* Amiga RWE bits */
  217.             G.pInfo->file_attr = (unsigned)(tmp<<6 | tmp<<3 | tmp);
  218.             break;
  219.         case UNIX_:
  220.         case VMS_:
  221.         case ACORN_:
  222.         case ATARI_:
  223.         case ATHEOS_:
  224.         case BEOS_:
  225.         case QDOS_:
  226.         case TANDEM_:
  227.             G.pInfo->file_attr = (unsigned)(tmp >> 16);
  228.             if (G.pInfo->file_attr != 0 || !G.extra_field) {
  229.                 return 0;
  230.             } else {
  231.                 /* Some (non-Info-ZIP) implementations of Zip for Unix and
  232.                  * VMS (and probably others ??) leave 0 in the upper 16-bit
  233.                  * part of the external_file_attributes field. Instead, they
  234.                  * store file permission attributes in some extra field.
  235.                  * As a work-around, we search for the presence of one of
  236.                  * these extra fields and fall back to the MSDOS compatible
  237.                  * part of external_file_attributes if one of the known
  238.                  * e.f. types has been detected.
  239.                  * Later, we might implement extraction of the permission
  240.                  * bits from the VMS extra field. But for now, the work-around
  241.                  * should be sufficient to provide "readable" extracted files.
  242.                  * (For ASI Unix e.f., an experimental remap from the e.f.
  243.                  * mode value IS already provided!)
  244.                  */
  245.                 ush ebID;
  246.                 unsigned ebLen;
  247.                 uch *ef = G.extra_field;
  248.                 unsigned ef_len = G.crec.extra_field_length;
  249.                 int r = FALSE;
  250.  
  251.                 while (!r && ef_len >= EB_HEADSIZE) {
  252.                     ebID = makeword(ef);
  253.                     ebLen = (unsigned)makeword(ef+EB_LEN);
  254.                     if (ebLen > (ef_len - EB_HEADSIZE))
  255.                         /* discoverd some e.f. inconsistency! */
  256.                         break;
  257.                     switch (ebID) {
  258.                       case EF_ASIUNIX:
  259.                         if (ebLen >= (EB_ASI_MODE+2)) {
  260.                             G.pInfo->file_attr =
  261.                               (unsigned)makeword(ef+(EB_HEADSIZE+EB_ASI_MODE));
  262.                             /* force stop of loop: */
  263.                             ef_len = (ebLen + EB_HEADSIZE);
  264.                             break;
  265.                         }
  266.                         /* else: fall through! */
  267.                       case EF_PKVMS:
  268.                         /* "found nondecypherable e.f. with perm. attr" */
  269.                         r = TRUE;
  270.                       default:
  271.                         break;
  272.                     }
  273.                     ef_len -= (ebLen + EB_HEADSIZE);
  274.                     ef += (ebLen + EB_HEADSIZE);
  275.                 }
  276.                 if (!r)
  277.                     return 0;
  278.             }
  279.             /* fall through! */
  280.         /* all remaining cases:  expand MSDOS read-only bit into write perms */
  281.         case FS_FAT_:
  282.             /* PKWARE's PKZip for Unix marks entries as FS_FAT_, but stores the
  283.              * Unix attributes in the upper 16 bits of the external attributes
  284.              * field, just like Info-ZIP's Zip for Unix.  We try to use that
  285.              * value, after a check for consistency with the MSDOS attribute
  286.              * bits (see below).
  287.              */
  288.             G.pInfo->file_attr = (unsigned)(tmp >> 16);
  289.             /* fall through! */
  290.         case FS_HPFS_:
  291.         case FS_NTFS_:
  292.         case MAC_:
  293.         case TOPS20_:
  294.         default:
  295.             /* Ensure that DOS subdir bit is set when the entry's name ends
  296.              * in a '/'.  Some third-party Zip programs fail to set the subdir
  297.              * bit for directory entries.
  298.              */
  299.             if ((tmp & 0x10) == 0) {
  300.                 extent fnlen = strlen(G.filename);
  301.                 if (fnlen > 0 && G.filename[fnlen-1] == '/')
  302.                     tmp |= 0x10;
  303.             }
  304.             /* read-only bit --> write perms; subdir bit --> dir exec bit */
  305.             tmp = !(tmp & 1) << 1  |  (tmp & 0x10) >> 4;
  306.             if ((G.pInfo->file_attr & 0700) == (unsigned)(0400 | tmp<<6))
  307.                 /* keep previous G.pInfo->file_attr setting, when its "owner"
  308.                  * part appears to be consistent with DOS attribute flags!
  309.                  */
  310.                 return 0;
  311.             G.pInfo->file_attr = (unsigned)(0444 | tmp<<6 | tmp<<3 | tmp);
  312.             break;
  313.     } /* end switch (host-OS-created-by) */
  314.  
  315.     /* for originating systems with no concept of "group," "other," "system": */
  316.     umask( (int)(tmp=umask(0)) );    /* apply mask to expanded r/w(/x) perms */
  317.     G.pInfo->file_attr &= ~tmp;
  318.  
  319.     return 0;
  320.  
  321. } /* end function mapattr() */
  322.  
  323.  
  324.  
  325.  
  326. /**********************/
  327. /* Function mapname() */
  328. /**********************/
  329.  
  330. int mapname(__G__ renamed)
  331.     __GDEF
  332.     int renamed;
  333. /*
  334.  * returns:
  335.  *  MPN_OK          - no problem detected
  336.  *  MPN_INF_TRUNC   - caution (truncated filename)
  337.  *  MPN_INF_SKIP    - info "skip entry" (dir doesn't exist)
  338.  *  MPN_ERR_SKIP    - error -> skip entry
  339.  *  MPN_ERR_TOOLONG - error -> path is too long
  340.  *  MPN_NOMEM       - error (memory allocation failed) -> skip entry
  341.  *  [also MPN_VOL_LABEL, MPN_CREATED_DIR]
  342.  */
  343. {
  344.     char pathcomp[FILNAMSIZ];      /* path-component buffer */
  345.     char *pp, *cp=(char *)NULL;    /* character pointers */
  346.     char *lastsemi=(char *)NULL;   /* pointer to last semi-colon in pathcomp */
  347. #ifdef ACORN_FTYPE_NFS
  348.     char *lastcomma=(char *)NULL;  /* pointer to last comma in pathcomp */
  349.     RO_extra_block *ef_spark;      /* pointer Acorn FTYPE ef block */
  350. #endif
  351.     int killed_ddot = FALSE;       /* is set when skipping "../" pathcomp */
  352.     int error = MPN_OK;
  353.     register unsigned workch;      /* hold the character being tested */
  354.  
  355.  
  356. /*---------------------------------------------------------------------------
  357.     Initialize various pointers and counters and stuff.
  358.   ---------------------------------------------------------------------------*/
  359.  
  360.     if (G.pInfo->vollabel)
  361.         return MPN_VOL_LABEL;   /* can't set disk volume labels in Netware */
  362.  
  363.     /* can create path as long as not just freshening, or if user told us */
  364.     G.create_dirs = (!uO.fflag || renamed);
  365.  
  366.     created_dir = FALSE;        /* not yet */
  367.  
  368.     /* user gave full pathname:  don't prepend rootpath */
  369.     renamed_fullpath = (renamed && (*G.filename == '/'));
  370.  
  371.     if (checkdir(__G__ (char *)NULL, INIT) == MPN_NOMEM)
  372.         return MPN_NOMEM;       /* initialize path buffer, unless no memory */
  373.  
  374.     *pathcomp = '\0';           /* initialize translation buffer */
  375.     pp = pathcomp;              /* point to translation buffer */
  376.     if (uO.jflag)               /* junking directories */
  377.         cp = (char *)strrchr(G.filename, '/');
  378.     if (cp == (char *)NULL)     /* no '/' or not junking dirs */
  379.         cp = G.filename;        /* point to internal zipfile-member pathname */
  380.     else
  381.         ++cp;                   /* point to start of last component of path */
  382.  
  383. /*---------------------------------------------------------------------------
  384.     Begin main loop through characters in filename.
  385.   ---------------------------------------------------------------------------*/
  386.  
  387.     while ((workch = (uch)*cp++) != 0) {
  388.  
  389.         switch (workch) {
  390.             case '/':             /* can assume -j flag not given */
  391.                 *pp = '\0';
  392.                 if (strcmp(pathcomp, ".") == 0) {
  393.                     /* don't bother appending "./" to the path */
  394.                     *pathcomp = '\0';
  395.                 } else if (!uO.ddotflag && strcmp(pathcomp, "..") == 0) {
  396.                     /* "../" dir traversal detected, skip over it */
  397.                     *pathcomp = '\0';
  398.                     killed_ddot = TRUE;     /* set "show message" flag */
  399.                 }
  400.                 /* when path component is not empty, append it now */
  401.                 if (*pathcomp != '\0' &&
  402.                     ((error = checkdir(__G__ pathcomp, APPEND_DIR))
  403.                      & MPN_MASK) > MPN_INF_TRUNC)
  404.                     return error;
  405.                 pp = pathcomp;    /* reset conversion buffer for next piece */
  406.                 lastsemi = (char *)NULL; /* leave direct. semi-colons alone */
  407.                 break;
  408.  
  409.             case ';':             /* VMS version (or DEC-20 attrib?) */
  410.                 lastsemi = pp;
  411.                 *pp++ = ';';      /* keep for now; remove VMS ";##" */
  412.                 break;            /*  later, if requested */
  413.  
  414. #ifdef ACORN_FTYPE_NFS
  415.             case ',':             /* NFS filetype extension */
  416.                 lastcomma = pp;
  417.                 *pp++ = ',';      /* keep for now; may need to remove */
  418.                 break;            /*  later, if requested */
  419. #endif
  420.  
  421.             default:
  422.                 /* allow European characters in filenames: */
  423.                 if (isprint(workch) || (128 <= workch && workch <= 254))
  424.                     *pp++ = (char)workch;
  425.         } /* end switch */
  426.  
  427.     } /* end while loop */
  428.  
  429.     /* Show warning when stripping insecure "parent dir" path components */
  430.     if (killed_ddot && QCOND2) {
  431.         Info(slide, 0, ((char *)slide,
  432.           "warning:  skipped \"../\" path component(s) in %s\n",
  433.           FnFilter1(G.filename)));
  434.         if (!(error & ~MPN_MASK))
  435.             error = (error & MPN_MASK) | PK_WARN;
  436.     }
  437.  
  438. /*---------------------------------------------------------------------------
  439.     Report if directory was created (and no file to create:  filename ended
  440.     in '/'), check name to be sure it exists, and combine path and name be-
  441.     fore exiting.
  442.   ---------------------------------------------------------------------------*/
  443.  
  444.     if (G.filename[strlen(G.filename) - 1] == '/') {
  445.         checkdir(__G__ G.filename, GETPATH);
  446.         if (created_dir) {
  447.             if (QCOND2) {
  448.                 Info(slide, 0, ((char *)slide, "   creating: %s\n",
  449.                   FnFilter1(G.filename)));
  450.             }
  451. #if !defined(NO_CHMOD) && !defined(NLM)
  452.             /* In NetWare, chmod does not work on directories */
  453.             /* set approx. dir perms (make sure can still read/write in dir) */
  454.             if (chmod(G.filename, (0xffff & G.pInfo->file_attr) | 0700))
  455.                 perror("chmod (directory attributes) error");
  456. #endif
  457.             /* set dir time (note trailing '/') */
  458.             return (error & ~MPN_MASK) | MPN_CREATED_DIR;
  459.         }
  460.         /* dir existed already; don't look for data to extract */
  461.         return (error & ~MPN_MASK) | MPN_INF_SKIP;
  462.     }
  463.  
  464.     *pp = '\0';                   /* done with pathcomp:  terminate it */
  465.  
  466.     /* if not saving them, remove VMS version numbers (appended ";###") */
  467.     if (!uO.V_flag && lastsemi) {
  468.         pp = lastsemi + 1;
  469.         while (isdigit((uch)(*pp)))
  470.             ++pp;
  471.         if (*pp == '\0')          /* only digits between ';' and end:  nuke */
  472.             *lastsemi = '\0';
  473.     }
  474.  
  475. #ifdef ACORN_FTYPE_NFS
  476.     /* translate Acorn filetype information if asked to do so */
  477.     if (uO.acorn_nfs_ext &&
  478.         (ef_spark = (RO_extra_block *)
  479.                     getRISCOSexfield(G.extra_field, G.lrec.extra_field_length))
  480.         != (RO_extra_block *)NULL)
  481.     {
  482.         /* file *must* have a RISC OS extra field */
  483.         long ft = (long)makelong(ef_spark->loadaddr);
  484.         /*32-bit*/
  485.         if (lastcomma) {
  486.             pp = lastcomma + 1;
  487.             while (isxdigit((uch)(*pp))) ++pp;
  488.             if (pp == lastcomma+4 && *pp == '\0') *lastcomma='\0'; /* nuke */
  489.         }
  490.         if ((ft & 1<<31)==0) ft=0x000FFD00;
  491.         sprintf(pathcomp+strlen(pathcomp), ",%03x", (int)(ft>>8) & 0xFFF);
  492.     }
  493. #endif /* ACORN_FTYPE_NFS */
  494.  
  495.     if (*pathcomp == '\0') {
  496.         Info(slide, 1, ((char *)slide, "mapname:  conversion of %s failed\n",
  497.           FnFilter1(G.filename)));
  498.         return (error & ~MPN_MASK) | MPN_ERR_SKIP;
  499.     }
  500.  
  501.     checkdir(__G__ pathcomp, APPEND_NAME);  /* returns 1 if truncated: care? */
  502.     checkdir(__G__ G.filename, GETPATH);
  503.  
  504.     return error;
  505.  
  506. } /* end function mapname() */
  507.  
  508.  
  509.  
  510.  
  511. /***********************/
  512. /* Function checkdir() */
  513. /***********************/
  514.  
  515. int checkdir(__G__ pathcomp, flag)
  516.     __GDEF
  517.     char *pathcomp;
  518.     int flag;
  519. /*
  520.  * returns:
  521.  *  MPN_OK          - no problem detected
  522.  *  MPN_INF_TRUNC   - (on APPEND_NAME) truncated filename
  523.  *  MPN_INF_SKIP    - path doesn't exist, not allowed to create
  524.  *  MPN_ERR_SKIP    - path doesn't exist, tried to create and failed; or path
  525.  *                    exists and is not a directory, but is supposed to be
  526.  *  MPN_ERR_TOOLONG - path is too long
  527.  *  MPN_NOMEM       - can't allocate memory for filename buffers
  528.  */
  529. {
  530.     static int rootlen = 0;   /* length of rootpath */
  531.     static char *rootpath;    /* user's "extract-to" directory */
  532.     static char *buildpath;   /* full path (so far) to extracted file */
  533.     static char *end;         /* pointer to end of buildpath ('\0') */
  534.  
  535. #   define FN_MASK   7
  536. #   define FUNCTION  (flag & FN_MASK)
  537.  
  538.  
  539. /*---------------------------------------------------------------------------
  540.     APPEND_DIR:  append the path component to the path being built and check
  541.     for its existence.  If doesn't exist and we are creating directories, do
  542.     so for this one; else signal success or error as appropriate.
  543.   ---------------------------------------------------------------------------*/
  544.  
  545.     if (FUNCTION == APPEND_DIR) {
  546.         int too_long = FALSE;
  547. #ifdef SHORT_NAMES
  548.         char *old_end = end;
  549. #endif
  550.  
  551.         Trace((stderr, "appending dir segment [%s]\n", FnFilter1(pathcomp)));
  552.         while ((*end = *pathcomp++) != '\0')
  553.             ++end;
  554. #ifdef SHORT_NAMES   /* path components restricted to 14 chars, typically */
  555.         if ((end-old_end) > FILENAME_MAX)  /* GRR:  proper constant? */
  556.             *(end = old_end + FILENAME_MAX) = '\0';
  557. #endif
  558.  
  559.         /* GRR:  could do better check, see if overrunning buffer as we go:
  560.          * check end-buildpath after each append, set warning variable if
  561.          * within 20 of FILNAMSIZ; then if var set, do careful check when
  562.          * appending.  Clear variable when begin new path. */
  563.  
  564.         if ((end-buildpath) > FILNAMSIZ-3)  /* need '/', one-char name, '\0' */
  565.             too_long = TRUE;                /* check if extracting directory? */
  566.         if (stat(buildpath, &G.statbuf)) {  /* path doesn't exist */
  567.             if (!G.create_dirs) { /* told not to create (freshening) */
  568.                 free(buildpath);
  569.                 /* path doesn't exist:  nothing to do */
  570.                 return MPN_INF_SKIP;
  571.             }
  572.             if (too_long) {
  573.                 Info(slide, 1, ((char *)slide,
  574.                   "checkdir error:  path too long: %s\n",
  575.                   FnFilter1(buildpath)));
  576.                 free(buildpath);
  577.                 /* no room for filenames:  fatal */
  578.                 return MPN_ERR_TOOLONG;
  579.             }
  580.             if (mkdir(buildpath) == -1) {   /* create the directory */
  581.                 Info(slide, 1, ((char *)slide,
  582.                   "checkdir error:  cannot create %s\n\
  583.                 unable to process %s.\n",
  584.                   FnFilter2(buildpath), FnFilter1(G.filename)));
  585.                 free(buildpath);
  586.                 /* path didn't exist, tried to create, failed */
  587.                 return MPN_ERR_SKIP;
  588.             }
  589.             created_dir = TRUE;
  590.         } else if (!S_ISDIR(G.statbuf.st_mode)) {
  591.             Info(slide, 1, ((char *)slide,
  592.               "checkdir error:  %s exists but is not directory\n\
  593.                 unable to process %s.\n",
  594.               FnFilter2(buildpath), FnFilter1(G.filename)));
  595.             free(buildpath);
  596.             /* path existed but wasn't dir */
  597.             return MPN_ERR_SKIP;
  598.         }
  599.         if (too_long) {
  600.             Info(slide, 1, ((char *)slide,
  601.               "checkdir error:  path too long: %s\n", FnFilter1(buildpath)));
  602.             free(buildpath);
  603.             /* no room for filenames:  fatal */
  604.             return MPN_ERR_TOOLONG;
  605.         }
  606.         *end++ = '/';
  607.         *end = '\0';
  608.         Trace((stderr, "buildpath now = [%s]\n", FnFilter1(buildpath)));
  609.         return MPN_OK;
  610.  
  611.     } /* end if (FUNCTION == APPEND_DIR) */
  612.  
  613. /*---------------------------------------------------------------------------
  614.     GETPATH:  copy full path to the string pointed at by pathcomp, and free
  615.     buildpath.
  616.   ---------------------------------------------------------------------------*/
  617.  
  618.     if (FUNCTION == GETPATH) {
  619.         strcpy(pathcomp, buildpath);
  620.         Trace((stderr, "getting and freeing path [%s]\n",
  621.           FnFilter1(pathcomp)));
  622.         free(buildpath);
  623.         buildpath = end = (char *)NULL;
  624.         return MPN_OK;
  625.     }
  626.  
  627. /*---------------------------------------------------------------------------
  628.     APPEND_NAME:  assume the path component is the filename; append it and
  629.     return without checking for existence.
  630.   ---------------------------------------------------------------------------*/
  631.  
  632.     if (FUNCTION == APPEND_NAME) {
  633. #ifdef SHORT_NAMES
  634.         char *old_end = end;
  635. #endif
  636.  
  637.         Trace((stderr, "appending filename [%s]\n", FnFilter1(pathcomp)));
  638.         while ((*end = *pathcomp++) != '\0') {
  639.             ++end;
  640. #ifdef SHORT_NAMES  /* truncate name at 14 characters, typically */
  641.             if ((end-old_end) > FILENAME_MAX)      /* GRR:  proper constant? */
  642.                 *(end = old_end + FILENAME_MAX) = '\0';
  643. #endif
  644.             if ((end-buildpath) >= FILNAMSIZ) {
  645.                 *--end = '\0';
  646.                 Info(slide, 0x201, ((char *)slide,
  647.                   "checkdir warning:  path too long; truncating\n\
  648.                   %s\n                -> %s\n",
  649.                   FnFilter1(G.filename), FnFilter2(buildpath)));
  650.                 return MPN_INF_TRUNC;   /* filename truncated */
  651.             }
  652.         }
  653.         Trace((stderr, "buildpath now = [%s]\n", FnFilter1(buildpath)));
  654.         /* could check for existence here, prompt for new name... */
  655.         return MPN_OK;
  656.     }
  657.  
  658. /*---------------------------------------------------------------------------
  659.     INIT:  allocate and initialize buffer space for the file currently being
  660.     extracted.  If file was renamed with an absolute path, don't prepend the
  661.     extract-to path.
  662.   ---------------------------------------------------------------------------*/
  663.  
  664. /* GRR:  for VMS and TOPS-20, add up to 13 to strlen */
  665.  
  666.     if (FUNCTION == INIT) {
  667.         Trace((stderr, "initializing buildpath to "));
  668. #ifdef ACORN_FTYPE_NFS
  669.         if ((buildpath = (char *)malloc(strlen(G.filename)+rootlen+
  670.                                         (uO.acorn_nfs_ext ? 5 : 1)))
  671. #else
  672.         if ((buildpath = (char *)malloc(strlen(G.filename)+rootlen+1))
  673. #endif
  674.             == (char *)NULL)
  675.             return MPN_NOMEM;
  676.         if ((rootlen > 0) && !renamed_fullpath) {
  677.             strcpy(buildpath, rootpath);
  678.             end = buildpath + rootlen;
  679.         } else {
  680.             *buildpath = '\0';
  681.             end = buildpath;
  682.         }
  683.         Trace((stderr, "[%s]\n", FnFilter1(buildpath)));
  684.         return MPN_OK;
  685.     }
  686.  
  687. /*---------------------------------------------------------------------------
  688.     ROOT:  if appropriate, store the path in rootpath and create it if neces-
  689.     sary; else assume it's a zipfile member and return.  This path segment
  690.     gets used in extracting all members from every zipfile specified on the
  691.     command line.
  692.   ---------------------------------------------------------------------------*/
  693.  
  694. #if (!defined(SFX) || defined(SFX_EXDIR))
  695.     if (FUNCTION == ROOT) {
  696.         Trace((stderr, "initializing root path to [%s]\n",
  697.           FnFilter1(pathcomp)));
  698.         if (pathcomp == (char *)NULL) {
  699.             rootlen = 0;
  700.             return MPN_OK;
  701.         }
  702.         if ((rootlen = strlen(pathcomp)) > 0) {
  703.             if (pathcomp[rootlen-1] == '/') {
  704.                 pathcomp[--rootlen] = '\0';
  705.             }
  706.             if (rootlen > 0 && (stat(pathcomp, &G.statbuf) ||
  707.                 !S_ISDIR(G.statbuf.st_mode)))       /* path does not exist */
  708.             {
  709.                 if (!G.create_dirs /* || iswild(pathcomp) */ ) {
  710.                     rootlen = 0;
  711.                     /* skip (or treat as stored file) */
  712.                     return MPN_INF_SKIP;
  713.                 }
  714.                 /* create the directory (could add loop here to scan pathcomp
  715.                  * and create more than one level, but why really necessary?) */
  716.                 if (mkdir(pathcomp) == -1) {
  717.                     Info(slide, 1, ((char *)slide,
  718.                       "checkdir:  cannot create extraction directory: %s\n",
  719.                       FnFilter1(pathcomp)));
  720.                     rootlen = 0;
  721.                     /* path didn't exist, tried to create, and failed: */
  722.                     /* file exists, or 2+ subdirectory levels required */
  723.                     return MPN_ERR_SKIP;
  724.                 }
  725.             }
  726.             if ((rootpath = (char *)malloc(rootlen+2)) == (char *)NULL) {
  727.                 rootlen = 0;
  728.                 return MPN_NOMEM;
  729.             }
  730.             strcpy(rootpath, pathcomp);
  731.             rootpath[rootlen++] = '/';
  732.             rootpath[rootlen] = '\0';
  733.             Trace((stderr, "rootpath now = [%s]\n", FnFilter1(rootpath)));
  734.         }
  735.         return MPN_OK;
  736.     }
  737. #endif /* !SFX || SFX_EXDIR */
  738.  
  739. /*---------------------------------------------------------------------------
  740.     END:  free rootpath, immediately prior to program exit.
  741.   ---------------------------------------------------------------------------*/
  742.  
  743.     if (FUNCTION == END) {
  744.         Trace((stderr, "freeing rootpath\n"));
  745.         if (rootlen > 0) {
  746.             free(rootpath);
  747.             rootlen = 0;
  748.         }
  749.         return MPN_OK;
  750.     }
  751.  
  752.     return MPN_INVALID; /* should never reach */
  753.  
  754. } /* end function checkdir() */
  755.  
  756.  
  757.  
  758. /****************************/
  759. /* Function close_outfile() */
  760. /****************************/
  761.  
  762. void close_outfile(__G)    /* GRR: change to return PK-style warning level */
  763.     __GDEF
  764. {
  765.     fclose(G.outfile);
  766.  
  767.     /* skip restoring time stamps on user's request */
  768.     if (uO.D_flag <= 1) {
  769.         WORD date = G.lrec.last_mod_dos_datetime >> 16;
  770.         WORD time = G.lrec.last_mod_dos_datetime & 0xffff;
  771.         static struct ModifyStructure changeBuffer;
  772.  
  773.         /* set the file's access and modification times */
  774.         changeBuffer.MLastAccessedDate = date;
  775.         changeBuffer.MLastUpdatedDate = date;
  776.         changeBuffer.MLastUpdatedTime = time;
  777.         if (ChangeDirectoryEntry(G.filename, &changeBuffer,
  778.               MLastAccessedDateBit | MLastUpdatedDateBit | MLastUpdatedTimeBit,
  779.               0))
  780.         {
  781.             if (uO.qflag)
  782.                 Info(slide, 0x201, ((char *)slide,
  783.                   "warning:  cannot set times for %s\n",
  784.                   FnFilter1(G.filename)));
  785.             else
  786.                 Info(slide, 0x201, ((char *)slide,
  787.                   " (warning) cannot set times"));
  788.         }
  789.     }
  790.  
  791. /*---------------------------------------------------------------------------
  792.     Change the file permissions from default ones to those stored in the
  793.     zipfile.
  794.   ---------------------------------------------------------------------------*/
  795.  
  796.     if (chmod(G.filename, 0xffff & G.pInfo->file_attr))
  797.         perror("chmod (file attributes) error");
  798.  
  799. } /* end function close_outfile() */
  800.  
  801.  
  802. #ifdef TIMESTAMP
  803.  
  804. /***************************/
  805. /*  Function stamp_file()  */
  806. /***************************/
  807.  
  808. int stamp_file(fname, modtime)
  809.     ZCONST char *fname;
  810.     time_t modtime;
  811. {
  812.     ztimbuf tp;
  813.  
  814.     tp.modtime = tp.actime = modtime;
  815.     return (utime(fname, &tp));
  816.  
  817. } /* end function stamp_file() */
  818.  
  819. #endif /* TIMESTAMP */
  820.  
  821.  
  822. /************************/
  823. /*  Function version()  */
  824. /************************/
  825.  
  826. void version(__G)
  827.     __GDEF
  828. {
  829.     int len;
  830. #if defined(__IBMC__) || defined(__WATCOMC__) || defined(_MSC_VER)
  831.     char buf[80];
  832. #endif
  833.  
  834.     len = sprintf((char *)slide, LoadFarString(CompiledWith),
  835.  
  836. #if defined(__GNUC__)
  837. #  ifdef __EMX__  /* __EMX__ is defined as "1" only (sigh) */
  838.       "emx+gcc ", __VERSION__,
  839. #  else
  840.       "gcc/2 ", __VERSION__,
  841. #  endif
  842. #elif defined(__WATCOMC__)
  843.       "Watcom C", (sprintf(buf, " (__WATCOMC__ = %d)", __WATCOMC__), buf),
  844. #elif defined(__TURBOC__)
  845. #  ifdef __BORLANDC__
  846.       "Borland C++",
  847. #    if (__BORLANDC__ < 0x0460)
  848.         " 1.0",
  849. #    elif (__BORLANDC__ == 0x0460)
  850.         " 1.5",                     /* from Kai Uwe:  three less than DOS */
  851. #    else
  852.         " 2.0",                     /* (__BORLANDC__ == 0x0500)? */
  853. #    endif
  854. #  else
  855.       "Turbo C",                    /* these are probably irrelevant */
  856. #    if (__TURBOC__ >= 661)
  857.        "++ 1.0 or later",
  858. #    elif (__TURBOC__ == 661)
  859.        " 3.0?",
  860. #    elif (__TURBOC__ == 397)
  861.        " 2.0",
  862. #    else
  863.        " 1.0 or 1.5?",
  864. #    endif
  865. #  endif
  866. #elif defined(MSC)
  867.       "Microsoft C ",
  868. #  ifdef _MSC_VER
  869.       (sprintf(buf, "%d.%02d", _MSC_VER/100, _MSC_VER%100), buf),
  870. #  else
  871.       "5.1 or earlier",
  872. #  endif
  873. #else
  874.       "unknown compiler", "",
  875. #endif /* ?compilers */
  876.  
  877.       "NetWare",
  878.       " (32-bit)",
  879.  
  880. #ifdef __DATE__
  881.       " on ", __DATE__
  882. #else
  883.       "", ""
  884. #endif
  885.     );
  886.  
  887.     (*G.message)((zvoid *)&G, slide, (ulg)len, 0);
  888.                                 /* MSC can't handle huge macro expansions */
  889.  
  890. } /* end function version() */
  891.  
  892.  
  893. #ifdef MORE
  894.  
  895. /*************************/
  896. /* Function screensize() */
  897. /*************************/
  898.  
  899. int screensize(int *tt_rows, int *tt_cols)
  900. {
  901.     WORD height;
  902.     WORD width;
  903.  
  904.     if (GetSizeOfScreen(&height, &width) == 0) {
  905.         if (tt_rows != NULL) *tt_rows = height;
  906.         if (tt_cols != NULL) *tt_cols = width;
  907.         return 0;       /* signal success */
  908.     } else {
  909.         if (tt_rows != NULL) *tt_rows = 25;
  910.         if (tt_cols != NULL) *tt_cols = 80;
  911.         return 1;       /* signal failure */
  912.     }
  913. }
  914.  
  915. #endif /* MORE */
  916.