beos.c
上传用户:andy_li
上传日期:2007-01-06
资源大小:1019k
文件大小:40k
源码类别:

压缩解压

开发平台:

MultiPlatform

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