home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 9 Archive / 09-Archive.zip / unzip532.zip / human68k / human68k.c < prev    next >
C/C++ Source or Header  |  1997-10-21  |  26KB  |  799 lines

  1. /*---------------------------------------------------------------------------
  2.  
  3.   human68k.c
  4.  
  5.   Human68K-specific routines for use with Info-ZIP's UnZip 5.1 and later.
  6.  
  7.   Contains:  do_wild()
  8.              mapattr()
  9.              mapname()
  10.              checkdir()
  11.              close_outfile()
  12.              stamp_file()                   (TIMESTAMP only)
  13.              version()
  14.              TwentyOne()
  15.              normalize_name()
  16.  
  17.   ---------------------------------------------------------------------------*/
  18.  
  19.  
  20. #include <dirent.h>
  21. #include <sys/dos.h>
  22. #include <sys/xunistd.h>
  23. #include <jstring.h>
  24. #define UNZIP_INTERNAL
  25. #include "unzip.h"
  26.  
  27.  
  28. static void normalize_name(char *);
  29.  
  30. static int created_dir;        /* used in mapname(), checkdir() */
  31. static int renamed_fullpath;   /* ditto */
  32.  
  33. #ifndef SFX
  34.  
  35. /**********************/
  36. /* Function do_wild() */
  37. /**********************/
  38.  
  39. char *do_wild(__G__ wildspec)
  40.     __GDEF
  41.     char *wildspec;         /* only used first time on a given dir */
  42. {
  43.     static DIR *dir = NULL;
  44.     static char *dirname, *wildname, matchname[FILNAMSIZ];
  45.     static int firstcall=TRUE, have_dirname, dirnamelen;
  46.     struct dirent *file;
  47.  
  48.  
  49.     /* Even when we're just returning wildspec, we *always* do so in
  50.      * matchname[]--calling routine is allowed to append four characters
  51.      * to the returned string, and wildspec may be a pointer to argv[].
  52.      */
  53.     if (firstcall) {        /* first call:  must initialize everything */
  54.         firstcall = FALSE;
  55.  
  56.         /* break the wildspec into a directory part and a wildcard filename */
  57.         if ((wildname = strrchr(wildspec, '/')) == NULL) {
  58.             dirname = ".";
  59.             dirnamelen = 1;
  60.             have_dirname = FALSE;
  61.             wildname = wildspec;
  62.         } else {
  63.             ++wildname;     /* point at character after '/' */
  64.             dirnamelen = wildname - wildspec;
  65.             if ((dirname = (char *)malloc(dirnamelen+1)) == NULL) {
  66.                 Info(slide, 1, ((char *)slide,
  67.                   "warning:  cannot allocate wildcard buffers\n"));
  68.                 strcpy(matchname, wildspec);
  69.                 return matchname;   /* but maybe filespec was not a wildcard */
  70.             }
  71.             strncpy(dirname, wildspec, dirnamelen);
  72.             dirname[dirnamelen] = '\0';   /* terminate for strcpy below */
  73.             have_dirname = TRUE;
  74.         }
  75.  
  76.         if ((dir = opendir(dirname)) != NULL) {
  77.             while ((file = readdir(dir)) != NULL) {
  78.                 if (file->d_name[0] == '.' && wildname[0] != '.')
  79.                     continue;  /* Unix:  '*' and '?' do not match leading dot */
  80.                 if (match(file->d_name, wildname, 0)) {  /* 0 == case sens. */
  81.                     if (have_dirname) {
  82.                         strcpy(matchname, dirname);
  83.                         strcpy(matchname+dirnamelen, file->d_name);
  84.                     } else
  85.                         strcpy(matchname, file->d_name);
  86.                     return matchname;
  87.                 }
  88.             }
  89.             /* if we get to here directory is exhausted, so close it */
  90.             closedir(dir);
  91.             dir = NULL;
  92.         }
  93.  
  94.         /* return the raw wildspec in case that works (e.g., directory not
  95.          * searchable, but filespec was not wild and file is readable) */
  96.         strcpy(matchname, wildspec);
  97.         return matchname;
  98.     }
  99.  
  100.     /* last time through, might have failed opendir but returned raw wildspec */
  101.     if (dir == NULL) {
  102.         firstcall = TRUE;  /* nothing left to try--reset for new wildspec */
  103.         if (have_dirname)
  104.             free(dirname);
  105.         return (char *)NULL;
  106.     }
  107.  
  108.     /* If we've gotten this far, we've read and matched at least one entry
  109.      * successfully (in a previous call), so dirname has been copied into
  110.      * matchname already.
  111.      */
  112.     while ((file = readdir(dir)) != NULL)
  113.         if (match(file->d_name, wildname, 0)) {   /* 0 == don't ignore case */
  114.             if (have_dirname) {
  115.                 /* strcpy(matchname, dirname); */
  116.                 strcpy(matchname+dirnamelen, file->d_name);
  117.             } else
  118.                 strcpy(matchname, file->d_name);
  119.             return matchname;
  120.         }
  121.  
  122.     closedir(dir);     /* have read at least one dir entry; nothing left */
  123.     dir = NULL;
  124.     firstcall = TRUE;  /* reset for new wildspec */
  125.     if (have_dirname)
  126.         free(dirname);
  127.     return (char *)NULL;
  128.  
  129. } /* end function do_wild() */
  130.  
  131. #endif /* !SFX */
  132.  
  133.  
  134.  
  135.  
  136. /**********************/
  137. /* Function mapattr() */
  138. /**********************/
  139.  
  140. int mapattr(__G)
  141.     __GDEF
  142. {
  143.     ulg  tmp = G.crec.external_file_attributes;
  144.  
  145.     switch( G.pInfo->hostnum ) {
  146.         case UNIX_:
  147.         case VMS_:
  148.         case ACORN_:
  149.         case ATARI_:
  150.         case BEOS_:
  151.         case QDOS_:
  152.             G.pInfo->file_attr = _mode2dos(tmp >> 16);
  153.             break;
  154.         default:
  155.             /* set archive bit (file is not backed up) */
  156.             G.pInfo->file_attr =
  157.               (unsigned)(G.crec.external_file_attributes|32) & 0xff;
  158.             break;
  159.     }
  160.     return 0;
  161.  
  162. } /* end function mapattr() */
  163.  
  164.  
  165.  
  166.  
  167.  
  168. /**********************/
  169. /* Function mapname() */
  170. /**********************/
  171.                              /* return 0 if no error, 1 if caution (filename */
  172. int mapname(__G__ renamed)   /*  truncated), 2 if warning (skip file because */
  173.     __GDEF                   /*  dir doesn't exist), 3 if error (skip file), */
  174.     int renamed;             /*  or 10 if out of memory (skip file) */
  175. {                            /*  [also IZ_VOL_LABEL, IZ_CREATED_DIR] */
  176.     char pathcomp[FILNAMSIZ];    /* path-component buffer */
  177.     char *pp, *cp=(char *)NULL;  /* character pointers */
  178.     char *lastsemi=(char *)NULL; /* pointer to last semi-colon in pathcomp */
  179.     int quote = FALSE;           /* flags */
  180.     int error = 0;
  181.     register unsigned workch;    /* hold the character being tested */
  182.  
  183.  
  184. /*---------------------------------------------------------------------------
  185.     Initialize various pointers and counters and stuff.
  186.   ---------------------------------------------------------------------------*/
  187.  
  188.     if (G.pInfo->vollabel)
  189.         return IZ_VOL_LABEL;    /* can't set disk volume labels in Unix */
  190.  
  191.     /* can create path as long as not just freshening, or if user told us */
  192.     G.create_dirs = (!G.fflag || renamed);
  193.  
  194.     created_dir = FALSE;        /* not yet */
  195.  
  196.     /* user gave full pathname:  don't prepend rootpath */
  197.     renamed_fullpath = (renamed && (*filename == '/'));
  198.  
  199.     if (checkdir(__G__ (char *)NULL, INIT) == 10)
  200.         return 10;              /* initialize path buffer, unless no memory */
  201.  
  202.     *pathcomp = '\0';           /* initialize translation buffer */
  203.     pp = pathcomp;              /* point to translation buffer */
  204.     if (G.jflag)                /* junking directories */
  205.         cp = (char *)strrchr(G.filename, '/');
  206.     if (cp == NULL)             /* no '/' or not junking dirs */
  207.         cp = G.filename;        /* point to internal zipfile-member pathname */
  208.     else
  209.         ++cp;                   /* point to start of last component of path */
  210.  
  211. /*---------------------------------------------------------------------------
  212.     Begin main loop through characters in filename.
  213.   ---------------------------------------------------------------------------*/
  214.  
  215.     while ((workch = (uch)*cp++) != 0) {
  216.         if (iskanji(workch)) {
  217.             *pp++ = (char)workch;
  218.             quote = TRUE;
  219.         } else if (quote) {                 /* if character quoted, */
  220.             *pp++ = (char)workch;    /*  include it literally */
  221.             quote = FALSE;
  222.         } else
  223.             switch (workch) {
  224.             case '/':             /* can assume -j flag not given */
  225.                 *pp = '\0';
  226.                 if ((error = checkdir(__G__ pathcomp, APPEND_DIR)) > 1)
  227.                     return error;
  228.                 pp = pathcomp;    /* reset conversion buffer for next piece */
  229.                 lastsemi = NULL;  /* leave directory semi-colons alone */
  230.                 break;
  231.  
  232.             case ';':             /* VMS version (or DEC-20 attrib?) */
  233.                 lastsemi = pp;         /* keep for now; remove VMS ";##" */
  234.                 *pp++ = (char)workch;  /*  later, if requested */
  235.                 break;
  236.  
  237.             case '\026':          /* control-V quote for special chars */
  238.                 quote = TRUE;     /* set flag for next character */
  239.                 break;
  240.  
  241.             case ' ':             /* change spaces to underscore under */
  242.                 *pp++ = '_';      /*  MTS; leave as spaces under Unix */
  243.                 break;
  244.  
  245.             default:
  246.                 /* allow European characters in filenames: */
  247.                 if (isprint(workch) || (128 <= workch && workch <= 254))
  248.                     *pp++ = (char)workch;
  249.             } /* end switch */
  250.  
  251.     } /* end while loop */
  252.  
  253.     *pp = '\0';                   /* done with pathcomp:  terminate it */
  254.  
  255.     /* if not saving them, remove VMS version numbers (appended ";###") */
  256.     if (!G.V_flag && lastsemi) {
  257.         pp = lastsemi + 1;
  258.         while (isdigit((uch)(*pp)))
  259.             ++pp;
  260.         if (*pp == '\0')          /* only digits between ';' and end:  nuke */
  261.             *lastsemi = '\0';
  262.     }
  263.  
  264. /*---------------------------------------------------------------------------
  265.     Report if directory was created (and no file to create:  filename ended
  266.     in '/'), check name to be sure it exists, and combine path and name be-
  267.     fore exiting.
  268.   ---------------------------------------------------------------------------*/
  269.  
  270.     if (G.filename[strlen(G.filename) - 1] == '/') {
  271.         checkdir(__G__ G.filename, GETPATH);
  272.         if (created_dir) {
  273.             if (QCOND2) {
  274.                 Info(slide, 0, ((char *)slide, "   creating: %s\n",
  275.                   G.filename));
  276.             }
  277.             return IZ_CREATED_DIR;   /* set dir time (note trailing '/') */
  278.         }
  279.         return 2;   /* dir existed already; don't look for data to extract */
  280.     }
  281.  
  282.     if (*pathcomp == '\0') {
  283.         Info(slide, 1, ((char *)slide, "mapname:  conversion of %s failed\n",
  284.           G.filename));
  285.         return 3;
  286.     }
  287.  
  288.     checkdir(__G__ pathcomp, APPEND_NAME);  /* returns 1 if truncated: care? */
  289.     checkdir(__G__ G.filename, GETPATH);
  290.  
  291.     return error;
  292.  
  293. } /* end function mapname() */
  294.  
  295.  
  296.  
  297.  
  298.  
  299. /***********************/
  300. /* Function checkdir() */
  301. /***********************/
  302.  
  303. int checkdir(__G__ pathcomp, flag)
  304.     __GDEF
  305.     char *pathcomp;
  306.     int flag;
  307. /*
  308.  * returns:  1 - (on APPEND_NAME) truncated filename
  309.  *           2 - path doesn't exist, not allowed to create
  310.  *           3 - path doesn't exist, tried to create and failed; or
  311.  *               path exists and is not a directory, but is supposed to be
  312.  *           4 - path is too long
  313.  *          10 - can't allocate memory for filename buffers
  314.  */
  315. {
  316.     static int rootlen = 0;   /* length of rootpath */
  317.     static char *rootpath;    /* user's "extract-to" directory */
  318.     static char *buildpath;   /* full path (so far) to extracted file */
  319.     static char *end;         /* pointer to end of buildpath ('\0') */
  320.  
  321. #   define FN_MASK   7
  322. #   define FUNCTION  (flag & FN_MASK)
  323.  
  324.  
  325.  
  326. /*---------------------------------------------------------------------------
  327.     APPEND_DIR:  append the path component to the path being built and check
  328.     for its existence.  If doesn't exist and we are creating directories, do
  329.     so for this one; else signal success or error as appropriate.
  330.   ---------------------------------------------------------------------------*/
  331.  
  332.     if (FUNCTION == APPEND_DIR) {
  333.         int too_long = FALSE;
  334.         char *old_end = end;
  335.  
  336.         Trace((stderr, "appending dir segment [%s]\n", pathcomp));
  337.         while ((*end = *pathcomp++) != '\0')
  338.             ++end;
  339.         normalize_name(old_end);
  340.  
  341.         /* GRR:  could do better check, see if overrunning buffer as we go:
  342.          * check end-buildpath after each append, set warning variable if
  343.          * within 20 of FILNAMSIZ; then if var set, do careful check when
  344.          * appending.  Clear variable when begin new path. */
  345.  
  346.         if ((end-buildpath) > FILNAMSIZ-3)  /* need '/', one-char name, '\0' */
  347.             too_long = TRUE;                /* check if extracting directory? */
  348.         if (stat(buildpath, &G.statbuf))    /* path doesn't exist */
  349.         {
  350.             if (!G.create_dirs) { /* told not to create (freshening) */
  351.                 free(buildpath);
  352.                 return 2;         /* path doesn't exist:  nothing to do */
  353.             }
  354.             if (too_long) {
  355.                 Info(slide, 1, ((char *)slide,
  356.                   "checkdir error:  path too long: %s\n",
  357.                   buildpath));
  358.                 free(buildpath);
  359.                 return 4;         /* no room for filenames:  fatal */
  360.             }
  361.             if (MKDIR(buildpath, 0666) == -1) {   /* create the directory */
  362.                 Info(slide, 1, ((char *)slide,
  363.                   "checkdir error:  cannot create %s\n\
  364.                  unable to process %s.\n",
  365.                   buildpath, G.filename));
  366.                 free(buildpath);
  367.                 return 3;      /* path didn't exist, tried to create, failed */
  368.             }
  369.             created_dir = TRUE;
  370.         } else if (!S_ISDIR(G.statbuf.st_mode)) {
  371.             Info(slide, 1, ((char *)slide,
  372.               "checkdir error:  %s exists but is not directory\n\
  373.                  unable to process %s.\n",
  374.               buildpath, G.filename));
  375.             free(buildpath);
  376.             return 3;          /* path existed but wasn't dir */
  377.         }
  378.         if (too_long) {
  379.             Info(slide, 1, ((char *)slide,
  380.               "checkdir error:  path too long: %s\n",
  381.               buildpath));
  382.             free(buildpath);
  383.             return 4;         /* no room for filenames:  fatal */
  384.         }
  385.         *end++ = '/';
  386.         *end = '\0';
  387.         Trace((stderr, "buildpath now = [%s]\n", buildpath));
  388.         return 0;
  389.  
  390.     } /* end if (FUNCTION == APPEND_DIR) */
  391.  
  392. /*---------------------------------------------------------------------------
  393.     GETPATH:  copy full path to the string pointed at by pathcomp, and free
  394.     buildpath.
  395.   ---------------------------------------------------------------------------*/
  396.  
  397.     if (FUNCTION == GETPATH) {
  398.         strcpy(pathcomp, buildpath);
  399.         Trace((stderr, "getting and freeing path [%s]\n", pathcomp));
  400.         free(buildpath);
  401.         buildpath = end = (char *)NULL;
  402.         return 0;
  403.     }
  404.  
  405. /*---------------------------------------------------------------------------
  406.     APPEND_NAME:  assume the path component is the filename; append it and
  407.     return without checking for existence.
  408.   ---------------------------------------------------------------------------*/
  409.  
  410.     if (FUNCTION == APPEND_NAME) {
  411.         char *old_end = end;
  412.  
  413.         Trace((stderr, "appending filename [%s]\n", pathcomp));
  414.         while ((*end = *pathcomp++) != '\0') {
  415.             ++end;
  416.             normalize_name(old_end);
  417.             if ((end-buildpath) >= FILNAMSIZ) {
  418.                 *--end = '\0';
  419.                 Info(slide, 1, ((char *)slide,
  420.                   "checkdir warning:  path too long; truncating\n\
  421.                    %s\n                -> %s\n",
  422.                   G.filename, buildpath));
  423.                 return 1;   /* filename truncated */
  424.             }
  425.         }
  426.         Trace((stderr, "buildpath now = [%s]\n", buildpath));
  427.         return 0;  /* could check for existence here, prompt for new name... */
  428.     }
  429.  
  430. /*---------------------------------------------------------------------------
  431.     INIT:  allocate and initialize buffer space for the file currently being
  432.     extracted.  If file was renamed with an absolute path, don't prepend the
  433.     extract-to path.
  434.   ---------------------------------------------------------------------------*/
  435.  
  436. /* GRR:  for VMS and TOPS-20, add up to 13 to strlen */
  437.  
  438.     if (FUNCTION == INIT) {
  439.         Trace((stderr, "initializing buildpath to "));
  440.         if ((buildpath = (char *)malloc(strlen(G.filename)+rootlen+1)) ==
  441.             (char *)NULL)
  442.             return 10;
  443.         if ((rootlen > 0) && !renamed_fullpath) {
  444.             strcpy(buildpath, rootpath);
  445.             end = buildpath + rootlen;
  446.         } else {
  447.             *buildpath = '\0';
  448.             end = buildpath;
  449.         }
  450.         Trace((stderr, "[%s]\n", buildpath));
  451.         return 0;
  452.     }
  453.  
  454. /*---------------------------------------------------------------------------
  455.     ROOT:  if appropriate, store the path in rootpath and create it if neces-
  456.     sary; else assume it's a zipfile member and return.  This path segment
  457.     gets used in extracting all members from every zipfile specified on the
  458.     command line.
  459.   ---------------------------------------------------------------------------*/
  460.  
  461. #if (!defined(SFX) || defined(SFX_EXDIR))
  462.     if (FUNCTION == ROOT) {
  463.         Trace((stderr, "initializing root path to [%s]\n", pathcomp));
  464.         if (pathcomp == (char *)NULL) {
  465.             rootlen = 0;
  466.             return 0;
  467.         }
  468.         if ((rootlen = strlen(pathcomp)) > 0) {
  469.             int had_trailing_pathsep=FALSE;
  470.  
  471.             if (pathcomp[rootlen-1] == '/') {
  472.                 pathcomp[--rootlen] = '\0';
  473.                 had_trailing_pathsep = TRUE;
  474.             }
  475.             if (rootlen > 0 && (SSTAT(pathcomp, &G.statbuf) ||
  476.                 !S_ISDIR(G.statbuf.st_mode)))          /* path does not exist */
  477.             {
  478.                 if (!G.create_dirs /* || iswild(pathcomp) */ ) {
  479.                     rootlen = 0;
  480.                     return 2;   /* skip (or treat as stored file) */
  481.                 }
  482.                 /* create the directory (could add loop here to scan pathcomp
  483.                  * and create more than one level, but why really necessary?) */
  484.                 if (MKDIR(pathcomp, 0777) == -1) {
  485.                     Info(slide, 1, ((char *)slide,
  486.                       "checkdir:  cannot create extraction directory: %s\n",
  487.                       pathcomp));
  488.                     rootlen = 0;   /* path didn't exist, tried to create, and */
  489.                     return 3;  /* failed:  file exists, or 2+ levels required */
  490.                 }
  491.             }
  492.             if ((rootpath = (char *)malloc(rootlen+2)) == NULL) {
  493.                 rootlen = 0;
  494.                 return 10;
  495.             }
  496.             strcpy(rootpath, pathcomp);
  497.             rootpath[rootlen++] = '/';
  498.             rootpath[rootlen] = '\0';
  499.             Trace((stderr, "rootpath now = [%s]\n", rootpath));
  500.         }
  501.         return 0;
  502.     }
  503. #endif /* !SFX || SFX_EXDIR */
  504.  
  505. /*---------------------------------------------------------------------------
  506.     END:  free rootpath, immediately prior to program exit.
  507.   ---------------------------------------------------------------------------*/
  508.  
  509.     if (FUNCTION == END) {
  510.         Trace((stderr, "freeing rootpath\n"));
  511.         if (rootlen > 0) {
  512.             free(rootpath);
  513.             rootlen = 0;
  514.         }
  515.         return 0;
  516.     }
  517.  
  518.     return 99;  /* should never reach */
  519.  
  520. } /* end function checkdir() */
  521.  
  522.  
  523.  
  524.  
  525.  
  526. /****************************/
  527. /* Function close_outfile() */
  528. /****************************/
  529.  
  530. void close_outfile(__G)
  531.     __GDEF
  532. {
  533. #ifdef USE_EF_UT_TIME
  534.     iztimes z_utime;
  535.  
  536.     /* The following DOS date/time structure is machine-dependent as it
  537.      * assumes "little-endian" byte order.  For MSDOS-specific code, which
  538.      * is run on ix86 CPUs (or emulators), this assumption is valid; but
  539.      * care should be taken when using this code as template for other ports.
  540.      */
  541.     union {
  542.         ulg z_dostime;
  543.         struct {                /* date and time words */
  544.             union {             /* DOS file modification time word */
  545.                 ush ztime;
  546.                 struct {
  547.                     unsigned zt_se : 5;
  548.                     unsigned zt_mi : 6;
  549.                     unsigned zt_hr : 5;
  550.                 } _tf;
  551.             } _t;
  552.             union {             /* DOS file modification date word */
  553.                 ush zdate;
  554.                 struct {
  555.                     unsigned zd_dy : 5;
  556.                     unsigned zd_mo : 4;
  557.                     unsigned zd_yr : 7;
  558.                 } _df;
  559.             } _d;
  560.         } zt;
  561.     } dos_dt;
  562. #endif /* USE_EF_UT_TIME */
  563.  
  564.     if (G.cflag) {
  565.         fclose(G.outfile);
  566.         return;
  567.     }
  568.  
  569. #ifdef USE_EF_UT_TIME
  570.     if (G.extra_field &&
  571.         (ef_scan_for_izux(G.extra_field, G.lrec.extra_field_length, 0,
  572.                           G.lrec.last_mod_file_date, &z_utime, NULL)
  573.          & EB_UT_FL_MTIME))
  574.     {
  575.         struct tm *t;
  576.  
  577.         TTrace((stderr, "close_outfile:  Unix e.f. modif. time = %ld\n",
  578.           z_utime.mtime));
  579.         /* round up (down if "up" overflows) to even seconds */
  580.         if (z_utime.mtime & 1)
  581.             z_utime.mtime = (z_utime.mtime + 1 > z.utime.mtime) ?
  582.                              z_utime.mtime + 1 : z_utime.mtime - 1;
  583.         t = localtime(&(z_utime.mtime));
  584.         if (t->tm_year < 80) {
  585.             dos_dt.zt._t._tf.zt_se = 0;
  586.             dos_dt.zt._t._tf.zt_mi = 0;
  587.             dos_dt.zt._t._tf.zt_hr = 0;
  588.             dos_dt.zt._d._df.zd_dy = 1;
  589.             dos_dt.zt._d._df.zd_mo = 1;
  590.             dos_dt.zt._d._df.zd_yr = 0;
  591.         } else {
  592.             dos_dt.zt._t._tf.zt_se = t->tm_sec >> 1;
  593.             dos_dt.zt._t._tf.zt_mi = t->tm_min;
  594.             dos_dt.zt._t._tf.zt_hr = t->tm_hour;
  595.             dos_dt.zt._d._df.zd_dy = t->tm_mday;
  596.             dos_dt.zt._d._df.zd_mo = t->tm_mon + 1;
  597.             dos_dt.zt._d._df.zd_yr = t->tm_year - 80;
  598.         }
  599.     } else {
  600.         dos_dt.zt._t.ztime = G.lrec.last_mod_file_time;
  601.         dos_dt.zt._d.zdate = G.lrec.last_mod_file_date;
  602.     }
  603.     _dos_filedate(fileno(G.outfile), dos_dt.z_dostime);
  604. #else /* !USE_EF_UT_TIME */
  605.     _dos_filedate(fileno(G.outfile),
  606.       (ulg)G.lrec.last_mod_file_date << 16 | G.lrec.last_mod_file_time);
  607. #endif /* ?USE_EF_UT_TIME */
  608.  
  609.     fclose(G.outfile);
  610.  
  611.     _dos_chmod(G.filename, G.pInfo->file_attr);
  612.  
  613. } /* end function close_outfile() */
  614.  
  615.  
  616.  
  617.  
  618.  
  619. #ifdef TIMESTAMP
  620.  
  621. /*************************/
  622. /* Function stamp_file() */
  623. /*************************/
  624.  
  625. int stamp_file(fname, modtime)
  626.     ZCONST char *fname;
  627.     time_t modtime;
  628. {
  629.     union {
  630.         ulg z_dostime;
  631.         struct {                /* date and time words */
  632.             union {             /* DOS file modification time word */
  633.                 ush ztime;
  634.                 struct {
  635.                     unsigned zt_se : 5;
  636.                     unsigned zt_mi : 6;
  637.                     unsigned zt_hr : 5;
  638.                 } _tf;
  639.             } _t;
  640.             union {             /* DOS file modification date word */
  641.                 ush zdate;
  642.                 struct {
  643.                     unsigned zd_dy : 5;
  644.                     unsigned zd_mo : 4;
  645.                     unsigned zd_yr : 7;
  646.                 } _df;
  647.             } _d;
  648.         } zt;
  649.     } dos_dt;
  650.     time_t t_even;
  651.     struct tm *t;
  652.     int fd;                             /* file handle */
  653.  
  654.     /* round up (down if "up" overflows) to even seconds */
  655.     t_even = ((modtime + 1 > modtime) ? modtime + 1 : modtime) & (~1);
  656.     TIMET_TO_NATIVE(t_even)             /* NOP unless MSC 7.0 or Macintosh */
  657.     t = localtime(&t_even);
  658.     if (t->tm_year < 80) {
  659.         dos_dt.zt._t._tf.zt_se = 0;
  660.         dos_dt.zt._t._tf.zt_mi = 0;
  661.         dos_dt.zt._t._tf.zt_hr = 0;
  662.         dos_dt.zt._d._df.zd_dy = 1;
  663.         dos_dt.zt._d._df.zd_mo = 1;
  664.         dos_dt.zt._d._df.zd_yr = 0;
  665.     } else {
  666.         dos_dt.zt._t._tf.zt_se = t->tm_sec >> 1;
  667.         dos_dt.zt._t._tf.zt_mi = t->tm_min;
  668.         dos_dt.zt._t._tf.zt_hr = t->tm_hour;
  669.         dos_dt.zt._d._df.zd_dy = t->tm_mday;
  670.         dos_dt.zt._d._df.zd_mo = t->tm_mon + 1;
  671.         dos_dt.zt._d._df.zd_yr = t->tm_year - 80;
  672.     }
  673.     if (((fd = open(fname, 0)) == -1) ||
  674.         (_dos_filedate(fileno(G.outfile), dos_dt.z_dostime)))
  675.         return -1;
  676.     close(fd);
  677.     return 0;
  678.  
  679. } /* end function stamp_file() */
  680.  
  681. #endif /* TIMESTAMP */
  682.  
  683.  
  684.  
  685.  
  686. #ifndef SFX
  687.  
  688. /************************/
  689. /*  Function version()  */
  690. /************************/
  691.  
  692. void version(__G)
  693.     __GDEF
  694. {
  695.     int len;
  696. #if 0
  697.     char buf[40];
  698. #endif
  699.  
  700.     len = sprintf((char *)slide, LoadFarString(CompiledWith),
  701.  
  702. #ifdef __GNUC__
  703.       "gcc ", __VERSION__,
  704. #else
  705. #  if 0
  706.       "cc ", (sprintf(buf, " version %d", _RELEASE), buf),
  707. #  else
  708.       "unknown compiler", "",
  709. #  endif
  710. #endif
  711.  
  712.       "Human68k", " (X68000)",
  713.  
  714. #ifdef __DATE__
  715.       " on ", __DATE__
  716. #else
  717.       "", ""
  718. #endif
  719.       );
  720.  
  721.     (*G.message)((zvoid *)&G, slide, (ulg)len, 0);
  722.  
  723. } /* end function version() */
  724.  
  725. #endif /* !SFX */
  726.  
  727.  
  728.  
  729.  
  730.  
  731. /* Human68K-specific routines */
  732.  
  733. #define VALID_CHAR "&#()@_^{}!"
  734.  
  735. extern ulg TwentyOneOptions(void);
  736.  
  737. static int multi_period = 0;
  738. static int special_char = 0;
  739.  
  740. void
  741. InitTwentyOne(void)
  742. {
  743.     ulg stat;
  744.  
  745.     stat = TwentyOneOptions();
  746.     if (stat == 0 || stat == (unsigned long) -1) {
  747.         special_char = 0;
  748.         multi_period = 0;
  749.         return;
  750.     }
  751.     if (stat & (1UL << 29))
  752.         special_char = 1;
  753.     if (stat & (1UL << 28))
  754.         multi_period = 1;
  755. }
  756.  
  757. static void
  758. normalize_name(char *name)
  759. {
  760.     char *dot;
  761.     char *p;
  762.  
  763.     if (strlen(name) > 18) {    /* too long */
  764.         char base[18 + 1];
  765.         char ext[4 + 1];
  766.  
  767.         if ((dot = jstrrchr(name, '.')) != NULL)
  768.             *dot = '\0';
  769.         strncpy(base, name, 18);
  770.         base[18] = '\0';
  771.         if (dot) {
  772.             *dot = '.';
  773.             strncpy(ext, dot, 4);
  774.             ext[4] = '\0';
  775.         } else
  776.             *ext = '\0';
  777.         strcpy(name, base);
  778.         strcat(name, ext);
  779.     }
  780.     dot = NULL;
  781.     for (p = name; *p; p++) {
  782.         if (iskanji((unsigned char)*p) && p[1] != '\0')
  783.             p++;
  784.         else if (*p == '.') {
  785.             if (!multi_period) {
  786.                 dot = p;
  787.                 *p = '_';
  788.             }
  789.         } else if (!special_char && !isalnum (*p)
  790.                    && strchr(VALID_CHAR, *p) == NULL)
  791.             *p = '_';
  792.     }
  793.     if (dot != NULL) {
  794.         *dot = '.';
  795.         if (strlen(dot) > 4)
  796.             dot[4] = '\0';
  797.     }
  798. }
  799.