home *** CD-ROM | disk | FTP | other *** search
/ vsiftp.vmssoftware.com / VSIPUBLIC@vsiftp.vmssoftware.com.tar / FREEWARE / FREEWARE40.ZIP / unzip / file_io.c < prev    next >
C/C++ Source or Header  |  1992-06-19  |  24KB  |  818 lines

  1. /*---------------------------------------------------------------------------
  2.  
  3.   file_io.c
  4.  
  5.   This file contains routines for doing direct input/output, file-related
  6.   sorts of things.
  7.  
  8.   ---------------------------------------------------------------------------*/
  9.  
  10.  
  11. #include "unzip.h"
  12.  
  13.  
  14. /************************************/
  15. /*  File_IO Includes, Defines, etc. */
  16. /************************************/
  17.  
  18. #ifdef VMS
  19. #include <rms.h>                /* RMS prototypes, error codes, etc. */
  20. #include <ssdef.h>              /* system services error codes */
  21. #include <descrip.h>            /* "descriptor" format stuff */
  22. #endif
  23.  
  24. static int WriteBuffer __((int fd, unsigned char *buf, int len));
  25. static int dos2unix __((unsigned char *buf, int len));
  26.  
  27. int CR_flag = 0;        /* when last char of buffer == CR (for dos2unix()) */
  28. globaldef long last_line = 0;    /* line counter */
  29.  
  30.  
  31.  
  32.  
  33. /*******************************/
  34. /*  Function open_input_file() */
  35. /*******************************/
  36.  
  37. int open_input_file()
  38. {                               /* return non-0 if open failed */
  39.     /*
  40.      *  open the zipfile for reading and in BINARY mode to prevent cr/lf
  41.      *  translation, which would corrupt the bitstreams
  42.      */
  43.  
  44. #ifndef UNIX
  45.     zipfd = open(zipfn, O_RDONLY | O_BINARY);
  46. #else
  47.     zipfd = open(zipfn, O_RDONLY);
  48. #endif
  49.     if (zipfd < 1) {
  50.         fprintf(stderr, "error:  can't open zipfile [ %s ]\n", zipfn);
  51.         return (1);
  52.     }
  53.     return 0;
  54. }
  55.  
  56.  
  57.  
  58.  
  59.  
  60. /************************/
  61. /*  Function readbuf()  */
  62. /************************/
  63.  
  64. int readbuf(buf, size)
  65. char *buf;
  66. register unsigned size;
  67. {                               /* return number of bytes read into buf */
  68.     register int count;
  69.     int n;
  70.  
  71.     n = size;
  72.     while (size) {
  73.         if (incnt == 0) {
  74.             if ((incnt = read(zipfd, inbuf, INBUFSIZ)) <= 0)
  75.                 return (n-size);
  76.             /* buffer ALWAYS starts on a block boundary:  */
  77.             last_line++;
  78.             cur_zipfile_bufstart += INBUFSIZ;
  79.             inptr = inbuf;
  80.         }
  81.         count = min(size, incnt);
  82.         memcpy(buf, inptr, count);
  83.         buf += count;
  84.         inptr += count;
  85.         incnt -= count;
  86.         size -= count;
  87.     }
  88.     return (n);
  89. }
  90.  
  91.  
  92.  
  93.  
  94.  
  95. #ifdef VMS
  96.  
  97. /**********************************/
  98. /*  Function create_output_file() */
  99. /**********************************/
  100.  
  101. int create_output_file()
  102. {                               /* return non-0 if sys$create failed */
  103.     /*
  104.      * VMS VERSION (generic version is below)
  105.      *
  106.      * Create the output file and set its date/time using VMS Record Management
  107.      * Services From Hell.  Then reopen for appending with normal Unix/C-type
  108.      * I/O functions.  This is the EASY way to set the file date/time under VMS.
  109.      */
  110.     int ierr, yr, mo, dy, hh, mm, ss;
  111.     char timbuf[24];            /* length = first entry in "stupid" + 1 */
  112.     struct FAB fileblk;
  113.     struct XABDAT dattim;
  114.     static char *month[] =
  115.     {"JAN", "FEB", "MAR", "APR", "MAY", "JUN",
  116.      "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"};
  117. /*  fixed-length string descriptor (why not just a pointer to timbuf? sigh.) */
  118.     struct dsc$descriptor stupid =
  119.     {23, DSC$K_DTYPE_T, DSC$K_CLASS_S, timbuf};
  120.  
  121.  
  122.  
  123. /*---------------------------------------------------------------------------
  124.     First initialize the necessary RMS and date/time variables.  "FAB" stands
  125.     for "file attribute block," "XAB" for "extended attribute block."  Files
  126.     under VMS are usually in "variable-length records" format with "carriage-
  127.     return carriage control" (at least for text files).  Unfortunately, some-
  128.     where along the line extra "carriage returns" (i.e., line feed characters)
  129.     get stuck in files which are opened in the variable format.  This may be
  130.     a VMS problem, an RMS problem, or a Unix/C I/O problem, but every 8192
  131.     characters of text file is followed by a spurious LF, and more often than
  132.     that for binary files.  So we use the stream-LF format instead (which is
  133.     what the Unix/C I/O routines do by default).  EDT complains about such
  134.     files but goes ahead and edits them; TPU (Adam, Eve) and vi don't seem
  135.     to care at all.
  136.   ---------------------------------------------------------------------------*/
  137.  
  138.     yr = ((lrec.last_mod_file_date >> 9) & 0x7f) + 1980; /* dissect date */
  139.     mo = ((lrec.last_mod_file_date >> 5) & 0x0f) - 1;
  140.     dy = (lrec.last_mod_file_date & 0x1f);
  141.     hh = (lrec.last_mod_file_time >> 11) & 0x1f;        /* dissect time */
  142.     mm = (lrec.last_mod_file_time >> 5) & 0x3f;
  143.     ss = (lrec.last_mod_file_time & 0x1f) * 2;
  144.  
  145.     fileblk = cc$rms_fab;               /* fill FAB with default values */
  146.     fileblk.fab$l_fna = filename;       /* l_fna, b_fns are the only re- */
  147.     fileblk.fab$b_fns = strlen(filename); /*  quired user-supplied fields */
  148.     fileblk.fab$b_rfm = FAB$C_STMLF;    /* stream-LF record format */
  149.     fileblk.fab$b_rat = FAB$M_CR;       /* carriage-return carriage ctrl */
  150.     /*                      ^^^^ *NOT* V_CR!!!     */
  151.     fileblk.fab$l_xab = &dattim;        /* chain XAB to FAB */
  152.     dattim = cc$rms_xabdat;             /* fill XAB with default values */
  153.  
  154.     CR_flag = 0;                /* Hack to get CR at end of buffer working
  155.                                    (dos2unix) */
  156.  
  157. /*---------------------------------------------------------------------------
  158.     Next convert date into an ASCII string, then use a VMS service to con-
  159.     vert the string into internal time format.  Obviously this is a bit of a
  160.     kludge, but I have absolutely NO intention of figuring out how to convert
  161.     MS-DOS time into tenths of microseconds elapsed since freaking 17 Novem-
  162.     ber 1858!!  Particularly since DEC doesn't even have a native 64-bit data
  163.     type.  Bleah.
  164.   ---------------------------------------------------------------------------*/
  165.  
  166.     sprintf(timbuf, "%02d-%3s-%04d %02d:%02d:%02d.00", dy, month[mo], yr,
  167.             hh, mm, ss);
  168.  
  169. /*  "xab$q_cdt" is the XAB field which holds the file's creation date/time */
  170.     sys$bintim(&stupid, &dattim.xab$q_cdt);
  171.  
  172. /*---------------------------------------------------------------------------
  173.     Next create the file under RMS.  If sys$create chokes with an error of
  174.     RMS$_SYN (syntax error), it's probably because a Unix-style directory was
  175.     specified, so try to create the file again using the regular creat() func-
  176.     tion (date/time won't be set properly in this case, obviously).
  177.   ---------------------------------------------------------------------------*/
  178.  
  179.     if ((ierr = sys$create(&fileblk)) != RMS$_NORMAL)
  180.         if (ierr == RMS$_SYN) { /* try Unix/C create:  0 = default perms */
  181.             outfd = creat(filename, 0, "rfm=stmlf", "rat=cr");
  182.             if (outfd < 1) {
  183.                 fprintf(stderr, "Can't create output file:  %s\n", filename);
  184.                 return (1);
  185.             } else {
  186.                 return (0);
  187.             }
  188.         } else {                /* probably access violation */
  189.             fprintf(stderr, "Can't create output file:  %s\n", filename);
  190.             return (1);
  191.         }
  192.  
  193. /*---------------------------------------------------------------------------
  194.     Finally, close the file under RMS and reopen with Unix/C open() function.
  195.   ---------------------------------------------------------------------------*/
  196.  
  197.     sys$close(&fileblk);
  198.     outfd = open(filename, O_RDWR);
  199.  
  200.     return (0);
  201. }
  202.  
  203.  
  204.  
  205.  
  206.  
  207. #else                           /* !VMS */
  208.  
  209. /**********************************/
  210. /*  Function create_output_file() */
  211. /**********************************/
  212.  
  213. int create_output_file()
  214. {                               /* return non-0 if creat failed */
  215.     /*
  216.      * GENERIC VERSION (special VMS version is above)
  217.      *
  218.      * Create the output file with default permissions.
  219.      */
  220.     extern int do_all;
  221.     char answerbuf[10];
  222.     UWORD holder;
  223.  
  224.  
  225.  
  226.     CR_flag = 0;                /* Hack to get CR at end of buffer working. */
  227.  
  228.     /*
  229.      * check if the file exists, unless do_all
  230.      */
  231.     if (!do_all) {
  232.         outfd = open(filename, 0);
  233.         if (outfd >= 0) {       /* first close it, before you forget! */
  234.             close(outfd);
  235.  
  236.             /* ask the user before blowing it away */
  237.             fprintf(stderr, "replace %s, y-yes, n-no, a-all: ", filename);
  238.             fgets(answerbuf, 9, stdin);
  239.  
  240.             switch (answerbuf[0]) {
  241.             case 'y':
  242.             case 'Y':
  243.                 break;
  244.             case 'a':
  245.             case 'A':
  246.                 do_all = 1;
  247.                 break;
  248.             case 'n':
  249.             case 'N':
  250.             default:
  251.                 while (ReadByte(&holder));
  252.                 return 1;       /* it's done! */
  253.             }
  254.         }
  255.     }
  256. #ifndef UNIX
  257.     outfd = creat(filename, (S_IWRITE | S_IREAD) & f_attr);
  258. #else
  259.     {
  260.       int mask;
  261.       mask = umask(0);
  262.       outfd = creat(filename, 0777 & f_attr);
  263.       umask(mask);
  264.     }
  265. #endif
  266.  
  267.     if (outfd < 1) {
  268.         fprintf(stderr, "Can't create output: %s\n", filename);
  269.         return 1;
  270.     }
  271.     /*
  272.      * close the newly created file and reopen it in BINARY mode to
  273.      * disable all CR/LF translations
  274.      */
  275. #ifndef UNIX
  276. #ifdef THINK_C
  277.     /*
  278.      * THINKC's stdio routines have the horrible habit of
  279.      * making any file you open look like generic files
  280.      * this code tells the OS that it's a text file
  281.      */
  282.     if (aflag) {
  283.         fileParam pb;
  284.         OSErr err;
  285.  
  286.         CtoPstr(filename);
  287.         pb.ioNamePtr = filename;
  288.         pb.ioVRefNum = 0;
  289.         pb.ioFVersNum = 0;
  290.         pb.ioFDirIndex = 0;
  291.         err = PBGetFInfo(&pb,0);
  292.         if (err == noErr) {
  293.             pb.ioFlFndrInfo.fdCreator = '????';
  294.             pb.ioFlFndrInfo.fdType = 'TEXT';
  295.             err = PBSetFInfo(&pb, 0);
  296.         }
  297.         PtoCstr(filename);
  298.     }
  299. #endif
  300.     if (!aflag) {
  301.         close(outfd);
  302.         outfd = open(filename, O_RDWR | O_BINARY);
  303.     }
  304. #endif
  305.     return 0;
  306. }
  307.  
  308. #endif                          /* !VMS */
  309.  
  310.  
  311.  
  312.  
  313.  
  314. /*****************************/
  315. /*  Function FillBitBuffer() */
  316. /*****************************/
  317.  
  318. int FillBitBuffer(bits)
  319. register int bits;
  320. {
  321.     /*
  322.      * Get the bits that are left and read the next UWORD.  This
  323.      * function is only used by the READBIT macro (which is used
  324.      * by all of the uncompression routines).
  325.      */
  326.     register int result = bitbuf;
  327.     UWORD temp;
  328.     int sbits = bits_left;
  329.  
  330.  
  331.     bits -= bits_left;
  332.  
  333.     /* read next UWORD of input */
  334.     bits_left = ReadByte(&bitbuf);
  335.     bits_left += ReadByte(&temp);
  336.  
  337.     bitbuf |= (temp << 8);
  338.     if (bits_left == 0)
  339.         zipeof = 1;
  340.  
  341.     /* get the remaining bits */
  342.     result = result | (int) ((bitbuf & mask_bits[bits]) << sbits);
  343.     bitbuf >>= bits;
  344.     bits_left -= bits;
  345.     return result;
  346. }
  347.  
  348.  
  349.  
  350.  
  351.  
  352. /************************/
  353. /*  Function ReadByte() */
  354. /************************/
  355.  
  356. int ReadByte(x)
  357. UWORD *x;
  358. {
  359.     /*
  360.      * read a byte; return 8 if byte available, 0 if not
  361.      */
  362.  
  363.  
  364.     if (csize-- <= 0)
  365.         return 0;
  366.  
  367.     if (incnt == 0) {
  368.         if ((incnt = read(zipfd, inbuf, INBUFSIZ)) <= 0)
  369.             return 0;
  370.         /* buffer ALWAYS starts on a block boundary:  */
  371.         cur_zipfile_bufstart += INBUFSIZ;
  372.         inptr = inbuf;
  373.     }
  374.     *x = *inptr++;
  375.     --incnt;
  376.     return 8;
  377. }
  378.  
  379.  
  380.  
  381. #ifdef FLUSH_AND_WRITE
  382. /***************************/
  383. /*  Function FlushOutput() */
  384. /***************************/
  385.  
  386. int FlushOutput()
  387. {                               /* return PK-type error code */
  388.     /* flush contents of output buffer */
  389.     /*
  390.      * This combined version doesn't work, and I sure can't see why not...
  391.      * probably something stupid, but how much can you screw up in 6 lines???
  392.      * [optimization problem??]
  393.      */
  394.     int len;
  395.  
  396.  
  397.     if (outcnt) {
  398.         UpdateCRC(outbuf, outcnt);
  399.  
  400.         if (!tflag) {
  401.             if (aflag)
  402.                 len = dos2unix(outbuf, outcnt);
  403.             if (write(outfd, outout, len) != len) {
  404.                 fprintf(stderr, "Fatal write error.\n");
  405.                 return (50);    /* 50:  disk full */
  406.             }
  407.         }
  408.         outpos += outcnt;
  409.         outcnt = 0;
  410.         outptr = outbuf;
  411.     }
  412.     return (0);                 /* 0:  no error */
  413. }
  414.  
  415. #else                           /* separate flush and write routines */
  416. /***************************/
  417. /*  Function FlushOutput() */
  418. /***************************/
  419.  
  420. int FlushOutput()
  421. {                               /* return PK-type error code */
  422.     /* flush contents of output buffer */
  423.     if (outcnt) {
  424.         UpdateCRC(outbuf, outcnt);
  425.  
  426.         if (!tflag && WriteBuffer(outfd, outbuf, outcnt))
  427.             return (50);        /* 50:  disk full */
  428.  
  429.         outpos += outcnt;
  430.         outcnt = 0;
  431.         outptr = outbuf;
  432.     }
  433.     return (0);                 /* 0:  no error */
  434. }
  435.  
  436. /***************************/
  437. /*  Function WriteBuffer() */
  438. /***************************/
  439.  
  440. static int WriteBuffer(fd, buf, len)    /* return 0 if successful, 1 if not */
  441. int fd;
  442. unsigned char *buf;
  443. int len;
  444. {
  445.     if (aflag)
  446.         len = dos2unix(buf, len);
  447.     if (write(fd, outout, len) != len) {
  448. #ifdef DOS_OS2
  449.         if (!cflag) {           /* ^Z treated as EOF, removed with -c */
  450. #endif
  451.             fprintf(stderr, "Fatal write error.\n");
  452.             return (1);         /* FAILED */
  453. #ifdef DOS_OS2
  454.         }
  455. #endif
  456.     }
  457.     return (0);
  458. }
  459.  
  460. #endif
  461.  
  462.  
  463.  
  464.  
  465. /************************/
  466. /*  Function dos2unix() */
  467. /************************/
  468.  
  469. static int dos2unix(buf, len)
  470. unsigned char *buf;
  471. int len;
  472. {
  473.     int new_len;
  474.     int i;
  475.     unsigned char *walker;
  476.  
  477.     new_len = len;
  478.     walker = outout;
  479. #ifdef MACOS
  480.     /*
  481.      * Mac wants to strip LFs instead CRs from CRLF pairs
  482.      */
  483.     if (CR_flag && *buf == LF) {
  484.         buf++;
  485.         new_len--;
  486.         len--;
  487.         CR_flag = buf[len] == CR;
  488.     }
  489.     else
  490.         CR_flag = buf[len - 1] == CR;
  491.     for (i = 0; i < len; i += 1) {
  492.         *walker++ = ascii_to_native(*buf);
  493.         if (*buf == LF) walker[-1] = CR;
  494.         if (*buf++ == CR && *buf == LF) {
  495.             new_len--;
  496.             buf++;
  497.             i++;
  498.         }
  499.     }
  500. #else
  501.     if (CR_flag && *buf != LF)
  502.         *walker++ = ascii_to_native(CR);
  503.     CR_flag = buf[len - 1] == CR;
  504.     for (i = 0; i < len; i += 1) {
  505.         *walker++ = ascii_to_native(*buf);
  506.         if (*buf++ == CR && *buf == LF) {
  507.             new_len--;
  508.             walker[-1] = ascii_to_native(*buf++);
  509.             i++;
  510.         }
  511.     }
  512.     /*
  513.      * If the last character is a CR, then "ignore it" for now...
  514.      */
  515.     if (walker[-1] == ascii_to_native(CR))
  516.         new_len--;
  517. #endif
  518.     return new_len;
  519. }
  520.  
  521.  
  522.  
  523.  
  524.  
  525. #ifdef DOS_OS2
  526.  
  527. /***************************************/
  528. /*  Function set_file_time_and_close() */
  529. /***************************************/
  530.  
  531. void set_file_time_and_close()
  532.  /*
  533.   * MS-DOS AND OS/2 VERSION (Mac, Unix versions are below)
  534.   *
  535.   * Set the output file date/time stamp according to information from the
  536.   * zipfile directory record for this member, then close the file.  This
  537.   * is optional and can be deleted if your compiler does not easily support
  538.   * setftime().
  539.   */
  540. {
  541. /*---------------------------------------------------------------------------
  542.     Allocate local variables needed by OS/2 and Turbo C.  [OK, OK, so it's
  543.     a bogus comment...but this routine was getting way too cluttered and
  544.     needed some visual separators.  Bleah.]
  545.   ---------------------------------------------------------------------------*/
  546.  
  547. #ifdef OS2              /* (assuming only MSC or MSC-compatible compilers
  548.                          * for this part) */
  549.  
  550.     union {
  551.         FDATE fd;               /* system file date record */
  552.         UWORD zdate;            /* date word */
  553.     } ud;
  554.  
  555.     union {
  556.         FTIME ft;               /* system file time record */
  557.         UWORD ztime;            /* time word */
  558.     } ut;
  559.  
  560.     FILESTATUS fs;
  561.  
  562. #else                           /* !OS2 */
  563. #ifdef __TURBOC__
  564.  
  565.     union {
  566.         struct ftime ft;        /* system file time record */
  567.         struct {
  568.             UWORD ztime;        /* date and time words */
  569.             UWORD zdate;        /* .. same format as in .ZIP file */
  570.         } zt;
  571.     } td;
  572.  
  573. #endif                          /* __TURBOC__ */
  574. #endif                          /* !OS2 */
  575.  
  576.     /*
  577.      * Do not attempt to set the time stamp on standard output
  578.      */
  579.     if (cflag) {
  580.         close(outfd);
  581.         return;
  582.     }
  583.  
  584.  
  585. /*---------------------------------------------------------------------------
  586.     Copy and/or convert time and date variables, if necessary; then set the
  587.     file time/date.
  588.   ---------------------------------------------------------------------------*/
  589.  
  590. #ifdef OS2
  591.  
  592.     DosQFileInfo(outfd, 1, &fs, sizeof(fs));
  593.     ud.zdate = lrec.last_mod_file_date;
  594.     fs.fdateLastWrite = ud.fd;
  595.     ut.ztime = lrec.last_mod_file_time;
  596.     fs.ftimeLastWrite = ut.ft;
  597.     DosSetFileInfo(outfd, 1, &fs, sizeof(fs));
  598.  
  599. #else                           /* !OS2 */
  600. #ifdef __TURBOC__
  601.  
  602.     td.zt.ztime = lrec.last_mod_file_time;
  603.     td.zt.zdate = lrec.last_mod_file_date;
  604.     setftime(outfd, &td.ft);
  605.  
  606. #else                           /* !__TURBOC__:  MSC MS-DOS */
  607.  
  608.     _dos_setftime(outfd, lrec.last_mod_file_date, lrec.last_mod_file_time);
  609.  
  610. #endif                          /* !__TURBOC__ */
  611. #endif                          /* !OS2 */
  612.  
  613. /*---------------------------------------------------------------------------
  614.     And finally we can close the file...at least everybody agrees on how to
  615.     do *this*.  I think...
  616.   ---------------------------------------------------------------------------*/
  617.  
  618.     close(outfd);
  619. }
  620.  
  621.  
  622.  
  623.  
  624.  
  625. #else                           /* !DOS_OS2 ... */
  626. #ifndef VMS                     /* && !VMS (already done) ... */
  627. #ifndef MTS                     /* && !MTS (can't do):  Mac or UNIX */
  628. #ifdef MACOS                    /* Mac first */
  629.  
  630. /***************************************/
  631. /*  Function set_file_time_and_close() */
  632. /***************************************/
  633.  
  634. void set_file_time_and_close()
  635.  /*
  636.   * MAC VERSION
  637.   *
  638.   * First close the output file, then set its date/time stamp according
  639.   * to information from the zipfile directory record for this file.  [So
  640.   * technically this should be called "close_file_and_set_time()", but
  641.   * this way we can use the same prototype for either case, without extra
  642.   * #ifdefs.  So there.]
  643.   */
  644. {
  645.     long m_time;
  646.     DateTimeRec dtr;
  647.     ParamBlockRec pbr;
  648.     OSErr err;
  649.  
  650.     if (outfd != 1)
  651.     {
  652.         close(outfd);
  653.  
  654.         /*
  655.          * Macintosh bases all file modification times on the number of seconds
  656.          * elapsed since Jan 1, 1904, 00:00:00.  Therefore, to maintain
  657.          * compatibility with MS-DOS archives, which date from Jan 1, 1980,
  658.          * with NO relation to GMT, the following conversions must be made:
  659.          *      the Year (yr) must be incremented by 1980;
  660.          *      and converted to seconds using the Mac routine Date2Secs(),
  661.          *      almost similar in complexity to the Unix version :-)
  662.          *                                     J. Lee
  663.          */
  664.  
  665.         dtr.year = (((lrec.last_mod_file_date >> 9) & 0x7f) + 1980); /* dissect date */
  666.         dtr.month = ((lrec.last_mod_file_date >> 5) & 0x0f);
  667.         dtr.day = (lrec.last_mod_file_date & 0x1f);
  668.  
  669.         dtr.hour = ((lrec.last_mod_file_time >> 11) & 0x1f);      /* dissect time */
  670.         dtr.minute = ((lrec.last_mod_file_time >> 5) & 0x3f);
  671.         dtr.second = ((lrec.last_mod_file_time & 0x1f) * 2);
  672.         Date2Secs(&dtr, &m_time);
  673.         CtoPstr(filename);
  674.         pbr.fileParam.ioNamePtr = filename;
  675.         pbr.fileParam.ioVRefNum = pbr.fileParam.ioFVersNum = pbr.fileParam.ioFDirIndex = 0;
  676.         err = PBGetFInfo(&pbr, 0L);
  677.         pbr.fileParam.ioFlMdDat = pbr.fileParam.ioFlCrDat = m_time;
  678.         if (err == noErr) {
  679.             err = PBSetFInfo(&pbr, 0L);
  680.         }
  681.         if (err != noErr) {
  682.             printf("Error, can't set the time for %s\n",filename);
  683.         }
  684.  
  685.         /* set read-only perms if needed */
  686.         if (err != noErr && f_attr != 0) {
  687.             err = SetFLock(filename, 0);
  688.         }
  689.         PtoCstr(filename);
  690.     }
  691. }
  692.  
  693.  
  694.  
  695.  
  696.  
  697. #else                           /* !MACOS:  only one left is UNIX */
  698.  
  699. /***************************************/
  700. /*  Function set_file_time_and_close() */
  701. /***************************************/
  702.  
  703. void set_file_time_and_close()
  704.  /*
  705.   * UNIX VERSION (MS-DOS & OS/2, Mac versions are above)
  706.   *
  707.   * First close the output file, then set its date/time stamp according
  708.   * to information from the zipfile directory record for this file.  [So
  709.   * technically this should be called "close_file_and_set_time()", but
  710.   * this way we can use the same prototype for either case, without extra
  711.   * #ifdefs.  So there.]
  712.   */
  713. {
  714.     long m_time;
  715.     int yr, mo, dy, hh, mm, ss, leap, days = 0;
  716.     struct utimbuf {
  717.       time_t atime;             /* New access time */
  718.       time_t mtime;             /* New modification time */
  719.     } tp;
  720. #ifdef BSD
  721.     static struct timeb tbp;
  722. #else
  723.     extern long timezone;
  724. #endif
  725.  
  726.  
  727.     close(outfd);
  728.  
  729.     if (cflag)                  /* can't set time on stdout */
  730.         return;
  731.  
  732.     /*
  733.      * These date conversions look a little weird, so I'll explain.
  734.      * UNIX bases all file modification times on the number of seconds
  735.      * elapsed since Jan 1, 1970, 00:00:00 GMT.  Therefore, to maintain
  736.      * compatibility with MS-DOS archives, which date from Jan 1, 1980,
  737.      * with NO relation to GMT, the following conversions must be made:
  738.      *      the Year (yr) must be incremented by 10;
  739.      *      the Date (dy) must be decremented by 1;
  740.      *      and the whole mess must be adjusted by TWO factors:
  741.      *          relationship to GMT (ie.,Pacific Time adds 8 hrs.),
  742.      *          and whether or not it is Daylight Savings Time.
  743.      * Also, the usual conversions must take place to account for leap years,
  744.      * etc.
  745.      *                                     C. Seaman
  746.      */
  747.  
  748.     yr = (((lrec.last_mod_file_date >> 9) & 0x7f) + 10); /* dissect date */
  749.     mo = ((lrec.last_mod_file_date >> 5) & 0x0f);
  750.     dy = ((lrec.last_mod_file_date & 0x1f) - 1);
  751.  
  752.     hh = ((lrec.last_mod_file_time >> 11) & 0x1f);      /* dissect time */
  753.     mm = ((lrec.last_mod_file_time >> 5) & 0x3f);
  754.     ss = ((lrec.last_mod_file_time & 0x1f) * 2);
  755.  
  756.     /* leap = # of leap years from 1970 up to but not including
  757.        the current year */
  758.  
  759.     leap = ((yr + 1969) / 4);   /* Leap year base factor */
  760.  
  761.     /* How many days from 1970 to this year? */
  762.     days = (yr * 365) + (leap - 492);
  763.  
  764.     switch (mo) {               /* calculate expired days this year */
  765.     case 12:
  766.         days += 30;
  767.     case 11:
  768.         days += 31;
  769.     case 10:
  770.         days += 30;
  771.     case 9:
  772.         days += 31;
  773.     case 8:
  774.         days += 31;
  775.     case 7:
  776.         days += 30;
  777.     case 6:
  778.         days += 31;
  779.     case 5:
  780.         days += 30;
  781.     case 4:
  782.         days += 31;
  783.     case 3:
  784.         days += 28;             /* account for leap years (2000 IS one) */
  785.         if (((yr + 1970) % 4 == 0) && (yr + 1970) != 2100)  /* OK thru 2199 */
  786.             ++days;
  787.     case 2:
  788.         days += 31;
  789.     }
  790.  
  791.     /* convert date & time to seconds relative to 00:00:00, 01/01/1970 */
  792.     m_time = ((days + dy) * 86400) + (hh * 3600) + (mm * 60) + ss;
  793.  
  794. #ifdef BSD
  795.    ftime(&tbp);
  796.    m_time += tbp.timezone * 60L;
  797. #else                                   /* !BSD */
  798.     tzset();                            /* Set `timezone'. */
  799.     m_time += timezone;                 /* account for timezone differences */
  800. #endif
  801.  
  802.     if (localtime(&m_time)->tm_isdst)
  803.         m_time -= 60L * 60L;            /* Adjust for daylight savings time */
  804.  
  805.     /* set the time stamp on the file */
  806.  
  807.     tp.mtime = m_time;                  /* Set modification time */
  808.     tp.atime = m_time;                  /* Set access time */
  809.  
  810.     if (utime(filename, &tp))
  811.         fprintf(stderr, "Error, can't set the time for %s\n",filename);
  812. }
  813.  
  814. #endif                          /* ?MACOS */
  815. #endif                          /* ?MTS */
  816. #endif                          /* ?VMS */
  817. #endif                          /* ?DOS_OS2 */
  818.