Subversion Repositories Kolibri OS

Rev

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

  1. /*
  2.   Copyright (c) 1990-2007 Info-ZIP.  All rights reserved.
  3.  
  4.   See the accompanying file LICENSE, version 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.   acorn.c
  12.  
  13.   RISCOS-specific routines for use with Info-ZIP's UnZip 5.2 and later.
  14.  
  15.   Contains:  do_wild()           <-- generic enough to put in fileio.c?
  16.              mapattr()
  17.              mapname()
  18.              checkdir()
  19.              mkdir()
  20.              setRISCOSexfield()
  21.              printRISCOSexfield()
  22.              close_outfile()
  23.              stamp_file()
  24.              version()
  25.  
  26.   ---------------------------------------------------------------------------*/
  27.  
  28.  
  29. #define UNZIP_INTERNAL
  30. #include "^.unzip.h"
  31. #include "riscos.h"
  32.  
  33. #define FTYPE_FFF (1<<17)      /* set filetype to &FFF when extracting */
  34.  
  35. #ifdef WILD_STOP_AT_DIR
  36. #  define WESEP     , (oU.W_flag ? '.' : '\0')
  37. #else
  38. #  define WESEP
  39. #endif
  40.  
  41. static int created_dir;        /* used in mapname(), checkdir() */
  42. static int renamed_fullpath;   /* ditto */
  43. static int has_mimemap = -1;   /* used in mimemap() */
  44.  
  45. extern int mkdir(const char *path, int mode);
  46. static int has_NFS_ext(const char *name);
  47. static void setRISCOSexfield(ZCONST char *path, ZCONST void *ef_spark);
  48. #ifdef DEBUG
  49. static void printRISCOSexfield(int isdir, ZCONST void *extra_field);
  50. #endif
  51. static int uxtime2acornftime(unsigned *pexadr, unsigned *pldadr, time_t ut);
  52. static int mimemap(const char *name);
  53.  
  54.  
  55. #ifndef SFX
  56.  
  57. /**********************/
  58. /* Function do_wild() */   /* for porting: dir separator; match(ignore_case) */
  59. /**********************/
  60.  
  61. char *do_wild(__G__ wildspec)
  62.     __GDEF
  63.     ZCONST char *wildspec;  /* only used first time on a given dir */
  64. {
  65.     static DIR *wild_dir = (DIR *)NULL;
  66.     static ZCONST char *wildname;
  67.     static char *dirname, matchname[FILNAMSIZ];
  68.     static int notfirstcall=FALSE, have_dirname, dirnamelen;
  69.     struct dirent *file;
  70.  
  71.     /* Even when we're just returning wildspec, we *always* do so in
  72.      * matchname[]--calling routine is allowed to append four characters
  73.      * to the returned string, and wildspec may be a pointer to argv[].
  74.      */
  75.     if (!notfirstcall) {    /* first call:  must initialize everything */
  76.         notfirstcall = TRUE;
  77.  
  78.         /* break the wildspec into a directory part and a wildcard filename */
  79.         if ((wildname = (ZCONST char *)strrchr(wildspec, '.')) ==
  80.             (ZCONST char *)NULL)
  81.         {
  82.             dirname = ".";
  83.             dirnamelen = 1;
  84.             have_dirname = FALSE;
  85.             wildname = wildspec;
  86.         } else {
  87.             ++wildname;     /* point at character after '/' */
  88.             dirnamelen = wildname - wildspec;
  89.             if ((dirname = (char *)malloc(dirnamelen+1)) == (char *)NULL) {
  90.                 Info(slide, 0x201, ((char *)slide,
  91.                   "warning:  cannot allocate wildcard buffers\n"));
  92.                 strncpy(matchname, wildspec, FILNAMSIZ);
  93.                 matchname[FILNAMSIZ-1] = '\0';
  94.                 return matchname;   /* but maybe filespec was not a wildcard */
  95.             }
  96.             strncpy(dirname, wildspec, dirnamelen);
  97.             dirname[dirnamelen] = '\0';   /* terminate for strcpy below */
  98.             have_dirname = TRUE;
  99.         }
  100.  
  101.         if ((wild_dir = opendir(dirname)) != (DIR *)NULL) {
  102.             while ((file = readdir(wild_dir)) != (struct dirent *)NULL) {
  103.                 if (file->d_name[0] == '/' && wildname[0] != '/')
  104.                     continue; /* Unix:  '*' and '?' do not match leading dot */
  105.                 if (match(file->d_name, wildname, 0 WESEP)) { /* 0=case sens.*/
  106.                     if (have_dirname) {
  107.                         strcpy(matchname, dirname);
  108.                         strcpy(matchname+dirnamelen, file->d_name);
  109.                     } else
  110.                         strcpy(matchname, file->d_name);
  111.                     return matchname;
  112.                 }
  113.             }
  114.             /* if we get to here directory is exhausted, so close it */
  115.             closedir(wild_dir);
  116.             wild_dir = (DIR *)NULL;
  117.         }
  118.  
  119.         /* return the raw wildspec in case that works (e.g., directory not
  120.          * searchable, but filespec was not wild and file is readable) */
  121.         strncpy(matchname, wildspec, FILNAMSIZ);
  122.         matchname[FILNAMSIZ-1] = '\0';
  123.         return matchname;
  124.     }
  125.  
  126.     /* last time through, might have failed opendir but returned raw wildspec */
  127.     if (wild_dir == (DIR *)NULL) {
  128.         notfirstcall = FALSE; /* nothing left to try--reset for new wildspec */
  129.         if (have_dirname)
  130.             free(dirname);
  131.         return (char *)NULL;
  132.     }
  133.  
  134.     /* If we've gotten this far, we've read and matched at least one entry
  135.      * successfully (in a previous call), so dirname has been copied into
  136.      * matchname already.
  137.      */
  138.     while ((file = readdir(wild_dir)) != (struct dirent *)NULL)
  139.         if (match(file->d_name, wildname, 0 WESEP)) {   /* 0 == case sens. */
  140.             if (have_dirname) {
  141.                 /* strcpy(matchname, dirname); */
  142.                 strcpy(matchname+dirnamelen, file->d_name);
  143.             } else
  144.                 strcpy(matchname, file->d_name);
  145.             return matchname;
  146.         }
  147.  
  148.     closedir(wild_dir); /* have read at least one dir entry; nothing left */
  149.     wild_dir = (DIR *)NULL;
  150.     notfirstcall = FALSE;   /* reset for new wildspec */
  151.     if (have_dirname)
  152.         free(dirname);
  153.     return (char *)NULL;
  154.  
  155. } /* end function do_wild() */
  156.  
  157. #endif /* !SFX */
  158.  
  159.  
  160.  
  161. /**************************/
  162. /* Function has_NFS_ext() */
  163. /**************************/
  164.  
  165. static int has_NFS_ext(const char* name)
  166. {
  167.   int i = strlen(name) - 4;
  168.  
  169.   return (i >= 0 && name[i] == ',' && (i > 0 || name[i-1]=='/') &&
  170.           isxdigit(name[i+1]) && isxdigit(name[i+2]) && isxdigit(name[i+3]));
  171. } /* end function has_NFS_ext() */
  172.  
  173.  
  174.  
  175. /**********************/
  176. /* Function mapattr() */
  177. /**********************/
  178.  
  179. int mapattr(__G)
  180.     __GDEF
  181. {
  182.     ulg tmp = G.crec.external_file_attributes;
  183.  
  184.     switch (G.pInfo->hostnum) {
  185.         case AMIGA_:
  186.             tmp = (unsigned)(tmp>>17 & 7);   /* Amiga RWE bits */
  187.             G.pInfo->file_attr = (unsigned)(tmp<<6 | tmp<<3 | tmp);
  188.             break;
  189.         case THEOS_:
  190.             tmp &= 0xF1FFFFFFL;
  191.             if ((tmp & 0xF0000000L) != 0x40000000L)
  192.                 tmp &= 0x01FFFFFFL;     /* not a dir, mask all ftype bits */
  193.             else
  194.                 tmp &= 0x41FFFFFFL;     /* leave directory bit as set */
  195.             /* fall through! */
  196.         case ACORN_:
  197.         case UNIX_:
  198.         case VMS_:
  199.         case ATARI_:
  200.         case ATHEOS_:
  201.         case BEOS_:
  202.         case QDOS_:
  203.         case TANDEM_:
  204.             G.pInfo->file_attr = (unsigned)(tmp >> 16);
  205.             if (G.pInfo->file_attr != 0 || !G.extra_field) {
  206.                 break;
  207.             } else {
  208.                 /* Some (non-Info-ZIP) implementations of Zip for Unix and
  209.                    VMS (and probably others ??) leave 0 in the upper 16-bit
  210.                    part of the external_file_attributes field. Instead, they
  211.                    store file permission attributes in some extra field.
  212.                    As a work-around, we search for the presence of one of
  213.                    these extra fields and fall back to the MSDOS compatible
  214.                    part of external_file_attributes if one of the known
  215.                    e.f. types has been detected.
  216.                    Later, we might implement extraction of the permission
  217.                    bits from the VMS extra field. But for now, the work-around
  218.                    should be sufficient to provide "readable" extracted files.
  219.                    (For ASI Unix e.f., an experimental remap of the e.f.
  220.                    mode value IS already provided!)
  221.                  */
  222.                 ush ebID;
  223.                 unsigned ebLen;
  224.                 uch *ef = G.extra_field;
  225.                 unsigned ef_len = G.crec.extra_field_length;
  226.                 int r = FALSE;
  227.  
  228.                 while (!r && ef_len >= EB_HEADSIZE) {
  229.                     ebID = makeword(ef);
  230.                     ebLen = (unsigned)makeword(ef+EB_LEN);
  231.                     if (ebLen > (ef_len - EB_HEADSIZE))
  232.                         /* discoverd some e.f. inconsistency! */
  233.                         break;
  234.                     switch (ebID) {
  235.                       case EF_ASIUNIX:
  236.                         if (ebLen >= (EB_ASI_MODE+2)) {
  237.                             G.pInfo->file_attr =
  238.                               (unsigned)makeword(ef+(EB_HEADSIZE+EB_ASI_MODE));
  239.                             /* force stop of loop: */
  240.                             ef_len = (ebLen + EB_HEADSIZE);
  241.                             break;
  242.                         }
  243.                         /* else: fall through! */
  244.                       case EF_PKVMS:
  245.                         /* "found nondecypherable e.f. with perm. attr" */
  246.                         r = TRUE;
  247.                       default:
  248.                         break;
  249.                     }
  250.                     ef_len -= (ebLen + EB_HEADSIZE);
  251.                     ef += (ebLen + EB_HEADSIZE);
  252.                 }
  253.                 if (!r)
  254.                     break;
  255.             }
  256.             /* fall through! */
  257.         /* all remaining cases:  expand MSDOS read-only bit into write perms */
  258.         case FS_FAT_:
  259.             /* PKWARE's PKZip for Unix marks entries as FS_FAT_, but stores the
  260.              * Unix attributes in the upper 16 bits of the external attributes
  261.              * field, just like Info-ZIP's Zip for Unix.  We try to use that
  262.              * value, after a check for consistency with the MSDOS attribute
  263.              * bits (see below).
  264.              */
  265.             G.pInfo->file_attr = (unsigned)(tmp >> 16);
  266.             /* fall through! */
  267.         case FS_HPFS_:
  268.         case FS_NTFS_:
  269.         case MAC_:
  270.         case TOPS20_:
  271.         default:
  272.             /* Ensure that DOS subdir bit is set when the entry's name ends
  273.              * in a '/'.  Some third-party Zip programs fail to set the subdir
  274.              * bit for directory entries.
  275.              */
  276.             if ((tmp & 0x10) == 0) {
  277.                 extent fnlen = strlen(G.filename);
  278.                 if (fnlen > 0 && G.filename[fnlen-1] == '/')
  279.                     tmp |= 0x10;
  280.             }
  281.             /* read-only bit --> write perms; subdir bit --> dir exec bit */
  282.             tmp = !(tmp & 1) << 1  |  (tmp & 0x10) >> 4;
  283.             if ((G.pInfo->file_attr & 0700) == (unsigned)(0400 | tmp<<6))
  284.                 /* keep previous G.pInfo->file_attr setting, when its "owner"
  285.                  * part appears to be consistent with DOS attribute flags!
  286.                  */
  287.                 break;
  288.             G.pInfo->file_attr = (unsigned)(0444 | tmp<<6 | tmp<<3 | tmp);
  289.             break;
  290.     } /* end switch (host-OS-created-by) */
  291.  
  292.     G.pInfo->file_attr&=0xFFFF;
  293.  
  294.     G.pInfo->file_attr|=(0xFFDu<<20);
  295.  
  296.     if (has_NFS_ext(G.filename)) {
  297.       int ftype=strtol(G.filename+strlen(G.filename)-3,NULL,16)&0xFFF;
  298.  
  299.       G.pInfo->file_attr = (G.pInfo->file_attr & 0x000FFFFF) | (ftype<<20);
  300.     } else {
  301.       int type = mimemap(G.filename);
  302.       if (type == -1)
  303.         type = (G.crec.internal_file_attributes & 1) ? 0xFFF : 0xFFD;
  304.       G.pInfo->file_attr = (G.pInfo->file_attr & 0x000FFFFF) | (type<<20);
  305.     }
  306.  
  307.     return 0;
  308.  
  309. } /* end function mapattr() */
  310.  
  311.  
  312.  
  313. /************************/
  314. /*  Function mimemap()  */
  315. /************************/
  316.  
  317. static int mimemap(const char *name)
  318. {
  319.   const char *ext = name;
  320.   int type;
  321.  
  322.   if (has_mimemap < 0)
  323.     has_mimemap =
  324.     !(SWI_OS_CLI("%RMEnsure MimeMap 0.05 RMLoad System:Modules.Network.MimeMap")
  325.       || SWI_OS_CLI("%RMEnsure MimeMap 0.05"));
  326.  
  327.   if (!has_mimemap)
  328.     return -1; /* no MimeMap module; fall back on text flag test */
  329.  
  330.   do {
  331.     while (*ext && *ext!='.')
  332.       ext++;
  333.     if (!*ext)
  334.       return -1; /* no suitable extension; fallback */
  335.     type = SWI_MimeMap_Translate(ext++);
  336.   } while (type == -1);
  337.  
  338.   return type;
  339. }
  340.  
  341.  
  342.  
  343. /************************/
  344. /*  Function mapname()  */
  345. /************************/
  346.  
  347. int mapname(__G__ renamed)
  348.     __GDEF
  349.     int renamed;
  350. /*
  351.  * returns:
  352.  *  MPN_OK          - no problem detected
  353.  *  MPN_INF_TRUNC   - caution (truncated filename)
  354.  *  MPN_INF_SKIP    - info "skip entry" (dir doesn't exist)
  355.  *  MPN_ERR_SKIP    - error -> skip entry
  356.  *  MPN_ERR_TOOLONG - error -> path is too long
  357.  *  MPN_NOMEM       - error (memory allocation failed) -> skip entry
  358.  *  [also MPN_VOL_LABEL, MPN_CREATED_DIR]
  359.  */
  360. {
  361.     char pathcomp[FILNAMSIZ];      /* path-component buffer */
  362.     char *pp, *cp=(char *)NULL;    /* character pointers */
  363.     char *lastsemi=(char *)NULL;   /* pointer to last semi-colon in pathcomp */
  364.     int error = MPN_OK;
  365.     register unsigned workch;      /* hold the character being tested */
  366.     char *checkswap=NULL;          /* pointer the the extension to check */
  367.  
  368.  
  369. /*---------------------------------------------------------------------------
  370.     Initialize various pointers and counters and stuff.
  371.   ---------------------------------------------------------------------------*/
  372.  
  373.     if (G.pInfo->vollabel)
  374.         return MPN_VOL_LABEL;   /* can't set disk volume labels in RISCOS */
  375.  
  376.     /* can create path as long as not just freshening, or if user told us */
  377.     G.create_dirs = (!uO.fflag || renamed);
  378.  
  379.     created_dir = FALSE;        /* not yet */
  380.  
  381.     /* user gave full pathname:  don't prepend rootpath */
  382.     renamed_fullpath = (renamed && (*G.filename == '/'));
  383.  
  384.     if (checkdir(__G__ (char *)NULL, INIT) == MPN_NOMEM)
  385.         return MPN_NOMEM;       /* initialize path buffer, unless no memory */
  386.  
  387.     *pathcomp = '\0';           /* initialize translation buffer */
  388.     pp = pathcomp;              /* point to translation buffer */
  389.     if (uO.jflag)               /* junking directories */
  390.         cp = (char *)strrchr(G.filename, '/');
  391.     if (cp == (char *)NULL)     /* no '/' or not junking dirs */
  392.         cp = G.filename;        /* point to internal zipfile-member pathname */
  393.     else
  394.         ++cp;                   /* point to start of last component of path */
  395.  
  396. /*---------------------------------------------------------------------------
  397.     Begin main loop through characters in filename.
  398.   ---------------------------------------------------------------------------*/
  399.  
  400.     while ((workch = (uch)*cp++) != 0) {
  401.  
  402.         switch (workch) {
  403.             case '/':             /* can assume -j flag not given */
  404.                 *pp = '\0';
  405.                 if (((error = checkdir(__G__ pathcomp, APPEND_DIR))
  406.                      & MPN_MASK) > MPN_INF_TRUNC)
  407.                     return error;
  408.                 pp = pathcomp;    /* reset conversion buffer for next piece */
  409.                 lastsemi = (char *)NULL; /* leave direct. semi-colons alone */
  410.                 checkswap=NULL;  /* reset checking at start of new leafname */
  411.                 break;
  412.  
  413.             case '.':
  414.                 *pp++ = '/';
  415.                 checkswap=pp;
  416.                 break;
  417.  
  418.             case ';':             /* VMS version (or DEC-20 attrib?) */
  419.                 lastsemi = pp;
  420.                 *pp++ = ';';      /* keep for now; remove VMS ";##" */
  421.                 break;            /*  later, if requested */
  422.  
  423.             case ' ':             /* change spaces to hard-spaces */
  424.                 *pp++ = 160;      /* (ISO 8859-1 Latin-1 codepage) */
  425.                 break;
  426.  
  427.             /* The following substitutions, unless stated otherwise, follow
  428.              * those for DOSFS. They translate special symbols into other
  429.              * characters which have no special meaning to RISC OS. */
  430.             case '#': *pp++ = '?'; break;   /* single-char wildcard */
  431.             case '&': *pp++ = '+'; break;
  432.             case '@': *pp++ = '='; break;
  433.             case '%': *pp++ = ';'; break;
  434.             case '$': *pp++ = '<'; break;
  435.             case '^': *pp++ = '>'; break;   /* parent-dir reference */
  436.  
  437.             /* The following substitutions deal with the remaining special
  438.              * symbols. ('.' is handled above.) */
  439.             case '*': *pp++ = 0xD7; break;  /* Latin-1 'multiply' */
  440.             case '"': *pp++ = '~'; break;
  441.             case ':': *pp++ = ';'; break;
  442.             case '\\': *pp++ = '/'; break;
  443.             case '|': *pp++ = 0xA6; break;  /* Latin-1 'broken bar' */
  444.  
  445.             default:
  446.                 /* allow European characters in filenames: */
  447.                 if (isprint(workch) || (128 <= workch && workch <= 254))
  448.                     *pp++ = (char)workch;
  449.         } /* end switch */
  450.  
  451.     } /* end while loop */
  452.  
  453. /*---------------------------------------------------------------------------
  454.     Report if directory was created (and no file to create:  filename ended
  455.     in '/'), check name to be sure it exists, and combine path and name be-
  456.     fore exiting.
  457.   ---------------------------------------------------------------------------*/
  458.  
  459.     if (G.filename[strlen(G.filename) - 1] == '/') {
  460.         checkdir(__G__ G.filename, GETPATH);
  461.         if (created_dir) {
  462.             if (QCOND2) {
  463.                 Info(slide, 0, ((char *)slide, "   creating: %s\n",
  464.                   FnFilter1(G.filename)));
  465.             }
  466.             /* set dir time (note trailing '/') */
  467.             return (error & ~MPN_MASK) | MPN_CREATED_DIR;
  468.         }
  469.         /* dir existed already; don't look for data to extract */
  470.         return (error & ~MPN_MASK) | MPN_INF_SKIP;
  471.     }
  472.  
  473.     *pp = '\0';                   /* done with pathcomp:  terminate it */
  474.  
  475.     /* if not saving them, remove VMS version numbers (appended ";###") */
  476.     if (!uO.V_flag && lastsemi) {
  477.         pp = lastsemi + 1;
  478.         while (isdigit((uch)(*pp)))
  479.             ++pp;
  480.         if (*pp == '\0')          /* only digits between ';' and end:  nuke */
  481.             *lastsemi = '\0';
  482.     }
  483.  
  484.     if (*pathcomp == '\0') {
  485.         Info(slide, 1, ((char *)slide, "mapname:  conversion of %s failed\n",
  486.           FnFilter1(G.filename)));
  487.         return (error & ~MPN_MASK) | MPN_ERR_SKIP;
  488.     }
  489.  
  490.     if (checkswap!=NULL) {
  491.         if (checkext(checkswap)) {
  492.             if ((error = checkdir(__G__ checkswap, APPEND_DIR)) > 1)
  493.                 return error;
  494.             *(checkswap-1)=0;    /* remove extension from pathcomp */
  495.         }
  496.     }
  497.  
  498.     if (!uO.acorn_nfs_ext && has_NFS_ext(pathcomp)) {
  499.       /* remove the filetype extension unless requested otherwise */
  500.       /* the filetype should be already set by mapattr() */
  501.       pathcomp[strlen(pathcomp)-4]=0;
  502.     }
  503.  
  504.     checkdir(__G__ pathcomp, APPEND_NAME);  /* returns 1 if truncated: care? */
  505.     checkdir(__G__ G.filename, GETPATH);
  506.  
  507.     return error;
  508.  
  509. } /* end function mapname() */
  510.  
  511.  
  512.  
  513.  
  514. /***********************/
  515. /* Function checkdir() */
  516. /***********************/
  517.  
  518. int checkdir(__G__ pathcomp, flag)
  519.     __GDEF
  520.     char *pathcomp;
  521.     int flag;
  522. /*
  523.  * returns:
  524.  *  MPN_OK          - no problem detected
  525.  *  MPN_INF_TRUNC   - (on APPEND_NAME) truncated filename
  526.  *  MPN_INF_SKIP    - path doesn't exist, not allowed to create
  527.  *  MPN_ERR_SKIP    - path doesn't exist, tried to create and failed; or path
  528.  *                    exists and is not a directory, but is supposed to be
  529.  *  MPN_ERR_TOOLONG - path is too long
  530.  *  MPN_NOMEM       - can't allocate memory for filename buffers
  531.  */
  532. {
  533.     static int rootlen = 0;   /* length of rootpath */
  534.     static char *rootpath;    /* user's "extract-to" directory */
  535.     static char *buildpath;   /* full path (so far) to extracted file */
  536.     static char *end;         /* pointer to end of buildpath ('\0') */
  537.  
  538. #   define FN_MASK   7
  539. #   define FUNCTION  (flag & FN_MASK)
  540.  
  541.  
  542. /*---------------------------------------------------------------------------
  543.     APPEND_DIR:  append the path component to the path being built and check
  544.     for its existence.  If doesn't exist and we are creating directories, do
  545.     so for this one; else signal success or error as appropriate.
  546.   ---------------------------------------------------------------------------*/
  547.  
  548.     if (FUNCTION == APPEND_DIR) {
  549.         int too_long = FALSE;
  550. #ifdef SHORT_NAMES
  551.         char *old_end = end;
  552. #endif
  553.  
  554.         Trace((stderr, "appending dir segment [%s]\n", FnFilter1(pathcomp)));
  555.         while ((*end = *pathcomp++) != '\0')
  556.             ++end;
  557. #ifdef SHORT_NAMES   /* path components restricted to 14 chars, typically */
  558.         if ((end-old_end) > FILENAME_MAX)  /* GRR:  proper constant? */
  559.             *(end = old_end + FILENAME_MAX) = '\0';
  560. #endif
  561.  
  562.         /* GRR:  could do better check, see if overrunning buffer as we go:
  563.          * check end-buildpath after each append, set warning variable if
  564.          * within 20 of FILNAMSIZ; then if var set, do careful check when
  565.          * appending.  Clear variable when begin new path. */
  566.  
  567.         /* next check: need to append '/', at least one-char name, '\0' */
  568.         if ((end-buildpath) > FILNAMSIZ-3)
  569.             too_long = TRUE;                    /* check if extracting dir? */
  570.         if (stat(buildpath, &G.statbuf)) {      /* path doesn't exist */
  571.             if (!G.create_dirs) { /* told not to create (freshening) */
  572.                 free(buildpath);
  573.                 return MPN_INF_SKIP;    /* path doesn't exist: nothing to do */
  574.             }
  575.             if (too_long) {
  576.                 Info(slide, 1, ((char *)slide,
  577.                   "checkdir error:  path too long: %s\n",
  578.                   FnFilter1(buildpath)));
  579.                 fflush(stderr);
  580.                 free(buildpath);
  581.                 /* no room for filenames:  fatal */
  582.                 return MPN_ERR_TOOLONG;
  583.             }
  584.             if (mkdir(buildpath, 0777) == -1) {   /* create the directory */
  585.                 Info(slide, 1, ((char *)slide,
  586.                   "checkdir error:  cannot create %s\n\
  587.                 unable to process %s.\n",
  588.                   FnFilter2(buildpath), FnFilter1(G.filename)));
  589.                 free(buildpath);
  590.                 /* path didn't exist, tried to create, failed */
  591.                 return MPN_ERR_SKIP;
  592.             }
  593.             created_dir = TRUE;
  594.         } else if (!S_ISDIR(G.statbuf.st_mode)) {
  595.             Info(slide, 1, ((char *)slide,
  596.               "checkdir error:  %s exists but is not directory\n\
  597.                 unable to process %s.\n",
  598.               FnFilter2(buildpath), FnFilter1(G.filename)));
  599.             free(buildpath);
  600.             /* path existed but wasn't dir */
  601.             return MPN_ERR_SKIP;
  602.         }
  603.         if (too_long) {
  604.             Info(slide, 1, ((char *)slide,
  605.               "checkdir error:  path too long: %s\n", FnFilter1(buildpath)));
  606.             free(buildpath);
  607.             /* no room for filenames:  fatal */
  608.             return MPN_ERR_TOOLONG;
  609.         }
  610.         *end++ = '.';    /************* was '/' *************/
  611.         *end = '\0';
  612.         Trace((stderr, "buildpath now = [%s]\n", FnFilter1(buildpath)));
  613.         return MPN_OK;
  614.  
  615.     } /* end if (FUNCTION == APPEND_DIR) */
  616.  
  617. /*---------------------------------------------------------------------------
  618.     GETPATH:  copy full path to the string pointed at by pathcomp, and free
  619.     buildpath.
  620.   ---------------------------------------------------------------------------*/
  621.  
  622.     if (FUNCTION == GETPATH) {
  623.         strcpy(pathcomp, buildpath);
  624.         Trace((stderr, "getting and freeing path [%s]\n",
  625.           FnFilter1(pathcomp)));
  626.         free(buildpath);
  627.         buildpath = end = (char *)NULL;
  628.         return MPN_OK;
  629.     }
  630.  
  631. /*---------------------------------------------------------------------------
  632.     APPEND_NAME:  assume the path component is the filename; append it and
  633.     return without checking for existence.
  634.   ---------------------------------------------------------------------------*/
  635.  
  636.     if (FUNCTION == APPEND_NAME) {
  637. #ifdef SHORT_NAMES
  638.         char *old_end = end;
  639. #endif
  640.  
  641.         Trace((stderr, "appending filename [%s]\n", FnFilter1(pathcomp)));
  642.         while ((*end = *pathcomp++) != '\0') {
  643.             ++end;
  644. #ifdef SHORT_NAMES  /* truncate name at 14 characters, typically */
  645.             if ((end-old_end) > FILENAME_MAX)      /* GRR:  proper constant? */
  646.                 *(end = old_end + FILENAME_MAX) = '\0';
  647. #endif
  648.             if ((end-buildpath) >= FILNAMSIZ) {
  649.                 *--end = '\0';
  650.                 Info(slide, 0x201, ((char *)slide,
  651.                   "checkdir warning:  path too long; truncating\n\
  652.                   %s\n                -> %s\n",
  653.                   FnFilter1(G.filename), FnFilter2(buildpath)));
  654.                 return MPN_INF_TRUNC;   /* filename truncated */
  655.             }
  656.         }
  657.         Trace((stderr, "buildpath now = [%s]\n", FnFilter1(buildpath)));
  658.         /* could check for existence here, prompt for new name... */
  659.         return MPN_OK;
  660.     }
  661.  
  662. /*---------------------------------------------------------------------------
  663.     INIT:  allocate and initialize buffer space for the file currently being
  664.     extracted.  If file was renamed with an absolute path, don't prepend the
  665.     extract-to path.
  666.   ---------------------------------------------------------------------------*/
  667.  
  668. /* GRR:  for VMS and TOPS-20, add up to 13 to strlen */
  669.  
  670.     if (FUNCTION == INIT) {
  671.         Trace((stderr, "initializing buildpath to "));
  672.         if ((buildpath = (char *)malloc(strlen(G.filename)+rootlen+1))
  673.             == (char *)NULL)
  674.             return MPN_NOMEM;
  675.         if ((rootlen > 0) && !renamed_fullpath) {
  676.             strcpy(buildpath, rootpath);
  677.             end = buildpath + rootlen;
  678.         } else {
  679.             *buildpath = '\0';
  680.             end = buildpath;
  681.         }
  682.         Trace((stderr, "[%s]\n", FnFilter1(buildpath)));
  683.         return MPN_OK;
  684.     }
  685.  
  686. /*---------------------------------------------------------------------------
  687.     ROOT:  if appropriate, store the path in rootpath and create it if
  688.     necessary; else assume it's a zipfile member and return.  This path
  689.     segment gets used in extracting all members from every zipfile specified
  690.     on the command line.
  691.   ---------------------------------------------------------------------------*/
  692.  
  693. #if (!defined(SFX) || defined(SFX_EXDIR))
  694.     if (FUNCTION == ROOT) {
  695.         Trace((stderr, "initializing root path to [%s]\n",
  696.           FnFilter1(pathcomp)));
  697.         if (pathcomp == (char *)NULL) {
  698.             rootlen = 0;
  699.             return MPN_OK;
  700.         }
  701.         if (rootlen > 0)        /* rootpath was already set, nothing to do */
  702.             return MPN_OK;
  703.         if ((rootlen = strlen(pathcomp)) > 0) {
  704.             char *tmproot;
  705.  
  706.             if ((tmproot = (char *)malloc(rootlen+2)) == (char *)NULL) {
  707.                 rootlen = 0;
  708.                 return MPN_NOMEM;
  709.             }
  710.             strcpy(tmproot, pathcomp);
  711.             if (tmproot[rootlen-1] == '.') {    /****** was '/' ********/
  712.                 tmproot[--rootlen] = '\0';
  713.             }
  714.             if (rootlen > 0 && (SSTAT(tmproot, &G.statbuf) ||
  715.                                 !S_ISDIR(G.statbuf.st_mode)))
  716.             {   /* path does not exist */
  717.                 if (!G.create_dirs /* || isshexp(tmproot) */ ) {
  718.                     free(tmproot);
  719.                     rootlen = 0;
  720.                     /* skip (or treat as stored file) */
  721.                     return MPN_INF_SKIP;
  722.                 }
  723.                 /* create the directory (could add loop here scanning tmproot
  724.                  * to create more than one level, but why really necessary?) */
  725.                 if (mkdir(tmproot, 0777) == -1) {
  726.                     Info(slide, 1, ((char *)slide,
  727.                       "checkdir:  cannot create extraction directory: %s\n",
  728.                       FnFilter1(tmproot)));
  729.                     free(tmproot);
  730.                     rootlen = 0;
  731.                     /* path didn't exist, tried to create, and failed: */
  732.                     /* file exists, or 2+ subdir levels required */
  733.                     return MPN_ERR_SKIP;
  734.                 }
  735.             }
  736.             tmproot[rootlen++] = '.';   /*********** was '/' *************/
  737.             tmproot[rootlen] = '\0';
  738.             if ((rootpath = (char *)realloc(tmproot, rootlen+1)) == NULL) {
  739.                 free(tmproot);
  740.                 rootlen = 0;
  741.                 return MPN_NOMEM;
  742.             }
  743.             Trace((stderr, "rootpath now = [%s]\n", FnFilter1(rootpath)));
  744.         }
  745.         return MPN_OK;
  746.     }
  747. #endif /* !SFX || SFX_EXDIR */
  748.  
  749. /*---------------------------------------------------------------------------
  750.     END:  free rootpath, immediately prior to program exit.
  751.   ---------------------------------------------------------------------------*/
  752.  
  753.     if (FUNCTION == END) {
  754.         Trace((stderr, "freeing rootpath\n"));
  755.         if (rootlen > 0) {
  756.             free(rootpath);
  757.             rootlen = 0;
  758.         }
  759.         return MPN_OK;
  760.     }
  761.  
  762.     return MPN_INVALID; /* should never reach */
  763.  
  764. } /* end function checkdir() */
  765.  
  766.  
  767.  
  768.  
  769.  
  770. /********************/
  771. /* Function mkdir() */
  772. /********************/
  773.  
  774. int mkdir(path, mode)
  775.     const char *path;
  776.     int mode;   /* ignored */
  777. /*
  778.  * returns:   0 - successful
  779.  *           -1 - failed (errno not set, however)
  780.  */
  781. {
  782.     return (SWI_OS_File_8((char *)path) == NULL)? 0 : -1;
  783. }
  784.  
  785.  
  786.  
  787.  
  788. /*********************************/
  789. /* extra_field-related functions */
  790. /*********************************/
  791.  
  792. static void setRISCOSexfield(ZCONST char *path, ZCONST void *ef_spark)
  793. {
  794.   if (ef_spark!=NULL) {
  795.     extra_block *block=(extra_block *)ef_spark;
  796.     SWI_OS_File_1((char *)path,block->loadaddr,block->execaddr,block->attr);
  797.   }
  798. }
  799.  
  800. #ifdef DEBUG
  801. static void printRISCOSexfield(int isdir, ZCONST void *extra_field)
  802. {
  803.  extra_block *block=(extra_block *)extra_field;
  804.  printf("\n  This file has RISC OS file informations in the local extra field.\n");
  805.  
  806.  if (isdir) {
  807. /*   I prefer not to print this string... should change later... */
  808. /*   printf("  The file is a directory.\n");*/
  809.  } else if ((block->loadaddr & 0xFFF00000) != 0xFFF00000) {
  810.    printf("  Load address: %.8X\n",block->loadaddr);
  811.    printf("  Exec address: %.8X\n",block->execaddr);
  812.  } else {
  813.    /************* should change this to use OS_FSControl 18 to get filetype string ************/
  814.    char tmpstr[16];
  815.    char ftypestr[32];
  816.    int flen;
  817.    sprintf(tmpstr,"File$Type_%03x",(block->loadaddr & 0x000FFF00) >> 8);
  818.    if (SWI_OS_ReadVarVal(tmpstr,ftypestr,32,&flen)==NULL) {
  819.      ftypestr[flen]=0;
  820.      printf("  Filetype: %s (&%.3X)\n",ftypestr,(block->loadaddr & 0x000FFF00) >> 8);
  821.    } else {
  822.      printf("  Filetype: &%.3X\n",(block->loadaddr & 0x000FFF00) >> 8);
  823.    }
  824.  }
  825.  printf("  Access: ");
  826.  if (block->attr & (1<<3))
  827.    printf("L");
  828.  if (block->attr & (1<<0))
  829.    printf("W");
  830.  if (block->attr & (1<<1))
  831.    printf("R");
  832.  printf("/");
  833.  if (block->attr & (1<<4))
  834.    printf("w");
  835.  if (block->attr & (1<<5))
  836.    printf("r");
  837.  printf("\n\n");
  838. }
  839. #endif /* DEBUG */
  840.  
  841.  
  842. /**********************************************/
  843. /* internal help function for time conversion */
  844. /**********************************************/
  845. static int uxtime2acornftime(unsigned *pexadr, unsigned *pldadr, time_t ut)
  846. {
  847.    unsigned timlo;      /* 3 lower bytes of acorn file-time plus carry byte */
  848.    unsigned timhi;      /* 2 high bytes of acorn file-time */
  849.  
  850.    timlo = ((unsigned)ut & 0x00ffffffU) * 100 + 0x00996a00U;
  851.    timhi = ((unsigned)ut >> 24);
  852.    timhi = timhi * 100 + 0x0000336eU + (timlo >> 24);
  853.    if (timhi & 0xffff0000U)
  854.        return 1;        /* calculation overflow, do not change time */
  855.  
  856.    /* insert the five time bytes into loadaddr and execaddr variables */
  857.    *pexadr = (timlo & 0x00ffffffU) | ((timhi & 0x000000ffU) << 24);
  858.    *pldadr = (*pldadr & 0xffffff00U) | ((timhi >> 8) & 0x000000ffU);
  859.  
  860.    return 0;            /* subject to future extension to signal overflow */
  861. }
  862.  
  863.  
  864. /****************************/
  865. /* Function close_outfile() */
  866. /****************************/
  867.  
  868. void close_outfile(__G)
  869.     __GDEF
  870. {
  871.   zvoid *spark_ef;
  872.  
  873.   fclose(G.outfile);
  874.  
  875.   if ((spark_ef = getRISCOSexfield(G.extra_field, G.lrec.extra_field_length))
  876.       != NULL) {
  877.     setRISCOSexfield(G.filename, spark_ef);
  878.   } else {
  879.     unsigned int loadaddr, execaddr;
  880.     int attr;
  881.     int mode=G.pInfo->file_attr&0xffff;   /* chmod equivalent mode */
  882.  
  883.     time_t m_time;
  884. #ifdef USE_EF_UT_TIME
  885.     iztimes z_utime;
  886. #endif
  887.  
  888.     /* skip restoring time stamps on user's request */
  889.     if (uO.D_flag <= 1) {
  890. #ifdef USE_EF_UT_TIME
  891.         if (G.extra_field &&
  892. #ifdef IZ_CHECK_TZ
  893.             G.tz_is_valid &&
  894. #endif
  895.             (ef_scan_for_izux(G.extra_field, G.lrec.extra_field_length, 0,
  896.                               G.lrec.last_mod_dos_datetime, &z_utime, NULL)
  897.              & EB_UT_FL_MTIME))
  898.         {
  899.             TTrace((stderr, "close_outfile:  Unix e.f. modif. time = %ld\n",
  900.               z_utime.mtime));
  901.             m_time = z_utime.mtime;
  902.         } else
  903. #endif /* USE_EF_UT_TIME */
  904.             m_time = dos_to_unix_time(G.lrec.last_mod_dos_datetime);
  905.     }
  906.  
  907.     /* set the file's time-stamp and attributes */
  908.     SWI_OS_File_5(G.filename, NULL, &loadaddr, NULL, NULL, &attr);
  909.  
  910.     if (uO.D_flag <= 1)
  911.         /* set the file's modification time */
  912.         uxtime2acornftime(&execaddr, &loadaddr, m_time);
  913.  
  914.     loadaddr = (loadaddr & 0xfff000ffU) |
  915.                ((G.pInfo->file_attr&0xfff00000) >> 12);
  916.  
  917.     attr=(attr&0xffffff00) | ((mode&0400) >> 8) | ((mode&0200) >> 6) |
  918.                              ((mode&0004) << 2) | ((mode&0002) << 4);
  919.  
  920.     SWI_OS_File_1(G.filename, loadaddr, execaddr, attr);
  921.   }
  922.  
  923. } /* end function close_outfile() */
  924.  
  925.  
  926.  
  927.  
  928. #ifdef TIMESTAMP
  929.  
  930. /***************************/
  931. /*  Function stamp_file()  */
  932. /***************************/
  933.  
  934. int stamp_file(fname, modtime)
  935.     ZCONST char *fname;
  936.     time_t modtime;
  937. {
  938.     unsigned int loadaddr, execaddr;
  939.     int attr;
  940.  
  941.     /* set the file's modification time */
  942.     if (SWI_OS_File_5((char *)fname, NULL, &loadaddr, NULL, NULL, &attr)
  943.         != NULL)
  944.         return -1;
  945.  
  946.     if (uxtime2acornftime(&execaddr, &loadaddr, modtime) != 0)
  947.         return -1;
  948.  
  949.     return (SWI_OS_File_1((char *)fname, loadaddr, execaddr, attr) == NULL) ?
  950.            0 : -1;
  951.  
  952. } /* end function stamp_file() */
  953.  
  954. #endif /* TIMESTAMP */
  955.  
  956.  
  957.  
  958.  
  959. #ifndef SFX
  960.  
  961. /************************/
  962. /*  Function version()  */
  963. /************************/
  964.  
  965. void version(__G)
  966.     __GDEF
  967. {
  968.     sprintf((char *)slide, LoadFarString(CompiledWith),
  969. #ifdef __GNUC__
  970.       "gcc ", __VERSION__,
  971. #else
  972. #  ifdef __CC_NORCROFT
  973.       "Norcroft ", "cc",
  974. #  else
  975.       "cc", "",
  976. #  endif
  977. #endif
  978.  
  979.       "RISC OS",
  980.  
  981.       " (Acorn Computers Ltd)",
  982.  
  983. #ifdef __DATE__
  984.       " on ", __DATE__
  985. #else
  986.       "", ""
  987. #endif
  988.       );
  989.  
  990.     (*G.message)((zvoid *)&G, slide, (ulg)strlen((char *)slide), 0);
  991.  
  992. } /* end function version() */
  993.  
  994. #endif /* !SFX */
  995.