Subversion Repositories Kolibri OS

Rev

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

  1. /*
  2.   Copyright (c) 1990-2005 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.   theos.c
  12.  
  13.   Theos-specific routines for use with Info-ZIP's UnZip 5.41 and later.
  14.  
  15.   Contains:  do_wild()           <-- generic enough to put in fileio.c?
  16.              mapattr()
  17.              mapname()
  18.              checkdir()
  19.              close_outfile()
  20.              defer_dir_attribs()
  21.              set_direc_attribs()
  22.              stamp_file()
  23.              version()
  24.  
  25.   ---------------------------------------------------------------------------*/
  26.  
  27.  
  28. #define UNZIP_INTERNAL
  29. #include "unzip.h"
  30. #include <direct.h>
  31. #include <sc.h>
  32. #include <fdb.h>
  33. #include <nuc.h>
  34. #include <peek.h>
  35.  
  36. /* standard function doesn't work with a trailing / */
  37. #define opendir(a) _opendir(a)
  38. extern DIR* _opendir(const char* dirpath);
  39.  
  40. #ifdef SET_DIR_ATTRIB
  41. typedef struct uxdirattr {      /* struct for holding unix style directory */
  42.     struct uxdirattr *next;     /*  info until can be sorted and set at end */
  43.     char *fn;                   /* filename of directory */
  44.     union {
  45.         iztimes t3;             /* mtime, atime, ctime */
  46.         ztimbuf t2;             /* modtime, actime */
  47.     } u;
  48.     unsigned perms;             /* same as min_info.file_attr */
  49.     char fnbuf[1];              /* buffer stub for directory name */
  50. } uxdirattr;
  51. #define UxAtt(d)  ((uxdirattr *)d)    /* typecast shortcut */
  52. #endif /* SET_DIR_ATTRIB */
  53.  
  54. #ifdef ACORN_FTYPE_NFS
  55. /* Acorn bits for NFS filetyping */
  56. typedef struct {
  57.   uch ID[2];
  58.   uch size[2];
  59.   uch ID_2[4];
  60.   uch loadaddr[4];
  61.   uch execaddr[4];
  62.   uch attr[4];
  63. } RO_extra_block;
  64.  
  65. #endif /* ACORN_FTYPE_NFS */
  66.  
  67. static int created_dir;        /* used in mapname(), checkdir() */
  68. static int renamed_fullpath;   /* ditto */
  69.  
  70. #define _FDB_SHARED_EXECUTE_PROTECT _FDB_MODIFIED
  71.  
  72. uch _um2tm_(ush mask);
  73.  
  74. int chmodv2_3(const char *fname, short mask)
  75. {
  76.     return _filechange(fname,'p',(size_t) _um2tm_(mask)|0x80);
  77. }
  78.  
  79. int chlen(char *fname, unsigned short reclen, unsigned short keylen)
  80. {
  81.     size_t a = reclen + ((size_t) keylen << 16);
  82.     return _filechange(fname,'ma',(size_t) &a);
  83. }
  84.  
  85. #define chgrow(a,b) ((int) _filechange(a,'g',(size_t)(b)))
  86. #define chorg(a,b)  ((int) _filechange(a,'m',(size_t)(b)))
  87.  
  88. #ifndef SFX
  89.  
  90. /*************************/
  91. /* Function dateformat() */
  92. /*************************/
  93.  
  94. int dateformat()
  95. {
  96. /*---------------------------------------------------------------------------
  97.     For those operating systems that support it, this function returns a
  98.     value that tells how national convention says that numeric dates are
  99.     displayed.  Return values are DF_YMD, DF_DMY and DF_MDY (the meanings
  100.     should be fairly obvious).
  101.   ---------------------------------------------------------------------------*/
  102.  
  103.     switch (peeknuc(&NUC->dateopt) & (aform|eform|iform)) {
  104.     case aform: return DF_MDY;
  105.     case eform: return DF_DMY;
  106.     }
  107.     return DF_YMD;
  108. }
  109.  
  110.  
  111. /* usual THEOS match function for filenames */
  112.  
  113. /* match from Phase One Systems */
  114.  
  115. /* Returns non-zero if string matches the literal mask */
  116. int match(string, pattern, ignore_case __WDL)
  117.     ZCONST char *string;
  118.     ZCONST char *pattern;
  119.     int ignore_case;            /* unused in this variant of match()! */
  120.     __WDLDEF
  121. {
  122.     int matched, k;
  123.  
  124.     if (!(*pattern))
  125.         return 1;
  126.     for(;;) {
  127.         if ( (!(*string)) && (!(*pattern)) )
  128.             return(1);
  129.         else if ( !(*pattern) )
  130.             return(0);
  131.         else if (*pattern == '*') {
  132.             if (!*(pattern+1))
  133.                 return(1);
  134.             k=0;
  135.             do {
  136.                 matched = match(string+k, pattern+1, ignore_case __WDL);
  137.                 k++;
  138.             } while ( (!matched) && *(string+k));
  139.             return(matched);
  140.         } else if (*pattern == '@') {
  141.             if (!((*string >= 'a' && *string <= 'z')
  142.                 ||(*string >= 'A' && *string <= 'Z')))
  143.                 return(0);
  144.         } else if (*pattern == '#') {
  145.             if (*string < '0' || *string > '9')
  146.                 return(0);
  147.         } else if (*pattern != '?') {
  148.             if (toupper(*string) != toupper(*pattern))
  149.                 return(0);
  150.  
  151.         }
  152.         string++; pattern++;
  153.     }
  154. } /* end function match() */
  155.  
  156.  
  157.  
  158. /**********************/
  159. /* Function do_wild() */   /* for porting:  dir separator; match(ignore_case) */
  160. /**********************/
  161.  
  162. char *do_wild(__G__ wildspec)
  163.     __GDEF
  164.     ZCONST char *wildspec;  /* only used first time on a given dir */
  165. {
  166.     static DIR *wild_dir = (DIR *)NULL;
  167.     static ZCONST char *wildname;
  168.     static char *dirname, matchname[FILNAMSIZ];
  169.     static int notfirstcall=FALSE, have_dirname, dirnamelen;
  170.     struct dirent *file;
  171.  
  172.     /* Even when we're just returning wildspec, we *always* do so in
  173.      * matchname[]--calling routine is allowed to append four characters
  174.      * to the returned string, and wildspec may be a pointer to argv[].
  175.      */
  176.     if (!notfirstcall) {    /* first call:  must initialize everything */
  177.         notfirstcall = TRUE;
  178.  
  179.         if (!iswild(wildspec)) {
  180.             strcpy(matchname, wildspec);
  181.             have_dirname = FALSE;
  182.             wild_dir = NULL;
  183.             return matchname;
  184.         }
  185.  
  186.         /* break the wildspec into a directory part and a wildcard filename */
  187.         if ((wildname = (ZCONST char *)strrchr(wildspec, '/')) ==
  188.             (ZCONST char *)NULL)
  189.         {
  190.             dirname = ".";
  191.             dirnamelen = 1;
  192.             have_dirname = FALSE;
  193.             wildname = wildspec;
  194.         } else {
  195.             ++wildname;     /* point at character after '/' */
  196.             dirnamelen = wildname - wildspec;
  197.             if ((dirname = (char *)malloc(dirnamelen+1)) == (char *)NULL) {
  198.                 Info(slide, 0x201, ((char *)slide,
  199.                   "warning:  cannot allocate wildcard buffers\n"));
  200.                 strncpy(matchname, wildspec, FILNAMSIZ);
  201.                 matchname[FILNAMSIZ-1] = '\0';
  202.                 return matchname;   /* but maybe filespec was not a wildcard */
  203.             }
  204.             strncpy(dirname, wildspec, dirnamelen);
  205.             dirname[dirnamelen] = '\0';   /* terminate for strcpy below */
  206.             have_dirname = TRUE;
  207.         }
  208.  
  209.         if ((wild_dir = opendir(dirname)) != (DIR *)NULL) {
  210.             while ((file = readdir(wild_dir)) != (struct dirent *)NULL) {
  211.                 Trace((stderr, "do_wild:  readdir returns %s\n",
  212.                   FnFilter1(file->d_name)));
  213.                 if (match(file->d_name, wildname, 1 WISEP)) { /*1=ignore case*/
  214.                     Trace((stderr, "do_wild:  match() succeeds\n"));
  215.                     if (have_dirname) {
  216.                         strcpy(matchname, dirname);
  217.                         strcpy(matchname+dirnamelen, file->d_name);
  218.                     } else
  219.                         strcpy(matchname, file->d_name);
  220.                     return matchname;
  221.                 }
  222.             }
  223.             /* if we get to here directory is exhausted, so close it */
  224.             closedir(wild_dir);
  225.             wild_dir = (DIR *)NULL;
  226.         }
  227.  
  228.         /* return the raw wildspec in case that works (e.g., directory not
  229.          * searchable, but filespec was not wild and file is readable) */
  230.         strncpy(matchname, wildspec, FILNAMSIZ);
  231.         matchname[FILNAMSIZ-1] = '\0';
  232.         return matchname;
  233.     }
  234.  
  235.     /* last time through, might have failed opendir but returned raw wildspec */
  236.     if (wild_dir == (DIR *)NULL) {
  237.         notfirstcall = FALSE; /* nothing left to try--reset for new wildspec */
  238.         if (have_dirname)
  239.             free(dirname);
  240.         return (char *)NULL;
  241.     }
  242.  
  243.     /* If we've gotten this far, we've read and matched at least one entry
  244.      * successfully (in a previous call), so dirname has been copied into
  245.      * matchname already.
  246.      */
  247.     while ((file = readdir(wild_dir)) != (struct dirent *)NULL) {
  248.         Trace((stderr, "do_wild:  readdir returns %s\n",
  249.           FnFilter1(file->d_name)));
  250.         if (file->d_name[0] == '.' && wildname[0] != '.')
  251.             continue;   /* Unix:  '*' and '?' do not match leading dot */
  252.         if (match(file->d_name, wildname, 1 WISEP)) {   /* 1 == ignore case */
  253.             Trace((stderr, "do_wild:  match() succeeds\n"));
  254.             if (have_dirname) {
  255.                 /* strcpy(matchname, dirname); */
  256.                 strcpy(matchname+dirnamelen, file->d_name);
  257.             } else
  258.                 strcpy(matchname, file->d_name);
  259.             return matchname;
  260.         }
  261.     }
  262.  
  263.     closedir(wild_dir);     /* have read at least one entry; nothing left */
  264.     wild_dir = (DIR *)NULL;
  265.     notfirstcall = FALSE;   /* reset for new wildspec */
  266.     if (have_dirname)
  267.         free(dirname);
  268.     return (char *)NULL;
  269.  
  270. } /* end function do_wild() */
  271.  
  272. #endif /* !SFX */
  273.  
  274.  
  275.  
  276.  
  277.  
  278. /**********************/
  279. /* Function mapattr() */
  280. /**********************/
  281.  
  282. int mapattr(__G)
  283.     __GDEF
  284. {
  285.     ulg tmp = G.crec.external_file_attributes;
  286.  
  287.     G.pInfo->file_attr = 0;
  288.     /* initialized to 0 for check in "default" branch below... */
  289.  
  290.     switch (G.pInfo->hostnum) {
  291.         case AMIGA_:
  292.             tmp = (unsigned)(tmp>>17 & 7);   /* Amiga RWE bits */
  293.             G.pInfo->file_attr = (unsigned)(tmp<<6 | tmp<<3 | tmp);
  294.             break;
  295.         case UNIX_:
  296.         case VMS_:
  297.         case ACORN_:
  298.         case ATARI_:
  299.         case ATHEOS_:
  300.         case BEOS_:
  301.         case QDOS_:
  302.         case TANDEM_:
  303.         case THEOS_:
  304.             G.pInfo->file_attr = (unsigned)(tmp >> 16);
  305.             if (G.pInfo->file_attr != 0 || !G.extra_field) {
  306.                 return 0;
  307.             } else {
  308.                 /* Some (non-Info-ZIP) implementations of Zip for Unix and
  309.                  * VMS (and probably others ??) leave 0 in the upper 16-bit
  310.                  * part of the external_file_attributes field. Instead, they
  311.                  * store file permission attributes in some extra field.
  312.                  * As a work-around, we search for the presence of one of
  313.                  * these extra fields and fall back to the MSDOS compatible
  314.                  * part of external_file_attributes if one of the known
  315.                  * e.f. types has been detected.
  316.                  * Later, we might implement extraction of the permission
  317.                  * bits from the VMS extra field. But for now, the work-around
  318.                  * should be sufficient to provide "readable" extracted files.
  319.                  * (For ASI Unix e.f., an experimental remap from the e.f.
  320.                  * mode value IS already provided!)
  321.                  */
  322.                 ush ebID;
  323.                 unsigned ebLen;
  324.                 uch *ef = G.extra_field;
  325.                 unsigned ef_len = G.crec.extra_field_length;
  326.                 int r = FALSE;
  327.  
  328.                 while (!r && ef_len >= EB_HEADSIZE) {
  329.                     ebID = makeword(ef);
  330.                     ebLen = (unsigned)makeword(ef+EB_LEN);
  331.                     if (ebLen > (ef_len - EB_HEADSIZE))
  332.                         /* discoverd some e.f. inconsistency! */
  333.                         break;
  334.                     switch (ebID) {
  335.                       case EF_ASIUNIX:
  336.                         if (ebLen >= (EB_ASI_MODE+2)) {
  337.                             G.pInfo->file_attr =
  338.                               (unsigned)makeword(ef+(EB_HEADSIZE+EB_ASI_MODE));
  339.                             /* force stop of loop: */
  340.                             ef_len = (ebLen + EB_HEADSIZE);
  341.                             break;
  342.                         }
  343.                         /* else: fall through! */
  344.                       case EF_PKVMS:
  345.                         /* "found nondecypherable e.f. with perm. attr" */
  346.                         r = TRUE;
  347.                       default:
  348.                         break;
  349.                     }
  350.                     ef_len -= (ebLen + EB_HEADSIZE);
  351.                     ef += (ebLen + EB_HEADSIZE);
  352.                 }
  353.                 if (!r)
  354.                     return 0;
  355.             }
  356.             /* fall through! */
  357.         /* all remaining cases:  expand MSDOS read-only bit into write perms */
  358.         case FS_FAT_:
  359.             /* PKWARE's PKZip for Unix marks entries as FS_FAT_, but stores the
  360.              * Unix attributes in the upper 16 bits of the external attributes
  361.              * field, just like Info-ZIP's Zip for Unix.  We try to use that
  362.              * value, after a check for consistency with the MSDOS attribute
  363.              * bits (see below).
  364.              */
  365.             G.pInfo->file_attr = (unsigned)(tmp >> 16);
  366.             /* fall through! */
  367.         case FS_HPFS_:
  368.         case FS_NTFS_:
  369.         case MAC_:
  370.         case TOPS20_:
  371.         default:
  372.             /* Ensure that DOS subdir bit is set when the entry's name ends
  373.              * in a '/'.  Some third-party Zip programs fail to set the subdir
  374.              * bit for directory entries.
  375.              */
  376.             if ((tmp & 0x10) == 0) {
  377.                 extent fnlen = strlen(G.filename);
  378.                 if (fnlen > 0 && G.filename[fnlen-1] == '/')
  379.                     tmp |= 0x10;
  380.             }
  381.             /* read-only bit --> write perms; subdir bit --> dir exec bit */
  382.             tmp = !(tmp & 1) << 1  |  (tmp & 0x10) >> 4;
  383.             if ((G.pInfo->file_attr & 0700) == (unsigned)(0400 | tmp<<6))
  384.                 /* keep previous G.pInfo->file_attr setting, when its "owner"
  385.                  * part appears to be consistent with DOS attribute flags!
  386.                  */
  387.                 return 0;
  388.             G.pInfo->file_attr = (unsigned)(0444 | tmp<<6 | tmp<<3 | tmp);
  389.             break;
  390.     } /* end switch (host-OS-created-by) */
  391.  
  392.     /* for originating systems with no concept of "group," "other," "system": */
  393.     G.pInfo->file_attr &= ~tmp;
  394.  
  395.     return 0;
  396.  
  397. } /* end function mapattr() */
  398.  
  399.  
  400.  
  401. /* portabilibity functions to ensure access to port 2.0 and new ports */
  402.  
  403. int isv2_3()
  404. {
  405.     return G.extra_field[0] == 'T' && G.extra_field[1] == 'h';
  406. }
  407.  
  408. int isv2_3lib()
  409. {
  410.     return isv2_3() && S_ISLIB((G.pInfo->file_attr >> 8) & 0xFF);
  411. }
  412.  
  413. int isv2_3dir()
  414. {
  415.     return isv2_3() && S_ISDIR((G.pInfo->file_attr >> 8) & 0xFF);
  416. }
  417.  
  418. #ifdef OLD_THEOS_EXTRA
  419. #define S_IFMT_     0xf800      /* type of file */
  420. #define S_IFLIB_    0x8000      /* library */
  421. #define S_IFDIR_    0x4000      /* directory */
  422.  
  423. #define S_ISLIB_(a) (((a) & 0xff) == _FDB_STAT_LIBRARY)
  424. #define S_ISDIR_(a) (((a) & 0xff) == _FDB_STAT_DIRECTORY)
  425.  
  426. struct extra_block
  427. {
  428.     ush     signature;
  429.     ush     size;
  430.     ush     flags;
  431.     ulg     filesize;
  432.     ush     reclen;
  433.     ush     keylen;
  434.     uch     filegrow;
  435.     uch     reserved[3];
  436. };
  437.  
  438. struct extra_block *v2_0extra()
  439. {
  440.     return (struct extra_block *) G.extra_field;
  441. }
  442.  
  443. int isv2_0()
  444. {
  445.     return ((struct extra_block *) G.extra_field)->signature == 0x4854;
  446. }
  447.  
  448. int isv2_0lib()
  449. {
  450.   return isv2_0() && S_ISLIB_(G.pInfo->file_attr);
  451. }
  452.  
  453. int isv2_0dir()
  454. {
  455.   return isv2_0() && S_ISDIR_(G.pInfo->file_attr);
  456. }
  457.  
  458. #define islib() (isv2_0lib() || isv2_3lib())
  459. #define isdir() (isv2_0dir() || isv2_3dir())
  460. #define chmodv2_0(a,b)  ((int) _sc_168(a,'p',(size_t)(b)|0x80))
  461. #undef chmod
  462. #define chmod(a,b) (isv2_0() && chmodv2_0(a,b))||(isv2_3() && chmodv2_3(a,b))
  463. #else
  464. #define islib() isv2_3lib()
  465. #define isdir() isv2_3dir()
  466. #undef chmod
  467. #define chmod(a,b) chmodv2_0(a,b)
  468. #endif
  469.  
  470. /************************/
  471. /*  Function mapname()  */
  472. /************************/
  473.  
  474. int mapname(__G__ renamed)
  475.     __GDEF
  476.     int renamed;
  477. /*
  478.  * returns:
  479.  *  MPN_OK          - no problem detected
  480.  *  MPN_INF_TRUNC   - caution (truncated filename)
  481.  *  MPN_INF_SKIP    - info "skip entry" (dir doesn't exist)
  482.  *  MPN_ERR_SKIP    - error -> skip entry
  483.  *  MPN_ERR_TOOLONG - error -> path is too long
  484.  *  MPN_NOMEM       - error (memory allocation failed) -> skip entry
  485.  *  [also MPN_VOL_LABEL, MPN_CREATED_DIR]
  486.  */
  487. {
  488.     char pathcomp[FILNAMSIZ];      /* path-component buffer */
  489.     char *pp, *cp=(char *)NULL;    /* character pointers */
  490.     char *lastsemi=(char *)NULL;   /* pointer to last semi-colon in pathcomp */
  491. #ifdef ACORN_FTYPE_NFS
  492.     char *lastcomma=(char *)NULL;  /* pointer to last comma in pathcomp */
  493.     RO_extra_block *ef_spark;      /* pointer Acorn FTYPE ef block */
  494. #endif
  495.     int killed_ddot = FALSE;       /* is set when skipping "../" pathcomp */
  496.     int error = MPN_OK;
  497.     register unsigned workch;      /* hold the character being tested */
  498.  
  499.  
  500. /*---------------------------------------------------------------------------
  501.     Initialize various pointers and counters and stuff.
  502.   ---------------------------------------------------------------------------*/
  503.  
  504.     if (G.pInfo->vollabel)
  505.         return MPN_VOL_LABEL;   /* can't set disk volume labels in Unix */
  506.  
  507.     /* can create path as long as not just freshening, or if user told us */
  508.     G.create_dirs = (!uO.fflag || renamed);
  509.  
  510.     created_dir = FALSE;        /* not yet */
  511.  
  512.     /* user gave full pathname:  don't prepend rootpath */
  513.     renamed_fullpath = (renamed && (*G.filename == '/'));
  514.  
  515.     if (checkdir(__G__ (char *)NULL, INIT) == MPN_NOMEM)
  516.         return MPN_NOMEM;       /* initialize path buffer, unless no memory */
  517.  
  518.     *pathcomp = '\0';           /* initialize translation buffer */
  519.     pp = pathcomp;              /* point to translation buffer */
  520.     if (uO.jflag)               /* junking directories */
  521.         cp = (char *)strrchr(G.filename, '/');
  522.     if (cp == (char *)NULL)     /* no '/' or not junking dirs */
  523.         cp = G.filename;        /* point to internal zipfile-member pathname */
  524.     else
  525.         ++cp;                   /* point to start of last component of path */
  526.  
  527. /*---------------------------------------------------------------------------
  528.     Begin main loop through characters in filename.
  529.   ---------------------------------------------------------------------------*/
  530.  
  531.     while ((workch = (uch)*cp++) != 0) {
  532.  
  533.         switch (workch) {
  534.             case '/':             /* can assume -j flag not given */
  535.                 *pp = '\0';
  536.                 if (strcmp(pathcomp, ".") == 0) {
  537.                     /* don't bother appending "./" to the path */
  538.                     *pathcomp = '\0';
  539.                 } else if (!uO.ddotflag && strcmp(pathcomp, "..") == 0) {
  540.                     /* "../" dir traversal detected, skip over it */
  541.                     *pathcomp = '\0';
  542.                     killed_ddot = TRUE;     /* set "show message" flag */
  543.                 }
  544.                 /* when path component is not empty, append it now */
  545.                 if (*pathcomp != '\0' &&
  546.                     ((error = checkdir(__G__ pathcomp, APPEND_DIR))
  547.                      & MPN_MASK) > MPN_INF_TRUNC)
  548.                     return error;
  549.                 pp = pathcomp;    /* reset conversion buffer for next piece */
  550.                 lastsemi = (char *)NULL; /* leave direct. semi-colons alone */
  551.                 break;
  552.  
  553. #ifdef ACORN_FTYPE_NFS
  554.             case ',':             /* NFS filetype extension */
  555.                 lastcomma = pp;
  556.                 *pp++ = ',';      /* keep for now; may need to remove */
  557.                 break;            /*  later, if requested */
  558. #endif
  559.  
  560.             default:
  561.                 if (isfnsym(workch) || workch == '.')
  562.                     *pp++ = (char)workch;
  563.                 else
  564.                     *pp++ = '_';
  565.         } /* end switch */
  566.  
  567.     } /* end while loop */
  568.  
  569.     /* Show warning when stripping insecure "parent dir" path components */
  570.     if (killed_ddot && QCOND2) {
  571.         Info(slide, 0, ((char *)slide,
  572.           "warning:  skipped \"../\" path component(s) in %s\n",
  573.           FnFilter1(G.filename)));
  574.         if (!(error & ~MPN_MASK))
  575.             error = (error & MPN_MASK) | PK_WARN;
  576.     }
  577.  
  578. /*---------------------------------------------------------------------------
  579.     Report if directory was created (and no file to create:  filename ended
  580.     in '/'), check name to be sure it exists, and combine path and name be-
  581.     fore exiting.
  582.   ---------------------------------------------------------------------------*/
  583.  
  584.     if (G.filename[strlen(G.filename) - 1] == '/') {
  585.         checkdir(__G__ G.filename, GETPATH);
  586.         if (islib() && G.filename[strlen(G.filename) - 1] == '/')
  587.             G.filename[strlen(G.filename) - 1] = '\0';
  588.  
  589.         if (created_dir) {
  590.             if (QCOND2) {
  591.                 Info(slide, 0, ((char *)slide, "   creating: %s\n",
  592.                   FnFilter1(G.filename)));
  593.             }
  594.             /* set dir time (note trailing '/') */
  595.             return (error & ~MPN_MASK) | MPN_CREATED_DIR;
  596.         }
  597.         /* dir existed already; don't look for data to extract */
  598.         return (error & ~MPN_MASK) | MPN_INF_SKIP;
  599.     }
  600.  
  601.     *pp = '\0';                   /* done with pathcomp:  terminate it */
  602.  
  603. #ifdef ACORN_FTYPE_NFS
  604.     /* translate Acorn filetype information if asked to do so */
  605.     if (uO.acorn_nfs_ext &&
  606.         (ef_spark = (RO_extra_block *)
  607.                     getRISCOSexfield(G.extra_field, G.lrec.extra_field_length))
  608.         != (RO_extra_block *)NULL)
  609.     {
  610.         /* file *must* have a RISC OS extra field */
  611.         long ft = (long)makelong(ef_spark->loadaddr);
  612.         /*32-bit*/
  613.         if (lastcomma) {
  614.             pp = lastcomma + 1;
  615.             while (isxdigit((uch)(*pp))) ++pp;
  616.             if (pp == lastcomma+4 && *pp == '\0') *lastcomma='\0'; /* nuke */
  617.         }
  618.         if ((ft & 1<<31)==0) ft=0x000FFD00;
  619.         sprintf(pathcomp+strlen(pathcomp), ",%03x", (int)(ft>>8) & 0xFFF);
  620.     }
  621. #endif /* ACORN_FTYPE_NFS */
  622.  
  623.     if (*pathcomp == '\0') {
  624.         Info(slide, 1, ((char *)slide, "mapname:  conversion of %s failed\n",
  625.           FnFilter1(G.filename)));
  626.         return (error & ~MPN_MASK) | MPN_ERR_SKIP;
  627.     }
  628.  
  629.     checkdir(__G__ pathcomp, APPEND_NAME);  /* returns 1 if truncated: care? */
  630.     checkdir(__G__ G.filename, GETPATH);
  631.  
  632.     return error;
  633.  
  634. } /* end function mapname() */
  635.  
  636.  
  637.  
  638.  
  639. #if 0  /*========== NOTES ==========*/
  640.  
  641.   extract-to dir:      a:path/
  642.   buildpath:           path1/path2/ ...   (NULL-terminated)
  643.   pathcomp:                filename
  644.  
  645.   mapname():
  646.     loop over chars in zipfile member name
  647.       checkdir(path component, COMPONENT | CREATEDIR) --> map as required?
  648.         (d:/tmp/unzip/)                    (disk:[tmp.unzip.)
  649.         (d:/tmp/unzip/jj/)                 (disk:[tmp.unzip.jj.)
  650.         (d:/tmp/unzip/jj/temp/)            (disk:[tmp.unzip.jj.temp.)
  651.     finally add filename itself and check for existence? (could use with rename)
  652.         (d:/tmp/unzip/jj/temp/msg.outdir)  (disk:[tmp.unzip.jj.temp]msg.outdir)
  653.     checkdir(name, GETPATH)     -->  copy path to name and free space
  654.  
  655. #endif /* 0 */
  656.  
  657.  
  658.  
  659.  
  660. /***********************/
  661. /* Function checkdir() */
  662. /***********************/
  663.  
  664. int checkdir(__G__ pathcomp, flag)
  665.     __GDEF
  666.     char *pathcomp;
  667.     int flag;
  668. /*
  669.  * returns:
  670.  *  MPN_OK          - no problem detected
  671.  *  MPN_INF_TRUNC   - (on APPEND_NAME) truncated filename
  672.  *  MPN_INF_SKIP    - path doesn't exist, not allowed to create
  673.  *  MPN_ERR_SKIP    - path doesn't exist, tried to create and failed; or path
  674.  *                    exists and is not a directory, but is supposed to be
  675.  *  MPN_ERR_TOOLONG - path is too long
  676.  *  MPN_NOMEM       - can't allocate memory for filename buffers
  677.  */
  678. {
  679.     static int rootlen = 0;   /* length of rootpath */
  680.     static char *rootpath;    /* user's "extract-to" directory */
  681.     static char rootdisk[3];  /* user's "extract-to" disk */
  682.     static char *buildpath;   /* full path (so far) to extracted file */
  683.     static char *end;         /* pointer to end of buildpath ('\0') */
  684.  
  685. #   define FN_MASK   7
  686. #   define FUNCTION  (flag & FN_MASK)
  687.  
  688.  
  689.  
  690. /*---------------------------------------------------------------------------
  691.     APPEND_DIR:  append the path component to the path being built and check
  692.     for its existence.  If doesn't exist and we are creating directories, do
  693.     so for this one; else signal success or error as appropriate.
  694.   ---------------------------------------------------------------------------*/
  695.  
  696.     if (FUNCTION == APPEND_DIR) {
  697.         int too_long = FALSE;
  698. #ifdef SHORT_NAMES
  699.         char *old_end = end;
  700. #endif
  701.  
  702.         Trace((stderr, "appending dir segment [%s]\n", FnFilter1(pathcomp)));
  703.         while ((*end = *pathcomp++) != '\0')
  704.             ++end;
  705. #ifdef SHORT_NAMES   /* path components restricted to 14 chars, typically */
  706.         if ((end-old_end) > FILENAME_MAX)  /* GRR:  proper constant? */
  707.             *(end = old_end + FILENAME_MAX) = '\0';
  708. #endif
  709.  
  710.         /* GRR:  could do better check, see if overrunning buffer as we go:
  711.          * check end-buildpath after each append, set warning variable if
  712.          * within 20 of FILNAMSIZ; then if var set, do careful check when
  713.          * appending.  Clear variable when begin new path. */
  714.  
  715.         if ((end-buildpath) > FILNAMSIZ-3)  /* need '/', one-char name, '\0' */
  716.             too_long = TRUE;                /* check if extracting directory? */
  717.         Trace((stderr, "appending disk segment [%s]\n", FnFilter1(rootdisk)));
  718.         strcat(buildpath, rootdisk);
  719.         if (stat(buildpath, &G.statbuf)) {  /* path doesn't exist */
  720.             if (!G.create_dirs) { /* told not to create (freshening) */
  721.                 free(buildpath);
  722.                 return MPN_INF_SKIP;    /* path doesn't exist: nothing to do */
  723.             }
  724.             if (too_long) {
  725.                 Info(slide, 1, ((char *)slide,
  726.                   "checkdir error:  path too long: %s\n",
  727.                   FnFilter1(buildpath)));
  728.                 free(buildpath);
  729.                 /* no room for filenames:  fatal */
  730.                 return MPN_ERR_TOOLONG;
  731.             }
  732.             if (islib()) {
  733.                 ulg size;
  734. #ifdef OLD_THEOS_EXTRA
  735.                 if (isv2_0lib())
  736.                     size = v2_0extra()->filesize;
  737.                 else
  738. #endif
  739.                 {
  740.                     size = (ulg) G.extra_field[5] |
  741.                            ((ulg) G.extra_field[6] << 8) |
  742.                            ((ulg) G.extra_field[7] << 16) |
  743.                            ((ulg) G.extra_field[8] << 24);
  744.                 }
  745.                 if (makelib(buildpath, size / 64)) {
  746.                     Info(slide, 1, ((char*)slide,
  747.                       "checkdir error:  can't create library %s\n\
  748.                     unable to process %s.\n",
  749.                       FnFilter2(buildpath), FnFilter1(G.filename));
  750.                     free(buildpath);
  751.                     /* path didn't exist, tried to create, failed */
  752.                     return MPN_ERR_SKIP;
  753.                 }
  754.             } else if (mkdir(buildpath) == -1) {   /* create the directory */
  755.                 Info(slide, 1, ((char *)slide,
  756.                   "checkdir error:  cannot create %s\n\
  757.                 unable to process %s.\n",
  758.                   FnFilter2(buildpath), FnFilter1(G.filename));
  759.                 free(buildpath);
  760.                 /* path didn't exist, tried to create, failed */
  761.                 return MPN_ERR_SKIP;
  762.             }
  763.             created_dir = TRUE;
  764.         } else if (!S_ISDIR(G.statbuf.st_mode) && isdir()) {
  765.             Info(slide, 1, ((char *)slide,
  766.               "checkdir error:  %s exists but is not directory\n\
  767.                 unable to process %s.\n",
  768.               FnFilter2(buildpath), FnFilter1(G.filename));
  769.             free(buildpath);
  770.             /* path existed but wasn't dir */
  771.             return MPN_ERR_SKIP;
  772.         } else if (!S_ISLIB(G.statbuf.st_mode) && islib()) {
  773.             Info(slide, 1, ((char *)slide,
  774.               "checkdir error:  %s exists but is not library\n\
  775.                 unable to process %s.\n",
  776.               FnFilter2(buildpath), FnFilter1(G.filename));
  777.             free(buildpath);
  778.             /* path existed but wasn't lib */
  779.             return MPN_ERR_SKIP;
  780.         }
  781.         if (too_long) {
  782.             Info(slide, 1, ((char *)slide,
  783.               "checkdir error:  path too long: %s\n", FnFilter1(buildpath)));
  784.             free(buildpath);
  785.             /* no room for filenames:  fatal */
  786.             return MPN_ERR_TOOLONG;
  787.         }
  788.         *end++ = '/';
  789.         *end = '\0';
  790.         Trace((stderr, "buildpath now = [%s]\n", FnFilter1(buildpath)));
  791.         return MPN_OK;
  792.  
  793.     } /* end if (FUNCTION == APPEND_DIR) */
  794.  
  795. /*---------------------------------------------------------------------------
  796.     GETPATH:  copy full path to the string pointed at by pathcomp, and free
  797.     buildpath.
  798.   ---------------------------------------------------------------------------*/
  799.  
  800.     if (FUNCTION == GETPATH) {
  801.         strcpy(pathcomp, buildpath);
  802.         Trace((stderr, "getting and freeing path [%s]\n",
  803.           FnFilter1(pathcomp)));
  804.         free(buildpath);
  805.         buildpath = end = (char *)NULL;
  806.         return MPN_OK;
  807.     }
  808.  
  809. /*---------------------------------------------------------------------------
  810.     APPEND_NAME:  assume the path component is the filename; append it and
  811.     return without checking for existence.
  812.   ---------------------------------------------------------------------------*/
  813.  
  814.     if (FUNCTION == APPEND_NAME) {
  815. #ifdef SHORT_NAMES
  816.         char *old_end = end;
  817. #endif
  818.  
  819.         Trace((stderr, "appending filename [%s]\n", FnFilter1(pathcomp)));
  820.         while ((*end = *pathcomp++) != '\0') {
  821.             ++end;
  822. #ifdef SHORT_NAMES  /* truncate name at 14 characters, typically */
  823.             if ((end-old_end) > FILENAME_MAX)      /* GRR:  proper constant? */
  824.                 *(end = old_end + FILENAME_MAX) = '\0';
  825. #endif
  826.             if ((end-buildpath) >= FILNAMSIZ) {
  827.                 *--end = '\0';
  828.                 Info(slide, 0x201, ((char *)slide,
  829.                   "checkdir warning:  path too long; truncating\n\
  830.                   %s\n                -> %s\n",
  831.                   FnFilter1(G.filename), FnFilter2(buildpath)));
  832.                 return MPN_INF_TRUNC;   /* filename truncated */
  833.             }
  834.         }
  835.         strcat(buildpath, rootdisk);
  836.         Trace((stderr, "buildpath now = [%s]\n", FnFilter1(buildpath)));
  837.         /* could check for existence here, prompt for new name... */
  838.         return MPN_OK;
  839.     }
  840.  
  841. /*---------------------------------------------------------------------------
  842.     INIT:  allocate and initialize buffer space for the file currently being
  843.     extracted.  If file was renamed with an absolute path, don't prepend the
  844.     extract-to path.
  845.   ---------------------------------------------------------------------------*/
  846.  
  847. /* GRR:  for VMS and TOPS-20, add up to 13 to strlen */
  848.  
  849.     if (FUNCTION == INIT) {
  850.         Trace((stderr, "initializing buildpath to "));
  851. #ifdef ACORN_FTYPE_NFS
  852.         if ((buildpath = (char *)malloc(strlen(G.filename)+rootlen+
  853.                                         (uO.acorn_nfs_ext ? 5 : 1)))
  854. #else
  855.         if ((buildpath = (char *)malloc(strlen(G.filename)+rootlen+1))
  856. #endif
  857.             == (char *)NULL)
  858.             return MPN_NOMEM;
  859.         if ((rootlen > 0) && !renamed_fullpath) {
  860.             strcpy(buildpath, rootpath);
  861.             end = buildpath + rootlen;
  862.         } else {
  863.             *buildpath = '\0';
  864.             end = buildpath;
  865.         }
  866.         Trace((stderr, "[%s]\n", FnFilter1(buildpath)));
  867.         return MPN_OK;
  868.     }
  869.  
  870. /*---------------------------------------------------------------------------
  871.     ROOT:  if appropriate, store the path in rootpath and create it if
  872.     necessary; else assume it's a zipfile member and return.  This path
  873.     segment gets used in extracting all members from every zipfile specified
  874.     on the command line.
  875.   ---------------------------------------------------------------------------*/
  876.  
  877. #if (!defined(SFX) || defined(SFX_EXDIR))
  878.     if (FUNCTION == ROOT) {
  879.         if (pathcomp == (char *)NULL) {
  880.             rootlen = 0;
  881.             return MPN_OK;
  882.         }
  883.         if (rootlen > 0)        /* rootpath was already set, nothing to do */
  884.             return MPN_OK;
  885.         if ((rootlen = strlen(pathcomp)) > 0) {
  886.             int prepend_slash = 0;
  887.             char *tmproot, *p;
  888.  
  889.             if (*pathcomp == ':') {
  890.                 preprend_slash = 1;
  891.                 rootlen++;
  892.             }
  893.             if ((tmproot = (char *)malloc(rootlen+2)) == (char *)NULL) {
  894.                 rootlen = 0;
  895.                 return MPN_NOMEM;
  896.             }
  897.             if (prepend_slash)
  898.                 strcpy(tmproot, "/");
  899.             else
  900.                 *tmproot = '\0';
  901.             strcat(tmproot, pathcomp);
  902.             Trace((stderr, "initializing root path to [%s]\n",
  903.               FnFilter1(tmproot)));
  904.             if (tmproot[rootlen-1] == '/') {
  905.                 tmproot[--rootlen] = '\0';
  906.             }
  907.             if (rootlen > 0 && (SSTAT(tmproot, &G.statbuf) ||
  908.                                 !S_ISDIR(G.statbuf.st_mode)))
  909.             {   /* path does not exist */
  910.                 if (!G.create_dirs /* || iswild(tmproot) */ ) {
  911.                     free(tmproot);
  912.                     rootlen = 0;
  913.                     /* skip (or treat as stored file) */
  914.                     return MPN_INF_SKIP;
  915.                 }
  916.                 /* create the directory (could add loop here scanning tmproot
  917.                  * to create more than one level, but why really necessary?) */
  918.                 if (mkdir(tmproot) == -1) {
  919.                     Info(slide, 1, ((char *)slide,
  920.                       "checkdir:  cannot create extraction directory: %s\n",
  921.                       FnFilter1(tmproot)));
  922.                     free(tmproot);
  923.                     rootlen = 0;
  924.                     /* path didn't exist, tried to create, and failed: */
  925.                     /* file exists, or 2+ subdir levels required */
  926.                     return MPN_ERR_SKIP;
  927.                 }
  928.             }
  929.             /* split rootpath in path and disk */
  930.             if ((p = strchr(tmproot, ':')) != NULL) {
  931.                 strncpy(rootdisk, p, 2);
  932.                 rootdisk[2] = '\0';
  933.                 *p = '\0';
  934.                 rootlen = p - tmproot;
  935.             } else
  936.                 rootdisk[0] = '\0';
  937.             if (rootpath[rootlen - 1] != '/') {
  938.                 rootpath[rootlen++] = '/';
  939.                 rootpath[rootlen] = '\0';
  940.             }
  941.             if ((rootpath = (char *)realloc(tmproot, rootlen+1)) == NULL) {
  942.                 free(tmproot);
  943.                 rootlen = 0;
  944.                 return MPN_NOMEM;
  945.             }
  946.             Trace((stderr, "rootpath now = [%s], rootdisk now = [%s]\n",
  947.                 FnFilter1(rootpath), FnFilter2(rootdisk)));
  948.         }
  949.         return MPN_OK;
  950.     }
  951. #endif /* !SFX || SFX_EXDIR */
  952.  
  953. /*---------------------------------------------------------------------------
  954.     END:  free rootpath, immediately prior to program exit.
  955.   ---------------------------------------------------------------------------*/
  956.  
  957.     if (FUNCTION == END) {
  958.         Trace((stderr, "freeing rootpath\n"));
  959.         if (rootlen > 0) {
  960.             free(rootpath);
  961.             rootlen = 0;
  962.         }
  963.         return MPN_OK;
  964.     }
  965.  
  966.     return MPN_INVALID; /* should never reach */
  967.  
  968. } /* end function checkdir() */
  969.  
  970.  
  971.  
  972.  
  973. #ifdef MORE
  974.  
  975. /**************************/
  976. /* Function screenlines() */
  977. /**************************/
  978.  
  979. int screenlines()
  980. {
  981.     return getpl(27) + 1;
  982. }
  983.  
  984. #endif /* MORE */
  985.  
  986.  
  987.  
  988.  
  989.  
  990. #if (!defined(MTS) || defined(SET_DIR_ATTRIB))
  991. static void get_extattribs OF((__GPRO__ iztimes *pzt));
  992.  
  993. static int get_extattribs(__G__ pzt)
  994.     __GDEF
  995.     iztimes *pzt;
  996. {
  997. /*---------------------------------------------------------------------------
  998.     Convert from MSDOS-format local time and date to Unix-format 32-bit GMT
  999.     time:  adjust base year from 1980 to 1970, do usual conversions from
  1000.     yy/mm/dd hh:mm:ss to elapsed seconds, and account for timezone and day-
  1001.     light savings time differences.  If we have a Unix extra field, however,
  1002.     we're laughing:  both mtime and atime are ours.
  1003.   ---------------------------------------------------------------------------*/
  1004. #ifdef USE_EF_UT_TIME
  1005.     unsigned eb_izux_flg;
  1006.  
  1007.     eb_izux_flg = (G.extra_field ? ef_scan_for_izux(G.extra_field,
  1008.                    G.lrec.extra_field_length, 0, G.lrec.last_mod_dos_datetime,
  1009. #ifdef IZ_CHECK_TZ
  1010.                    (G.tz_is_valid ? pzt : NULL),
  1011. #else
  1012.                    pzt,
  1013. #endif
  1014.                    z_uidgid) : 0);
  1015.     if (eb_izux_flg & EB_UT_FL_MTIME) {
  1016.         TTrace((stderr, "\nget_extattribs:  Unix e.f. modif. time = %ld\n",
  1017.           pzt->mtime));
  1018.     } else {
  1019.         pzt->mtime = dos_to_unix_time(G.lrec.last_mod_dos_datetime);
  1020.     }
  1021.     if (eb_izux_flg & EB_UT_FL_ATIME) {
  1022.         TTrace((stderr, "get_extattribs:  Unix e.f. access time = %ld\n",
  1023.           pzt->atime));
  1024.     } else {
  1025.         pzt->atime = pzt->mtime;
  1026.         TTrace((stderr, "\nget_extattribs:  modification/access times = %ld\n",
  1027.           pzt->mtime));
  1028.     }
  1029. #else
  1030.     pzt->mtime = dos_to_unix_time(G.lrec.last_mod_dos_datetime);
  1031.     pzt->atime = pzt->mtime;
  1032. #endif
  1033. }
  1034. #endif /* !MTS || SET_DIR_ATTRIB */
  1035.  
  1036.  
  1037.  
  1038. #ifndef MTS
  1039.  
  1040. /****************************/
  1041. /* Function close_outfile() */
  1042. /****************************/
  1043.  
  1044. void close_outfile(__G)    /* GRR: change to return PK-style warning level */
  1045.     __GDEF
  1046. {
  1047.     union {
  1048.         iztimes t3;             /* mtime, atime, ctime */
  1049.         ztimbuf t2;             /* modtime, actime */
  1050.     } zt;
  1051.  
  1052. /*---------------------------------------------------------------------------
  1053.     If symbolic links are supported, allocate a storage area, put the uncom-
  1054.     pressed "data" in it, and create the link.  Since we know it's a symbolic
  1055.     link to start with, we shouldn't have to worry about overflowing unsigned
  1056.     ints with unsigned longs.
  1057.   ---------------------------------------------------------------------------*/
  1058.  
  1059.     fclose(G.outfile);
  1060.  
  1061.     get_extattribs(__G__ &(zt.t3));
  1062.  
  1063.     /* set the file's access and modification times */
  1064.     if (utime(G.filename, &(zt.t2))) {
  1065.         if (uO.qflag)
  1066.             Info(slide, 0x201, ((char *)slide,
  1067.               "warning:  cannot set times for %s\n", FnFilter1(G.filename)));
  1068.         else
  1069.             Info(slide, 0x201, ((char *)slide,
  1070.               " (warning) cannot set times"));
  1071.     }
  1072.  
  1073. /*---------------------------------------------------------------------------
  1074.     Change the file permissions from default ones to those stored in the
  1075.     zipfile.
  1076.   ---------------------------------------------------------------------------*/
  1077.  
  1078.     if (chmod(G.filename, 0xffff & G.pInfo->file_attr))
  1079.         perror("chmod (file attributes) error");
  1080.  
  1081. /*---------------------------------------------------------------------------
  1082.     Change the file structure and set native .
  1083.   ---------------------------------------------------------------------------*/
  1084.  
  1085.     if (isv2_3()) {
  1086.         chorg(G.filename, G.extra_field[9]);
  1087.         chlen(G.filename,
  1088.             (ush) G.extra_field[10] | ((ush) G.extra_field[11] << 8),
  1089.             (ush) G.extra_field[12] | ((ush) G.extra_field[13] << 8));
  1090.         chgrow(G.filename, G.extra_field[14]);
  1091.     }
  1092. #if OLD_THEOS_EXTRA
  1093.      else if (isv2_0()) {
  1094.         chorg(G.filename, G.pInfo->file_attr & 0xFF);
  1095.         chlen(G.filename, v2_0extra()->reclen, v2_0extra()->keylen);
  1096.         chgrow(G.filename, v2_0extra()->filegrow);
  1097.     }
  1098. #endif
  1099. } /* end function close_outfile() */
  1100.  
  1101. #endif /* !MTS */
  1102.  
  1103.  
  1104.  
  1105.  
  1106. #ifdef SET_DIR_ATTRIB
  1107. /* messages of code for setting directory attributes */
  1108. static ZCONST char Far DirlistUidGidFailed[] =
  1109.   "warning:  cannot set UID %d and/or GID %d for %s\n";
  1110. static ZCONST char Far DirlistUtimeFailed[] =
  1111.   "warning:  cannot set modification, access times for %s\n";
  1112. #  ifndef NO_CHMOD
  1113.   static ZCONST char Far DirlistChmodFailed[] =
  1114.     "warning:  cannot set permissions for %s\n";
  1115. #  endif
  1116.  
  1117.  
  1118. int defer_dir_attribs(__G__ pd)
  1119.     __GDEF
  1120.     direntry **pd;
  1121. {
  1122.     uxdirattr *d_entry;
  1123.  
  1124.     d_entry = (uxdirattr *)malloc(sizeof(uxdirattr) + strlen(G.filename));
  1125.     *pd = (direntry *)d_entry;
  1126.     if (d_entry == (uxdirattr *)NULL) {
  1127.         return PK_MEM;
  1128.     }
  1129.     d_entry->fn = d_entry->fnbuf;
  1130.     strcpy(d_entry->fn, G.filename);
  1131.  
  1132.     d_entry->perms = G.pInfo->file_attr;
  1133.  
  1134.     get_extattribs(__G__ &(d_entry->u.t3));
  1135.     return PK_OK;
  1136. } /* end function defer_dir_attribs() */
  1137.  
  1138.  
  1139. int set_direc_attribs(__G__ d)
  1140.     __GDEF
  1141.     direntry *d;
  1142. {
  1143.     int errval = PK_OK;
  1144.  
  1145.     if (utime(d->fn, &UxAtt(d)->u.t2)) {
  1146.         Info(slide, 0x201, ((char *)slide,
  1147.           LoadFarString(DirlistUtimeFailed), FnFilter1(d->fn)));
  1148.         if (!errval)
  1149.             errval = PK_WARN;
  1150.     }
  1151.     if (chmod(d->fn, 0xffff & UxAtt(d)->perms)) {
  1152.         Info(slide, 0x201, ((char *)slide,
  1153.           LoadFarString(DirlistChmodFailed), FnFilter1(d->fn)));
  1154.         /* perror("chmod (file attributes) error"); */
  1155.         if (!errval)
  1156.             errval = PK_WARN;
  1157.     }
  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.     char buf1[40];
  1198.     extern char Far  CompiledWith[];
  1199.  
  1200.     sprintf(slide, CompiledWith,
  1201.         "THEOS C ","5.28", "THEOS ", "4.x", " on ", __DATE__);
  1202.     (*G.message)((zvoid *)&G, slide, (ulg)strlen((char *)slide), 0);
  1203.  
  1204. } /* end function version() */
  1205.  
  1206. #endif /* !SFX */
  1207.