home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 9 Archive / 09-Archive.zip / unzip540.zip / beos / beos.c next >
C/C++ Source or Header  |  1998-09-20  |  41KB  |  1,183 lines

  1. /*---------------------------------------------------------------------------
  2.  
  3.   beos.c
  4.  
  5.   BeOS-specific routines for use with Info-ZIP's UnZip 5.30 and later.
  6.   (based on unix/unix.c)
  7.  
  8.   Contains:  do_wild()           <-- generic enough to put in fileio.c?
  9.              mapattr()
  10.              mapname()
  11.              checkdir()
  12.              close_outfile()
  13.              set_direc_attribs()
  14.              stamp_file()
  15.              version()
  16.              scanBeOSexfield()
  17.              set_file_attrs()
  18.              setBeOSexfield()
  19.              printBeOSexfield()
  20.              assign_MIME()
  21.  
  22.   ---------------------------------------------------------------------------*/
  23.  
  24.  
  25. #define UNZIP_INTERNAL
  26. #include "unzip.h"
  27.  
  28. #include "beos.h"
  29. #include <errno.h>             /* Just make sure we've got a few things... */
  30. #include <sys/types.h>
  31. #include <sys/stat.h>
  32. #include <fcntl.h>
  33.  
  34. #include <dirent.h>
  35.  
  36. /* For the new post-DR8 file attributes */
  37. #include <kernel/fs_attr.h>
  38. #include <support/byteorder.h>
  39. #include <storage/Mime.h>
  40.  
  41. static uch *scanBeOSexfield  OF((const uch *ef_ptr, unsigned ef_len));
  42. static int  set_file_attrs( const char *, const unsigned char *, const off_t );
  43. static void setBeOSexfield   OF((const char *path, uch *extra_field));
  44. static void printBeOSexfield OF((int isdir, uch *extra_field));
  45. #ifdef BEOS_ASSIGN_FILETYPE
  46. static void assign_MIME( const char * );
  47. #endif
  48.  
  49. static int created_dir;        /* used in mapname(), checkdir() */
  50. static int renamed_fullpath;   /* ditto */
  51.  
  52. #ifndef SFX
  53.  
  54. /**********************/
  55. /* Function do_wild() */   /* for porting:  dir separator; match(ignore_case) */
  56. /**********************/
  57.  
  58. char *do_wild(__G__ wildspec)
  59.     __GDEF
  60.     char *wildspec;         /* only used first time on a given dir */
  61. {
  62.     static DIR *dir = (DIR *)NULL;
  63.     static char *dirname, *wildname, matchname[FILNAMSIZ];
  64.     static int firstcall=TRUE, have_dirname, dirnamelen;
  65.     struct dirent *file;
  66.  
  67.  
  68.     /* Even when we're just returning wildspec, we *always* do so in
  69.      * matchname[]--calling routine is allowed to append four characters
  70.      * to the returned string, and wildspec may be a pointer to argv[].
  71.      */
  72.     if (firstcall) {        /* first call:  must initialize everything */
  73.         firstcall = FALSE;
  74.  
  75.         if (!iswild(wildspec)) {
  76.             strcpy(matchname, wildspec);
  77.             have_dirname = FALSE;
  78.             dir = NULL;
  79.             return matchname;
  80.         }
  81.  
  82.         /* break the wildspec into a directory part and a wildcard filename */
  83.         if ((wildname = strrchr(wildspec, '/')) == (char *)NULL) {
  84.             dirname = ".";
  85.             dirnamelen = 1;
  86.             have_dirname = FALSE;
  87.             wildname = wildspec;
  88.         } else {
  89.             ++wildname;     /* point at character after '/' */
  90.             dirnamelen = wildname - wildspec;
  91.             if ((dirname = (char *)malloc(dirnamelen+1)) == (char *)NULL) {
  92.                 Info(slide, 0x201, ((char *)slide,
  93.                   "warning:  cannot allocate wildcard buffers\n"));
  94.                 strcpy(matchname, wildspec);
  95.                 return matchname;   /* but maybe filespec was not a wildcard */
  96.             }
  97.             strncpy(dirname, wildspec, dirnamelen);
  98.             dirname[dirnamelen] = '\0';   /* terminate for strcpy below */
  99.             have_dirname = TRUE;
  100.         }
  101.  
  102.         if ((dir = opendir(dirname)) != (DIR *)NULL) {
  103.             while ((file = readdir(dir)) != (struct dirent *)NULL) {
  104.                 if (file->d_name[0] == '.' && wildname[0] != '.')
  105.                     continue;  /* Unix:  '*' and '?' do not match leading dot */
  106.                 if (match(file->d_name, wildname, 0)) {  /* 0 == case sens. */
  107.                     if (have_dirname) {
  108.                         strcpy(matchname, dirname);
  109.                         strcpy(matchname+dirnamelen, file->d_name);
  110.                     } else
  111.                         strcpy(matchname, file->d_name);
  112.                     return matchname;
  113.                 }
  114.             }
  115.             /* if we get to here directory is exhausted, so close it */
  116.             closedir(dir);
  117.             dir = (DIR *)NULL;
  118.         }
  119.  
  120.         /* return the raw wildspec in case that works (e.g., directory not
  121.          * searchable, but filespec was not wild and file is readable) */
  122.         strcpy(matchname, wildspec);
  123.         return matchname;
  124.     }
  125.  
  126.     /* last time through, might have failed opendir but returned raw wildspec */
  127.     if (dir == (DIR *)NULL) {
  128.         firstcall = TRUE;  /* nothing left to try--reset for new wildspec */
  129.         if (have_dirname)
  130.             free(dirname);
  131.         return (char *)NULL;
  132.     }
  133.  
  134.     /* If we've gotten this far, we've read and matched at least one entry
  135.      * successfully (in a previous call), so dirname has been copied into
  136.      * matchname already.
  137.      */
  138.     while ((file = readdir(dir)) != (struct dirent *)NULL) {
  139.         if (file->d_name[0] == '.' && wildname[0] != '.')
  140.             continue;   /* Unix:  '*' and '?' do not match leading dot */
  141.         if (match(file->d_name, wildname, 0)) {   /* 0 == don't ignore case */
  142.             if (have_dirname) {
  143.                 /* strcpy(matchname, dirname); */
  144.                 strcpy(matchname+dirnamelen, file->d_name);
  145.             } else
  146.                 strcpy(matchname, file->d_name);
  147.             return matchname;
  148.         }
  149.     }
  150.  
  151.     closedir(dir);     /* have read at least one dir entry; nothing left */
  152.     dir = (DIR *)NULL;
  153.     firstcall = TRUE;  /* reset for new wildspec */
  154.     if (have_dirname)
  155.         free(dirname);
  156.     return (char *)NULL;
  157.  
  158. } /* end function do_wild() */
  159.  
  160. #endif /* !SFX */
  161.  
  162.  
  163.  
  164.  
  165.  
  166. /**********************/
  167. /* Function mapattr() */
  168. /**********************/
  169.  
  170. int mapattr(__G)
  171.     __GDEF
  172. {
  173.     ulg tmp = G.crec.external_file_attributes;
  174.  
  175.     switch (G.pInfo->hostnum) {
  176.         case AMIGA_:
  177.             tmp = (unsigned)(tmp>>17 & 7);   /* Amiga RWE bits */
  178.             G.pInfo->file_attr = (unsigned)(tmp<<6 | tmp<<3 | tmp);
  179.             break;
  180.         case UNIX_:
  181.         case VMS_:
  182.         case ACORN_:
  183.         case ATARI_:
  184.         case BEOS_:
  185.         case QDOS_:
  186.         case TANDEM_:
  187.             G.pInfo->file_attr = (unsigned)(tmp >> 16);
  188.             if (G.pInfo->file_attr != 0 || !G.extra_field) {
  189.                 return 0;
  190.             } else {
  191.                 /* Some (non-Info-ZIP) implementations of Zip for Unix and
  192.                    VMS (and probably others ??) leave 0 in the upper 16-bit
  193.                    part of the external_file_attributes field. Instead, they
  194.                    store file permission attributes in some extra field.
  195.                    As a work-around, we search for the presence of one of
  196.                    these extra fields and fall back to the MSDOS compatible
  197.                    part of external_file_attributes if one of the known
  198.                    e.f. types has been detected.
  199.                    Later, we might implement extraction of the permission
  200.                    bits from the VMS extra field. But for now, the work-around
  201.                    should be sufficient to provide "readable" extracted files.
  202.                    (For ASI Unix e.f., an experimental remap of the e.f.
  203.                    mode value IS already provided!)
  204.                  */
  205.                 ush ebID;
  206.                 unsigned ebLen;
  207.                 uch *ef = G.extra_field;
  208.                 unsigned ef_len = G.crec.extra_field_length;
  209.                 int r = FALSE;
  210.  
  211.                 while (!r && ef_len >= EB_HEADSIZE) {
  212.                     ebID = makeword(ef);
  213.                     ebLen = (unsigned)makeword(ef+EB_LEN);
  214.                     if (ebLen > (ef_len - EB_HEADSIZE))
  215.                         /* discoverd some e.f. inconsistency! */
  216.                         break;
  217.                     switch (ebID) {
  218.                       case EF_ASIUNIX:
  219.                         if (ebLen >= (EB_ASI_MODE+2)) {
  220.                             G.pInfo->file_attr =
  221.                               (unsigned)makeword(ef+(EB_HEADSIZE+EB_ASI_MODE));
  222.                             /* force stop of loop: */
  223.                             ef_len = (ebLen + EB_HEADSIZE);
  224.                             break;
  225.                         }
  226.                         /* else: fall through! */
  227.                       case EF_PKVMS:
  228.                         /* "found nondecypherable e.f. with perm. attr" */
  229.                         r = TRUE;
  230.                       default:
  231.                         break;
  232.                     }
  233.                     ef_len -= (ebLen + EB_HEADSIZE);
  234.                     ef += (ebLen + EB_HEADSIZE);
  235.                 }
  236.                 if (!r)
  237.                     return 0;
  238.             }
  239.             /* fall through! */
  240.         /* all remaining cases:  expand MSDOS read-only bit into write perms */
  241.         case FS_FAT_:
  242.         case FS_HPFS_:
  243.         case FS_NTFS_:
  244.         case MAC_:
  245.         case TOPS20_:
  246.         default:
  247.             tmp = !(tmp & 1) << 1;   /* read-only bit --> write perms bits */
  248.             G.pInfo->file_attr = (unsigned)(0444 | tmp<<6 | tmp<<3 | tmp);
  249.             break;
  250.     } /* end switch (host-OS-created-by) */
  251.  
  252.     /* for originating systems with no concept of "group," "other," "system": */
  253.     umask( (int)(tmp=umask(0)) );    /* apply mask to expanded r/w(/x) perms */
  254.     G.pInfo->file_attr &= ~tmp;
  255.  
  256.     return 0;
  257.  
  258. } /* end function mapattr() */
  259.  
  260.  
  261.  
  262.  
  263.  
  264. /************************/
  265. /*  Function mapname()  */
  266. /************************/
  267.                              /* return 0 if no error, 1 if caution (filename */
  268. int mapname(__G__ renamed)   /*  truncated), 2 if warning (skip file because */
  269.     __GDEF                   /*  dir doesn't exist), 3 if error (skip file), */
  270.     int renamed;             /*  or 10 if out of memory (skip file) */
  271. {                            /*  [also IZ_VOL_LABEL, IZ_CREATED_DIR] */
  272.     char pathcomp[FILNAMSIZ];      /* path-component buffer */
  273.     char *pp, *cp=(char *)NULL;    /* character pointers */
  274.     char *lastsemi=(char *)NULL;   /* pointer to last semi-colon in pathcomp */
  275.     int quote = FALSE;             /* flags */
  276.     int error = 0;
  277.     register unsigned workch;      /* hold the character being tested */
  278.  
  279.  
  280. /*---------------------------------------------------------------------------
  281.     Initialize various pointers and counters and stuff.
  282.   ---------------------------------------------------------------------------*/
  283.  
  284.     if (G.pInfo->vollabel)
  285.         return IZ_VOL_LABEL;    /* can't set disk volume labels in BeOS */
  286.  
  287.     /* can create path as long as not just freshening, or if user told us */
  288.     G.create_dirs = (!uO.fflag || renamed);
  289.  
  290.     created_dir = FALSE;        /* not yet */
  291.  
  292.     /* user gave full pathname:  don't prepend rootpath */
  293.     renamed_fullpath = (renamed && (*G.filename == '/'));
  294.  
  295.     if (checkdir(__G__ (char *)NULL, INIT) == 10)
  296.         return 10;              /* initialize path buffer, unless no memory */
  297.  
  298.     *pathcomp = '\0';           /* initialize translation buffer */
  299.     pp = pathcomp;              /* point to translation buffer */
  300.     if (uO.jflag)               /* junking directories */
  301.         cp = (char *)strrchr(G.filename, '/');
  302.     if (cp == (char *)NULL)     /* no '/' or not junking dirs */
  303.         cp = G.filename;        /* point to internal zipfile-member pathname */
  304.     else
  305.         ++cp;                   /* point to start of last component of path */
  306.  
  307. /*---------------------------------------------------------------------------
  308.     Begin main loop through characters in filename.
  309.   ---------------------------------------------------------------------------*/
  310.  
  311.     while ((workch = (uch)*cp++) != 0) {
  312.  
  313.         if (quote) {                 /* if character quoted, */
  314.             *pp++ = (char)workch;    /*  include it literally */
  315.             quote = FALSE;
  316.         } else
  317.             switch (workch) {
  318.             case '/':             /* can assume -j flag not given */
  319.                 *pp = '\0';
  320.                 if ((error = checkdir(__G__ pathcomp, APPEND_DIR)) > 1)
  321.                     return error;
  322.                 pp = pathcomp;    /* reset conversion buffer for next piece */
  323.                 lastsemi = (char *)NULL; /* leave directory semi-colons alone */
  324.                 break;
  325.  
  326.             case ';':             /* VMS version (or DEC-20 attrib?) */
  327.                 lastsemi = pp;
  328.                 *pp++ = ';';      /* keep for now; remove VMS ";##" */
  329.                 break;            /*  later, if requested */
  330.  
  331.             case '\026':          /* control-V quote for special chars */
  332.                 quote = TRUE;     /* set flag for next character */
  333.                 break;
  334.  
  335.             default:
  336.                 /* allow European characters in filenames: */
  337.                 if (isprint(workch) || (128 <= workch && workch <= 254))
  338.                     *pp++ = (char)workch;
  339.             } /* end switch */
  340.  
  341.     } /* end while loop */
  342.  
  343.     *pp = '\0';                   /* done with pathcomp:  terminate it */
  344.  
  345.     /* if not saving them, remove VMS version numbers (appended ";###") */
  346.     if (!uO.V_flag && lastsemi) {
  347.         pp = lastsemi + 1;
  348.         while (isdigit((uch)(*pp)))
  349.             ++pp;
  350.         if (*pp == '\0')          /* only digits between ';' and end:  nuke */
  351.             *lastsemi = '\0';
  352.     }
  353.  
  354. /*---------------------------------------------------------------------------
  355.     Report if directory was created (and no file to create:  filename ended
  356.     in '/'), check name to be sure it exists, and combine path and name be-
  357.     fore exiting.
  358.   ---------------------------------------------------------------------------*/
  359.  
  360.     if (G.filename[strlen(G.filename) - 1] == '/') {
  361.         checkdir(__G__ G.filename, GETPATH);
  362.         if (created_dir) {
  363.             if (QCOND2) {
  364.                 Info(slide, 0, ((char *)slide, "   creating: %s\n",
  365.                   G.filename));
  366.             }
  367.  
  368. #ifndef NO_CHMOD
  369.             /* set approx. dir perms (make sure can still read/write in dir) */
  370.             if (chmod(G.filename, (0xffff & G.pInfo->file_attr) | 0700))
  371.                 perror("chmod (directory attributes) error");
  372. #endif
  373.  
  374.             if (!uO.J_flag) {   /* Handle the BeOS extra field if present. */
  375.                 void *ptr = scanBeOSexfield( G.extra_field,
  376.                                              G.lrec.extra_field_length );
  377.                 if (ptr) {
  378.                     setBeOSexfield( G.filename, ptr );
  379.                 } else {
  380. #ifdef BEOS_ASSIGN_FILETYPE
  381.                     /* Otherwise, ask the system to assign a MIME type. */
  382.                     assign_MIME( G.filename );
  383. #else
  384.                     ; /* optimise me away baby */
  385. #endif
  386.                 }
  387.             }
  388.  
  389.             return IZ_CREATED_DIR;   /* set dir time (note trailing '/') */
  390.         }
  391.         /* TODO: should we re-write the BeOS extra field data in case it's */
  392.         /* changed?                                                        */
  393.         return 2;   /* dir existed already; don't look for data to extract */
  394.     }
  395.  
  396.     if (*pathcomp == '\0') {
  397.         Info(slide, 1, ((char *)slide, "mapname:  conversion of %s failed\n",
  398.           G.filename));
  399.         return 3;
  400.     }
  401.  
  402.     checkdir(__G__ pathcomp, APPEND_NAME);  /* returns 1 if truncated: care? */
  403.     checkdir(__G__ G.filename, GETPATH);
  404.  
  405.     return error;
  406.  
  407. } /* end function mapname() */
  408.  
  409.  
  410.  
  411.  
  412.  
  413. /***********************/
  414. /* Function checkdir() */
  415. /***********************/
  416.  
  417. int checkdir(__G__ pathcomp, flag)
  418.     __GDEF
  419.     char *pathcomp;
  420.     int flag;
  421. /*
  422.  * returns:  1 - (on APPEND_NAME) truncated filename
  423.  *           2 - path doesn't exist, not allowed to create
  424.  *           3 - path doesn't exist, tried to create and failed; or
  425.  *               path exists and is not a directory, but is supposed to be
  426.  *           4 - path is too long
  427.  *          10 - can't allocate memory for filename buffers
  428.  */
  429. {
  430.     static int rootlen = 0;   /* length of rootpath */
  431.     static char *rootpath;    /* user's "extract-to" directory */
  432.     static char *buildpath;   /* full path (so far) to extracted file */
  433.     static char *end;         /* pointer to end of buildpath ('\0') */
  434.  
  435. #   define FN_MASK   7
  436. #   define FUNCTION  (flag & FN_MASK)
  437.  
  438.  
  439.  
  440. /*---------------------------------------------------------------------------
  441.     APPEND_DIR:  append the path component to the path being built and check
  442.     for its existence.  If doesn't exist and we are creating directories, do
  443.     so for this one; else signal success or error as appropriate.
  444.   ---------------------------------------------------------------------------*/
  445.  
  446.     if (FUNCTION == APPEND_DIR) {
  447.         int too_long = FALSE;
  448. #ifdef SHORT_NAMES
  449.         char *old_end = end;
  450. #endif
  451.  
  452.         Trace((stderr, "appending dir segment [%s]\n", pathcomp));
  453.         while ((*end = *pathcomp++) != '\0')
  454.             ++end;
  455. #ifdef SHORT_NAMES   /* path components restricted to 14 chars, typically */
  456.         if ((end-old_end) > FILENAME_MAX)  /* GRR:  proper constant? */
  457.             *(end = old_end + FILENAME_MAX) = '\0';
  458. #endif
  459.  
  460.         /* GRR:  could do better check, see if overrunning buffer as we go:
  461.          * check end-buildpath after each append, set warning variable if
  462.          * within 20 of FILNAMSIZ; then if var set, do careful check when
  463.          * appending.  Clear variable when begin new path. */
  464.  
  465.         if ((end-buildpath) > FILNAMSIZ-3)  /* need '/', one-char name, '\0' */
  466.             too_long = TRUE;                /* check if extracting directory? */
  467.         if (stat(buildpath, &G.statbuf)) {  /* path doesn't exist */
  468.             if (!G.create_dirs) { /* told not to create (freshening) */
  469.                 free(buildpath);
  470.                 return 2;         /* path doesn't exist:  nothing to do */
  471.             }
  472.             if (too_long) {
  473.                 Info(slide, 1, ((char *)slide,
  474.                   "checkdir error:  path too long: %s\n", buildpath));
  475.                 free(buildpath);
  476.                 return 4;         /* no room for filenames:  fatal */
  477.             }
  478.             if (mkdir(buildpath, 0777) == -1) {   /* create the directory */
  479.                 Info(slide, 1, ((char *)slide,
  480.                   "checkdir error:  cannot create %s\n\
  481.                  unable to process %s.\n", buildpath, G.filename));
  482.                 free(buildpath);
  483.                 return 3;      /* path didn't exist, tried to create, failed */
  484.             }
  485.             created_dir = TRUE;
  486.         } else if (!S_ISDIR(G.statbuf.st_mode)) {
  487.             Info(slide, 1, ((char *)slide,
  488.               "checkdir error:  %s exists but is not directory\n\
  489.                  unable to process %s.\n", buildpath, G.filename));
  490.             free(buildpath);
  491.             return 3;          /* path existed but wasn't dir */
  492.         }
  493.         if (too_long) {
  494.             Info(slide, 1, ((char *)slide,
  495.               "checkdir error:  path too long: %s\n", buildpath));
  496.             free(buildpath);
  497.             return 4;         /* no room for filenames:  fatal */
  498.         }
  499.         *end++ = '/';
  500.         *end = '\0';
  501.         Trace((stderr, "buildpath now = [%s]\n", buildpath));
  502.         return 0;
  503.  
  504.     } /* end if (FUNCTION == APPEND_DIR) */
  505.  
  506. /*---------------------------------------------------------------------------
  507.     GETPATH:  copy full path to the string pointed at by pathcomp, and free
  508.     buildpath.
  509.   ---------------------------------------------------------------------------*/
  510.  
  511.     if (FUNCTION == GETPATH) {
  512.         strcpy(pathcomp, buildpath);
  513.         Trace((stderr, "getting and freeing path [%s]\n", pathcomp));
  514.         free(buildpath);
  515.         buildpath = end = (char *)NULL;
  516.         return 0;
  517.     }
  518.  
  519. /*---------------------------------------------------------------------------
  520.     APPEND_NAME:  assume the path component is the filename; append it and
  521.     return without checking for existence.
  522.   ---------------------------------------------------------------------------*/
  523.  
  524.     if (FUNCTION == APPEND_NAME) {
  525. #ifdef SHORT_NAMES
  526.         char *old_end = end;
  527. #endif
  528.  
  529.         Trace((stderr, "appending filename [%s]\n", pathcomp));
  530.         while ((*end = *pathcomp++) != '\0') {
  531.             ++end;
  532. #ifdef SHORT_NAMES  /* truncate name at 14 characters, typically */
  533.             if ((end-old_end) > FILENAME_MAX)      /* GRR:  proper constant? */
  534.                 *(end = old_end + FILENAME_MAX) = '\0';
  535. #endif
  536.             if ((end-buildpath) >= FILNAMSIZ) {
  537.                 *--end = '\0';
  538.                 Info(slide, 0x201, ((char *)slide,
  539.                   "checkdir warning:  path too long; truncating\n\
  540.                    %s\n                -> %s\n", G.filename, buildpath));
  541.                 return 1;   /* filename truncated */
  542.             }
  543.         }
  544.         Trace((stderr, "buildpath now = [%s]\n", buildpath));
  545.         return 0;  /* could check for existence here, prompt for new name... */
  546.     }
  547.  
  548. /*---------------------------------------------------------------------------
  549.     INIT:  allocate and initialize buffer space for the file currently being
  550.     extracted.  If file was renamed with an absolute path, don't prepend the
  551.     extract-to path.
  552.   ---------------------------------------------------------------------------*/
  553.  
  554. /* GRR:  for VMS and TOPS-20, add up to 13 to strlen */
  555.  
  556.     if (FUNCTION == INIT) {
  557.         Trace((stderr, "initializing buildpath to "));
  558.         if ((buildpath = (char *)malloc(strlen(G.filename)+rootlen+1)) ==
  559.             (char *)NULL)
  560.             return 10;
  561.         if ((rootlen > 0) && !renamed_fullpath) {
  562.             strcpy(buildpath, rootpath);
  563.             end = buildpath + rootlen;
  564.         } else {
  565.             *buildpath = '\0';
  566.             end = buildpath;
  567.         }
  568.         Trace((stderr, "[%s]\n", buildpath));
  569.         return 0;
  570.     }
  571.  
  572. /*---------------------------------------------------------------------------
  573.     ROOT:  if appropriate, store the path in rootpath and create it if neces-
  574.     sary; else assume it's a zipfile member and return.  This path segment
  575.     gets used in extracting all members from every zipfile specified on the
  576.     command line.
  577.   ---------------------------------------------------------------------------*/
  578.  
  579. #if (!defined(SFX) || defined(SFX_EXDIR))
  580.     if (FUNCTION == ROOT) {
  581.         Trace((stderr, "initializing root path to [%s]\n", pathcomp));
  582.         if (pathcomp == (char *)NULL) {
  583.             rootlen = 0;
  584.             return 0;
  585.         }
  586.         if ((rootlen = strlen(pathcomp)) > 0) {
  587.             if (pathcomp[rootlen-1] == '/') {
  588.                 pathcomp[--rootlen] = '\0';
  589.             }
  590.             if (rootlen > 0 && (stat(pathcomp, &G.statbuf) ||
  591.                 !S_ISDIR(G.statbuf.st_mode)))       /* path does not exist */
  592.             {
  593.                 if (!G.create_dirs /* || iswild(pathcomp) */ ) {
  594.                     rootlen = 0;
  595.                     return 2;   /* skip (or treat as stored file) */
  596.                 }
  597.                 /* create the directory (could add loop here to scan pathcomp
  598.                  * and create more than one level, but why really necessary?) */
  599.                 if (mkdir(pathcomp, 0777) == -1) {
  600.                     Info(slide, 1, ((char *)slide,
  601.                       "checkdir:  cannot create extraction directory: %s\n",
  602.                       pathcomp));
  603.                     rootlen = 0;   /* path didn't exist, tried to create, and */
  604.                     return 3;  /* failed:  file exists, or 2+ levels required */
  605.                 }
  606.             }
  607.             if ((rootpath = (char *)malloc(rootlen+2)) == (char *)NULL) {
  608.                 rootlen = 0;
  609.                 return 10;
  610.             }
  611.             strcpy(rootpath, pathcomp);
  612.             rootpath[rootlen++] = '/';
  613.             rootpath[rootlen] = '\0';
  614.             Trace((stderr, "rootpath now = [%s]\n", rootpath));
  615.         }
  616.         return 0;
  617.     }
  618. #endif /* !SFX || SFX_EXDIR */
  619.  
  620. /*---------------------------------------------------------------------------
  621.     END:  free rootpath, immediately prior to program exit.
  622.   ---------------------------------------------------------------------------*/
  623.  
  624.     if (FUNCTION == END) {
  625.         Trace((stderr, "freeing rootpath\n"));
  626.         if (rootlen > 0) {
  627.             free(rootpath);
  628.             rootlen = 0;
  629.         }
  630.         return 0;
  631.     }
  632.  
  633.     return 99;  /* should never reach */
  634.  
  635. } /* end function checkdir() */
  636.  
  637.  
  638.  
  639.  
  640.  
  641.  
  642. /****************************/
  643. /* Function close_outfile() */
  644. /****************************/
  645.  
  646. void close_outfile(__G)    /* GRR: change to return PK-style warning level */
  647.     __GDEF
  648. {
  649.     iztimes zt;
  650.     ush z_uidgid[2];
  651.     unsigned eb_izux_flg;
  652.  
  653. /*---------------------------------------------------------------------------
  654.     If symbolic links are supported, allocate a storage area, put the uncom-
  655.     pressed "data" in it, and create the link.  Since we know it's a symbolic
  656.     link to start with, we shouldn't have to worry about overflowing unsigned
  657.     ints with unsigned longs.
  658.   ---------------------------------------------------------------------------*/
  659.  
  660. #ifdef SYMLINKS
  661.     if (G.symlnk) {
  662.         unsigned ucsize = (unsigned)G.lrec.ucsize;
  663.         char *linktarget = (char *)malloc((unsigned)G.lrec.ucsize+1);
  664.  
  665.         fclose(G.outfile);                      /* close "data" file... */
  666.         G.outfile = fopen(G.filename, FOPR);    /* ...and reopen for reading */
  667.         if (!linktarget || fread(linktarget, 1, ucsize, G.outfile) !=
  668.                            (int)ucsize)
  669.         {
  670.             Info(slide, 0x201, ((char *)slide,
  671.               "warning:  symbolic link (%s) failed\n", G.filename));
  672.             if (linktarget)
  673.                 free(linktarget);
  674.             fclose(G.outfile);
  675.             return;
  676.         }
  677.         fclose(G.outfile);                  /* close "data" file for good... */
  678.         unlink(G.filename);                 /* ...and delete it */
  679.         linktarget[ucsize] = '\0';
  680.         if (QCOND2)
  681.             Info(slide, 0, ((char *)slide, "-> %s ", linktarget));
  682.         if (symlink(linktarget, G.filename))  /* create the real link */
  683.             perror("symlink error");
  684.  
  685.         if (!uO.J_flag) {
  686.             /* Symlinks can have attributes, too. */
  687.             void *ptr = scanBeOSexfield( G.extra_field,
  688.                                          G.lrec.extra_field_length );
  689.             if (ptr) {
  690.                 setBeOSexfield( G.filename, ptr );
  691.             } else {
  692.                 /* Otherwise, ask the system to try assigning a MIME type. */
  693. #ifdef BEOS_ASSIGN_FILETYPE
  694.                 assign_MIME( G.filename );
  695. #else
  696.                 ; /* optimise me away, baby */
  697. #endif
  698.             }
  699.         }
  700.  
  701.         free(linktarget);
  702.         return;                             /* can't set time on symlinks */
  703.     }
  704. #endif /* SYMLINKS */
  705.  
  706.     fclose(G.outfile);
  707.  
  708. /*---------------------------------------------------------------------------
  709.     Change the file permissions from default ones to those stored in the
  710.     zipfile.
  711.   ---------------------------------------------------------------------------*/
  712.  
  713. #ifndef NO_CHMOD
  714.     if (chmod(G.filename, 0xffff & G.pInfo->file_attr))
  715.         perror("chmod (file attributes) error");
  716. #endif
  717.  
  718. /*---------------------------------------------------------------------------
  719.     Convert from MSDOS-format local time and date to Unix-format 32-bit GMT
  720.     time:  adjust base year from 1980 to 1970, do usual conversions from
  721.     yy/mm/dd hh:mm:ss to elapsed seconds, and account for timezone and day-
  722.     light savings time differences.  If we have a Unix extra field, however,
  723.     we're laughing:  both mtime and atime are ours.  On the other hand, we
  724.     then have to check for restoration of UID/GID.
  725.   ---------------------------------------------------------------------------*/
  726.  
  727.     eb_izux_flg = (G.extra_field ? ef_scan_for_izux(G.extra_field,
  728.                    G.lrec.extra_field_length, 0, G.lrec.last_mod_dos_datetime,
  729. #ifdef IZ_CHECK_TZ
  730.                    (G.tz_is_valid ? &zt : NULL),
  731. #else
  732.                    &zt,
  733. #endif
  734.                    z_uidgid) : 0);
  735.     if (eb_izux_flg & EB_UT_FL_MTIME) {
  736.         TTrace((stderr, "\nclose_outfile:  Unix e.f. modif. time = %ld\n",
  737.           zt.mtime));
  738.     } else {
  739.         zt.mtime = dos_to_unix_time(G.lrec.last_mod_dos_datetime);
  740.     }
  741.     if (eb_izux_flg & EB_UT_FL_ATIME) {
  742.         TTrace((stderr, "close_outfile:  Unix e.f. access time = %ld\n",
  743.           zt.atime));
  744.     } else {
  745.         zt.atime = zt.mtime;
  746.         TTrace((stderr, "\nclose_outfile:  modification/access times = %ld\n",
  747.           zt.mtime));
  748.     }
  749.  
  750.     /* if -X option was specified and we have UID/GID info, restore it */
  751.     if (uO.X_flag && eb_izux_flg & EB_UX2_VALID) {
  752.         TTrace((stderr, "close_outfile:  restoring Unix UID/GID info\n"));
  753.         if (chown(G.filename, (uid_t)z_uidgid[0], (gid_t)z_uidgid[1]))
  754.         {
  755.             if (uO.qflag)
  756.                 Info(slide, 0x201, ((char *)slide,
  757.                   "warning:  cannot set UID %d and/or GID %d for %s\n",
  758.                   z_uidgid[0], z_uidgid[1], G.filename));
  759.             else
  760.                 Info(slide, 0x201, ((char *)slide,
  761.                   " (warning) cannot set UID %d and/or GID %d",
  762.                   z_uidgid[0], z_uidgid[1]));
  763.         }
  764.     }
  765.  
  766.     /* set the file's access and modification times */
  767.     if (utime(G.filename, (struct utimbuf *)&zt)) {
  768.         if (uO.qflag)
  769.             Info(slide, 0x201, ((char *)slide,
  770.               "warning:  cannot set time for %s\n", G.filename));
  771.         else
  772.             Info(slide, 0x201, ((char *)slide,
  773.               " (warning) cannot set time"));
  774.     }
  775.  
  776.     /* handle the BeOS extra field if present */
  777.     if (!uO.J_flag) {
  778.         void *ptr = scanBeOSexfield( G.extra_field,
  779.                                      G.lrec.extra_field_length );
  780.  
  781.         if (ptr) {
  782.             setBeOSexfield( G.filename, ptr );
  783.         } else {
  784. #ifdef BEOS_ASSIGN_FILETYPE
  785.             /* Otherwise, ask the system to try assigning a MIME type. */
  786.             assign_MIME( G.filename );
  787. #else
  788.             ; /* optimise me away baby */
  789. #endif
  790.         }
  791.     }
  792.  
  793. } /* end function close_outfile() */
  794.  
  795.  
  796.  
  797.  
  798. #ifdef SET_DIR_ATTRIB
  799. /* messages of code for setting directory attributes */
  800. static char Far DirlistUidGidFailed[] =
  801.   "warning:  cannot set UID %d and/or GID %d for %s\n";
  802. static char Far DirlistUtimeFailed[] =
  803.   "warning:  cannot set modification, access times for %s\n";
  804. #  ifndef NO_CHMOD
  805.   static char Far DirlistChmodFailed[] =
  806.     "warning:  cannot set permissions for %s\n";
  807. #  endif
  808.  
  809.  
  810. int set_direc_attribs(__G__ d)
  811.     __GDEF
  812.     dirtime *d;
  813. {
  814.     int errval = PK_OK;
  815.  
  816.     if (d->have_uidgid &&
  817.         chown(d->fn, (uid_t)d->uidgid[0], (gid_t)d->uidgid[1]))
  818.     {
  819.         Info(slide, 0x201, ((char *)slide,
  820.           LoadFarString(DirlistUidGidFailed),
  821.           d->uidgid[0], d->uidgid[1], d->fn));
  822.         if (!errval)
  823.             errval = PK_WARN;
  824.     }
  825.     if (utime(d->fn, (const struct utimbuf *)&d->u.t2)) {
  826.         Info(slide, 0x201, ((char *)slide,
  827.           LoadFarString(DirlistUtimeFailed), d->fn));
  828.         if (!errval)
  829.             errval = PK_WARN;
  830.     }
  831. #ifndef NO_CHMOD
  832.     if (chmod(d->fn, 0xffff & d->perms)) {
  833.         Info(slide, 0x201, ((char *)slide,
  834.           LoadFarString(DirlistChmodFailed), d->fn));
  835.         /* perror("chmod (file attributes) error"); */
  836.         if (!errval)
  837.             errval = PK_WARN;
  838.     }
  839. #endif /* !NO_CHMOD */
  840.     return errval;
  841. } /* end function set_directory_attributes() */
  842.  
  843. #endif /* SET_DIR_ATTRIB */
  844.  
  845.  
  846.  
  847.  
  848. #ifdef TIMESTAMP
  849.  
  850. /***************************/
  851. /*  Function stamp_file()  */
  852. /***************************/
  853.  
  854. int stamp_file(fname, modtime)
  855.     ZCONST char *fname;
  856.     time_t modtime;
  857. {
  858.     struct utimbuf tp;
  859.  
  860.     tp.modtime = tp.actime = modtime;
  861.     return (utime(fname, &tp));
  862.  
  863. } /* end function stamp_file() */
  864.  
  865. #endif /* TIMESTAMP */
  866.  
  867.  
  868.  
  869.  
  870. #ifndef SFX
  871.  
  872. /************************/
  873. /*  Function version()  */
  874. /************************/
  875.  
  876. void version(__G)
  877.     __GDEF
  878. {
  879.     sprintf((char *)slide, LoadFarString(CompiledWith),
  880. #if defined(__MWERKS__)
  881.       "Metrowerks CodeWarrior", "",
  882. #elif defined(__GNUC__)
  883.       "GNU C ", __VERSION__,
  884. #endif
  885.       "BeOS ",
  886.  
  887. #ifdef __POWERPC__
  888.       "(PowerPC)",
  889. #else
  890. # ifdef __INTEL__
  891.       "(x86)",
  892. # else
  893.       "(unknown)",   /* someday we may have other architectures... */
  894. # endif
  895. #endif
  896.  
  897. #ifdef __DATE__
  898.       " on ", __DATE__
  899. #else
  900.       "", ""
  901. #endif
  902.     );
  903.  
  904.     (*G.message)((zvoid *)&G, slide, (ulg)strlen((char *)slide), 0);
  905.  
  906. } /* end function version() */
  907.  
  908. #endif /* !SFX */
  909.  
  910.  
  911.  
  912. /******************************/
  913. /* Extra field functions      */
  914. /******************************/
  915.  
  916. /*
  917. ** Scan the extra fields in extra_field, and look for a BeOS EF; return a
  918. ** pointer to that EF, or NULL if it's not there.
  919. */
  920. static uch *scanBeOSexfield( const uch *ef_ptr, unsigned ef_len )
  921. {
  922.     while( ef_ptr != NULL && ef_len >= EB_HEADSIZE ) {
  923.         unsigned eb_id  = makeword(EB_ID + ef_ptr);
  924.         unsigned eb_len = makeword(EB_LEN + ef_ptr);
  925.  
  926.         if (eb_len > (ef_len - EB_HEADSIZE)) {
  927.             Trace((stderr,
  928.               "scanBeOSexfield: block length %u > rest ef_size %u\n", eb_len,
  929.               ef_len - EB_HEADSIZE));
  930.             break;
  931.         }
  932.  
  933.         if( eb_id == EF_BEOS && eb_len >= EB_BEOS_HLEN ) {
  934.             return (uch *)ef_ptr;
  935.         }
  936.  
  937.         ef_ptr += (eb_len + EB_HEADSIZE);
  938.         ef_len -= (eb_len + EB_HEADSIZE);
  939.     }
  940.  
  941.     return NULL;
  942. }
  943.  
  944. /* Used by setBeOSexfield():
  945.  
  946. Set a file/directory's attributes to the attributes passed in.
  947.  
  948. If set_file_attrs() fails, an error will be returned:
  949.  
  950.      EOK - no errors occurred
  951.  
  952. (other values will be whatever the failed function returned; no docs
  953. yet, or I'd list a few)
  954. */
  955. static int set_file_attrs( const char *name,
  956.                            const unsigned char *attr_buff,
  957.                            const off_t attr_size )
  958. {
  959.     int                  retval = EOK;
  960.     unsigned char       *ptr;
  961.     const unsigned char *guard;
  962.     int                  fd;
  963.  
  964.     ptr   = (unsigned char *)attr_buff;
  965.     guard = ptr + attr_size;
  966.  
  967.     fd = open( name, O_RDWR | O_NOTRAVERSE );
  968.     if( fd < 0 ) {
  969.         return errno; /* should it be -fd ? */
  970.     }
  971.  
  972.     while( ptr < guard ) {
  973.         ssize_t              wrote_bytes;
  974.         struct attr_info     fa_info;
  975.         const char          *attr_name;
  976.         unsigned char       *attr_data;
  977.  
  978.         attr_name  = (char *)&(ptr[0]);
  979.         ptr       += strlen( attr_name ) + 1;
  980.  
  981.         /* The attr_info data is stored in big-endian format because the */
  982.         /* PowerPC port was here first.                                  */
  983.         memcpy( &fa_info, ptr, sizeof( struct attr_info ) );
  984.         fa_info.type = (uint32)B_BENDIAN_TO_HOST_INT32( fa_info.type );
  985.         fa_info.size = (off_t)B_BENDIAN_TO_HOST_INT64( fa_info.size );
  986.         ptr     += sizeof( struct attr_info );
  987.  
  988.         if( fa_info.size < 0LL ) {
  989.             Info(slide, 0x201, ((char *)slide,
  990.                  "warning: skipping attribute with invalid length (%Ld)\n",
  991.                  fa_info.size));
  992.             break;
  993.         }
  994.  
  995.         attr_data  = ptr;
  996.         ptr       += fa_info.size;
  997.  
  998.         if( ptr > guard ) {
  999.             /* We've got a truncated attribute. */
  1000.             Info(slide, 0x201, ((char *)slide,
  1001.                  "warning: truncated attribute\n"));
  1002.             break;
  1003.         }
  1004.  
  1005.         /* Wave the magic wand... this will swap Be-known types properly. */
  1006.         (void)swap_data( fa_info.type, attr_data, fa_info.size,
  1007.                          B_SWAP_BENDIAN_TO_HOST );
  1008.  
  1009.         wrote_bytes = fs_write_attr( fd, attr_name, fa_info.type, 0,
  1010.                                      attr_data, fa_info.size );
  1011.         if( wrote_bytes != fa_info.size ) {
  1012.             Info(slide, 0x201, ((char *)slide,
  1013.                  "warning: wrote %ld attribute bytes of %ld\n",
  1014.                  (unsigned long)wrote_bytes,(unsigned long)fa_info.size));
  1015.         }
  1016.     }
  1017.  
  1018.     close( fd );
  1019.  
  1020.     return retval;
  1021. }
  1022.  
  1023. static void setBeOSexfield( const char *path, uch *extra_field )
  1024. {
  1025.     uch *ptr       = extra_field;
  1026.     ush  id        = 0;
  1027.     ush  size      = 0;
  1028.     ulg  full_size = 0;
  1029.     uch  flags     = 0;
  1030.     uch *attrbuff  = NULL;
  1031.     int retval;
  1032.  
  1033.     if( extra_field == NULL ) {
  1034.         return;
  1035.     }
  1036.  
  1037.     /* Collect the data from the extra field buffer. */
  1038.     id        = makeword( ptr );    ptr += 2;   /* we don't use this... */
  1039.     size      = makeword( ptr );    ptr += 2;
  1040.     full_size = makelong( ptr );    ptr += 4;
  1041.     flags     = *ptr;               ptr++;
  1042.  
  1043.     /* Do a little sanity checking. */
  1044.     if( flags & EB_BE_FL_BADBITS ) {
  1045.         /* corrupted or unsupported */
  1046.         Info(slide, 0x201, ((char *)slide,
  1047.              "Unsupported flags set for this BeOS extra field, skipping.\n"));
  1048.         return;
  1049.     }
  1050.     if( size <= EB_BEOS_HLEN ) {
  1051.         /* corrupted, unsupported, or truncated */
  1052.         Info(slide, 0x201, ((char *)slide,
  1053.              "BeOS extra field is %d bytes, should be at least %d.\n", size,
  1054.              EB_BEOS_HLEN));
  1055.         return;
  1056.     }
  1057.     if( full_size < ( size - EB_BEOS_HLEN ) ) {
  1058.         /* possible old archive? will this screw up on valid archives? */
  1059.         Info(slide, 0x201, ((char *)slide,
  1060.              "Skipping attributes: BeOS extra field is %d bytes, "
  1061.              "data size is %ld.\n", size - EB_BEOS_HLEN, full_size));
  1062.         return;
  1063.     }
  1064.  
  1065.     /* Find the BeOS file attribute data. */
  1066.     if( flags & EB_BE_FL_UNCMPR ) {
  1067.         /* Uncompressed data */
  1068.         attrbuff = ptr;
  1069.     } else {
  1070.         /* Compressed data */
  1071.         attrbuff = (uch *)malloc( full_size );
  1072.         if( attrbuff == NULL ) {
  1073.             /* No memory to uncompress attributes */
  1074.             Info(slide, 0x201, ((char *)slide,
  1075.                  "Can't allocate memory to uncompress file attributes.\n"));
  1076.             return;
  1077.         }
  1078.  
  1079.         retval = memextract( __G__ attrbuff, full_size,
  1080.                              ptr, size - EB_BEOS_HLEN );
  1081.         if( retval != PK_OK ) {
  1082.             /* error uncompressing attributes */
  1083.             Info(slide, 0x201, ((char *)slide,
  1084.                  "Error uncompressing file attributes.\n"));
  1085.  
  1086.             /* Some errors here might not be so bad; we should expect */
  1087.             /* some truncated data, for example.  If the data was     */
  1088.             /* corrupt, we should _not_ attempt to restore the attrs  */
  1089.             /* for this file... there's no way to detect what attrs   */
  1090.             /* are good and which are bad.                            */
  1091.             free( attrbuff );
  1092.             return;
  1093.         }
  1094.     }
  1095.  
  1096.     /* Now attempt to set the file attributes on the extracted file. */
  1097.     retval = set_file_attrs( path, attrbuff, (off_t)full_size );
  1098.     if( retval != EOK ) {
  1099.         Info(slide, 0x201, ((char *)slide,
  1100.              "Error writing file attributes.\n"));
  1101.     }
  1102.  
  1103.     /* Clean up, if necessary */
  1104.     if( attrbuff != ptr ) {
  1105.         free( attrbuff );
  1106.     }
  1107.  
  1108.     return;
  1109. }
  1110.  
  1111. static void printBeOSexfield( int isdir, uch *extra_field )
  1112. {
  1113.     uch *ptr       = extra_field;
  1114.     ush  id        = 0;
  1115.     ush  size      = 0;
  1116.     ulg  full_size = 0;
  1117.     uch  flags     = 0;
  1118.  
  1119.     /* Tell picky compilers to be quiet. */
  1120.     isdir = isdir;
  1121.  
  1122.     if( extra_field == NULL ) {
  1123.         return;
  1124.     }
  1125.  
  1126.     /* Collect the data from the buffer. */
  1127.     id        = makeword( ptr );    ptr += 2;
  1128.     size      = makeword( ptr );    ptr += 2;
  1129.     full_size = makelong( ptr );    ptr += 4;
  1130.     flags     = *ptr;               ptr++;
  1131.  
  1132.     if( id != EF_BEOS ) {
  1133.         /* not a 'Be' field */
  1134.         printf("\t*** Unknown field type (0x%04x, '%c%c')\n", id,
  1135.                (char)(id >> 8), (char)id);
  1136.     }
  1137.  
  1138.     if( flags & EB_BE_FL_BADBITS ) {
  1139.         /* corrupted or unsupported */
  1140.         printf("\t*** Corrupted BeOS extra field:\n");
  1141.         printf("\t*** unknown bits set in the flags\n");
  1142.         printf("\t*** (Possibly created by an old version of zip for BeOS.\n");
  1143.     }
  1144.  
  1145.     if( size <= EB_BEOS_HLEN ) {
  1146.         /* corrupted, unsupported, or truncated */
  1147.         printf("\t*** Corrupted BeOS extra field:\n");
  1148.         printf("\t*** size is %d, should be larger than %d\n", size,
  1149.                EB_BEOS_HLEN );
  1150.     }
  1151.  
  1152.     if( flags & EB_BE_FL_UNCMPR ) {
  1153.         /* Uncompressed data */
  1154.         printf("\tBeOS extra field data (uncompressed):\n");
  1155.         printf("\t\t%ld data bytes\n", full_size);
  1156.     } else {
  1157.         /* Compressed data */
  1158.         printf("\tBeOS extra field data (compressed):\n");
  1159.         printf("\t\t%d compressed bytes\n", size - EB_BEOS_HLEN);
  1160.         printf("\t\t%ld uncompressed bytes\n", full_size);
  1161.     }
  1162. }
  1163.  
  1164. #ifdef BEOS_ASSIGN_FILETYPE
  1165. /* Note: This will no longer be necessary in BeOS PR4; update_mime_info()    */
  1166. /* will be updated to build its own absolute pathname if it's not given one. */
  1167. static void assign_MIME( const char *file )
  1168. {
  1169.     char *fullname;
  1170.     char buff[PATH_MAX], cwd_buff[PATH_MAX];
  1171.     int retval;
  1172.  
  1173.     if( file[0] == '/' ) {
  1174.         fullname = (char *)file;
  1175.     } else {
  1176.         sprintf( buff, "%s/%s", getcwd( cwd_buff, PATH_MAX ), file );
  1177.         fullname = buff;
  1178.     }
  1179.  
  1180.     retval = update_mime_info( fullname, FALSE, TRUE, TRUE );
  1181. }
  1182. #endif
  1183.