home *** CD-ROM | disk | FTP | other *** search
- /* pkzip.c - Handle all PKZip-file specific tasks
- ** Copyright (C) 1997,1998 Karl J. Ots
- **
- ** This program is free software; you can redistribute it and/or modify
- ** it under the terms of the GNU General Public License as published by
- ** the Free Software Foundation; either version 2 of the License, or
- ** (at your option) any later version.
- **
- ** This program is distributed in the hope that it will be useful,
- ** but WITHOUT ANY WARRANTY; without even the implied warranty of
- ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ** GNU General Public License for more details.
- **
- ** You should have received a copy of the GNU General Public License
- ** along with this program; if not, write to the Free Software
- ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
- /*---------------------*/
- /* PKZip file routines */
- /*---------------------*/
-
- #include <exec/types.h>
- #include <dos/dos.h>
- #include <clib/dos_protos.h>
-
- #include <stdlib.h>
- #include <string.h>
- #include <ctype.h>
-
- #include "pkzip.h"
- #include "main.h"
- #include "util.h"
- #include "errors.h"
-
- /* Private functions */
- LONG GotoFirstField (BPTR file, ULONG FieldID);
-
-
- /*----------------------------------*/
- /* Constants, structures and macros */
- /*----------------------------------*/
-
- extern const char ZipComment[]; /* Defined in version.c */
-
- /* PKZip Local Header structure */
- struct PKZHead {
- ULONG MagicNum; /* Magic Number, = 0x504B0304 ('P','K',3,4). */
- UBYTE MinVer; /* Min. required UnZip version (20 for defl). */
- UBYTE MinOS; /* Min. requires OS, MS-DOS = 0. */
- UWORD Flags; /* Flags. */
- UWORD CMethod; /* Compression method, deflate = 8. */
- ULONG Date; /* Last moddification date. */
- ULONG CRC; /* Uncompressed Data CRC. */
- ULONG CSize; /* Compressed data length. */
- ULONG USize; /* Uncompressed data length. */
- UWORD FNameLen; /* File name length. */
- UWORD EFieldLen; /* Extra field length. */
- }; /* sizeof = 30 */
-
-
- /* PKZip Central Record structure */
- struct PKZCenRec {
- ULONG MagicNum; /* Magic Number, = 0x504B0102 ('P','K',1,2). */
- UBYTE ZipVer; /* Zip version number x10 (ie 20 = v2.0). */
- UBYTE HostOS; /* Host OS, Amiga = 1. */
- UBYTE MinVer; /* Min. required UnZip version (20 for defl). */
- UBYTE MinOS; /* Min. requires OS, MS-DOS = 0. */
- UWORD Flags; /* Flags. */
- UWORD CMethod; /* Compression method, deflate = 8. */
- ULONG Date; /* Last moddification date. */
- ULONG CRC; /* Uncompressed Data CRC. */
- ULONG CSize; /* Compressed data length. */
- ULONG USize; /* Uncompressed data length. */
- UWORD FNameLen; /* File name length. */
- UWORD EFieldLen; /* Extra field length. */
- UWORD FCommentLen; /* File comment length. */
- UWORD DiskNum; /* Disk number in multi-part zip. */
- UWORD FileType; /* File Type, set bit 0 for text. */
- UWORD MSDAttrib; /* MS-DOS File Attributes. */
- UWORD Attrib; /* File Attributes, Amiga rw-d = 0x0D04 (LE). */
- ULONG LHeadOff; /* Offset of local header. */
- }; /* sizeof = 46 */
-
-
- struct PKZEndCRec {
- ULONG MagicNum; /* Magic Number, = 0x504B0506 ('P','K',5,6). */
- UWORD DiskNum; /* Number of this disk in multi-disk zip. */
- UWORD CentRecDisk; /* Disk number with start of Central Record. */
- UWORD EntriesOnDisk; /* Number of entries on this disk. */
- UWORD CenRecEntries; /* Total number of Central Record Entries. */
- ULONG CenRecSize; /* Total Size of Central Record. */
- ULONG CenRecOffset; /* Offset of start of Central Record. */
- UWORD CommentLen; /* Length of Zip Comment. */
- }; /* sizeof = 22 */
-
-
- #define LHD_MAGNUM 0x504B0304 /* Local Header Magic Number */
- #define CRC_MAGNUM 0x504B0102 /* Central Record Magic Number */
- #define ECR_MAGNUM 0x504B0506 /* End of Central Record Magic Number */
-
-
- /* Used to store the date/time that compression started */
- ULONG startDate;
-
- /* Used to store the location of the `current' PKZip header */
- LONG pkzHeadOffset;
- STRPTR pkzOrigName;
-
- /* Used to save the central record of archive when adding */
- ULONG Adding; /* Set to 0x12345678 if adding, cleared afterwards */
- ULONG pkzOldCenRecSize;
- APTR pkzOldCenRec;
-
- /* End of central record - one per zip archive */
- struct PKZEndCRec pkzECRec;
-
-
- /*
- ** Output a PKZip Header to the specified file.
- ** Return TRUE if no errors. else FALSE.
- */
- BOOL writePKZHead (BPTR outFile, STRPTR origName)
- {
- UBYTE Empty[30];
- UWORD nlen;
-
- /* Simply write an empty sizeof (struct PKZHead) + strlen (origName). */
- /* We'll fill in the details later. */
-
- /* Note the date and time */
- startDate = dosDate();
-
- /* Make sure we have a name */
- if (!origName) origName = "disk.adf";
-
- /* Save the current position and name */
- pkzHeadOffset = Seek (outFile, 0, OFFSET_CURRENT);
- pkzOrigName = strdup (origName);
-
- /* Write the `header' and the name to the file */
- if ( Write (outFile, Empty, 30) != 30) return FALSE;
-
- /* Get the filename length and write the filename */
- nlen = strlen (origName);
- if ( Write (outFile, origName, nlen) != nlen) return FALSE;
-
- /* PKZip file is now ready to recieve the compressed data */
- return TRUE;
- }
-
-
- /*
- ** Finish writing a PKZip file.
- ** Return TRUE if no errors. else FALSE.
- */
- BOOL finishPKZFile (BPTR outFile, ULONG CRC, ULONG CSize, ULONG USize)
- {
- LONG CRecPos;
- ULONG Date;
- UWORD FNameLen, ZCommentLen;
- /* Local header - one per zipped file */
- struct PKZHead pkzHead = {LHD_MAGNUM, /* PKZip Magic Number */
- 20, /* Min. UnZip version (2.0) */
- 0, /* Min. OS (MS-DOS) */
- 0, /* Flags */
- 0x0800, /* Compression Method (LE) */
- 0, /* Date (to be filled in) */
- 0, /* CRC (to be filled in) */
- 0, /* CSize (to be filled in) */
- 0, /* USize (to be filled in) */
- 0, /* Filename length (tbfi) */
- 0}; /* Extra field length */
- /* Central record entry - one per zipped file */
- struct PKZCenRec pkzCenRec = {CRC_MAGNUM, /* Central Rec Magic Number */
- 20, /* Zip version (2.0) */
- 1, /* Host OS (Amiga) */
- 20, /* Min. UnZip version (2.0) */
- 0, /* Min. Host OS (MS-DOS) */
- 0, /* Flags */
- 0x0800, /* Compression Mode (LE) */
- 0, /* Date (to be filled in) */
- 0, /* CRC (to be filled in) */
- 0, /* CSize (to be filled in) */
- 0, /* USize (to be filled in) */
- 0, /* File Name Length (tbfi) */
- 0, /* Extra field length */
- 0, /* File comment length */
- 0, /* Disk Number */
- 0, /* File Type (binary) */
- 0, /* MS-DOS file attributes */
- 0x0D04, /* File Attributes (rw-d) */
- 0}; /* Local header offset (tbfi) */
-
-
- /* Seek to the local header and save the `current' position */
- CRecPos = Seek (outFile, pkzHeadOffset, OFFSET_BEGINNING);
-
- /* 'pre-process' CRC, CSize and USize */
- CRC = LEL (CRC);
- CSize = LEL (CSize);
- USize = LEL (USize);
- Date = LEL (startDate);
- FNameLen = strlen (pkzOrigName);
- ZCommentLen = strlen (ZipComment);
-
- /* Fill the structures */
- pkzHead.Date = Date;
- pkzHead.CRC = CRC;
- pkzHead.CSize = CSize;
- pkzHead.USize = USize;
- pkzHead.FNameLen = LES (FNameLen);
-
- pkzCenRec.Date = Date;
- pkzCenRec.CRC = CRC;
- pkzCenRec.CSize = CSize;
- pkzCenRec.USize = USize;
- pkzCenRec.FNameLen = LES (FNameLen);
- pkzCenRec.LHeadOff = LEL (pkzHeadOffset);
-
- /* Fill the End of Central Record structure */
- pkzECRec.MagicNum = ECR_MAGNUM;
- pkzECRec.DiskNum = 0;
- pkzECRec.CentRecDisk = 0;
- pkzECRec.CenRecOffset = LEL (CRecPos);
- pkzECRec.CommentLen = LES (ZCommentLen);
- if (Adding == 0x12345678)
- {
- pkzECRec.EntriesOnDisk = LES ( LES (pkzECRec.EntriesOnDisk) + 1);
- pkzECRec.CenRecEntries = LES ( LES (pkzECRec.CenRecEntries) + 1);
- pkzECRec.CenRecSize = LEL (pkzOldCenRecSize + 46 + FNameLen);
- }
- else
- {
- pkzECRec.EntriesOnDisk = LES (1);
- pkzECRec.CenRecEntries = LES (1);
- pkzECRec.CenRecSize = LEL (46 + FNameLen);
- }
-
- /* Write the Local Header and seek to the Central Record offset */
- if ( Write (outFile, &pkzHead, 30) != 30) return FALSE;
- /* Write (outFile, pkzOrigName, FNameLen); */ /* Already done */
- Seek (outFile, CRecPos, OFFSET_BEGINNING);
-
- if (Adding == 0x12345678)
- {
- /* Write the old central record before adding the new one */
- if ( Write (outFile, pkzOldCenRec, pkzOldCenRecSize) != pkzOldCenRecSize)
- return FALSE;
- }
-
- /* Write the Central Record and origName */
- if ( Write (outFile, &pkzCenRec, 46) != 46) return FALSE;
- if ( Write (outFile, pkzOrigName, FNameLen) != FNameLen) return FALSE;
-
- /* Write the End of Central Record and File Comment */
- if ( Write (outFile, &pkzECRec, 22) != 22) return FALSE;
- if ( Write (outFile, ZipComment, ZCommentLen) != ZCommentLen) return FALSE;
-
- /* Don't need the name anymore */
- free (pkzOrigName);
-
- /* Don't need the old record anymore */
- if (Adding == 0x123456780) free (pkzOldCenRec);
-
- /* No longer adding */
- Adding = 0;
-
- /* Done! */
- return TRUE;
- }
-
-
- /*
- ** Output a PKZip Header to the specified file, adding
- ** it to an existing archive.
- ** Return TRUE if no errors. else FALSE.
- */
- BOOL writePKZHeadAdd (BPTR outFile, STRPTR origName)
- {
- LONG DOSError;
-
- /* We're adding */
- Adding = 0x12345678;
- FPuts (StdOut, "Updating archive.\n");
-
- /* Get the End of Central Record */
- if ( GotoFirstField (outFile, ECR_MAGNUM) == -1)
- {
- DOSError = IoErr();
-
- if (DOSError)
- {
- FPrintf (StdErr, "%s: Error reading file - ", ProgName);
- reportDOSError(DOSError);
- }
- else
- FPrintf (StdErr, "%s: Can't add ADF, Not a Zip file.\n", ProgName);
-
- cleanExit (RETURN_ERROR, NULL);
- }
- if ( Read (outFile, &pkzECRec, 22) != 22) return FALSE;
-
- /* We'll be adding this header at the current position of the Cent Rec */
- pkzHeadOffset = LEL (pkzECRec.CenRecOffset);
-
- /* Save the current central record for re-writing later */
- pkzOldCenRecSize = LEL (pkzECRec.CenRecSize);
- pkzOldCenRec = calloc (pkzOldCenRecSize, 1);
- if (!pkzOldCenRec)
- {
- /* Memory error */
- return FALSE;
- }
- Seek (outFile, pkzHeadOffset, OFFSET_BEGINNING);
- if ( Read (outFile, pkzOldCenRec, pkzOldCenRecSize) != pkzOldCenRecSize)
- return FALSE;
-
- /* Write our dummy header and get outa here */
- Seek (outFile, pkzHeadOffset, OFFSET_BEGINNING);
- return writePKZHead (outFile, origName);
- }
-
-
- /*
- ** Finish writing a PKZip file, adding new a new record.
- ** Return TRUE if no errors. else FALSE.
- */
- BOOL finishPKZFileAdd (BPTR outFile, ULONG CRC, ULONG CSize, ULONG USize)
- {
- return finishPKZFile (outFile, CRC, CSize, USize);
- }
-
-
- /*
- ** Skip the PKZip Header of a specified file.
- ** If origName is specified, search for that name within file.
- ** Return TRUE if no errors. else FALSE.
- */
- BOOL skipPKZHead (BPTR inFile, STRPTR origName)
- {
- UBYTE FName[128]; /* This should be large enough */
- struct PKZHead pkzHead;
- struct PKZCenRec pkzCenRec;
- ULONG origNameLen, pOrigNameLen;
- LONG DOSError;
- STRPTR pOrigName;
- UWORD nlen, elen, clen;
- int i;
-
-
- if (origName)
- {
- /* Get the first central record entry */
- if ( GotoFirstField (inFile, CRC_MAGNUM) == -1)
- {
- DOSError = IoErr();
-
- if (DOSError)
- {
- FPrintf (StdErr, "%s: Error reading file - ", ProgName);
- reportDOSError(DOSError);
- }
- else
- FPrintf (StdErr, "%s: Error - Not a Zip file.\n", ProgName);
-
- cleanExit (RETURN_ERROR, NULL);
- }
-
- /* Parse the pattern */
- origNameLen = strlen (origName);
- for (i=0; i < origNameLen; i++)
- origName[i] = toupper (origName[i]);
-
- pOrigNameLen = (2 * origNameLen) + 2;
- pOrigName = calloc (pOrigNameLen, 1);
- if (!pOrigName)
- {
- FPrintf (StdErr, "%s: Error - Not enough memory to match filename.\n",
- ProgName);
- cleanExit (RETURN_FAIL, ERROR_NO_FREE_STORE);
- }
-
- if (ParsePatternNoCase (origName, pOrigName, pOrigNameLen) == -1)
- {
- FPrintf (StdErr, "%s: Error - Can't match filename.\n", ProgName);
- cleanExit (RETURN_FAIL, NULL);
- }
-
- /* Now go through all the names and search for a match */
- while (1)
- {
- /* Read the entry */
- if ( Read (inFile, &pkzCenRec, 46) != 46) return FALSE;
-
- /* Test the current magic number */
- if (pkzCenRec.MagicNum != CRC_MAGNUM)
- {
- /* We went through the whole list without a match */
- FPrintf (StdErr, "%s: No match for filename.\n", ProgName);
- cleanExit (RETURN_WARN, NULL);
- }
-
- /* Get extra info lengths */
- nlen = LES (pkzCenRec.FNameLen);
- elen = LES (pkzCenRec.EFieldLen);
- clen = LES (pkzCenRec.FCommentLen);
-
- /* Read in the name */
- if ( Read (inFile, FName, nlen) != nlen) return FALSE;
- FName[nlen] = 0;
-
- /* Attempt to match the pattern */
- if (MatchPatternNoCase (pOrigName, FName))
- {
- /* Output information */
- FPrintf (StdOut, "Extracting %s.\n", FName);
-
- /* Got a name, seek to the start of this item */
- Seek (inFile, LEL (pkzCenRec.LHeadOff), OFFSET_BEGINNING);
-
- /* Break the loop */
- break;
- }
-
- /* No match, move along */
- Seek (inFile, elen + clen, OFFSET_CURRENT);
- }
-
- free (pOrigName);
- }
-
- /* Save the current position */
- pkzHeadOffset = Seek (inFile, 0, OFFSET_CURRENT);
-
- /* Consume the header and extra info */
- if ( Read (inFile, &pkzHead, 30) != 30) return FALSE;
- if (pkzHead.MagicNum != LHD_MAGNUM)
- {
- FPrintf (StdErr,
- "%s: Error - Didn't read a Local header - not a Zip file.\n",
- ProgName);
- cleanExit (RETURN_ERROR, NULL);
- }
- nlen = LES (pkzHead.FNameLen);
- elen = LES (pkzHead.EFieldLen);
- Seek (inFile, (nlen + elen), OFFSET_CURRENT);
-
- /* Check the compression */
- if ( LES (pkzHead.CMethod) != 8)
- {
- FPrintf (StdErr,
- "%s: Error - Only Deflate compression method supported.\n",
- ProgName);
- cleanExit (RETURN_ERROR, NULL);
- }
-
- /* inFile now points to the compressed data */
- return TRUE;
- }
-
-
- /*
- ** Read the header of a PKZip file and return the CRC
- ** and USize in supplied arrays.
- ** Return TRUE if no errors, else FALSE.
- */
- BOOL readPKZTail (BPTR inFile, ULONG *CRC, ULONG *USize)
- {
- struct PKZHead pkzHead;
- LONG oldp;
-
- /* Get the `current' header */
- oldp = Seek (inFile, pkzHeadOffset, OFFSET_BEGINNING);
- if ( Read (inFile, &pkzHead, 30) != 30) return FALSE;
- Seek (inFile, oldp, OFFSET_BEGINNING);
-
- /* Fill the return values */
- *CRC = LEL (pkzHead.CRC);
- *USize = LEL (pkzHead.USize);
-
- return TRUE;
- }
-
-
- /*
- ** Jump to the start of the first field with FieldID.
- ** Return the offset as well as setting the file pointer.
- ** Return -1 for error.
- */
- LONG GotoFirstField (BPTR file, ULONG FieldID)
- {
- UBYTE Buffer[46];
- /* Convenient `gateways' */
- struct PKZHead *pkzHead = (struct PKZHead *) Buffer;
- struct PKZCenRec *pkzCenRec = (struct PKZCenRec *) Buffer;
- ULONG item, nextSeek;
- int cont = TRUE;
-
-
- do
- {
- /* Read in an item */
- if ( Read (file, &item, 4) != 4) return -1;
-
- /* Identify item and get the rest of the field if required */
- switch (item) {
- case LHD_MAGNUM:
- /* Local header */
-
- /* Check if want to stop here */
- if (FieldID == LHD_MAGNUM)
- cont = FALSE;
- else
- {
- /* Move along */
- if ( Read (file, Buffer+4, 26) != 26) return -1; /* 26 = 30 - 4 */
- nextSeek = LES (pkzHead->FNameLen) + LES (pkzHead->EFieldLen) +
- LEL (pkzHead->CSize);
- Seek (file, nextSeek, OFFSET_CURRENT);
- }
- break;
-
- case CRC_MAGNUM:
- /* Central record entry */
-
- /* Check if want to stop here */
- if (FieldID == CRC_MAGNUM)
- cont = FALSE;
- else
- {
- /* Move along */
- if ( Read (file, Buffer+4, 42) != 42) return -1; /* 42 = 46 - 4 */
- nextSeek = LES (pkzCenRec->FNameLen) + LES (pkzCenRec->EFieldLen) +
- LES (pkzCenRec->FCommentLen);
- Seek (file, nextSeek, OFFSET_CURRENT);
- }
- break;
-
- case ECR_MAGNUM:
- /* End of central record */
-
- /* Final destination - stop here! */
- cont = FALSE;
- break;
-
- default:
- /* Can't id the field - error */
- return -1;
- }
- } while (cont);
-
- /* Seek back to the start of the current item */
- Seek (file, -4, OFFSET_CURRENT);
-
- /* return the current offset */
- return Seek (file, 0, OFFSET_CURRENT);
- }
-