Subversion Repositories Kolibri OS

Rev

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

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