/*--------------------------------------------------------------------------- file_io.c This file contains routines for doing direct input/output, file-related sorts of things. ---------------------------------------------------------------------------*/ #include "unzip.h" #ifdef MSWIN #include #include "wizunzip.h" #include "replace.h" #endif /************************************/ /* File_IO Local Prototypes, etc. */ /************************************/ static int WriteBuffer __((int fd, unsigned char *buf, int len)); static int dos2unix __((unsigned char *buf, int len)); int CR_flag = 0; /* when last char of buffer == CR (for dos2unix()) */ /*******************************/ /* Function open_input_file() */ /*******************************/ int open_input_file() { /* return non-0 if open failed */ /* * open the zipfile for reading and in BINARY mode to prevent cr/lf * translation, which would corrupt the bitstreams */ #ifndef UNIX zipfd = open(zipfn, O_RDONLY | O_BINARY); #else zipfd = open(zipfn, O_RDONLY); #endif if (zipfd < 1) { fprintf(stderr, "error: can't open zipfile [ %s ]\n", zipfn); return (1); } return 0; } /************************/ /* Function readbuf() */ /************************/ int readbuf(buf, size) char *buf; register unsigned size; { /* return number of bytes read into buf */ register int count; int n; n = size; while (size) { if (incnt == 0) { if ((incnt = read(zipfd, inbuf, INBUFSIZ)) <= 0) return (n-size); /* buffer ALWAYS starts on a block boundary: */ cur_zipfile_bufstart += INBUFSIZ; inptr = inbuf; } count = min(size, incnt); memcpy(buf, inptr, count); buf += count; inptr += count; incnt -= count; size -= count; } return (n); } /**********************************/ /* Function create_output_file() */ /**********************************/ int create_output_file() { /* return non-0 if creat failed */ /* * Create the output file with default permissions. */ extern int do_all; char answerbuf[10]; UWORD holder; int already_exists; CR_flag = 0; /* Hack to get CR at end of buffer working. */ #ifndef VMS /* creates higher version number instead of overwriting (will * have to modify for VMS-style names with specific version * numbers: check for failure on creat()??? */ /* * check if the file exists, unless do_all */ already_exists = open(filename, 0); if (already_exists >= 0) close(already_exists); /* before you forget */ if (!do_all) { if (already_exists >= 0) { /* first close it, before you forget! */ /* ask the user before blowing it away */ #ifdef MSWIN { FARPROC lpProcReplace; int ReplaceDlgRetVal; /* replace dialog return value */ ShowCursor(FALSE); /* turn off cursor */ SetCursor(hSaveCursor); /* restore the cursor */ lpProcReplace = MakeProcInstance(Replace, hInst); ReplaceDlgRetVal = DialogBoxParam(hInst, "Replace", hMainWnd, lpProcReplace, (DWORD)(LPSTR)filename); FreeProcInstance(lpProcReplace); switch (ReplaceDlgRetVal) { case IDM_REPLACE_YES: break; case IDM_REPLACE_ALL: do_all = 1; break; case IDM_REPLACE_NO: while (ReadByte(&holder)); hSaveCursor = SetCursor(hHourGlass); ShowCursor(TRUE); /* show it */ return 1; /* it's done! */ } hSaveCursor = SetCursor(hHourGlass); ShowCursor(TRUE); /* show it */ } #else fprintf(stderr, "replace %s, y-yes, n-no, a-all: ", filename); #ifdef AMIGA fflush(stderr); #endif fgets(answerbuf, 9, stdin); switch (answerbuf[0]) { case 'y': case 'Y': break; case 'a': case 'A': do_all = 1; break; case 'n': case 'N': default: while (ReadByte(&holder)); return 1; /* it's done! */ } #endif } } #if defined(UNIX) && !defined(AMIGA) { int mask; if (already_exists >= 0 && unlink(filename) < 0) fprintf(stderr, "Can't unlink %s\n", filename); /* So we own it */ mask = umask(0); outfd = creat(filename, 0777 & f_attr); /* Unix */ umask(mask); } #else /* !UNIX || AMIGA */ /* Some Unix archives yield impossible f_attr's !!! Also, creating a file read-only makes absolutely no sense here because we immediately close it and then open it using open() and O_RDWR which could not work then. */ outfd = creat(filename, (S_IWRITE | S_IREAD) /* & f_attr */); /* PCs */ #endif /* ?(UNIX && !AMIGA) */ #else /* VMS */ outfd = creat(filename, 0, "rfm=stmlf", "rat=cr"); /* VMS */ #endif /* ?VMS */ if (outfd < 1) { fprintf(stderr, "Can't create output file: %s\n", filename); return 1; } /* * close the newly created file and reopen it in BINARY mode to * disable all CR/LF translations */ #ifndef UNIX #ifdef THINK_C /* * THINKC's stdio routines have the horrible habit of * making any file you open look like generic files * this code tells the OS that it's a text file */ if (aflag) { fileParam pb; OSErr err; CtoPstr(filename); pb.ioNamePtr = filename; pb.ioVRefNum = 0; pb.ioFVersNum = 0; pb.ioFDirIndex = 0; err = PBGetFInfo(&pb,0); if (err == noErr) { pb.ioFlFndrInfo.fdCreator = 0x3F3F3F3F; pb.ioFlFndrInfo.fdType = 'TEXT'; err = PBSetFInfo(&pb, 0); } PtoCstr(filename); } #endif /* THINK_C */ if (!aflag) { close(outfd); outfd = open(filename, O_RDWR | O_BINARY); } #endif /* !UNIX */ if (outfd < 1) { fprintf(stderr, "Can't open output: %s\n", filename); return 1; } return 0; } /*****************************/ /* Function FillBitBuffer() */ /*****************************/ int FillBitBuffer(bits) register int bits; { /* * Get the bits that are left and read the next UWORD. This * function is only used by the READBIT macro (which is used * by all of the uncompression routines). */ register int result = bitbuf; UWORD temp; int sbits = bits_left; bits -= bits_left; /* read next UWORD of input */ bits_left = ReadByte(&bitbuf); bits_left += ReadByte(&temp); bitbuf |= (temp << 8); if (bits_left == 0) zipeof = 1; /* get the remaining bits */ result = result | (int) ((bitbuf & mask_bits[bits]) << sbits); bitbuf >>= bits; bits_left -= bits; return result; } /************************/ /* Function ReadByte() */ /************************/ int ReadByte(x) UWORD *x; { /* * read a byte; return 8 if byte available, 0 if not */ if (csize-- <= 0) return 0; if (incnt == 0) { if ((incnt = read(zipfd, inbuf, INBUFSIZ)) <= 0) return 0; /* buffer ALWAYS starts on a block boundary: */ cur_zipfile_bufstart += INBUFSIZ; inptr = inbuf; } *x = *inptr++; --incnt; return 8; } #ifdef FLUSH_AND_WRITE /***************************/ /* Function FlushOutput() */ /***************************/ int FlushOutput() { /* return PK-type error code */ /* flush contents of output buffer */ /* * This combined version doesn't work, and I sure can't see why not... * probably something stupid, but how much can you screw up in 6 lines??? * [optimization problem??] */ int len; if (outcnt) { UpdateCRC(outbuf, outcnt); if (!tflag) { if (aflag) len = dos2unix(outbuf, outcnt); #ifdef MSWIN if (_lwrite(outfd, outout, len) != len) { #else if (write(outfd, outout, len) != len) { #endif fprintf(stderr, "Fatal write error.\n"); return (50); /* 50: disk full */ } } outpos += outcnt; outcnt = 0; outptr = outbuf; } return (0); /* 0: no error */ } #else /* separate flush and write routines */ /***************************/ /* Function FlushOutput() */ /***************************/ int FlushOutput() { /* return PK-type error code */ /* flush contents of output buffer */ if (outcnt) { UpdateCRC(outbuf, outcnt); if (!tflag && WriteBuffer(outfd, outbuf, outcnt)) return (50); /* 50: disk full */ outpos += outcnt; outcnt = 0; outptr = outbuf; } return (0); /* 0: no error */ } /***************************/ /* Function WriteBuffer() */ /***************************/ static int WriteBuffer(fd, buf, len) /* return 0 if successful, 1 if not */ int fd; unsigned char *buf; int len; { if (aflag) len = dos2unix(buf, len); #ifdef MSWIN if (cflag) /* if writing to console vs. actual file, write to Msg Window */ { WriteBufferToMsgWin(outout, len, FALSE); return 0; } if (_lwrite(fd, outout, len) != len) { #else if (write(fd, outout, len) != len) { #endif #ifdef DOS_OS2 if (!cflag) { /* ^Z treated as EOF, removed with -c */ #endif fprintf(stderr, "Fatal write error.\n"); return (1); /* FAILED */ #ifdef DOS_OS2 } #endif } return (0); } #endif /************************/ /* Function dos2unix() */ /************************/ static int dos2unix(buf, len) unsigned char *buf; int len; { int new_len; int i; #ifdef MSWIN unsigned char _far *walker; #else unsigned char *walker; #endif new_len = len; walker = outout; #ifdef MACOS /* * Mac wants to strip LFs instead CRs from CRLF pairs */ if (CR_flag && *buf == LF) { buf++; new_len--; len--; CR_flag = buf[len] == CR; } else CR_flag = buf[len - 1] == CR; for (i = 0; i < len; i += 1) { *walker++ = ascii_to_native(*buf); if (*buf == LF) walker[-1] = CR; if (*buf++ == CR && *buf == LF) { new_len--; buf++; i++; } } #else if (CR_flag && *buf != LF) *walker++ = ascii_to_native(CR); CR_flag = buf[len - 1] == CR; for (i = 0; i < len; i += 1) { *walker++ = ascii_to_native(*buf); if (*buf++ == CR && *buf == LF) { new_len--; walker[-1] = ascii_to_native(*buf++); i++; } } /* * If the last character is a CR, then "ignore it" for now... */ if (walker[-1] == ascii_to_native(CR)) new_len--; #endif return new_len; } #ifdef DOS_OS2 /***************************************/ /* Function set_file_time_and_close() */ /***************************************/ void set_file_time_and_close() /* * MS-DOS AND OS/2 VERSION (Mac, Unix/VMS versions are below) * * Set the output file date/time stamp according to information from the * zipfile directory record for this member, then close the file. This * is optional and can be deleted if your compiler does not easily support * setftime(). */ { /*--------------------------------------------------------------------------- Allocate local variables needed by OS/2 and Turbo C. [OK, OK, so it's a bogus comment...but this routine was getting way too cluttered and needed some visual separators. Bleah.] ---------------------------------------------------------------------------*/ #ifdef OS2 /* (assuming only MSC or MSC-compatible compilers * for this part) */ union { FDATE fd; /* system file date record */ UWORD zdate; /* date word */ } ud; union { FTIME ft; /* system file time record */ UWORD ztime; /* time word */ } ut; FILESTATUS fs; #else /* !OS2 */ #ifdef __TURBOC__ union { struct ftime ft; /* system file time record */ struct { UWORD ztime; /* date and time words */ UWORD zdate; /* .. same format as in .ZIP file */ } zt; } td; #endif /* __TURBOC__ */ #endif /* !OS2 */ /*--------------------------------------------------------------------------- Do not attempt to set the time stamp on standard output. ---------------------------------------------------------------------------*/ if (cflag) { close(outfd); return; } /*--------------------------------------------------------------------------- Copy and/or convert time and date variables, if necessary; then set the file time/date. ---------------------------------------------------------------------------*/ #ifdef OS2 DosQFileInfo(outfd, 1, &fs, sizeof(fs)); ud.zdate = lrec.last_mod_file_date; fs.fdateLastWrite = ud.fd; ut.ztime = lrec.last_mod_file_time; fs.ftimeLastWrite = ut.ft; DosSetFileInfo(outfd, 1, (PBYTE) &fs, sizeof(fs)); #else /* !OS2 */ #ifdef __TURBOC__ td.zt.ztime = lrec.last_mod_file_time; td.zt.zdate = lrec.last_mod_file_date; setftime(outfd, &td.ft); #else /* !__TURBOC__: MSC MS-DOS */ _dos_setftime(outfd, lrec.last_mod_file_date, lrec.last_mod_file_time); #endif /* !__TURBOC__ */ #endif /* !OS2 */ /*--------------------------------------------------------------------------- And finally we can close the file...at least everybody agrees on how to do *this*. I think... ---------------------------------------------------------------------------*/ close(outfd); } #else /* !DOS_OS2 */ #ifdef MACOS /* Mac first */ /***************************************/ /* Function set_file_time_and_close() */ /***************************************/ void set_file_time_and_close() /* * MAC VERSION * * First close the output file, then set its date/time stamp according * to information from the zipfile directory record for this file. [So * technically this should be called "close_file_and_set_time()", but * this way we can use the same prototype for either case, without extra * #ifdefs. So there.] */ { long m_time; DateTimeRec dtr; ParamBlockRec pbr; OSErr err; if (outfd != 1) { close(outfd); /* * Macintosh bases all file modification times on the number of seconds * elapsed since Jan 1, 1904, 00:00:00. Therefore, to maintain * compatibility with MS-DOS archives, which date from Jan 1, 1980, * with NO relation to GMT, the following conversions must be made: * the Year (yr) must be incremented by 1980; * and converted to seconds using the Mac routine Date2Secs(), * almost similar in complexity to the Unix version :-) * J. Lee */ dtr.year = (((lrec.last_mod_file_date >> 9) & 0x7f) + 1980); /* dissect date */ dtr.month = ((lrec.last_mod_file_date >> 5) & 0x0f); dtr.day = (lrec.last_mod_file_date & 0x1f); dtr.hour = ((lrec.last_mod_file_time >> 11) & 0x1f); /* dissect time */ dtr.minute = ((lrec.last_mod_file_time >> 5) & 0x3f); dtr.second = ((lrec.last_mod_file_time & 0x1f) * 2); Date2Secs(&dtr, &m_time); CtoPstr(filename); pbr.fileParam.ioNamePtr = filename; pbr.fileParam.ioVRefNum = pbr.fileParam.ioFVersNum = pbr.fileParam.ioFDirIndex = 0; err = PBGetFInfo(&pbr, 0L); pbr.fileParam.ioFlMdDat = pbr.fileParam.ioFlCrDat = m_time; if (err == noErr) { err = PBSetFInfo(&pbr, 0L); } if (err != noErr) { printf("Error, can't set the time for %s\n",filename); } /* set read-only perms if needed */ if (err != noErr && f_attr != 0) { err = SetFLock(filename, 0); } PtoCstr(filename); } } /* #elif defined(AMIGA) */ #else #ifdef AMIGA #include void set_file_time_and_close() { /* This routine is derived from the unix routine. In AmigaDos, the * counting begins 01-Jan-1978 */ long m_time; int yr, mo, dy, hh, mm, ss, leap, days = 0; struct DateStamp myadate; if (cflag) /* can't set time on stdout */ return; close(outfd); yr = (((lrec.last_mod_file_date >> 9) & 0x7f) + 2); /* dissect date */ mo = ((lrec.last_mod_file_date >> 5) & 0x0f); dy = ((lrec.last_mod_file_date & 0x1f) - 1); hh = ((lrec.last_mod_file_time >> 11) & 0x1f); /* dissect time */ mm = ((lrec.last_mod_file_time >> 5) & 0x3f); ss = ((lrec.last_mod_file_time & 0x1f) * 2); /* leap = # of leap years from 1978 up to but not including the current year */ leap = ((yr + 1977) / 4); /* Leap year base factor */ /* How many days from 1978 to this year? */ days = (yr * 365) + (leap - 492); switch (mo) { /* calculate expired days this year */ case 12: days += 30; case 11: days += 31; case 10: days += 30; case 9: days += 31; case 8: days += 31; case 7: days += 30; case 6: days += 31; case 5: days += 30; case 4: days += 31; case 3: days += 28; /* account for leap years (2000 IS one) */ if (((yr + 1978) % 4 == 0) && (yr + 1978) != 2100) /* OK thru 2199 */ ++days; case 2: days += 31; } myadate.ds_Days = days+dy-2; myadate.ds_Minute = hh*60+mm; myadate.ds_Tick = ss*TICKS_PER_SECOND; if (!(SetFileDate(filename, &myadate))) fprintf(stderr, "Error, can't set the time for %s\n",filename); } #else /* !MACOS... */ #ifndef MTS /* && !MTS (can't do): only one left is UNIX */ /***************************************/ /* Function set_file_time_and_close() */ /***************************************/ void set_file_time_and_close() /* * UNIX AND VMS VERSION (MS-DOS & OS/2, Mac versions are above) * * First close the output file, then set its date/time stamp according * to information from the zipfile directory record for this file. [So * technically this should be called "close_file_and_set_time()", but * this way we can use the same prototype for either case, without extra * #ifdefs. So there.] */ { long m_time; int yr, mo, dy, hh, mm, ss, leap, days = 0; #ifdef VMS char timbuf[24]; static char *month[] = {"JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"}; struct VMStimbuf { char *actime; /* VMS revision date, ASCII format */ char *modtime; /* VMS creation date, ASCII format */ } ascii_times; #else /* !VMS */ struct utimbuf { time_t atime; /* New access time */ time_t mtime; /* New modification time */ } tp; #ifdef BSD static struct timeb tbp; #else /* !BSD */ #ifdef AMIGA extern char *_TZ; #else /* !AMIGA */ extern long timezone; #endif /* ?AMIGA */ #endif /* ?BSD */ #endif /* ?VMS */ close(outfd); if (cflag) /* can't set time on stdout */ return; /* * These date conversions look a little weird, so I'll explain. * UNIX bases all file modification times on the number of seconds * elapsed since Jan 1, 1970, 00:00:00 GMT. Therefore, to maintain * compatibility with MS-DOS archives, which date from Jan 1, 1980, * with NO relation to GMT, the following conversions must be made: * the Year (yr) must be incremented by 10; * the Date (dy) must be decremented by 1; * and the whole mess must be adjusted by TWO factors: * relationship to GMT (ie.,Pacific Time adds 8 hrs.), * and whether or not it is Daylight Savings Time. * Also, the usual conversions must take place to account for leap years, * etc. * C. Seaman */ yr = ((lrec.last_mod_file_date >> 9) & 0x7f) + 10; /* dissect date */ mo = (lrec.last_mod_file_date >> 5) & 0x0f; dy = (lrec.last_mod_file_date & 0x1f) - 1; hh = (lrec.last_mod_file_time >> 11) & 0x1f; /* dissect time */ mm = (lrec.last_mod_file_time >> 5) & 0x3f; ss = (lrec.last_mod_file_time & 0x1f) * 2; #ifdef VMS sprintf(timbuf, "%02d-%3s-%04d %02d:%02d:%02d.00", dy+1, month[mo-1], yr+1970, hh, mm, ss); ascii_times.actime = timbuf; ascii_times.modtime = timbuf; if ((mm = VMSmunch(filename, SET_TIMES, &ascii_times)) != RMS$_NMF) fprintf(stderr, "error %d: can't set the time for %s\n", mm, filename); #else /* !VMS */ /* leap = # of leap years from 1970 up to but not including the current year */ leap = ((yr + 1969) / 4); /* Leap year base factor */ /* How many days from 1970 to this year? */ days = (yr * 365) + (leap - 492); switch (mo) { /* calculate expired days this year */ case 12: days += 30; case 11: days += 31; case 10: days += 30; case 9: days += 31; case 8: days += 31; case 7: days += 30; case 6: days += 31; case 5: days += 30; case 4: days += 31; case 3: days += 28; /* account for leap years (2000 IS one) */ if (((yr + 1970) % 4 == 0) && (yr + 1970) != 2100) /* OK thru 2199 */ ++days; case 2: days += 31; } /* convert date & time to seconds relative to 00:00:00, 01/01/1970 */ m_time = ((days + dy) * 86400) + (hh * 3600) + (mm * 60) + ss; #ifdef BSD ftime(&tbp); m_time += tbp.timezone * 60L; #else /* !BSD */ #ifdef AMIGA _TZ = getenv("TZ"); #endif tzset(); /* Set `timezone'. */ m_time += timezone; /* account for timezone differences */ #endif /* ?BSD */ if (localtime(&m_time)->tm_isdst) m_time -= 60L * 60L; /* Adjust for daylight savings time */ /* set the time stamp on the file */ tp.mtime = m_time; /* Set modification time */ tp.atime = m_time; /* Set access time */ if (utime(filename, &tp)) fprintf(stderr, "error: can't set the time for %s\n",filename); #endif /* ?VMS */ } #endif /* ?MTS */ #endif /* ?MACOS */ #endif /* ?AMIGA */ #endif /* ?DOS_OS2 */