home *** CD-ROM | disk | FTP | other *** search
- /******************************************************************************
- *
- *
- * ZIPV -- SHARE compatible ZIP file list utility.
- *
- *
- * This code was written as a demonstration of how to read
- * the ZIP file format. It does not attempt to support
- * ZIP files which span multiple diskettes (it is not even
- * clear to me how that would be done). It does support
- * multiple ZIP file names on the command line and does
- * support wildcards. Under DOS 3.0 or above all files are
- * opened in SHARE compatible mode.
- *
- * The output format is patterned after (stolen from) Vern
- * Buerg's ARCV format.
- *
- *
- * It appears that there are two ways to extract the names
- * of files contained in a ZIP file. One copy is kept in a
- * "local file header" which proceeds the compressed file. The
- * other copy is kept in the "central directory". The notes
- * that accompany PKZIP are not clear whether or not these
- * will always agree. I chose to read the information from
- * the central directory, even though I have to read past
- * all of the local file headers to get there.
- *
- * This code is placed in the public domain. You may do whatever
- * you like with it.
- *
- * Ken Brown
- *
- *
- *
- *
- * Syntax:
- *
- * ZIPV file_spec1[.ZIP] [file_spec2[.ZIP] ...]
- *
- *
- *
- ******************************************************************************
- */
-
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <malloc.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <bios.h>
- #include <dos.h>
- #include <io.h>
- #include <share.h>
- #include <fcntl.h>
-
-
- /* These structures must be byte aligned */
-
- #pragma pack(1)
-
- /* Structure of the local file header */
-
- struct LOCAL_HEADER {
- long Signature;
- int Version;
- int BitFlag;
- int CompressionMethod;
- int FileTime;
- int FileDate;
- long CRC32;
- long CompressedSize;
- long UnCompressedSize;
- int FileNameLength;
- int ExtraFieldLength;
- };
-
- /* Structure of the central directory record */
- struct CENTRAL_RECORD {
- long Signature;
- int VersionMadeBy;
- int VersionNeeded;
- int BitFlag;
- int CompressionMethod;
- int FileTime;
- int FileDate;
- long CRC32;
- long CompressedSize;
- long UnCompressedSize;
- int FileNameLength;
- int ExtraFieldLength;
- int CommentFieldLength;
- int DiskStartNumber;
- int InternalAttributes;
- long ExternalAttributes;
- long LocalHeaderOffset;
- };
-
- #pragma pack()
-
-
-
- /*
- * This structure will be used to build a list of files
- * from a wildcard file_spec.
- *
- */
- struct FILELIST {
- char FileName[83];
- struct FILELIST *NextFile;
- }
-
-
-
-
- /* Global variables */
-
- struct FILELIST *FileList,
- *FilePtr;
-
-
-
-
- static int TotalFiles;
-
- static long TotalBytes;
- static long TotalUnCompressedBytes;
-
-
-
- /* Function prototypes */
-
- extern int main(int argc,char * *argv);
- extern int ProcessFile(char *ZipFileName);
- extern int ListZipFile(char *ZipFileName);
- extern int DisplayHeaderRecord(struct CENTRAL_RECORD CentralDirRecord,char *FileName);
- extern int DisplayTotals(void );
- extern char *DisplayCompressionType(int Compression);
- extern char *DisplayMonthName(int Month);
- extern int BuildFileList(char *FileMask);
-
-
- main(int argc, char *argv[])
- {
- int Counter;
- char ZipFileName[83],
- *StrPtr;
-
- if(argc < 2) {
- printf("Syntax: ZIPV file_spec1[.ZIP] [file_spec2[.ZIP] ...]\n");
- exit(1);
- }
-
- for(Counter = 1; Counter < argc; Counter++) {
- if(strlen(argv[Counter]) > 82) {
- printf("Syntax: ZIPV file_spec1[.ZIP] [file_spec2[.ZIP] ...]\n");
- exit(1);
- }
-
- strcpy(ZipFileName,argv[Counter]);
- strupr(ZipFileName);
-
- /* If the .ZIP extension is missing add it. */
-
- if(strchr(ZipFileName,'.') == NULL) {
- strcat(ZipFileName,".ZIP");
- }
-
- ProcessFile(ZipFileName);
- }
-
- exit(0);
-
-
- }
-
- /* Process each command line argument (may contain a wildcard). */
-
- ProcessFile(char *ZipFileName)
- {
-
- FileList = NULL;
-
- /* Build a list of file names matching the file_spec. */
-
- BuildFileList(ZipFileName);
-
- /* If FileList == NULL no matching files were found. */
-
- if(FileList == NULL) {
- if(strchr(ZipFileName,'*') == NULL && strchr(ZipFileName,'?') == NULL) {
- printf("File not found: %s\n\n",ZipFileName);
- }
- else {
- printf("No matching files found: %s\n\n",ZipFileName);
- }
- }
- else {
- FilePtr = FileList;
- while(FilePtr != NULL) {
- ListZipFile(FilePtr->FileName);
- FilePtr = FilePtr->NextFile;
- }
- }
-
- return 0;
-
- }
-
-
- ListZipFile(char *ZipFileName)
- {
- int ZipFileHandle,
- BytesRead;
-
- long FileOffset;
-
- char *FileName,
- *ReadPtr;
-
- struct LOCAL_HEADER LocalFileHeader;
-
- struct CENTRAL_RECORD CentralDirRecord;
-
-
- /* Open the ZIP file. Use sopen() if possible. */
-
- if(_osmajor >= 3) {
- ZipFileHandle = sopen(ZipFileName,O_RDONLY|O_BINARY,SH_DENYWR);
-
- if(ZipFileHandle == -1) {
- printf("File is currently locked, cannot open: %s\n\n",ZipFileName);
- return 0;
- }
- }
- else {
- ZipFileHandle = open(ZipFileName,O_RDONLY|O_BINARY);
-
- if(ZipFileHandle == -1) {
- printf("Cannot open: %s\n\n",ZipFileName);
- return 0;
- }
- }
-
-
-
- printf("ZIP File: %s\n",ZipFileName);
- printf("\n");
-
- TotalFiles = 0;
- TotalBytes = 0L;
- TotalUnCompressedBytes = 0L;
-
- printf("Name Length Stowage SF Size now Date Time CRC \n");
- printf("============ ======== ======== ==== ======== ========= ====== ========\n");
-
-
- /* Read the signature from the first local header */
-
- BytesRead = read(ZipFileHandle,(char *)&LocalFileHeader,sizeof(long));
-
- if(BytesRead != sizeof(long)) {
- printf("Not a ZIP file\n\n");
- close(ZipFileHandle);
- return 0;
- }
- if(LocalFileHeader.Signature != 0x04034b50) {
- printf("Not a ZIP file\n\n");
- close(ZipFileHandle);
- return 0;
- }
-
- /* Skip over all of the compressed files and get to the central
- directory */
-
- while(1) {
-
- ReadPtr = (char *)&LocalFileHeader;
- ReadPtr += 4;
-
- /* Read the remainder of the local file header (we already read
- the signature. */
-
- BytesRead = read(ZipFileHandle,ReadPtr,sizeof(struct LOCAL_HEADER)-4);
-
- if(BytesRead != sizeof(struct LOCAL_HEADER) - 4) {
- printf("Invalid ZIP file format\n\n");
- close(ZipFileHandle);
- return 0;
- }
-
- FileOffset = LocalFileHeader.FileNameLength +
- LocalFileHeader.ExtraFieldLength +
- LocalFileHeader.CompressedSize;
-
- /* Jump to the next local file header */
-
- if(lseek(ZipFileHandle,FileOffset,SEEK_CUR) == -1) {
- printf("Invalid ZIP file format\n\n");
- close(ZipFileHandle);
- return 0;
- }
-
- /* Read the next signature */
-
- BytesRead = read(ZipFileHandle,(char *)&LocalFileHeader,sizeof(long));
- if(BytesRead != sizeof(long)) {
- printf("Invalid ZIP file format\n\n");
- close(ZipFileHandle);
- return 0;
- }
-
- /* If we get a match we have found the beginning of the central
- directory. */
-
- if(LocalFileHeader.Signature == 0x02014b50) {
- break;
- }
-
- }
-
-
- CentralDirRecord.Signature = LocalFileHeader.Signature;
-
- /* Read the records in the central directory one at a time */
-
- while(1) {
-
- /* Read the remainder of the file header record (we already
- have the signature. ReadPtr points into CentralDirRecord
- 4 bytes from the beginning (right behind the signature. */
-
- ReadPtr = (char *)&CentralDirRecord;
- ReadPtr += 4;
-
- BytesRead = read(ZipFileHandle,ReadPtr,sizeof(struct CENTRAL_RECORD)-4);
-
- if(BytesRead != sizeof(struct CENTRAL_RECORD) - 4) {
- printf("Invalid ZIP file format\n\n");
- close(ZipFileHandle);
- return 0;
- }
-
- /* It is probably not possible for the FileName to have a length
- of 0 but who knows. */
-
- if(CentralDirRecord.FileNameLength > 0) {
-
- /* The file name can be long so allocate space for the name as
- needed rather that write to a statically allocated array */
-
- if((FileName = malloc(CentralDirRecord.FileNameLength + 1)) == NULL) {
- printf("Out of memory\n\n");
- exit(1);
- }
-
- /* Read the file name. */
-
- BytesRead = read(ZipFileHandle,FileName,CentralDirRecord.FileNameLength);
-
- if(BytesRead != CentralDirRecord.FileNameLength) {
- printf("Invalid ZIP file format\n\n");
- close(ZipFileHandle);
- return 0;
- }
-
- /* Add the trailing \0 byte. */
-
- FileName[CentralDirRecord.FileNameLength] = '\0';
-
- /* Display the record. */
-
- DisplayHeaderRecord(CentralDirRecord,FileName);
-
- /* The file name is not needed any more. */
-
- free(FileName);
- }
-
-
- /* Skip over the extra field and the comment field */
-
- FileOffset = CentralDirRecord.ExtraFieldLength +
- CentralDirRecord.CommentFieldLength;
-
- if(FileOffset > 0L) {
- if(lseek(ZipFileHandle,FileOffset,SEEK_CUR) == -1) {
- printf("Invalid ZIP file format\n\n");
- close(ZipFileHandle);
- return 0;
- }
- }
-
-
- /* Read the signature from the next record. */
-
- BytesRead = read(ZipFileHandle,(char *)&CentralDirRecord,sizeof(long));
- if(BytesRead != sizeof(long)) {
- printf("Invalid ZIP file format\n\n");
- close(ZipFileHandle);
- return 0;
- }
-
- /* If we get a match then we found the "End of central dir record"
- and we are done. */
-
- if(CentralDirRecord.Signature == 0x06054b50) {
- break;
- }
-
- }
-
- close(ZipFileHandle);
-
- DisplayTotals();
-
- return 0;
-
- }
-
-
- DisplayHeaderRecord(struct CENTRAL_RECORD CentralDirRecord, char *FileName)
- {
- int Month,
- Day,
- Year,
- Hour,
- Minutes,
- StowageFactor;
- char *NamePtr;
- long SizeReduction;
-
- if(CentralDirRecord.UnCompressedSize == 0) {
- StowageFactor = 0;
- }
- else {
- if(CentralDirRecord.CompressedSize <= CentralDirRecord.UnCompressedSize) {
- SizeReduction = CentralDirRecord.UnCompressedSize - CentralDirRecord.CompressedSize;
- if(SizeReduction == 0L) {
- StowageFactor = 0;
- }
- else {
- SizeReduction = SizeReduction * 100L + 50;
- StowageFactor = (int)(SizeReduction/CentralDirRecord.UnCompressedSize);
- }
- }
- else {
- SizeReduction = CentralDirRecord.CompressedSize - CentralDirRecord.UnCompressedSize;
- SizeReduction = SizeReduction * 100L + 50;
- StowageFactor = (int)(SizeReduction/CentralDirRecord.UnCompressedSize);
- StowageFactor *= -1;
- }
- }
- if(StowageFactor >= 100) {
- StowageFactor = 0;
- }
-
-
- /* Convert the DOS internal date and time format to something we can
- use for output. */
-
- Month = (CentralDirRecord.FileDate >> 5) & 0x0f;
- Day = CentralDirRecord.FileDate & 0x1f;
- Year = ((CentralDirRecord.FileDate >> 9) & 0x7f) + 80;
-
- if(CentralDirRecord.FileTime > 0) {
- Hour = (CentralDirRecord.FileTime >> 11) & 0x1f;
- Minutes = (CentralDirRecord.FileTime >> 5) & 0x3f;
- }
- else {
- Hour = 0;
- Minutes = 0;
- }
-
- /* The ZIP documentation says that path names are stored with '/'s
- rather than '\'s. Look for a '/' and if so point to the file
- name rather than the whole path name. */
-
- if((NamePtr = strrchr(FileName,'/')) == NULL) {
- NamePtr = FileName;
- }
-
- printf("%-14s%8ld %s %2d%% %8ld %02d %s %2d %02d:%02d %08lX\n",
- NamePtr,
- CentralDirRecord.UnCompressedSize,
- DisplayCompressionType(CentralDirRecord.CompressionMethod),
- StowageFactor,
- CentralDirRecord.CompressedSize,
- Day,
- DisplayMonthName(Month),
- Year,
- Hour,
- Minutes,
- CentralDirRecord.CRC32);
-
- TotalFiles += 1;
- TotalBytes += CentralDirRecord.CompressedSize;
- TotalUnCompressedBytes += CentralDirRecord.UnCompressedSize;
-
- return 0;
- }
-
- DisplayTotals()
- {
- int StowageFactor;
- long SizeReduction;
-
- printf("============ ======== ======== ==== ======== ========= ====== ========\n");
-
- if(TotalUnCompressedBytes == 0) {
- StowageFactor = 0;
- }
- else {
- if(TotalBytes <= TotalUnCompressedBytes) {
- SizeReduction = TotalUnCompressedBytes - TotalBytes;
- if(SizeReduction == 0L) {
- StowageFactor = 0;
- }
- else {
- SizeReduction = SizeReduction * 100L + 50;
- StowageFactor = (int)(SizeReduction/TotalUnCompressedBytes);
- }
- }
- else {
- SizeReduction = TotalBytes - TotalUnCompressedBytes;
- SizeReduction = SizeReduction * 100L + 50;
- StowageFactor = (int)(SizeReduction/TotalUnCompressedBytes);
- StowageFactor *= -1;
- }
- }
- if(StowageFactor >= 100) {
- StowageFactor = 0;
- }
-
- printf("*total%6d %8ld %2d%% %8ld\n\n",
- TotalFiles,
- TotalUnCompressedBytes,
- StowageFactor,
- TotalBytes);
-
- return 0;
- }
-
-
- char *DisplayCompressionType(int Compression)
- {
- switch (Compression) {
- case 0:
- return " Stored ";
- break;
- case 1:
- return " Shrunk ";
- break;
- case 2:
- return "Reduced1";
- break;
- case 3:
- return "Reduced2";
- break;
- case 4:
- return "Reduced3";
- break;
- case 5:
- return "Reduced4";
- break;
- default:
- return "Unknown ";
- break;
- }
- }
-
- char *DisplayMonthName(int Month)
- {
- switch (Month) {
- case 1:
- return "Jan";
- break;
- case 2:
- return "Feb";
- break;
- case 3:
- return "Mar";
- break;
- case 4:
- return "Apr";
- break;
- case 5:
- return "May";
- break;
- case 6:
- return "Jun";
- break;
- case 7:
- return "Jul";
- break;
- case 8:
- return "Aug";
- break;
- case 9:
- return "Sep";
- break;
- case 10:
- return "Oct";
- break;
- case 11:
- return "Nov";
- break;
- case 12:
- return "Dec";
- break;
- }
- }
-
- /*
- * BuildFileList() uses the _dos_findfirst() and _dos_findnext() routines
- * to build a linked list of all files which match FileMask.
- *
- * _dos_findfirst() is from the Microsoft C library. There is a Turbo C
- * equivalent but I don't know the name.
- *
- */
-
- BuildFileList(char *FileMask)
- {
- char FileDirectory[83],
- *StrPtr;
-
- struct find_t FileInfo;
- struct stat StatBuffer;
-
- strcpy(FileDirectory,FileMask);
- if((StrPtr = strrchr(FileDirectory,'\\')) != NULL) {
- StrPtr++;
- *StrPtr = '\0';
- }
- else {
- if((StrPtr = strrchr(FileDirectory,':')) != NULL) {
- StrPtr++;
- *StrPtr = '\0';
- }
- else {
- FileDirectory[0] = '\0';
- }
- }
-
- if(_dos_findfirst(FileMask,_A_NORMAL|_A_RDONLY,&FileInfo) == 0) {
- do {
- if(FileList == NULL) {
- FileList = calloc(sizeof(struct FILELIST),1);
-
- /* This is not likely to happen but you never know. */
-
- if(FileList == NULL) {
- printf("Out of memory\n\n");
- exit(1);
- }
- FilePtr = FileList;
- }
- else {
- FilePtr->NextFile = calloc(sizeof(struct FILELIST),1);
- if(FilePtr->NextFile == NULL) {
- printf("Out of memory\n\n");
- exit(1);
- }
- FilePtr = FilePtr->NextFile;
- }
-
- /* Make a copy of the file name from the find_t structure. */
-
- strcpy(FilePtr->FileName,FileDirectory);
- strcat(FilePtr->FileName,FileInfo.name);
-
-
- } while (_dos_findnext(&FileInfo) == 0);
- }
- return 0;
- }
-
-