home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Simtel MSDOS 1992 June
/
SIMTEL_0692.cdr
/
msdos
/
zip
/
zipvinc.arc
/
ZIPV.C
next >
Wrap
C/C++ Source or Header
|
1989-02-15
|
17KB
|
679 lines
/******************************************************************************
*
*
* 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;
}