Subversion Repositories Kolibri OS

Rev

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

  1. /*
  2.   Copyright (c) 1990-2009 Info-ZIP.  All rights reserved.
  3.  
  4.   See the accompanying file LICENSE, version 2009-Jan-02 or later
  5.   (the contents of which are also included in unzip.h) for terms of use.
  6.   If, for some reason, all these files are missing, the Info-ZIP license
  7.   also may be found at:  ftp://ftp.info-zip.org/pub/infozip/license.html
  8. */
  9. /*---------------------------------------------------------------------------
  10.  
  11.   beos.c
  12.  
  13.   BeOS-specific routines for use with Info-ZIP's UnZip 5.30 and later.
  14.   (based on unix/unix.c)
  15.  
  16.   Contains:  do_wild()           <-- generic enough to put in fileio.c?
  17.              mapattr()
  18.              mapname()
  19.              checkdir()
  20.              close_outfile()
  21.              defer_dir_attribs()
  22.              set_direc_attribs()
  23.              stamp_file()
  24.              version()
  25.              scanBeOSexfield()
  26.              set_file_attrs()
  27.              setBeOSexfield()
  28.              printBeOSexfield()
  29.              assign_MIME()
  30.  
  31.   ---------------------------------------------------------------------------*/
  32.  
  33.  
  34. #define UNZIP_INTERNAL
  35. #include "unzip.h"
  36.  
  37. #include "beos.h"
  38. #include <errno.h>             /* Just make sure we've got a few things... */
  39. #include <sys/types.h>
  40. #include <sys/stat.h>
  41. #include <fcntl.h>
  42.  
  43. #include <dirent.h>
  44.  
  45. /* For the new post-DR8 file attributes */
  46. #include <kernel/fs_attr.h>
  47. #include <support/byteorder.h>
  48. #include <storage/Mime.h>
  49.  
  50. static unsigned filtattr OF((__GPRO__ unsigned perms));
  51. static uch *scanBeOSexfield  OF((const uch *ef_ptr, unsigned ef_len));
  52. static int  set_file_attrs( const char *, const unsigned char *, const off_t );
  53. static void setBeOSexfield   OF((const char *path, uch *extra_field));
  54. #ifdef BEOS_USE_PRINTEXFIELD
  55. static void printBeOSexfield OF((int isdir, uch *extra_field));
  56. #endif
  57. #ifdef BEOS_ASSIGN_FILETYPE
  58. static void assign_MIME( const char * );
  59. #endif
  60.  
  61. #ifdef SET_DIR_ATTRIB
  62. typedef struct uxdirattr {      /* struct for holding unix style directory */
  63.     struct uxdirattr *next;     /*  info until can be sorted and set at end */
  64.     char *fn;                   /* filename of directory */
  65.     union {
  66.         iztimes t3;             /* mtime, atime, ctime */
  67.         ztimbuf t2;             /* modtime, actime */
  68.     } u;
  69.     unsigned perms;             /* same as min_info.file_attr */
  70.     int have_uidgid;            /* flag */
  71.     ulg uidgid[2];
  72.     char fnbuf[1];              /* buffer stub for directory name */
  73. } uxdirattr;
  74. #define UxAtt(d)  ((uxdirattr *)d)    /* typecast shortcut */
  75. #endif /* SET_DIR_ATTRIB */
  76.  
  77. #ifdef ACORN_FTYPE_NFS
  78. /* Acorn bits for NFS filetyping */
  79. typedef struct {
  80.   uch ID[2];
  81.   uch size[2];
  82.   uch ID_2[4];
  83.   uch loadaddr[4];
  84.   uch execaddr[4];
  85.   uch attr[4];
  86. } RO_extra_block;
  87.  
  88. #endif /* ACORN_FTYPE_NFS */
  89.  
  90. /* static int created_dir;      */      /* used in mapname(), checkdir() */
  91. /* static int renamed_fullpath; */      /* ditto */
  92.  
  93.  
  94. /*****************************/
  95. /* Strings used multiple     */
  96. /* times in beos.c           */
  97. /*****************************/
  98.  
  99. /* messages of code for setting file/directory attributes */
  100. static ZCONST char CannotSetItemUidGid[] =
  101.   "warning:  cannot set UID %lu and/or GID %lu for %s\n          %s\n";
  102. static ZCONST char CannotSetUidGid[] =
  103.   " (warning) cannot set UID %lu and/or GID %lu\n          %s";
  104. static ZCONST char CannotSetItemTimestamps[] =
  105.   "warning:  cannot set modif./access times for %s\n          %s\n";
  106. static ZCONST char CannotSetTimestamps[] =
  107.   " (warning) cannot set modif./access times\n          %s";
  108.  
  109.  
  110. #ifndef SFX
  111.  
  112. /**********************/
  113. /* Function do_wild() */   /* for porting: dir separator; match(ignore_case) */
  114. /**********************/
  115.  
  116. char *do_wild(__G__ wildspec)
  117.     __GDEF
  118.     ZCONST char *wildspec;  /* only used first time on a given dir */
  119. {
  120. /* these statics are now declared in SYSTEM_SPECIFIC_GLOBALS in beocfg.h:
  121.     static DIR *wild_dir = (DIR *)NULL;
  122.     static ZCONST char *wildname;
  123.     static char *dirname, matchname[FILNAMSIZ];
  124.     static int notfirstcall=FALSE, have_dirname, dirnamelen;
  125. */
  126.     struct dirent *file;
  127.  
  128.     /* Even when we're just returning wildspec, we *always* do so in
  129.      * matchname[]--calling routine is allowed to append four characters
  130.      * to the returned string, and wildspec may be a pointer to argv[].
  131.      */
  132.     if (!G.notfirstcall) {  /* first call:  must initialize everything */
  133.         G.notfirstcall = TRUE;
  134.  
  135.         if (!iswild(wildspec)) {
  136.             strncpy(G.matchname, wildspec, FILNAMSIZ);
  137.             G.matchname[FILNAMSIZ-1] = '\0';
  138.             G.have_dirname = FALSE;
  139.             G.wild_dir = NULL;
  140.             return G.matchname;
  141.         }
  142.  
  143.         /* break the wildspec into a directory part and a wildcard filename */
  144.         if ((G.wildname = (ZCONST char *)strrchr(wildspec, '/')) == NULL) {
  145.             G.dirname = ".";
  146.             G.dirnamelen = 1;
  147.             G.have_dirname = FALSE;
  148.             G.wildname = wildspec;
  149.         } else {
  150.             ++G.wildname;     /* point at character after '/' */
  151.             G.dirnamelen = G.wildname - wildspec;
  152.             if ((G.dirname = (char *)malloc(G.dirnamelen+1)) == (char *)NULL) {
  153.                 Info(slide, 0x201, ((char *)slide,
  154.                   "warning:  cannot allocate wildcard buffers\n"));
  155.                 strncpy(G.matchname, wildspec, FILNAMSIZ);
  156.                 G.matchname[FILNAMSIZ-1] = '\0';
  157.                 return G.matchname; /* but maybe filespec was not a wildcard */
  158.             }
  159.             strncpy(G.dirname, wildspec, G.dirnamelen);
  160.             G.dirname[G.dirnamelen] = '\0';   /* terminate for strcpy below */
  161.             G.have_dirname = TRUE;
  162.         }
  163.  
  164.         if ((G.wild_dir = (zvoid *)opendir(G.dirname)) != (zvoid *)NULL) {
  165.             while ((file = readdir((DIR *)G.wild_dir)) !=
  166.                    (struct dirent *)NULL) {
  167.                 Trace((stderr, "do_wild:  readdir returns %s\n",
  168.                   FnFilter1(file->d_name)));
  169.                 if (file->d_name[0] == '.' && G.wildname[0] != '.')
  170.                     continue; /* Unix:  '*' and '?' do not match leading dot */
  171.                 if (match(file->d_name, G.wildname, 0 WISEP) &&/*0=case sens.*/
  172.                     /* skip "." and ".." directory entries */
  173.                     strcmp(file->d_name, ".") && strcmp(file->d_name, "..")) {
  174.                     Trace((stderr, "do_wild:  match() succeeds\n"));
  175.                     if (G.have_dirname) {
  176.                         strcpy(G.matchname, G.dirname);
  177.                         strcpy(G.matchname+G.dirnamelen, file->d_name);
  178.                     } else
  179.                         strcpy(G.matchname, file->d_name);
  180.                     return G.matchname;
  181.                 }
  182.             }
  183.             /* if we get to here directory is exhausted, so close it */
  184.             closedir((DIR *)G.wild_dir);
  185.             G.wild_dir = (zvoid *)NULL;
  186.         }
  187.         Trace((stderr, "do_wild:  opendir(%s) returns NULL\n",
  188.           FnFilter1(G.dirname)));
  189.  
  190.         /* return the raw wildspec in case that works (e.g., directory not
  191.          * searchable, but filespec was not wild and file is readable) */
  192.         strncpy(G.matchname, wildspec, FILNAMSIZ);
  193.         G.matchname[FILNAMSIZ-1] = '\0';
  194.         return G.matchname;
  195.     }
  196.  
  197.     /* last time through, might have failed opendir but returned raw wildspec */
  198.     if ((DIR *)G.wild_dir == (DIR *)NULL) {
  199.         G.notfirstcall = FALSE; /* nothing left--reset for new wildspec */
  200.         if (G.have_dirname)
  201.             free(G.dirname);
  202.         return (char *)NULL;
  203.     }
  204.  
  205.     /* If we've gotten this far, we've read and matched at least one entry
  206.      * successfully (in a previous call), so dirname has been copied into
  207.      * matchname already.
  208.      */
  209.     while ((file = readdir((DIR *)G.wild_dir)) != (struct dirent *)NULL) {
  210.         Trace((stderr, "do_wild:  readdir returns %s\n",
  211.           FnFilter1(file->d_name)));
  212.         if (file->d_name[0] == '.' && G.wildname[0] != '.')
  213.             continue;   /* Unix:  '*' and '?' do not match leading dot */
  214.         if (match(file->d_name, G.wildname, 0 WISEP)) { /* 0 == case sens. */
  215.             Trace((stderr, "do_wild:  match() succeeds\n"));
  216.             if (G.have_dirname) {
  217.                 /* strcpy(G.matchname, G.dirname); */
  218.                 strcpy(G.matchname+G.dirnamelen, file->d_name);
  219.             } else
  220.                 strcpy(G.matchname, file->d_name);
  221.             return G.matchname;
  222.         }
  223.     }
  224.  
  225.     closedir((DIR *)G.wild_dir);  /* at least one entry read; nothing left */
  226.     G.wild_dir = (zvoid *)NULL;
  227.     G.notfirstcall = FALSE;       /* reset for new wildspec */
  228.     if (G.have_dirname)
  229.         free(G.dirname);
  230.     return (char *)NULL;
  231.  
  232. } /* end function do_wild() */
  233.  
  234. #endif /* !SFX */
  235.  
  236.  
  237.  
  238.  
  239. #ifndef S_ISUID
  240. # define S_ISUID        0004000 /* set user id on execution */
  241. #endif
  242. #ifndef S_ISGID
  243. # define S_ISGID        0002000 /* set group id on execution */
  244. #endif
  245. #ifndef S_ISVTX
  246. # define S_ISVTX        0001000 /* save swapped text even after use */
  247. #endif
  248.  
  249. /************************/
  250. /*  Function filtattr() */
  251. /************************/
  252. /* This is used to clear or keep the SUID and SGID bits on file permissions.
  253.  * It's possible that a file in an archive could have one of these bits set
  254.  * and, unknown to the person unzipping, could allow others to execute the
  255.  * file as the user or group.  The new option -K bypasses this check.
  256.  */
  257.  
  258. static unsigned filtattr(__G__ perms)
  259.     __GDEF
  260.     unsigned perms;
  261. {
  262.     /* keep setuid/setgid/tacky perms? */
  263.     if (!uO.K_flag)
  264.         perms &= ~(S_ISUID | S_ISGID | S_ISVTX);
  265.  
  266.     return (0xffff & perms);
  267. } /* end function filtattr() */
  268.  
  269.  
  270.  
  271.  
  272.  
  273. /**********************/
  274. /* Function mapattr() */
  275. /**********************/
  276.  
  277. int mapattr(__G)
  278.     __GDEF
  279. {
  280.     int r;
  281.     ulg tmp = G.crec.external_file_attributes;
  282.  
  283.     G.pInfo->file_attr = 0;
  284.     /* initialized to 0 for check in "default" branch below... */
  285.  
  286.     switch (G.pInfo->hostnum) {
  287.         case AMIGA_:
  288.             tmp = (unsigned)(tmp>>17 & 7);   /* Amiga RWE bits */
  289.             G.pInfo->file_attr = (unsigned)(tmp<<6 | tmp<<3 | tmp);
  290.             break;
  291.         case THEOS_:
  292.             tmp &= 0xF1FFFFFFL;
  293.             if ((tmp & 0xF0000000L) != 0x40000000L)
  294.                 tmp &= 0x01FFFFFFL;     /* not a dir, mask all ftype bits */
  295.             else
  296.                 tmp &= 0x41FFFFFFL;     /* leave directory bit as set */
  297.             /* fall through! */
  298.         case BEOS_:
  299.         case UNIX_:
  300.         case VMS_:
  301.         case ACORN_:
  302.         case ATARI_:
  303.         case ATHEOS_:
  304.         case QDOS_:
  305.         case TANDEM_:
  306.             r = FALSE;
  307.             G.pInfo->file_attr = (unsigned)(tmp >> 16);
  308.             if (G.pInfo->file_attr == 0 && G.extra_field) {
  309.                 /* Some (non-Info-ZIP) implementations of Zip for Unix and
  310.                  * VMS (and probably others ??) leave 0 in the upper 16-bit
  311.                  * part of the external_file_attributes field. Instead, they
  312.                  * store file permission attributes in some extra field.
  313.                  * As a work-around, we search for the presence of one of
  314.                  * these extra fields and fall back to the MSDOS compatible
  315.                  * part of external_file_attributes if one of the known
  316.                  * e.f. types has been detected.
  317.                  * Later, we might implement extraction of the permission
  318.                  * bits from the VMS extra field. But for now, the work-around
  319.                  * should be sufficient to provide "readable" extracted files.
  320.                  * (For ASI Unix e.f., an experimental remap of the e.f.
  321.                  * mode value IS already provided!)
  322.                  */
  323.                 ush ebID;
  324.                 unsigned ebLen;
  325.                 uch *ef = G.extra_field;
  326.                 unsigned ef_len = G.crec.extra_field_length;
  327.  
  328.                 while (!r && ef_len >= EB_HEADSIZE) {
  329.                     ebID = makeword(ef);
  330.                     ebLen = (unsigned)makeword(ef+EB_LEN);
  331.                     if (ebLen > (ef_len - EB_HEADSIZE))
  332.                         /* discoverd some e.f. inconsistency! */
  333.                         break;
  334.                     switch (ebID) {
  335.                       case EF_ASIUNIX:
  336.                         if (ebLen >= (EB_ASI_MODE+2)) {
  337.                             G.pInfo->file_attr =
  338.                               (unsigned)makeword(ef+(EB_HEADSIZE+EB_ASI_MODE));
  339.                             /* force stop of loop: */
  340.                             ef_len = (ebLen + EB_HEADSIZE);
  341.                             break;
  342.                         }
  343.                         /* else: fall through! */
  344.                       case EF_PKVMS:
  345.                         /* "found nondecypherable e.f. with perm. attr" */
  346.                         r = TRUE;
  347.                       default:
  348.                         break;
  349.                     }
  350.                     ef_len -= (ebLen + EB_HEADSIZE);
  351.                     ef += (ebLen + EB_HEADSIZE);
  352.                 }
  353.             }
  354.             if (!r) {
  355. #ifdef SYMLINKS
  356.                 /* Check if the file is a (POSIX-compatible) symbolic link.
  357.                  * We restrict symlink support to those "made-by" hosts that
  358.                  * are known to support symbolic links.
  359.                  */
  360.                 G.pInfo->symlink = S_ISLNK(G.pInfo->file_attr) &&
  361.                                    SYMLINK_HOST(G.pInfo->hostnum);
  362. #endif
  363.                 return 0;
  364.             }
  365.             /* fall through! */
  366.         /* all remaining cases:  expand MSDOS read-only bit into write perms */
  367.         case FS_FAT_:
  368.             /* PKWARE's PKZip for Unix marks entries as FS_FAT_, but stores the
  369.              * Unix attributes in the upper 16 bits of the external attributes
  370.              * field, just like Info-ZIP's Zip for Unix.  We try to use that
  371.              * value, after a check for consistency with the MSDOS attribute
  372.              * bits (see below).
  373.              */
  374.             G.pInfo->file_attr = (unsigned)(tmp >> 16);
  375.             /* fall through! */
  376.         case FS_HPFS_:
  377.         case FS_NTFS_:
  378.         case MAC_:
  379.         case TOPS20_:
  380.         default:
  381.             /* Ensure that DOS subdir bit is set when the entry's name ends
  382.              * in a '/'.  Some third-party Zip programs fail to set the subdir
  383.              * bit for directory entries.
  384.              */
  385.             if ((tmp & 0x10) == 0) {
  386.                 extent fnlen = strlen(G.filename);
  387.                 if (fnlen > 0 && G.filename[fnlen-1] == '/')
  388.                     tmp |= 0x10;
  389.             }
  390.             /* read-only bit --> write perms; subdir bit --> dir exec bit */
  391.             tmp = !(tmp & 1) << 1  |  (tmp & 0x10) >> 4;
  392.             if ((G.pInfo->file_attr & 0700) == (unsigned)(0400 | tmp<<6)) {
  393.                 /* keep previous G.pInfo->file_attr setting, when its "owner"
  394.                  * part appears to be consistent with DOS attribute flags!
  395.                  */
  396. #ifdef SYMLINKS
  397.                 /* Entries "made by FS_FAT_" could have been zipped on a
  398.                  * system that supports POSIX-style symbolic links.
  399.                  */
  400.                 G.pInfo->symlink = S_ISLNK(G.pInfo->file_attr) &&
  401.                                    (G.pInfo->hostnum == FS_FAT_);
  402. #endif
  403.                 return 0;
  404.             }
  405.             G.pInfo->file_attr = (unsigned)(0444 | tmp<<6 | tmp<<3 | tmp);
  406.             break;
  407.     } /* end switch (host-OS-created-by) */
  408.  
  409.     /* for originating systems with no concept of "group," "other," "system": */
  410.     umask( (int)(tmp=umask(0)) );    /* apply mask to expanded r/w(/x) perms */
  411.     G.pInfo->file_attr &= ~tmp;
  412.  
  413.     return 0;
  414.  
  415. } /* end function mapattr() */
  416.  
  417.  
  418.  
  419.  
  420.  
  421. /************************/
  422. /*  Function mapname()  */
  423. /************************/
  424.  
  425. int mapname(__G__ renamed)
  426.     __GDEF
  427.     int renamed;
  428. /*
  429.  * returns:
  430.  *  MPN_OK          - no problem detected
  431.  *  MPN_INF_TRUNC   - caution (truncated filename)
  432.  *  MPN_INF_SKIP    - info "skip entry" (dir doesn't exist)
  433.  *  MPN_ERR_SKIP    - error -> skip entry
  434.  *  MPN_ERR_TOOLONG - error -> path is too long
  435.  *  MPN_NOMEM       - error (memory allocation failed) -> skip entry
  436.  *  [also MPN_VOL_LABEL, MPN_CREATED_DIR]
  437.  */
  438. {
  439.     char pathcomp[FILNAMSIZ];      /* path-component buffer */
  440.     char *pp, *cp=(char *)NULL;    /* character pointers */
  441.     char *lastsemi=(char *)NULL;   /* pointer to last semi-colon in pathcomp */
  442. #ifdef ACORN_FTYPE_NFS
  443.     char *lastcomma=(char *)NULL;  /* pointer to last comma in pathcomp */
  444.     RO_extra_block *ef_spark;      /* pointer Acorn FTYPE ef block */
  445. #endif
  446.     int killed_ddot = FALSE;       /* is set when skipping "../" pathcomp */
  447.     int error = MPN_OK;
  448.     register unsigned workch;      /* hold the character being tested */
  449.  
  450.  
  451. /*---------------------------------------------------------------------------
  452.     Initialize various pointers and counters and stuff.
  453.   ---------------------------------------------------------------------------*/
  454.  
  455.     if (G.pInfo->vollabel)
  456.         return MPN_VOL_LABEL;   /* can't set disk volume labels in BeOS */
  457.  
  458.     /* can create path as long as not just freshening, or if user told us */
  459.     G.create_dirs = (!uO.fflag || renamed);
  460.  
  461.     G.created_dir = FALSE;      /* not yet */
  462.  
  463.     /* user gave full pathname:  don't prepend rootpath */
  464.     G.renamed_fullpath = (renamed && (*G.filename == '/'));
  465.  
  466.     if (checkdir(__G__ (char *)NULL, INIT) == MPN_NOMEM)
  467.         return MPN_NOMEM;       /* initialize path buffer, unless no memory */
  468.  
  469.     *pathcomp = '\0';           /* initialize translation buffer */
  470.     pp = pathcomp;              /* point to translation buffer */
  471.     if (uO.jflag)               /* junking directories */
  472.         cp = (char *)strrchr(G.filename, '/');
  473.     if (cp == (char *)NULL)     /* no '/' or not junking dirs */
  474.         cp = G.filename;        /* point to internal zipfile-member pathname */
  475.     else
  476.         ++cp;                   /* point to start of last component of path */
  477.  
  478. /*---------------------------------------------------------------------------
  479.     Begin main loop through characters in filename.
  480.   ---------------------------------------------------------------------------*/
  481.  
  482.     while ((workch = (uch)*cp++) != 0) {
  483.  
  484.         switch (workch) {
  485.             case '/':             /* can assume -j flag not given */
  486.                 *pp = '\0';
  487.                 if (strcmp(pathcomp, ".") == 0) {
  488.                     /* don't bother appending "./" to the path */
  489.                     *pathcomp = '\0';
  490.                 } else if (!uO.ddotflag && strcmp(pathcomp, "..") == 0) {
  491.                     /* "../" dir traversal detected, skip over it */
  492.                     *pathcomp = '\0';
  493.                     killed_ddot = TRUE;     /* set "show message" flag */
  494.                 }
  495.                 /* when path component is not empty, append it now */
  496.                 if (*pathcomp != '\0' &&
  497.                     ((error = checkdir(__G__ pathcomp, APPEND_DIR))
  498.                      & MPN_MASK) > MPN_INF_TRUNC)
  499.                     return error;
  500.                 pp = pathcomp;    /* reset conversion buffer for next piece */
  501.                 lastsemi = (char *)NULL; /* leave direct. semi-colons alone */
  502.                 break;
  503.  
  504.             case ';':             /* VMS version (or DEC-20 attrib?) */
  505.                 lastsemi = pp;
  506.                 *pp++ = ';';      /* keep for now; remove VMS ";##" */
  507.                 break;            /*  later, if requested */
  508.  
  509. #ifdef ACORN_FTYPE_NFS
  510.             case ',':             /* NFS filetype extension */
  511.                 lastcomma = pp;
  512.                 *pp++ = ',';      /* keep for now; may need to remove */
  513.                 break;            /*  later, if requested */
  514. #endif
  515.  
  516.             default:
  517.                 /* allow European characters in filenames: */
  518.                 if (isprint(workch) || (128 <= workch && workch <= 254))
  519.                     *pp++ = (char)workch;
  520.         } /* end switch */
  521.  
  522.     } /* end while loop */
  523.  
  524.     /* Show warning when stripping insecure "parent dir" path components */
  525.     if (killed_ddot && QCOND2) {
  526.         Info(slide, 0, ((char *)slide,
  527.           "warning:  skipped \"../\" path component(s) in %s\n",
  528.           FnFilter1(G.filename)));
  529.         if (!(error & ~MPN_MASK))
  530.             error = (error & MPN_MASK) | PK_WARN;
  531.     }
  532.  
  533. /*---------------------------------------------------------------------------
  534.     Report if directory was created (and no file to create:  filename ended
  535.     in '/'), check name to be sure it exists, and combine path and name be-
  536.     fore exiting.
  537.   ---------------------------------------------------------------------------*/
  538.  
  539.     if (G.filename[strlen(G.filename) - 1] == '/') {
  540.         checkdir(__G__ G.filename, GETPATH);
  541.         if (G.created_dir) {
  542.             if (QCOND2) {
  543.                 Info(slide, 0, ((char *)slide, "   creating: %s\n",
  544.                   FnFilter1(G.filename)));
  545.             }
  546.  
  547.             if (!uO.J_flag) {   /* Handle the BeOS extra field if present. */
  548.                 void *ptr = scanBeOSexfield(G.extra_field,
  549.                                             G.lrec.extra_field_length);
  550.                 if (ptr) {
  551.                     setBeOSexfield(G.filename, ptr);
  552.                 }
  553.             }
  554.  
  555. #ifndef NO_CHMOD
  556.             /* set approx. dir perms (make sure can still read/write in dir) */
  557.             if (chmod(G.filename, filtattr(__G__ G.pInfo->file_attr) | 0700))
  558.                 perror("chmod (directory attributes) error");
  559. #endif
  560.  
  561.             /* set dir time (note trailing '/') */
  562.             return (error & ~MPN_MASK) | MPN_CREATED_DIR;
  563.         }
  564.         /* TODO: should we re-write the BeOS extra field data in case it's */
  565.         /* changed?  The answer is yes. [Sept 1999 - cjh]                  */
  566.         if (!uO.J_flag) {   /* Handle the BeOS extra field if present. */
  567.             void *ptr = scanBeOSexfield(G.extra_field,
  568.                                         G.lrec.extra_field_length);
  569.             if (ptr) {
  570.                 setBeOSexfield(G.filename, ptr);
  571.             }
  572.         }
  573.  
  574.         /* dir existed already; don't look for data to extract */
  575.         return (error & ~MPN_MASK) | MPN_INF_SKIP;
  576.     }
  577.  
  578.     *pp = '\0';                   /* done with pathcomp:  terminate it */
  579.  
  580.     /* if not saving them, remove VMS version numbers (appended ";###") */
  581.     if (!uO.V_flag && lastsemi) {
  582.         pp = lastsemi + 1;
  583.         while (isdigit((uch)(*pp)))
  584.             ++pp;
  585.         if (*pp == '\0')          /* only digits between ';' and end:  nuke */
  586.             *lastsemi = '\0';
  587.     }
  588.  
  589.     /* On UNIX (and compatible systems), "." and ".." are reserved for
  590.      * directory navigation and cannot be used as regular file names.
  591.      * These reserved one-dot and two-dot names are mapped to "_" and "__".
  592.      */
  593.     if (strcmp(pathcomp, ".") == 0)
  594.         *pathcomp = '_';
  595.     else if (strcmp(pathcomp, "..") == 0)
  596.         strcpy(pathcomp, "__");
  597.  
  598. #ifdef ACORN_FTYPE_NFS
  599.     /* translate Acorn filetype information if asked to do so */
  600.     if (uO.acorn_nfs_ext &&
  601.         (ef_spark = (RO_extra_block *)
  602.                     getRISCOSexfield(G.extra_field, G.lrec.extra_field_length))
  603.         != (RO_extra_block *)NULL)
  604.     {
  605.         /* file *must* have a RISC OS extra field */
  606.         long ft = (long)makelong(ef_spark->loadaddr);
  607.         /*32-bit*/
  608.         if (lastcomma) {
  609.             pp = lastcomma + 1;
  610.             while (isxdigit((uch)(*pp))) ++pp;
  611.             if (pp == lastcomma+4 && *pp == '\0') *lastcomma='\0'; /* nuke */
  612.         }
  613.         if ((ft & 1<<31)==0) ft=0x000FFD00;
  614.         sprintf(pathcomp+strlen(pathcomp), ",%03x", (int)(ft>>8) & 0xFFF);
  615.     }
  616. #endif /* ACORN_FTYPE_NFS */
  617.  
  618.     if (*pathcomp == '\0') {
  619.         Info(slide, 1, ((char *)slide, "mapname:  conversion of %s failed\n",
  620.           FnFilter1(G.filename)));
  621.         return (error & ~MPN_MASK) | MPN_ERR_SKIP;
  622.     }
  623.  
  624.     checkdir(__G__ pathcomp, APPEND_NAME);  /* returns 1 if truncated: care? */
  625.     checkdir(__G__ G.filename, GETPATH);
  626.  
  627.     return error;
  628.  
  629. } /* end function mapname() */
  630.  
  631.  
  632.  
  633.  
  634. /***********************/
  635. /* Function checkdir() */
  636. /***********************/
  637.  
  638. int checkdir(__G__ pathcomp, flag)
  639.     __GDEF
  640.     char *pathcomp;
  641.     int flag;
  642. /*
  643.  * returns:
  644.  *  MPN_OK          - no problem detected
  645.  *  MPN_INF_TRUNC   - (on APPEND_NAME) truncated filename
  646.  *  MPN_INF_SKIP    - path doesn't exist, not allowed to create
  647.  *  MPN_ERR_SKIP    - path doesn't exist, tried to create and failed; or path
  648.  *                    exists and is not a directory, but is supposed to be
  649.  *  MPN_ERR_TOOLONG - path is too long
  650.  *  MPN_NOMEM       - can't allocate memory for filename buffers
  651.  */
  652. {
  653.  /* static int rootlen = 0; */  /* length of rootpath */
  654.  /* static char *rootpath;  */  /* user's "extract-to" directory */
  655.  /* static char *buildpath; */  /* full path (so far) to extracted file */
  656.  /* static char *end;       */  /* pointer to end of buildpath ('\0') */
  657.  
  658. #   define FN_MASK   7
  659. #   define FUNCTION  (flag & FN_MASK)
  660.  
  661.  
  662.  
  663. /*---------------------------------------------------------------------------
  664.     APPEND_DIR:  append the path component to the path being built and check
  665.     for its existence.  If doesn't exist and we are creating directories, do
  666.     so for this one; else signal success or error as appropriate.
  667.   ---------------------------------------------------------------------------*/
  668.  
  669.     if (FUNCTION == APPEND_DIR) {
  670.         int too_long = FALSE;
  671. #ifdef SHORT_NAMES
  672.         char *old_end = end;
  673. #endif
  674.  
  675.         Trace((stderr, "appending dir segment [%s]\n", FnFilter1(pathcomp)));
  676.         while ((*G.end = *pathcomp++) != '\0')
  677.             ++G.end;
  678. #ifdef SHORT_NAMES   /* path components restricted to 14 chars, typically */
  679.         if ((G.end-old_end) > FILENAME_MAX)  /* GRR:  proper constant? */
  680.             *(G.end = old_end + FILENAME_MAX) = '\0';
  681. #endif
  682.  
  683.         /* GRR:  could do better check, see if overrunning buffer as we go:
  684.          * check end-buildpath after each append, set warning variable if
  685.          * within 20 of FILNAMSIZ; then if var set, do careful check when
  686.          * appending.  Clear variable when begin new path. */
  687.  
  688.         /* next check: need to append '/', at least one-char name, '\0' */
  689.         if ((G.end-G.buildpath) > FILNAMSIZ-3)
  690.             too_long = TRUE;                    /* check if extracting dir? */
  691.         if (SSTAT(G.buildpath, &G.statbuf)) {   /* path doesn't exist */
  692.             if (!G.create_dirs) { /* told not to create (freshening) */
  693.                 free(G.buildpath);
  694.                 return MPN_INF_SKIP;    /* path doesn't exist: nothing to do */
  695.             }
  696.             if (too_long) {
  697.                 Info(slide, 1, ((char *)slide,
  698.                   "checkdir error:  path too long: %s\n",
  699.                   FnFilter1(G.buildpath)));
  700.                 free(G.buildpath);
  701.                 /* no room for filenames:  fatal */
  702.                 return MPN_ERR_TOOLONG;
  703.             }
  704.             if (mkdir(G.buildpath, 0777) == -1) {   /* create the directory */
  705.                 Info(slide, 1, ((char *)slide,
  706.                   "checkdir error:  cannot create %s\n\
  707.                 %s\n\
  708.                 unable to process %s.\n",
  709.                   FnFilter2(G.buildpath),
  710.                   strerror(errno),
  711.                   FnFilter1(G.filename)));
  712.                 free(G.buildpath);
  713.                 /* path didn't exist, tried to create, failed */
  714.                 return MPN_ERR_SKIP;
  715.             }
  716.             G.created_dir = TRUE;
  717.         } else if (!S_ISDIR(G.statbuf.st_mode)) {
  718.             Info(slide, 1, ((char *)slide,
  719.               "checkdir error:  %s exists but is not directory\n\
  720.                 unable to process %s.\n",
  721.               FnFilter2(G.buildpath), FnFilter1(G.filename)));
  722.             free(G.buildpath);
  723.             /* path existed but wasn't dir */
  724.             return MPN_ERR_SKIP;
  725.         }
  726.         if (too_long) {
  727.             Info(slide, 1, ((char *)slide,
  728.               "checkdir error:  path too long: %s\n", FnFilter1(G.buildpath)));
  729.             free(G.buildpath);
  730.             /* no room for filenames:  fatal */
  731.             return MPN_ERR_TOOLONG;
  732.         }
  733.         *G.end++ = '/';
  734.         *G.end = '\0';
  735.         Trace((stderr, "buildpath now = [%s]\n", FnFilter1(G.buildpath)));
  736.         return MPN_OK;
  737.  
  738.     } /* end if (FUNCTION == APPEND_DIR) */
  739.  
  740. /*---------------------------------------------------------------------------
  741.     GETPATH:  copy full path to the string pointed at by pathcomp, and free
  742.     G.buildpath.
  743.   ---------------------------------------------------------------------------*/
  744.  
  745.     if (FUNCTION == GETPATH) {
  746.         strcpy(pathcomp, G.buildpath);
  747.         Trace((stderr, "getting and freeing path [%s]\n",
  748.           FnFilter1(pathcomp)));
  749.         free(G.buildpath);
  750.         G.buildpath = G.end = (char *)NULL;
  751.         return MPN_OK;
  752.     }
  753.  
  754. /*---------------------------------------------------------------------------
  755.     APPEND_NAME:  assume the path component is the filename; append it and
  756.     return without checking for existence.
  757.   ---------------------------------------------------------------------------*/
  758.  
  759.     if (FUNCTION == APPEND_NAME) {
  760. #ifdef SHORT_NAMES
  761.         char *old_end = end;
  762. #endif
  763.  
  764.         Trace((stderr, "appending filename [%s]\n", FnFilter1(pathcomp)));
  765.         while ((*G.end = *pathcomp++) != '\0') {
  766.             ++G.end;
  767. #ifdef SHORT_NAMES  /* truncate name at 14 characters, typically */
  768.             if ((G.end-old_end) > FILENAME_MAX)    /* GRR:  proper constant? */
  769.                 *(G.end = old_end + FILENAME_MAX) = '\0';
  770. #endif
  771.             if ((G.end-G.buildpath) >= FILNAMSIZ) {
  772.                 *--G.end = '\0';
  773.                 Info(slide, 0x201, ((char *)slide,
  774.                   "checkdir warning:  path too long; truncating\n\
  775.                   %s\n                -> %s\n",
  776.                   FnFilter1(G.filename), FnFilter2(G.buildpath)));
  777.                 return MPN_INF_TRUNC;   /* filename truncated */
  778.             }
  779.         }
  780.         Trace((stderr, "buildpath now = [%s]\n", FnFilter1(G.buildpath)));
  781.         /* could check for existence here, prompt for new name... */
  782.         return MPN_OK;
  783.     }
  784.  
  785. /*---------------------------------------------------------------------------
  786.     INIT:  allocate and initialize buffer space for the file currently being
  787.     extracted.  If file was renamed with an absolute path, don't prepend the
  788.     extract-to path.
  789.   ---------------------------------------------------------------------------*/
  790.  
  791. /* GRR:  for VMS and TOPS-20, add up to 13 to strlen */
  792.  
  793.     if (FUNCTION == INIT) {
  794.         Trace((stderr, "initializing buildpath to "));
  795. #ifdef ACORN_FTYPE_NFS
  796.         if ((G.buildpath = (char *)malloc(strlen(G.filename)+G.rootlen+
  797.                                           (uO.acorn_nfs_ext ? 5 : 1)))
  798. #else
  799.         if ((G.buildpath = (char *)malloc(strlen(G.filename)+G.rootlen+1))
  800. #endif
  801.             == (char *)NULL)
  802.             return MPN_NOMEM;
  803.         if ((G.rootlen > 0) && !G.renamed_fullpath) {
  804.             strcpy(G.buildpath, G.rootpath);
  805.             G.end = G.buildpath + G.rootlen;
  806.         } else {
  807.             *G.buildpath = '\0';
  808.             G.end = G.buildpath;
  809.         }
  810.         Trace((stderr, "[%s]\n", FnFilter1(G.buildpath)));
  811.         return MPN_OK;
  812.     }
  813.  
  814. /*---------------------------------------------------------------------------
  815.     ROOT:  if appropriate, store the path in rootpath and create it if
  816.     necessary; else assume it's a zipfile member and return.  This path
  817.     segment gets used in extracting all members from every zipfile specified
  818.     on the command line.
  819.   ---------------------------------------------------------------------------*/
  820.  
  821. #if (!defined(SFX) || defined(SFX_EXDIR))
  822.     if (FUNCTION == ROOT) {
  823.         Trace((stderr, "initializing root path to [%s]\n",
  824.           FnFilter1(pathcomp)));
  825.         if (pathcomp == (char *)NULL) {
  826.             G.rootlen = 0;
  827.             return MPN_OK;
  828.         }
  829.         if (G.rootlen > 0)      /* rootpath was already set, nothing to do */
  830.             return MPN_OK;
  831.         if ((G.rootlen = strlen(pathcomp)) > 0) {
  832.             char *tmproot;
  833.  
  834.             if ((tmproot = (char *)malloc(G.rootlen+2)) == (char *)NULL) {
  835.                 G.rootlen = 0;
  836.                 return MPN_NOMEM;
  837.             }
  838.             strcpy(tmproot, pathcomp);
  839.             if (tmproot[G.rootlen-1] == '/') {
  840.                 tmproot[--G.rootlen] = '\0';
  841.             }
  842.             if (G.rootlen > 0 && (SSTAT(tmproot, &G.statbuf) ||
  843.                                   !S_ISDIR(G.statbuf.st_mode)))
  844.             {   /* path does not exist */
  845.                 if (!G.create_dirs /* || iswild(tmproot) */ ) {
  846.                     free(tmproot);
  847.                     G.rootlen = 0;
  848.                     /* skip (or treat as stored file) */
  849.                     return MPN_INF_SKIP;
  850.                 }
  851.                 /* create the directory (could add loop here scanning tmproot
  852.                  * to create more than one level, but why really necessary?) */
  853.                 if (mkdir(tmproot, 0777) == -1) {
  854.                     Info(slide, 1, ((char *)slide,
  855.                       "checkdir:  cannot create extraction directory: %s\n\
  856.           %s\n",
  857.                       FnFilter1(tmproot), strerror(errno)));
  858.                     free(tmproot);
  859.                     G.rootlen = 0;
  860.                     /* path didn't exist, tried to create, and failed: */
  861.                     /* file exists, or 2+ subdir levels required */
  862.                     return MPN_ERR_SKIP;
  863.                 }
  864.             }
  865.             tmproot[G.rootlen++] = '/';
  866.             tmproot[G.rootlen] = '\0';
  867.             if ((G.rootpath = (char *)realloc(tmproot, G.rootlen+1)) == NULL) {
  868.                 free(tmproot);
  869.                 G.rootlen = 0;
  870.                 return MPN_NOMEM;
  871.             }
  872.             Trace((stderr, "rootpath now = [%s]\n", FnFilter1(G.rootpath)));
  873.         }
  874.         return MPN_OK;
  875.     }
  876. #endif /* !SFX || SFX_EXDIR */
  877.  
  878. /*---------------------------------------------------------------------------
  879.     END:  free rootpath, immediately prior to program exit.
  880.   ---------------------------------------------------------------------------*/
  881.  
  882.     if (FUNCTION == END) {
  883.         Trace((stderr, "freeing rootpath\n"));
  884.         if (G.rootlen > 0) {
  885.             free(G.rootpath);
  886.             G.rootlen = 0;
  887.         }
  888.         return MPN_OK;
  889.     }
  890.  
  891.     return MPN_INVALID; /* should never reach */
  892.  
  893. } /* end function checkdir() */
  894.  
  895.  
  896.  
  897.  
  898.  
  899. static int get_extattribs OF((__GPRO__ iztimes *pzt, ulg z_uidgid[2]));
  900.  
  901. static int get_extattribs(__G__ pzt, z_uidgid)
  902.     __GDEF
  903.     iztimes *pzt;
  904.     ulg z_uidgid[2];
  905. {
  906. /*---------------------------------------------------------------------------
  907.     Convert from MSDOS-format local time and date to Unix-format 32-bit GMT
  908.     time:  adjust base year from 1980 to 1970, do usual conversions from
  909.     yy/mm/dd hh:mm:ss to elapsed seconds, and account for timezone and day-
  910.     light savings time differences.  If we have a Unix extra field, however,
  911.     we're laughing:  both mtime and atime are ours.  On the other hand, we
  912.     then have to check for restoration of UID/GID.
  913.   ---------------------------------------------------------------------------*/
  914.     int have_uidgid_flg;
  915.     unsigned eb_izux_flg;
  916.  
  917.     eb_izux_flg = (G.extra_field ? ef_scan_for_izux(G.extra_field,
  918.                    G.lrec.extra_field_length, 0, G.lrec.last_mod_dos_datetime,
  919. #ifdef IZ_CHECK_TZ
  920.                    (G.tz_is_valid ? pzt : NULL),
  921. #else
  922.                    pzt,
  923. #endif
  924.                    z_uidgid) : 0);
  925.     if (eb_izux_flg & EB_UT_FL_MTIME) {
  926.         TTrace((stderr, "\nget_extattribs:  Unix e.f. modif. time = %ld\n",
  927.           pzt->mtime));
  928.     } else {
  929.         pzt->mtime = dos_to_unix_time(G.lrec.last_mod_dos_datetime);
  930.     }
  931.     if (eb_izux_flg & EB_UT_FL_ATIME) {
  932.         TTrace((stderr, "get_extattribs:  Unix e.f. access time = %ld\n",
  933.           pzt->atime));
  934.     } else {
  935.         pzt->atime = pzt->mtime;
  936.         TTrace((stderr, "\nget_extattribs:  modification/access times = %ld\n",
  937.           pzt->mtime));
  938.     }
  939.  
  940.     /* if -X option was specified and we have UID/GID info, restore it */
  941.     have_uidgid_flg =
  942. #ifdef RESTORE_UIDGID
  943.             (uO.X_flag && (eb_izux_flg & EB_UX2_VALID));
  944. #else
  945.             0;
  946. #endif
  947.     return have_uidgid_flg;
  948. }
  949.  
  950.  
  951.  
  952.  
  953. /****************************/
  954. /* Function close_outfile() */
  955. /****************************/
  956.  
  957. void close_outfile(__G)    /* GRR: change to return PK-style warning level */
  958.     __GDEF
  959. {
  960.     union {
  961.         iztimes t3;             /* mtime, atime, ctime */
  962.         ztimbuf t2;             /* modtime, actime */
  963.     } zt;
  964.     ulg z_uidgid[2];
  965.     int have_uidgid_flg;
  966.  
  967.     have_uidgid_flg = get_extattribs(__G__ &(zt.t3), z_uidgid);
  968.  
  969. /*---------------------------------------------------------------------------
  970.     If symbolic links are supported, allocate storage for a symlink control
  971.     structure, put the uncompressed "data" and other required info in it, and
  972.     add the structure to the "deferred symlinks" chain.  Since we know it's a
  973.     symbolic link to start with, we shouldn't have to worry about overflowing
  974.     unsigned ints with unsigned longs.
  975.   ---------------------------------------------------------------------------*/
  976.  
  977. #ifdef SYMLINKS
  978.     if (G.symlnk) {
  979.         extent ucsize = (extent)G.lrec.ucsize;
  980.         unsigned BeOSef_len = 0;
  981.         extent slnk_entrysize;
  982.         uch *BeOS_exfld;
  983.         slinkentry *slnk_entry;
  984.  
  985.         if (!uO.J_flag) {
  986.             /* Symlinks can have attributes, too. */
  987.             BeOS_exfld = scanBeOSexfield(G.extra_field,
  988.                                          G.lrec.extra_field_length);
  989.             if (BeOS_exfld) {
  990.                 BeOSef_len = makeword(EB_LEN + BeOS_exfld) + EB_HEADSIZE;
  991.             }
  992.         }
  993.  
  994.         /* size of the symlink entry is the sum of
  995.          *  (struct size (includes 1st '\0') + 1 additional trailing '\0'),
  996.          *  system specific attribute data size (might be 0),
  997.          *  and the lengths of name and link target.
  998.          */
  999.         slnk_entrysize = (sizeof(slinkentry) + 1) + BeOSef_len +
  1000.                          ucsize + strlen(G.filename);
  1001.  
  1002.         if (slnk_entrysize < ucsize) {
  1003.             Info(slide, 0x201, ((char *)slide,
  1004.               "warning:  symbolic link (%s) failed: mem alloc overflow\n",
  1005.               FnFilter1(G.filename)));
  1006.             fclose(G.outfile);
  1007.             return;
  1008.         }
  1009.  
  1010.         if ((slnk_entry = (slinkentry *)malloc(slnk_entrysize)) == NULL) {
  1011.             Info(slide, 0x201, ((char *)slide,
  1012.               "warning:  symbolic link (%s) failed: no mem\n",
  1013.               FnFilter1(G.filename)));
  1014.             fclose(G.outfile);
  1015.             return;
  1016.         }
  1017.         slnk_entry->next = NULL;
  1018.         slnk_entry->targetlen = ucsize;
  1019.         slnk_entry->attriblen = BeOSef_len;
  1020.         slnk_entry->target = slnk_entry->buf + BeOSef_len;
  1021.         slnk_entry->fname = slnk_entry->target + ucsize + 1;
  1022.         strcpy(slnk_entry->fname, G.filename);
  1023.         if (BeOSef_len > 0)
  1024.             memcpy(slnk_entry->buf, BeOS_exfld, BeOSef_len);
  1025.  
  1026.         /* move back to the start of the file to re-read the "link data" */
  1027.         rewind(G.outfile);
  1028.  
  1029.         if (fread(slnk_entry->target, 1, ucsize, G.outfile) != ucsize)
  1030.         {
  1031.             Info(slide, 0x201, ((char *)slide,
  1032.               "warning:  symbolic link (%s) failed\n",
  1033.               FnFilter1(G.filename)));
  1034.             free(slnk_entry);
  1035.             fclose(G.outfile);
  1036.             return;
  1037.         }
  1038.         fclose(G.outfile);                  /* close "link" file for good... */
  1039.         slnk_entry->target[ucsize] = '\0';
  1040.         if (QCOND2)
  1041.             Info(slide, 0, ((char *)slide, "-> %s ",
  1042.               FnFilter1(slnk_entry->target)));
  1043.         /* add this symlink record to the list of deferred symlinks */
  1044.         if (G.slink_last != NULL)
  1045.             G.slink_last->next = slnk_entry;
  1046.         else
  1047.             G.slink_head = slnk_entry;
  1048.         G.slink_last = slnk_entry;
  1049.         return;
  1050.     }
  1051. #endif /* SYMLINKS */
  1052.  
  1053.     fclose(G.outfile);
  1054.  
  1055.     /* handle the BeOS extra field if present */
  1056.     if (!uO.J_flag) {
  1057.         void *ptr = scanBeOSexfield(G.extra_field,
  1058.                                     G.lrec.extra_field_length);
  1059.  
  1060.         if (ptr) {
  1061.             setBeOSexfield(G.filename, ptr);
  1062. #ifdef BEOS_ASSIGN_FILETYPE
  1063.         } else {
  1064.             /* Otherwise, ask the system to try assigning a MIME type. */
  1065.             assign_MIME( G.filename );
  1066. #endif
  1067.         }
  1068.     }
  1069.  
  1070. /*---------------------------------------------------------------------------
  1071.     Change the file permissions from default ones to those stored in the
  1072.     zipfile.
  1073.   ---------------------------------------------------------------------------*/
  1074.  
  1075. #ifndef NO_CHMOD
  1076.     if (chmod(G.filename, filtattr(__G__ G.pInfo->file_attr)))
  1077.         perror("chmod (file attributes) error");
  1078. #endif
  1079.  
  1080.     /* if -X option was specified and we have UID/GID info, restore it */
  1081.     if (have_uidgid_flg
  1082.         /* check that both uid and gid values fit into their data sizes */
  1083.         && ((ulg)(uid_t)(z_uidgid[0]) == z_uidgid[0])
  1084.         && ((ulg)(gid_t)(z_uidgid[1]) == z_uidgid[1])) {
  1085.         TTrace((stderr, "close_outfile:  restoring Unix UID/GID info\n"));
  1086.         if (chown(G.filename, (uid_t)z_uidgid[0], (gid_t)z_uidgid[1]))
  1087.         {
  1088.             if (uO.qflag)
  1089.                 Info(slide, 0x201, ((char *)slide, CannotSetItemUidGid,
  1090.                   z_uidgid[0], z_uidgid[1], FnFilter1(G.filename),
  1091.                   strerror(errno)));
  1092.             else
  1093.                 Info(slide, 0x201, ((char *)slide, CannotSetUidGid,
  1094.                   z_uidgid[0], z_uidgid[1], strerror(errno)));
  1095.         }
  1096.     }
  1097.  
  1098.     /* skip restoring time stamps on user's request */
  1099.     if (uO.D_flag <= 1) {
  1100.         /* set the file's access and modification times */
  1101.         if (utime(G.filename, &(zt.t2))) {
  1102.             if (uO.qflag)
  1103.                 Info(slide, 0x201, ((char *)slide, CannotSetItemTimestamps,
  1104.                   FnFilter1(G.filename), strerror(errno)));
  1105.             else
  1106.                 Info(slide, 0x201, ((char *)slide, CannotSetTimestamps,
  1107.                   strerror(errno)));
  1108.         }
  1109.     }
  1110.  
  1111. } /* end function close_outfile() */
  1112.  
  1113.  
  1114.  
  1115.  
  1116. #ifdef SYMLINKS
  1117. int set_symlnk_attribs(__G__ slnk_entry)
  1118.     __GDEF
  1119.     slinkentry *slnk_entry;
  1120. {
  1121.     if (slnk_entry->attriblen > 0)
  1122.         setBeOSexfield(slnk_entry->fname, (uch *)slnk_entry->buf);
  1123.     /* currently, no error propagation... */
  1124.     return PK_OK;
  1125. } /* end function set_symlnk_attribs() */
  1126. #endif /* SYMLINKS */
  1127.  
  1128.  
  1129.  
  1130.  
  1131. #ifdef SET_DIR_ATTRIB
  1132. /* messages of code for setting directory attributes */
  1133. #  ifndef NO_CHMOD
  1134.   static ZCONST char DirlistChmodFailed[] =
  1135.     "warning:  cannot set permissions for %s\n          %s\n";
  1136. #  endif
  1137.  
  1138.  
  1139. int defer_dir_attribs(__G__ pd)
  1140.     __GDEF
  1141.     direntry **pd;
  1142. {
  1143.     uxdirattr *d_entry;
  1144.  
  1145.     d_entry = (uxdirattr *)malloc(sizeof(uxdirattr) + strlen(G.filename));
  1146.     *pd = (direntry *)d_entry;
  1147.     if (d_entry == (uxdirattr *)NULL) {
  1148.         return PK_MEM;
  1149.     }
  1150.     d_entry->fn = d_entry->fnbuf;
  1151.     strcpy(d_entry->fn, G.filename);
  1152.  
  1153.     d_entry->perms = G.pInfo->file_attr;
  1154.  
  1155.     d_entry->have_uidgid = get_extattribs(__G__ &(d_entry->u.t3),
  1156.                                           d_entry->uidgid);
  1157.     return PK_OK;
  1158. } /* end function defer_dir_attribs() */
  1159.  
  1160.  
  1161. int set_direc_attribs(__G__ d)
  1162.     __GDEF
  1163.     direntry *d;
  1164. {
  1165.     int errval = PK_OK;
  1166.  
  1167.     if (UxAtt(d)->have_uidgid &&
  1168.         /* check that both uid and gid values fit into their data sizes */
  1169.         ((ulg)(uid_t)(UxAtt(d)->uidgid[0]) == UxAtt(d)->uidgid[0]) &&
  1170.         ((ulg)(gid_t)(UxAtt(d)->uidgid[1]) == UxAtt(d)->uidgid[1]) &&
  1171.         chown(UxAtt(d)->fn, (uid_t)UxAtt(d)->uidgid[0],
  1172.               (gid_t)UxAtt(d)->uidgid[1]))
  1173.     {
  1174.         Info(slide, 0x201, ((char *)slide, CannotSetItemUidGid,
  1175.           UxAtt(d)->uidgid[0], UxAtt(d)->uidgid[1], FnFilter1(d->fn),
  1176.           strerror(errno)));
  1177.         if (!errval)
  1178.             errval = PK_WARN;
  1179.     }
  1180.     /* Skip restoring directory time stamps on user' request. */
  1181.     if (uO.D_flag <= 0) {
  1182.         /* restore directory timestamps */
  1183.         if (utime(d->fn, (const struct utimbuf *)&UxAtt(d)->u.t2)) {
  1184.             Info(slide, 0x201, ((char *)slide, CannotSetItemTimestamps,
  1185.               FnFilter1(d->fn), strerror(errno)));
  1186.             if (!errval)
  1187.                 errval = PK_WARN;
  1188.         }
  1189.     }
  1190. #ifndef NO_CHMOD
  1191.     if (chmod(d->fn, filtattr(__G__ UxAtt(d)->perms))) {
  1192.         Info(slide, 0x201, ((char *)slide, DirlistChmodFailed,
  1193.           FnFilter1(d->fn), strerror(errno)));
  1194.         if (!errval)
  1195.             errval = PK_WARN;
  1196.     }
  1197. #endif /* !NO_CHMOD */
  1198.     return errval;
  1199. } /* end function set_direc_attribs() */
  1200.  
  1201. #endif /* SET_DIR_ATTRIB */
  1202.  
  1203.  
  1204.  
  1205.  
  1206. #ifdef TIMESTAMP
  1207.  
  1208. /***************************/
  1209. /*  Function stamp_file()  */
  1210. /***************************/
  1211.  
  1212. int stamp_file(fname, modtime)
  1213.     ZCONST char *fname;
  1214.     time_t modtime;
  1215. {
  1216.     struct utimbuf tp;
  1217.  
  1218.     tp.modtime = tp.actime = modtime;
  1219.     return (utime(fname, &tp));
  1220.  
  1221. } /* end function stamp_file() */
  1222.  
  1223. #endif /* TIMESTAMP */
  1224.  
  1225.  
  1226.  
  1227.  
  1228. #ifndef SFX
  1229.  
  1230. /************************/
  1231. /*  Function version()  */
  1232. /************************/
  1233.  
  1234. void version(__G)
  1235.     __GDEF
  1236. {
  1237.     sprintf((char *)slide, LoadFarString(CompiledWith),
  1238. #if defined(__MWERKS__)
  1239.       "Metrowerks CodeWarrior", "",
  1240. #elif defined(__GNUC__)
  1241.       "GNU C ", __VERSION__,
  1242. #else
  1243.       "(unknown compiler) ","",
  1244. #endif
  1245.       "BeOS ",
  1246.  
  1247. #ifdef __POWERPC__
  1248.       "(PowerPC)",
  1249. #else
  1250. # ifdef __INTEL__
  1251.       "(x86)",
  1252. # else
  1253.       "(unknown)",   /* someday we may have other architectures... */
  1254. # endif
  1255. #endif
  1256.  
  1257. #ifdef __DATE__
  1258.       " on ", __DATE__
  1259. #else
  1260.       "", ""
  1261. #endif
  1262.     );
  1263.  
  1264.     (*G.message)((zvoid *)&G, slide, (ulg)strlen((char *)slide), 0);
  1265.  
  1266. } /* end function version() */
  1267.  
  1268. #endif /* !SFX */
  1269.  
  1270.  
  1271.  
  1272. /******************************/
  1273. /* Extra field functions      */
  1274. /******************************/
  1275.  
  1276. /*
  1277. ** Scan the extra fields in extra_field, and look for a BeOS EF; return a
  1278. ** pointer to that EF, or NULL if it's not there.
  1279. */
  1280. static uch *scanBeOSexfield(const uch *ef_ptr, unsigned ef_len)
  1281. {
  1282.     while( ef_ptr != NULL && ef_len >= EB_HEADSIZE ) {
  1283.         unsigned eb_id  = makeword(EB_ID + ef_ptr);
  1284.         unsigned eb_len = makeword(EB_LEN + ef_ptr);
  1285.  
  1286.         if (eb_len > (ef_len - EB_HEADSIZE)) {
  1287.             Trace((stderr,
  1288.               "scanBeOSexfield: block length %u > rest ef_size %u\n", eb_len,
  1289.               ef_len - EB_HEADSIZE));
  1290.             break;
  1291.         }
  1292.  
  1293.         if (eb_id == EF_BEOS && eb_len >= EB_BEOS_HLEN) {
  1294.             return (uch *)ef_ptr;
  1295.         }
  1296.  
  1297.         ef_ptr += (eb_len + EB_HEADSIZE);
  1298.         ef_len -= (eb_len + EB_HEADSIZE);
  1299.     }
  1300.  
  1301.     return NULL;
  1302. }
  1303.  
  1304. /* Used by setBeOSexfield():
  1305.  
  1306. Set a file/directory's attributes to the attributes passed in.
  1307.  
  1308. If set_file_attrs() fails, an error will be returned:
  1309.  
  1310.      EOK - no errors occurred
  1311.  
  1312. (other values will be whatever the failed function returned; no docs
  1313. yet, or I'd list a few)
  1314. */
  1315. static int set_file_attrs( const char *name,
  1316.                            const unsigned char *attr_buff,
  1317.                            const off_t attr_size )
  1318. {
  1319.     int                  retval = EOK;
  1320.     unsigned char       *ptr;
  1321.     const unsigned char *guard;
  1322.     int                  fd;
  1323.  
  1324.     ptr   = (unsigned char *)attr_buff;
  1325.     guard = ptr + attr_size;
  1326.  
  1327.     fd = open(name, O_RDWR | O_NOTRAVERSE);
  1328.     if (fd < 0) {
  1329.         return errno; /* should it be -fd ? */
  1330.     }
  1331.  
  1332.     while (ptr < guard) {
  1333.         ssize_t              wrote_bytes;
  1334.         struct attr_info     fa_info;
  1335.         const char          *attr_name;
  1336.         unsigned char       *attr_data;
  1337.  
  1338.         attr_name  = (char *)&(ptr[0]);
  1339.         ptr       += strlen(attr_name) + 1;
  1340.  
  1341.         /* The attr_info data is stored in big-endian format because the */
  1342.         /* PowerPC port was here first.                                  */
  1343.         memcpy(&fa_info, ptr, sizeof(struct attr_info));
  1344.         fa_info.type = (uint32)B_BENDIAN_TO_HOST_INT32( fa_info.type );
  1345.         fa_info.size = (off_t)B_BENDIAN_TO_HOST_INT64( fa_info.size );
  1346.         ptr     += sizeof(struct attr_info);
  1347.  
  1348.         if (fa_info.size < 0LL) {
  1349.             Info(slide, 0x201, ((char *)slide,
  1350.                  "warning: skipping attribute with invalid length (%Ld)\n",
  1351.                  fa_info.size));
  1352.             break;
  1353.         }
  1354.  
  1355.         attr_data  = ptr;
  1356.         ptr       += fa_info.size;
  1357.  
  1358.         if (ptr > guard) {
  1359.             /* We've got a truncated attribute. */
  1360.             Info(slide, 0x201, ((char *)slide,
  1361.                  "warning: truncated attribute\n"));
  1362.             break;
  1363.         }
  1364.  
  1365.         /* Wave the magic wand... this will swap Be-known types properly. */
  1366.         (void)swap_data( fa_info.type, attr_data, fa_info.size,
  1367.                          B_SWAP_BENDIAN_TO_HOST );
  1368.  
  1369.         wrote_bytes = fs_write_attr(fd, attr_name, fa_info.type, 0,
  1370.                                     attr_data, fa_info.size);
  1371.         if (wrote_bytes != fa_info.size) {
  1372.             Info(slide, 0x201, ((char *)slide,
  1373.                  "warning: wrote %ld attribute bytes of %ld\n",
  1374.                  (unsigned long)wrote_bytes,(unsigned long)fa_info.size));
  1375.         }
  1376.     }
  1377.  
  1378.     close(fd);
  1379.  
  1380.     return retval;
  1381. }
  1382.  
  1383. static void setBeOSexfield(const char *path, uch *extra_field)
  1384. {
  1385.     uch *ptr       = extra_field;
  1386.     ush  id        = 0;
  1387.     ush  size      = 0;
  1388.     ulg  full_size = 0;
  1389.     uch  flags     = 0;
  1390.     uch *attrbuff  = NULL;
  1391.     int retval;
  1392.  
  1393.     if( extra_field == NULL ) {
  1394.         return;
  1395.     }
  1396.  
  1397.     /* Collect the data from the extra field buffer. */
  1398.     id        = makeword(ptr);    ptr += 2;   /* we don't use this... */
  1399.     size      = makeword(ptr);    ptr += 2;
  1400.     full_size = makelong(ptr);    ptr += 4;
  1401.     flags     = *ptr;             ptr++;
  1402.  
  1403.     /* Do a little sanity checking. */
  1404.     if (flags & EB_BE_FL_BADBITS) {
  1405.         /* corrupted or unsupported */
  1406.         Info(slide, 0x201, ((char *)slide,
  1407.           "Unsupported flags set for this BeOS extra field, skipping.\n"));
  1408.         return;
  1409.     }
  1410.     if (size <= EB_BEOS_HLEN) {
  1411.         /* corrupted, unsupported, or truncated */
  1412.         Info(slide, 0x201, ((char *)slide,
  1413.              "BeOS extra field is %d bytes, should be at least %d.\n", size,
  1414.              EB_BEOS_HLEN));
  1415.         return;
  1416.     }
  1417.     if (full_size < (size - EB_BEOS_HLEN)) {
  1418.         /* possible old archive? will this screw up on valid archives? */
  1419.         Info(slide, 0x201, ((char *)slide,
  1420.              "Skipping attributes: BeOS extra field is %d bytes, "
  1421.              "data size is %ld.\n", size - EB_BEOS_HLEN, full_size));
  1422.         return;
  1423.     }
  1424.  
  1425.     /* Find the BeOS file attribute data. */
  1426.     if (flags & EB_BE_FL_UNCMPR) {
  1427.         /* Uncompressed data */
  1428.         attrbuff = ptr;
  1429.     } else {
  1430.         /* Compressed data */
  1431.         attrbuff = (uch *)malloc( full_size );
  1432.         if (attrbuff == NULL) {
  1433.             /* No memory to uncompress attributes */
  1434.             Info(slide, 0x201, ((char *)slide,
  1435.                  "Can't allocate memory to uncompress file attributes.\n"));
  1436.             return;
  1437.         }
  1438.  
  1439.         retval = memextract(__G__ attrbuff, full_size,
  1440.                             ptr, size - EB_BEOS_HLEN);
  1441.         if( retval != PK_OK ) {
  1442.             /* error uncompressing attributes */
  1443.             Info(slide, 0x201, ((char *)slide,
  1444.                  "Error uncompressing file attributes.\n"));
  1445.  
  1446.             /* Some errors here might not be so bad; we should expect */
  1447.             /* some truncated data, for example.  If the data was     */
  1448.             /* corrupt, we should _not_ attempt to restore the attrs  */
  1449.             /* for this file... there's no way to detect what attrs   */
  1450.             /* are good and which are bad.                            */
  1451.             free (attrbuff);
  1452.             return;
  1453.         }
  1454.     }
  1455.  
  1456.     /* Now attempt to set the file attributes on the extracted file. */
  1457.     retval = set_file_attrs(path, attrbuff, (off_t)full_size);
  1458.     if (retval != EOK) {
  1459.         Info(slide, 0x201, ((char *)slide,
  1460.              "Error writing file attributes.\n"));
  1461.     }
  1462.  
  1463.     /* Clean up, if necessary */
  1464.     if (attrbuff != ptr) {
  1465.         free(attrbuff);
  1466.     }
  1467.  
  1468.     return;
  1469. }
  1470.  
  1471. #ifdef BEOS_USE_PRINTEXFIELD
  1472. static void printBeOSexfield( int isdir, uch *extra_field )
  1473. {
  1474.     uch *ptr       = extra_field;
  1475.     ush  id        = 0;
  1476.     ush  size      = 0;
  1477.     ulg  full_size = 0;
  1478.     uch  flags     = 0;
  1479.  
  1480.     /* Tell picky compilers to be quiet. */
  1481.     isdir = isdir;
  1482.  
  1483.     if( extra_field == NULL ) {
  1484.         return;
  1485.     }
  1486.  
  1487.     /* Collect the data from the buffer. */
  1488.     id        = makeword( ptr );    ptr += 2;
  1489.     size      = makeword( ptr );    ptr += 2;
  1490.     full_size = makelong( ptr );    ptr += 4;
  1491.     flags     = *ptr;               ptr++;
  1492.  
  1493.     if( id != EF_BEOS ) {
  1494.         /* not a 'Be' field */
  1495.         printf("\t*** Unknown field type (0x%04x, '%c%c')\n", id,
  1496.                (char)(id >> 8), (char)id);
  1497.     }
  1498.  
  1499.     if( flags & EB_BE_FL_BADBITS ) {
  1500.         /* corrupted or unsupported */
  1501.         printf("\t*** Corrupted BeOS extra field:\n");
  1502.         printf("\t*** unknown bits set in the flags\n");
  1503.         printf("\t*** (Possibly created by an old version of zip for BeOS.\n");
  1504.     }
  1505.  
  1506.     if( size <= EB_BEOS_HLEN ) {
  1507.         /* corrupted, unsupported, or truncated */
  1508.         printf("\t*** Corrupted BeOS extra field:\n");
  1509.         printf("\t*** size is %d, should be larger than %d\n", size,
  1510.                EB_BEOS_HLEN );
  1511.     }
  1512.  
  1513.     if( flags & EB_BE_FL_UNCMPR ) {
  1514.         /* Uncompressed data */
  1515.         printf("\tBeOS extra field data (uncompressed):\n");
  1516.         printf("\t\t%ld data bytes\n", full_size);
  1517.     } else {
  1518.         /* Compressed data */
  1519.         printf("\tBeOS extra field data (compressed):\n");
  1520.         printf("\t\t%d compressed bytes\n", size - EB_BEOS_HLEN);
  1521.         printf("\t\t%ld uncompressed bytes\n", full_size);
  1522.     }
  1523. }
  1524. #endif
  1525.  
  1526. #ifdef BEOS_ASSIGN_FILETYPE
  1527. /* Note: This will no longer be necessary in BeOS PR4; update_mime_info()    */
  1528. /* will be updated to build its own absolute pathname if it's not given one. */
  1529. static void assign_MIME( const char *file )
  1530. {
  1531.     char *fullname;
  1532.     char buff[PATH_MAX], cwd_buff[PATH_MAX];
  1533.     int retval;
  1534.  
  1535.     if( file[0] == '/' ) {
  1536.         fullname = (char *)file;
  1537.     } else {
  1538.         sprintf( buff, "%s/%s", getcwd( cwd_buff, PATH_MAX ), file );
  1539.         fullname = buff;
  1540.     }
  1541.  
  1542.     retval = update_mime_info( fullname, FALSE, TRUE, TRUE );
  1543. }
  1544. #endif
  1545.