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

  1. /******************************************************************************
  2.  *
  3.  *
  4.  *    ZIPV  -- SHARE compatible ZIP file list utility.
  5.  *
  6.  *
  7.  *    This code was written as a demonstration of how to read
  8.  *    the ZIP file format.  It does not attempt to support
  9.  *    ZIP files which span multiple diskettes (it is not even
  10.  *    clear to me how that would be done).  It does support
  11.  *    multiple ZIP file names on the command line and does
  12.  *    support wildcards.  Under DOS 3.0 or above all files are
  13.  *    opened in SHARE compatible mode.
  14.  *
  15.  *    The output format is patterned after (stolen from) Vern
  16.  *    Buerg's ARCV format.
  17.  *
  18.  *
  19.  *    It appears that there are two ways to extract the names
  20.  *    of files contained in a ZIP file.  One copy is kept in a
  21.  *    "local file header" which proceeds the compressed file.  The
  22.  *    other copy is kept in the "central directory".  The notes
  23.  *    that accompany PKZIP are not clear whether or not these
  24.  *    will always agree.  I chose to read the information from
  25.  *    the central directory, even though I have to read past
  26.  *    all of the local file headers to get there.
  27.  *
  28.  *    This code is placed in the public domain.  You may do whatever
  29.  *    you like with it.
  30.  *
  31.  *    Ken Brown
  32.  *
  33.  *
  34.  *
  35.  *
  36.  *    Syntax:
  37.  *
  38.  *       ZIPV file_spec1[.ZIP] [file_spec2[.ZIP] ...]
  39.  *
  40.  *
  41.  *
  42.  ******************************************************************************
  43. */
  44.  
  45. #include <stdio.h>
  46. #include <stdlib.h>
  47. #include <string.h>
  48. #include <malloc.h>
  49. #include <sys/types.h>
  50. #include <sys/stat.h>
  51. #include <bios.h>
  52. #include <dos.h>
  53. #include <io.h>
  54. #include <share.h>
  55. #include <fcntl.h>
  56.  
  57.  
  58. /* These structures must be byte aligned */
  59.  
  60. #pragma pack(1)
  61.  
  62. /* Structure of the local file header */
  63.  
  64. struct LOCAL_HEADER {
  65.    long  Signature;
  66.    int   Version;
  67.    int   BitFlag;
  68.    int   CompressionMethod;
  69.    int   FileTime;
  70.    int   FileDate;
  71.    long  CRC32;
  72.    long  CompressedSize;
  73.    long  UnCompressedSize;
  74.    int   FileNameLength;
  75.    int   ExtraFieldLength;
  76.    };
  77.  
  78. /* Structure of the central directory record */
  79. struct CENTRAL_RECORD {
  80.    long  Signature;
  81.    int   VersionMadeBy;
  82.    int   VersionNeeded;
  83.    int   BitFlag;
  84.    int   CompressionMethod;
  85.    int   FileTime;
  86.    int   FileDate;
  87.    long  CRC32;
  88.    long  CompressedSize;
  89.    long  UnCompressedSize;
  90.    int   FileNameLength;
  91.    int   ExtraFieldLength;
  92.    int   CommentFieldLength;
  93.    int   DiskStartNumber;
  94.    int   InternalAttributes;
  95.    long  ExternalAttributes;
  96.    long  LocalHeaderOffset;
  97.    };
  98.  
  99. #pragma pack()
  100.  
  101.  
  102.  
  103. /*
  104.  *    This structure will be used to build a list of files
  105.  *    from a wildcard file_spec.
  106.  *
  107. */
  108. struct FILELIST {
  109.    char              FileName[83];
  110.    struct FILELIST   *NextFile;
  111.    }
  112.  
  113.  
  114.  
  115.  
  116. /* Global variables */
  117.  
  118. struct FILELIST *FileList,
  119.                 *FilePtr;
  120.  
  121.  
  122.  
  123.  
  124. static int   TotalFiles;
  125.  
  126. static long  TotalBytes;
  127. static long  TotalUnCompressedBytes;
  128.  
  129.  
  130.  
  131. /* Function prototypes */
  132.  
  133. extern  int main(int argc,char * *argv);
  134. extern  int ProcessFile(char *ZipFileName);
  135. extern  int ListZipFile(char *ZipFileName);
  136. extern  int DisplayHeaderRecord(struct CENTRAL_RECORD CentralDirRecord,char *FileName);
  137. extern  int DisplayTotals(void );
  138. extern  char *DisplayCompressionType(int Compression);
  139. extern  char *DisplayMonthName(int Month);
  140. extern  int BuildFileList(char *FileMask);
  141.  
  142.  
  143. main(int argc, char *argv[])
  144. {
  145.    int   Counter;
  146.    char  ZipFileName[83],
  147.          *StrPtr;
  148.  
  149.    if(argc < 2) {
  150.       printf("Syntax: ZIPV file_spec1[.ZIP] [file_spec2[.ZIP] ...]\n");
  151.       exit(1);
  152.       }
  153.  
  154.    for(Counter = 1; Counter < argc; Counter++) {
  155.       if(strlen(argv[Counter]) > 82) {
  156.          printf("Syntax: ZIPV file_spec1[.ZIP] [file_spec2[.ZIP] ...]\n");
  157.          exit(1);
  158.          }
  159.  
  160.       strcpy(ZipFileName,argv[Counter]);
  161.       strupr(ZipFileName);
  162.  
  163.       /* If the .ZIP extension is missing add it. */
  164.  
  165.       if(strchr(ZipFileName,'.') == NULL) {
  166.          strcat(ZipFileName,".ZIP");
  167.          }
  168.  
  169.       ProcessFile(ZipFileName);
  170.       }
  171.  
  172.    exit(0);
  173.  
  174.  
  175. }
  176.  
  177. /* Process each command line argument (may contain a wildcard). */
  178.  
  179. ProcessFile(char *ZipFileName)
  180. {
  181.  
  182.    FileList = NULL;
  183.  
  184.    /* Build a list of file names matching the file_spec. */
  185.  
  186.    BuildFileList(ZipFileName);
  187.  
  188.    /* If FileList == NULL no matching files were found. */
  189.  
  190.    if(FileList == NULL) {
  191.       if(strchr(ZipFileName,'*') == NULL && strchr(ZipFileName,'?') == NULL) {
  192.          printf("File not found: %s\n\n",ZipFileName);
  193.          }
  194.       else {
  195.          printf("No matching files found: %s\n\n",ZipFileName);
  196.          }
  197.       }
  198.    else {
  199.       FilePtr = FileList;
  200.       while(FilePtr != NULL) {
  201.          ListZipFile(FilePtr->FileName);
  202.          FilePtr = FilePtr->NextFile;
  203.          }
  204.       }
  205.  
  206.    return 0;
  207.  
  208. }
  209.  
  210.  
  211. ListZipFile(char *ZipFileName)
  212. {
  213.    int   ZipFileHandle,
  214.          BytesRead;
  215.  
  216.    long  FileOffset;
  217.  
  218.    char  *FileName,
  219.          *ReadPtr;
  220.  
  221.    struct LOCAL_HEADER LocalFileHeader;
  222.  
  223.    struct CENTRAL_RECORD CentralDirRecord;
  224.  
  225.  
  226.    /* Open the ZIP file.  Use sopen() if possible. */
  227.  
  228.    if(_osmajor >= 3) {
  229.       ZipFileHandle = sopen(ZipFileName,O_RDONLY|O_BINARY,SH_DENYWR);
  230.  
  231.       if(ZipFileHandle == -1) {
  232.          printf("File is currently locked, cannot open: %s\n\n",ZipFileName);
  233.          return 0;
  234.          }
  235.       }
  236.    else {
  237.       ZipFileHandle = open(ZipFileName,O_RDONLY|O_BINARY);
  238.  
  239.       if(ZipFileHandle == -1) {
  240.          printf("Cannot open: %s\n\n",ZipFileName);
  241.          return 0;
  242.          }
  243.       }
  244.  
  245.  
  246.  
  247.    printf("ZIP File: %s\n",ZipFileName);
  248.    printf("\n");
  249.  
  250.    TotalFiles = 0;
  251.    TotalBytes = 0L;
  252.    TotalUnCompressedBytes = 0L;
  253.  
  254.    printf("Name          Length    Stowage    SF   Size now  Date       Time    CRC \n");
  255.    printf("============  ========  ========  ====  ========  =========  ======  ========\n");
  256.  
  257.  
  258.    /* Read the signature from the first local header */
  259.  
  260.    BytesRead = read(ZipFileHandle,(char *)&LocalFileHeader,sizeof(long));
  261.  
  262.    if(BytesRead != sizeof(long)) {
  263.       printf("Not a ZIP file\n\n");
  264.       close(ZipFileHandle);
  265.       return 0;
  266.       }
  267.    if(LocalFileHeader.Signature != 0x04034b50) {
  268.       printf("Not a ZIP file\n\n");
  269.       close(ZipFileHandle);
  270.       return 0;
  271.       }
  272.  
  273.    /* Skip over all of the compressed files and get to the central
  274.       directory */
  275.  
  276.    while(1) {
  277.  
  278.       ReadPtr = (char *)&LocalFileHeader;
  279.       ReadPtr += 4;
  280.  
  281.       /* Read the remainder of the local file header (we already read
  282.          the signature. */
  283.  
  284.       BytesRead = read(ZipFileHandle,ReadPtr,sizeof(struct LOCAL_HEADER)-4);
  285.  
  286.       if(BytesRead != sizeof(struct LOCAL_HEADER) - 4) {
  287.          printf("Invalid ZIP file format\n\n");
  288.          close(ZipFileHandle);
  289.          return 0;
  290.          }
  291.  
  292.       FileOffset = LocalFileHeader.FileNameLength +
  293.                    LocalFileHeader.ExtraFieldLength +
  294.                    LocalFileHeader.CompressedSize;
  295.  
  296.       /* Jump to the next local file header */
  297.  
  298.       if(lseek(ZipFileHandle,FileOffset,SEEK_CUR) == -1) {
  299.          printf("Invalid ZIP file format\n\n");
  300.          close(ZipFileHandle);
  301.          return 0;
  302.          }
  303.  
  304.       /* Read the next signature */
  305.  
  306.       BytesRead = read(ZipFileHandle,(char *)&LocalFileHeader,sizeof(long));
  307.       if(BytesRead != sizeof(long)) {
  308.          printf("Invalid ZIP file format\n\n");
  309.          close(ZipFileHandle);
  310.          return 0;
  311.          }
  312.  
  313.       /* If we get a match we have found the beginning of the central
  314.          directory. */
  315.  
  316.       if(LocalFileHeader.Signature == 0x02014b50) {
  317.          break;
  318.          }
  319.  
  320.       }
  321.  
  322.  
  323.    CentralDirRecord.Signature = LocalFileHeader.Signature;
  324.  
  325.    /* Read the records in the central directory one at a time */
  326.    
  327.    while(1) {
  328.  
  329.       /* Read the remainder of the file header record (we already
  330.          have the signature.  ReadPtr points into CentralDirRecord
  331.          4 bytes from the beginning (right behind the signature. */
  332.  
  333.       ReadPtr = (char *)&CentralDirRecord;
  334.       ReadPtr += 4;
  335.  
  336.       BytesRead = read(ZipFileHandle,ReadPtr,sizeof(struct CENTRAL_RECORD)-4);
  337.  
  338.       if(BytesRead != sizeof(struct CENTRAL_RECORD) - 4) {
  339.          printf("Invalid ZIP file format\n\n");
  340.          close(ZipFileHandle);
  341.          return 0;
  342.          }
  343.  
  344.       /* It is probably not possible for the FileName to have a length
  345.          of 0 but who knows.  */
  346.  
  347.       if(CentralDirRecord.FileNameLength > 0) {
  348.  
  349.          /* The file name can be long so allocate space for the name as
  350.             needed rather that write to a statically allocated array */
  351.  
  352.          if((FileName = malloc(CentralDirRecord.FileNameLength + 1)) == NULL) {
  353.             printf("Out of memory\n\n");
  354.             exit(1);
  355.             }
  356.  
  357.          /* Read the file name. */
  358.  
  359.          BytesRead = read(ZipFileHandle,FileName,CentralDirRecord.FileNameLength);
  360.  
  361.          if(BytesRead != CentralDirRecord.FileNameLength) {
  362.             printf("Invalid ZIP file format\n\n");
  363.             close(ZipFileHandle);
  364.             return 0;
  365.             }
  366.  
  367.          /* Add the trailing \0 byte. */
  368.  
  369.          FileName[CentralDirRecord.FileNameLength] = '\0';
  370.  
  371.          /* Display the record. */
  372.  
  373.          DisplayHeaderRecord(CentralDirRecord,FileName);
  374.  
  375.          /* The file name is not needed any more. */
  376.  
  377.          free(FileName);
  378.          }
  379.  
  380.  
  381.       /* Skip over the extra field and the comment field */
  382.  
  383.       FileOffset = CentralDirRecord.ExtraFieldLength +
  384.                    CentralDirRecord.CommentFieldLength;
  385.  
  386.       if(FileOffset > 0L) {
  387.          if(lseek(ZipFileHandle,FileOffset,SEEK_CUR) == -1) {
  388.             printf("Invalid ZIP file format\n\n");
  389.             close(ZipFileHandle);
  390.             return 0;
  391.             }
  392.          }
  393.  
  394.  
  395.       /* Read the signature from the next record. */
  396.  
  397.       BytesRead = read(ZipFileHandle,(char *)&CentralDirRecord,sizeof(long));
  398.       if(BytesRead != sizeof(long)) {
  399.          printf("Invalid ZIP file format\n\n");
  400.          close(ZipFileHandle);
  401.          return 0;
  402.          }
  403.  
  404.       /* If we get a match then we found the "End of central dir record"
  405.          and we are done. */
  406.  
  407.       if(CentralDirRecord.Signature == 0x06054b50) {
  408.          break;
  409.          }
  410.  
  411.       }
  412.  
  413.    close(ZipFileHandle);
  414.  
  415.    DisplayTotals();
  416.  
  417.    return 0;
  418.  
  419. }
  420.  
  421.  
  422. DisplayHeaderRecord(struct CENTRAL_RECORD CentralDirRecord, char *FileName)
  423. {
  424.    int   Month,
  425.          Day,
  426.          Year,
  427.          Hour,
  428.          Minutes,
  429.          StowageFactor;
  430.    char  *NamePtr;
  431.    long  SizeReduction;
  432.  
  433.    if(CentralDirRecord.UnCompressedSize == 0) {
  434.       StowageFactor = 0;
  435.       }
  436.    else {
  437.       if(CentralDirRecord.CompressedSize <= CentralDirRecord.UnCompressedSize) {
  438.          SizeReduction = CentralDirRecord.UnCompressedSize - CentralDirRecord.CompressedSize;
  439.          if(SizeReduction == 0L) {
  440.             StowageFactor = 0;
  441.             }
  442.          else {
  443.             SizeReduction = SizeReduction * 100L + 50;
  444.             StowageFactor = (int)(SizeReduction/CentralDirRecord.UnCompressedSize);
  445.             }
  446.          }
  447.       else {
  448.          SizeReduction = CentralDirRecord.CompressedSize - CentralDirRecord.UnCompressedSize;
  449.          SizeReduction = SizeReduction * 100L + 50;
  450.          StowageFactor = (int)(SizeReduction/CentralDirRecord.UnCompressedSize);
  451.          StowageFactor *= -1;
  452.          }
  453.       }
  454.    if(StowageFactor >= 100) {
  455.       StowageFactor = 0;
  456.       }
  457.  
  458.  
  459.    /* Convert the DOS internal date and time format to something we can
  460.       use for output. */
  461.  
  462.    Month = (CentralDirRecord.FileDate >> 5) & 0x0f;
  463.    Day = CentralDirRecord.FileDate & 0x1f;
  464.    Year = ((CentralDirRecord.FileDate >> 9) & 0x7f) + 80;
  465.  
  466.    if(CentralDirRecord.FileTime > 0) {
  467.       Hour = (CentralDirRecord.FileTime >> 11) & 0x1f;
  468.       Minutes = (CentralDirRecord.FileTime >> 5) & 0x3f;
  469.       }
  470.    else {
  471.       Hour = 0;
  472.       Minutes = 0;
  473.       }
  474.  
  475.    /* The ZIP documentation says that path names are stored with '/'s
  476.       rather than '\'s.  Look for a '/' and if so point to the file
  477.       name rather than the whole path name. */
  478.  
  479.    if((NamePtr = strrchr(FileName,'/')) == NULL) {
  480.       NamePtr = FileName;
  481.       }
  482.  
  483.    printf("%-14s%8ld  %s   %2d%%  %8ld  %02d %s %2d  %02d:%02d   %08lX\n",
  484.            NamePtr,
  485.            CentralDirRecord.UnCompressedSize,
  486.            DisplayCompressionType(CentralDirRecord.CompressionMethod),
  487.            StowageFactor,
  488.            CentralDirRecord.CompressedSize,
  489.            Day,
  490.            DisplayMonthName(Month),
  491.            Year,
  492.            Hour,
  493.            Minutes,
  494.            CentralDirRecord.CRC32);
  495.  
  496.    TotalFiles += 1;
  497.    TotalBytes += CentralDirRecord.CompressedSize;
  498.    TotalUnCompressedBytes += CentralDirRecord.UnCompressedSize;
  499.  
  500.    return 0;
  501. }
  502.  
  503. DisplayTotals()
  504. {
  505.    int   StowageFactor;
  506.    long  SizeReduction;
  507.  
  508.    printf("============  ========  ========  ====  ========  =========  ======  ========\n");
  509.  
  510.    if(TotalUnCompressedBytes == 0) {
  511.       StowageFactor = 0;
  512.       }
  513.    else {
  514.       if(TotalBytes <= TotalUnCompressedBytes) {
  515.          SizeReduction = TotalUnCompressedBytes - TotalBytes;
  516.          if(SizeReduction == 0L) {
  517.             StowageFactor = 0;
  518.             }
  519.          else {
  520.             SizeReduction = SizeReduction * 100L + 50;
  521.             StowageFactor = (int)(SizeReduction/TotalUnCompressedBytes);
  522.             }
  523.          }
  524.       else {
  525.          SizeReduction = TotalBytes - TotalUnCompressedBytes;
  526.          SizeReduction = SizeReduction * 100L + 50;
  527.          StowageFactor = (int)(SizeReduction/TotalUnCompressedBytes);
  528.          StowageFactor *= -1;
  529.          }
  530.       }
  531.    if(StowageFactor >= 100) {
  532.       StowageFactor = 0;
  533.       }
  534.  
  535.    printf("*total%6d  %8ld             %2d%%  %8ld\n\n",
  536.           TotalFiles,
  537.           TotalUnCompressedBytes,
  538.           StowageFactor,
  539.           TotalBytes);
  540.  
  541.    return 0;
  542. }
  543.  
  544.  
  545. char *DisplayCompressionType(int Compression)
  546. {
  547.    switch (Compression) {
  548.       case 0:
  549.          return " Stored ";
  550.          break;
  551.       case 1:
  552.          return " Shrunk ";
  553.          break;
  554.       case 2:
  555.          return "Reduced1";
  556.          break;
  557.       case 3:
  558.          return "Reduced2";
  559.          break;
  560.       case 4:
  561.          return "Reduced3";
  562.          break;
  563.       case 5:
  564.          return "Reduced4";
  565.          break;
  566.       default:
  567.          return "Unknown ";
  568.          break;
  569.       }
  570. }
  571.  
  572. char *DisplayMonthName(int Month)
  573. {
  574.    switch (Month) {
  575.       case 1:
  576.          return "Jan";
  577.          break;
  578.       case 2:
  579.          return "Feb";
  580.          break;
  581.       case 3:
  582.          return "Mar";
  583.          break;
  584.       case 4:
  585.          return "Apr";
  586.          break;
  587.       case 5:
  588.          return "May";
  589.          break;
  590.       case 6:
  591.          return "Jun";
  592.          break;
  593.       case 7:
  594.          return "Jul";
  595.          break;
  596.       case 8:
  597.          return "Aug";
  598.          break;
  599.       case 9:
  600.          return "Sep";
  601.          break;
  602.       case 10:
  603.          return "Oct";
  604.          break;
  605.       case 11:
  606.          return "Nov";
  607.          break;
  608.       case 12:
  609.          return "Dec";
  610.          break;
  611.       }
  612. }
  613.  
  614. /*
  615.  * BuildFileList() uses the _dos_findfirst() and _dos_findnext() routines
  616.  * to build a linked list of all files which match FileMask.
  617.  *
  618.  * _dos_findfirst() is from the Microsoft C library.  There is a Turbo C
  619.  * equivalent but I don't know the name.
  620.  *
  621.  */
  622.  
  623. BuildFileList(char *FileMask)
  624. {
  625.    char  FileDirectory[83],
  626.          *StrPtr;
  627.  
  628.    struct find_t FileInfo;
  629.    struct stat StatBuffer;
  630.  
  631.    strcpy(FileDirectory,FileMask);
  632.    if((StrPtr = strrchr(FileDirectory,'\\')) != NULL) {
  633.       StrPtr++;
  634.       *StrPtr = '\0';
  635.       }
  636.    else {
  637.       if((StrPtr = strrchr(FileDirectory,':')) != NULL) {
  638.          StrPtr++;
  639.          *StrPtr = '\0';
  640.          }
  641.       else {
  642.          FileDirectory[0] = '\0';
  643.          }
  644.       }
  645.  
  646.    if(_dos_findfirst(FileMask,_A_NORMAL|_A_RDONLY,&FileInfo) == 0) {
  647.       do {
  648.          if(FileList == NULL) {
  649.             FileList = calloc(sizeof(struct FILELIST),1);
  650.  
  651.             /* This is not likely to happen but you never know. */
  652.  
  653.             if(FileList == NULL) {
  654.                printf("Out of memory\n\n");
  655.                exit(1);
  656.                }
  657.             FilePtr = FileList;
  658.             }
  659.          else {
  660.             FilePtr->NextFile = calloc(sizeof(struct FILELIST),1);
  661.             if(FilePtr->NextFile == NULL) {
  662.                printf("Out of memory\n\n");
  663.                exit(1);
  664.                }
  665.             FilePtr = FilePtr->NextFile;
  666.             }
  667.  
  668.          /* Make a copy of the file name from the find_t structure. */
  669.  
  670.          strcpy(FilePtr->FileName,FileDirectory);
  671.          strcat(FilePtr->FileName,FileInfo.name);
  672.  
  673.  
  674.          } while (_dos_findnext(&FileInfo) == 0);
  675.       }
  676.    return 0;
  677. }
  678.  
  679.