home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume31 / unzip50 / part07 / zipinfo.c.A
Text File  |  1992-08-23  |  30KB  |  751 lines

  1. /*---------------------------------------------------------------------------
  2.  
  3.   zipinfo.c
  4.  
  5.   This program reads all sorts of totally nifty information, including the
  6.   central directory stuff, from a ZIP archive ("zipfile" for short).  It
  7.   started as just a testbed for fooling with zipfiles, but at this point
  8.   it's actually a moderately useful utility.  It also became the basis
  9.   for the rewrite of unzip (3.16 -> 4.0), using the central directory for
  10.   processing rather than the individual (local) file headers.
  11.  
  12.   For myself, I find it convenient to define an alias "ii" (under Unix and
  13.   VMS) or to rename the executable to "ii.exe" (OS/2 and DOS).  This nicely
  14.   complements my Unix long-listing "ll" alias (ls -lF), since zipinfo's de-
  15.   fault action is to produce a Unix-like listing of the archive's contents.
  16.   "ii zipfile" is easier to type than "zipinfo zipfile"...
  17.  
  18.   Another dandy product from your buddies at Newtware!
  19.  
  20.   ---------------------------------------------------------------------------
  21.  
  22.   To compile (partial instructions; some of this stuff doesn't exist yet):
  23.  
  24.      under Unix (cc):  make zipinfo
  25.  
  26.      under MS-DOS (TurboC):  make -fMKZIPINF.DOS   (edit appropriately)
  27.  
  28.      under MS-DOS (MSC):  make MKZIPINF.DOS
  29.        (or use Makefile if you have MSC 6.0:  "nmake zi_dos")
  30.  
  31.      under OS/2 (MSC):  make MKZIPINF.DOS   (edit appropriately)
  32.        (or use Makefile if you have MSC 6.0:  "nmake zi_os2")
  33.  
  34.      under Atari OS:  beats me...
  35.  
  36.      under VMS:  @MAKE_ZIPINFO     (see also VMSNOTES)
  37.                  ZIPINFO == $DISKNAME:[DIRECTORY]ZIPINFO.EXE
  38.  
  39.      under Macintosh OS:  who knows?
  40.  
  41.   ---------------------------------------------------------------------------
  42.  
  43.   Source:     unzip50.zip (.tar.Z, etc.) for Unix, VMS, OS/2 and MS-DOS; see
  44.               `Where' in source distribution for ftp, uucp and mail-server
  45.               sites.
  46.   Author:     Greg Roelofs, roelofs@nas.nasa.gov, 23 August 1990
  47.   Copyright:  Portions copyright 1992 Greg Roelofs.  Portions adapted from
  48.               unzip 3.1.  SizeOfEAs() by Kai Uwe Rommel.
  49.  
  50.   ---------------------------------------------------------------------------*/
  51.  
  52.  
  53.  
  54.  
  55. #ifndef ZIPINFO
  56. #  define ZIPINFO   /* needed for Unix permissions in non-Unix environments */
  57. #endif /* !ZIPINFO */
  58. #include "unzip.h"
  59.  
  60. #define VERSION  "v1.0 of 21 August 92"
  61.  
  62. #define LFLAG    3        /* for short "ls -l" type listing */
  63.  
  64. #define EAID     0x0009   /* OS/2 EA extra field ID */
  65. typedef struct {          /* for OS/2 info in OS/2 and non-OS/2 environments */
  66.     unsigned short nID;
  67.     unsigned short nSize;
  68.     ULONG lSize;
  69. } EAHEADER, *PEAHEADER;
  70.  
  71.  
  72.  
  73.  
  74. /**********************/
  75. /*  Global Variables  */
  76. /**********************/
  77.  
  78. #ifdef EBCDIC
  79.    int  aflag=1;        /* this is so you can read it on the screen  */
  80. #else                   /* (basically, entire program is "unzip -c") */
  81.    int  aflag=0;
  82. #endif
  83. int lflag=(-1);         /* '-1slmv':  listing format */
  84. int hflag=0;            /* '-h':  header line */
  85. int tflag=0;            /* '-t':  totals line */
  86.  
  87. byte *inbuf, *inptr;    /* input buffer (any size is legal) and pointer */
  88. int incnt;
  89.  
  90. int zipfd;              /* zipfile file handle */
  91. char zipfn[FILNAMSIZ];
  92.  
  93. char local_hdr_sig[5] = "\120";    /* remaining signature bytes come later:  */
  94. char central_hdr_sig[5] = "\120";  /*  must initialize at runtime so zipinfo */
  95. char end_central_sig[5] = "\120";  /*  executable won't look like a zipfile  */
  96. char extd_local_sig[5] = "\120";
  97.  
  98. cdir_file_hdr crec;             /* used in zipinfo.c, misc.c */
  99. local_file_hdr lrec;
  100. ecdir_rec ecrec;
  101. struct stat statbuf;            /* used by main() */
  102.  
  103. int process_all_files;
  104. longint real_ecrec_offset, expect_ecrec_offset;
  105. longint extra_bytes=0;          /* used in zipinfo.c, misc.c */
  106. longint cur_zipfile_bufstart;   /* find_end_central_dir, readbuf */
  107.  
  108. min_info info, *pInfo=(&info);
  109.  
  110. byte *extra_field = NULL;       /* used by VMS, Mac and OS/2 versions */
  111. byte *outbuf;                   /* buffer for rle look-back, zipfile comment */
  112. byte *outout;                   /* scratch pad for ASCII-native trans */
  113.  
  114. char filename[FILNAMSIZ];
  115. char sig[5];
  116. char *fnames[2] = {"*", NULL};    /* default filenames vector */
  117. char **fnv = fnames;
  118.  
  119. static byte *hold;
  120. static longint ziplen;
  121. static UWORD hostnum;
  122. static UWORD methnum;
  123. static UWORD extnum;
  124.  
  125. char *EndSigMsg = "\nwarning:\
  126.   didn't find end-of-central-dir signature at end of central dir.\n";
  127. char *CentSigMsg =
  128.   "error:  expected central file header signature not found (file #%u).\n";
  129. char *SeekMsg =
  130.   "error:  attempt to seek before beginning of zipfile\n%s";
  131.  
  132. #ifdef VMS
  133. char *ReportMsg = "\
  134.   (please check that you have transferred or created the zipfile in the\n\
  135.   appropriate BINARY mode--this includes ftp, Kermit, AND unzip'd zipfiles)\n";
  136. #else /* !VMS */
  137. char *ReportMsg = "\
  138.   (please check that you have transferred or created the zipfile in the\n\
  139.   appropriate BINARY mode and that you have compiled unzip properly)\n";
  140. #endif /* ?VMS */
  141.  
  142.  
  143.  
  144.  
  145.  
  146.  
  147. /******************/
  148. /*  Main program  */
  149. /******************/
  150.  
  151. main(argc, argv)
  152.     int    argc;
  153.     char   *argv[];
  154. {
  155.     char   *s;
  156.     int    c, error=FALSE, negative=0;
  157.     int    hflag_slmv=TRUE, hflag_1=FALSE;  /* diff options => diff defaults */
  158.     int    tflag_slm=TRUE, tflag_1v=FALSE;
  159.     int    explicit_h=FALSE, explicit_t=FALSE;
  160.  
  161.  
  162.  
  163. /*---------------------------------------------------------------------------
  164.     Everybody is now "NOTINT16," but this is a nice little piece of code, so
  165.     just comment it out for future reference. :-)
  166.   ---------------------------------------------------------------------------*/
  167.  
  168. #if 0
  169. # ifndef KNOW_IT_WORKS  /* define this to save space, if things already work */
  170. # ifndef DOS_OS2        /* already works (no RISCy OS/2's yet...) */
  171. # ifndef NOTINT16       /* whole point is to see if this NEEDS defining */
  172.     {
  173.         int error=0;
  174.         long testsig;
  175.         static char *mach_type[3] = {"big-endian", "structure-padding",
  176.                                      "big-endian and structure-padding"};
  177.  
  178.         strcpy((char *)&testsig,"012");
  179.         if (testsig != 0x00323130)
  180.             error = 1;
  181.         if (sizeof(cdir_file_hdr) != CREC_SIZE)
  182.             error += 2;
  183.         if (error--)
  184.             fprintf(stderr, "It appears that your machine is %s.  If errors\n\
  185. occur, please try recompiling with \"NOTINT16\" defined (read the\n\
  186. Makefile, or try \"make zipinfo\").\n\n", mach_type[error]);
  187.     }
  188. # endif /* !NOTINT16 */
  189. # endif /* !DOS_OS2 */
  190. # endif /* !KNOW_IT_WORKS */
  191. #endif /* 0 */
  192.  
  193. /*---------------------------------------------------------------------------
  194.     Put environment-variable options into the queue, then rip through any
  195.     command-line options lurking about...
  196.   ---------------------------------------------------------------------------*/
  197.  
  198.     envargs(&argc, &argv, ENV_ZIPINFO);
  199.  
  200.     while (--argc > 0 && (*++argv)[0] == '-') {
  201.         s = argv[0] + 1;
  202.         while ((c = *s++) != 0) {    /* "!= 0":  prevent Turbo C warning */
  203.             switch (c) {
  204.                 case '-':
  205.                     ++negative;
  206.                     break;
  207.                 case '1':      /* shortest listing:  just filenames */
  208.                     if (negative)
  209.                         lflag = -2, negative = 0;
  210.                     else
  211.                         lflag = 1;
  212.                     break;
  213.                 case 'h':      /* header line */
  214.                     if (negative)
  215.                         hflag_1 = hflag_slmv = FALSE, negative = 0;
  216.                     else {
  217.                         hflag_1 = hflag_slmv = explicit_h = TRUE;
  218.                         if (lflag == -1)
  219.                             lflag = 0;
  220.                     }
  221.                     break;
  222.                 case 'l':      /* longer form of "ls -l" type listing */
  223.                     if (negative)
  224.                         lflag = -2, negative = 0;
  225.                     else
  226.                         lflag = 5;
  227.                     break;
  228.                 case 'm':      /* medium form of "ls -l" type listing */
  229.                     if (negative)
  230.                         lflag = -2, negative = 0;
  231.                     else
  232.                         lflag = 4;
  233.                     break;
  234.                 case 's':      /* default:  shorter "ls -l" type listing */
  235.                     if (negative)
  236.                         lflag = -2, negative = 0;
  237.                     else
  238.                         lflag = 3;
  239.                     break;
  240.                 case 't':      /* totals line */
  241.                     if (negative)
  242.                         tflag_1v = tflag_slm = FALSE, negative = 0;
  243.                     else {
  244.                         tflag_1v = tflag_slm = explicit_t = TRUE;
  245.                         if (lflag == -1)
  246.                             lflag = 0;
  247.                     }
  248.                     break;
  249.                 case 'v':      /* turbo-verbose listing */
  250.                     if (negative)
  251.                         lflag = -2, negative = 0;
  252.                     else
  253.                         lflag = 10;
  254.                     break;
  255.                 default:
  256.                     error = TRUE;
  257.                     break;
  258.             }
  259.         }
  260.     }
  261.     if ((argc-- == 0) || error)
  262.         RETURN(usage(error));
  263.  
  264.     if (argc != 0)
  265.         process_all_files = FALSE;
  266.     else
  267.         process_all_files = TRUE;   /* for speed */
  268.  
  269.     /* if no listing options given (or all negated), or if only -h/-t given
  270.      * with individual files specified, use default listing format */
  271.     if ((lflag < 0) || (!process_all_files && (lflag == 0)))
  272.         lflag = LFLAG;
  273.  
  274.     /* set header and totals flags to default or specified values */
  275.     switch (lflag) {
  276.         case 0:   /* 0:  can only occur if either -t or -h explicitly given; */
  277.         case 1:   /*  therefore set both flags equal to normally false value */
  278.             hflag = hflag_1;
  279.             tflag = tflag_1v;
  280.             break;
  281.         case 3:
  282.         case 4:
  283.         case 5:
  284.             hflag = (!process_all_files && !explicit_h)? FALSE : hflag_slmv;
  285.             tflag = (!process_all_files && !explicit_t)? FALSE : tflag_slm;
  286.             break;
  287.         case 10:
  288.             hflag = hflag_slmv;
  289.             tflag = tflag_1v;
  290.             break;
  291.     }
  292.  
  293. /*---------------------------------------------------------------------------
  294.     Now get the zipfile name from the command line and see if it exists as a
  295.     regular (non-directory) file.  If not, append the ".zip" suffix.  We don't
  296.     immediately check to see if this results in a good name, but we will do so
  297.     later.  In the meantime, see if there are any member filespecs on the com-
  298.     mand line, and if so, set the filename pointer to point at them.
  299.   ---------------------------------------------------------------------------*/
  300.  
  301.     strcpy(zipfn, *argv++);
  302.     if (stat(zipfn, &statbuf) || (statbuf.st_mode & S_IFMT) == S_IFDIR)
  303.         strcat(zipfn, ZSUFX);
  304. #if defined(UNIX) && !defined(VMS)  /* Unix executables have no extension-- */
  305.     else if (statbuf.st_mode & S_IXUSR)  /* might find zip, not zip.zip; etc */
  306.         fprintf(stderr, "\nnote:  file [ %s ] may be an executable\n\n", zipfn);
  307. #endif /* UNIX && !VMS */
  308.  
  309.     if (stat(zipfn, &statbuf)) {    /* try again */
  310.         fprintf(stderr, "error:  can't find zipfile [ %s ]\n", zipfn);
  311.         RETURN(9);                  /* 9:  file not found */
  312.     } else
  313.         ziplen = statbuf.st_size;
  314.  
  315.     if (!process_all_files)
  316.         fnv = argv;
  317.  
  318. /*---------------------------------------------------------------------------
  319.     Okey dokey, we have everything we need to get started.  Let's roll.
  320.   ---------------------------------------------------------------------------*/
  321.  
  322.     inbuf = (byte *) (malloc(INBUFSIZ + 4));    /* 4 extra for hold[] (below) */
  323.     outbuf = (byte *) (malloc(OUTBUFSIZ + 1));  /* 1 extra for string termin. */
  324.     if (aflag)                  /* if need an ascebc scratch, */
  325.         outout = (byte *) (malloc(OUTBUFSIZ));
  326.     else                        /*  allocate it... */
  327.         outout = outbuf;        /*  else just point to outbuf */
  328.  
  329.     if ((inbuf == NULL) || (outbuf == NULL) || (outout == NULL)) {
  330.         fprintf(stderr, "error:  can't allocate zipinfo buffers\n");
  331.         RETURN(4);              /* 4-8:  insufficient memory */
  332.     }
  333.     hold = &inbuf[INBUFSIZ];    /* to check for boundary-spanning signatures */
  334.  
  335.     RETURN(process_zipfile());  /* keep passing errors back... */
  336.  
  337. } /* end main() */
  338.  
  339.  
  340.  
  341.  
  342.  
  343. /**********************/
  344. /*  Function usage()  */
  345. /**********************/
  346.  
  347. int usage(error)
  348.     int error;
  349. {
  350.     FILE *usagefp;
  351.  
  352.  
  353. /*---------------------------------------------------------------------------
  354.     If user requested usage, send it to stdout; else send to stderr.
  355.   ---------------------------------------------------------------------------*/
  356.  
  357.     if (error)
  358.         usagefp = (FILE *) stderr;
  359.     else
  360.         usagefp = (FILE *) stdout;
  361.  
  362.     fprintf(usagefp, "\
  363.    ZipInfo:  Zipfile Information Utility %s\n\
  364.    (brought to you by Newtware, Inc., and the fine folks at Info-ZIP)\n\n\
  365.    Usage:  zipinfo [-1smlvht] file[.zip] [filespec...]\n", VERSION);
  366.     fprintf(usagefp, "\
  367.      -1  list filenames only, one per line (useful for pipes)\n\
  368.      -s  list zipfile info in short Unix \"ls -l\" format:  default\n\
  369.      -m  list zipfile info in medium Unix \"ls -l\" format\n\
  370.      -l  list zipfile info in long Unix \"ls -l\" format\n\
  371.      -v  list zipfile information in verbose, multi-page format\n\
  372.      -h  list header line\n\
  373.      -t  list totals for files listed or for all files\n");
  374. /*
  375.      -p  disable automatic \"more\" function (for pipes) [not implemented]\n");
  376.  */
  377.  
  378. #ifdef VMS
  379.     fprintf(usagefp, "\nRemember that non-lowercase filespecs must be quoted\
  380.  in VMS (e.g., \"Makefile\").\n");
  381. #endif
  382.  
  383.     if (error)
  384.         return 10;    /* 10:  bad or illegal parameters specified */
  385.     else
  386.         return 0;     /* just wanted usage screen: no error */
  387.  
  388. } /* end function usage() */
  389.  
  390.  
  391.  
  392.  
  393.  
  394. /********************************/
  395. /*  Function process_zipfile()  */
  396. /********************************/
  397.  
  398. int process_zipfile()   /* return PK-type error code */
  399. {
  400.     int error=0, error_in_archive;
  401.  
  402.  
  403. /*---------------------------------------------------------------------------
  404.     Open the zipfile for reading and in BINARY mode to prevent CR/LF trans-
  405.     lation, which would corrupt the bitstreams.
  406.   ---------------------------------------------------------------------------*/
  407.  
  408. #ifdef VMS
  409.     if (check_format())         /* check for variable-length format */
  410.         return 2;               /* 2:  error in zipfile */
  411. #endif /* VMS */
  412.  
  413.     if (open_input_file())      /* this should never happen, given the */
  414.         return 9;               /*   stat() test in main(), but... */
  415.  
  416. /*---------------------------------------------------------------------------
  417.     Reconstruct the various PK signature strings, and find and process the
  418.     end-of-central-directory header.
  419.   ---------------------------------------------------------------------------*/
  420.  
  421.     strcat(local_hdr_sig, LOCAL_HDR_SIG);
  422.     strcat(central_hdr_sig, CENTRAL_HDR_SIG);
  423.     strcat(end_central_sig, END_CENTRAL_SIG);
  424.     strcat(extd_local_sig, EXTD_LOCAL_SIG);
  425.  
  426.     if (find_end_central_dir()) {   /* not found; nothing to do */
  427.         close(zipfd);
  428.         return 2;                   /* 2:  error in zipfile */
  429.     }
  430.  
  431.     real_ecrec_offset = cur_zipfile_bufstart + (inptr-inbuf);
  432. #ifdef TEST
  433.     printf("\n  found end-of-central-dir signature at offset %ld (%.8lXh)\n",
  434.       real_ecrec_offset, real_ecrec_offset);
  435.     printf("    from beginning of file; offset %d (%.4Xh) within block\n",
  436.       inptr-inbuf, inptr-inbuf);
  437. #endif
  438.  
  439.     /* sets expect_ecrec_offset: */
  440.     if ((error_in_archive = process_end_central_dir()) > 1) {
  441.         close(zipfd);
  442.         return error_in_archive;
  443.     }
  444.  
  445. /*---------------------------------------------------------------------------
  446.     Test the end-of-central-directory info for incompatibilities (multi-disk
  447.     archives) or inconsistencies (missing or extra bytes in zipfile).
  448.   ---------------------------------------------------------------------------*/
  449.  
  450.     if (ecrec.number_this_disk != ecrec.num_disk_with_start_central_dir) {
  451.         fprintf(stderr, "\n\
  452.      Zipfile is part of a multi-disk archive, and this is not the disk on\
  453.      which the central zipfile directory begins.\n");
  454.         error_in_archive = 11;  /* 11:  no files found */
  455.     } else {
  456.         if ((extra_bytes = real_ecrec_offset - expect_ecrec_offset) < 0) {
  457.             fprintf(stderr, "\nerror:  missing %ld bytes in zipfile (\
  458. attempting to process anyway)\n\n", -extra_bytes);
  459.             error_in_archive = 2;       /* 2:  (weak) error in zipfile */
  460.         } else if (extra_bytes > 0) {
  461.             if ((ecrec.offset_start_central_directory == 0) &&
  462.                 (ecrec.size_central_directory != 0))   /* zip 1.5 -go bug */
  463.             {
  464.                 fprintf(stderr, "\nerror:  NULL central directory offset (\
  465. attempting to process anyway)\n\n");
  466.                 error_in_archive = 2;   /* 2:  (weak) error in zipfile */
  467.             } else {
  468.                 fprintf(stderr, "\nwarning:  extra %ld bytes at beginning or\
  469.  within zipfile\n          (attempting to process anyway)\n\n", extra_bytes);
  470.                 error_in_archive = 1;   /* 1:  warning error */
  471.             }
  472.         }
  473.  
  474.     /*-----------------------------------------------------------------------
  475.         Check for empty zipfile and exit now if so.
  476.       -----------------------------------------------------------------------*/
  477.  
  478.         if (expect_ecrec_offset == 0L  &&  ecrec.size_central_directory == 0) {
  479.             printf("%sEmpty zipfile.\n", lflag>9 ? "\n  " : "");
  480.             close(zipfd);
  481.             return (error_in_archive > 1)? error_in_archive : 1;
  482.         }
  483.  
  484.     /*-----------------------------------------------------------------------
  485.         Compensate for missing or extra bytes, and seek to where the start
  486.         of central directory should be.  If header not found, uncompensate
  487.         and try again (necessary for at least some Atari archives created
  488.         with STZIP, as well as archives created by J.H. Holm's ZIPSPLIT).
  489.       -----------------------------------------------------------------------*/
  490.  
  491.         LSEEK( ecrec.offset_start_central_directory )
  492.         if ((readbuf(sig, 4) <= 0) || strncmp(sig, central_hdr_sig, 4)) {
  493.             longint tmp = extra_bytes;
  494.  
  495.             extra_bytes = 0;
  496.             LSEEK( ecrec.offset_start_central_directory )
  497.             if ((readbuf(sig, 4) <= 0) || strncmp(sig, central_hdr_sig, 4)) {
  498.                 fprintf(stderr,
  499.             "error:  start of central directory not found; zipfile corrupt.\n");
  500.                 fprintf(stderr, ReportMsg);
  501.                 close(zipfd);
  502.                 return 3;           /* 3:  severe error in zipfile */
  503.             }
  504.             fprintf(stderr, "error:  reported length of central directory is \
  505. %d bytes too\n        long (Atari STZIP zipfile?  J.H. Holm ZIPSPLIT zipfile?)\
  506. .\n        Compensating...\n\n", -tmp);
  507.             error_in_archive = 2;   /* 2:  (weak) error in zipfile */
  508.         }
  509.  
  510.     /*-----------------------------------------------------------------------
  511.         Seek to the start of the central directory one last time, since we
  512.         have just read the first entry's signature bytes; then do the central
  513.         directory and close the zipfile.
  514.       -----------------------------------------------------------------------*/
  515.  
  516.         LSEEK( ecrec.offset_start_central_directory )
  517.         if ((error = process_central_dir()) > error_in_archive)
  518.             error_in_archive = error;    /* don't overwrite stronger error */
  519.         if (lflag > 9)
  520.             printf("\n");
  521.     }
  522.  
  523.     close(zipfd);
  524.     return error_in_archive;
  525.  
  526. } /* end function process_zipfile() */
  527.  
  528.  
  529.  
  530.  
  531.  
  532. /*************************************/
  533. /*  Function find_end_central_dir()  */
  534. /*************************************/
  535.  
  536. int find_end_central_dir()   /* return 0 if found, 1 otherwise */
  537. {
  538.     int       i, numblks;
  539.     longint   tail_len;
  540.  
  541.  
  542.  
  543. /*---------------------------------------------------------------------------
  544.     Treat case of short zipfile separately.
  545.   ---------------------------------------------------------------------------*/
  546.  
  547.     if (ziplen <= INBUFSIZ) {
  548.         lseek(zipfd, 0L, SEEK_SET);
  549.         if ((incnt = read(zipfd,inbuf,(unsigned int)ziplen)) == (int)ziplen)
  550.             /* 'P' must be at least 22 bytes from end of zipfile */
  551.             for (inptr = inbuf+(int)ziplen-22;  inptr >= inbuf;  --inptr)
  552.                 if ((ascii_to_native(*inptr) == 'P')  &&
  553.                      !strncmp((char *)inptr, end_central_sig, 4)) {
  554.                     incnt -= inptr - inbuf;
  555.                     return 0;   /* found it! */
  556.                 }               /* ...otherwise fall through & fail */
  557.  
  558. /*---------------------------------------------------------------------------
  559.     Zipfile is longer than INBUFSIZ:  may need to loop.  Start with short
  560.     block at end of zipfile (if not TOO short).
  561.   ---------------------------------------------------------------------------*/
  562.  
  563.     } else {
  564.         if ((tail_len = ziplen % INBUFSIZ) > ECREC_SIZE) {
  565.             cur_zipfile_bufstart = lseek(zipfd, ziplen-tail_len, SEEK_SET);
  566.             if ((incnt = read(zipfd,inbuf,(unsigned int)tail_len)) !=
  567.                 (int)tail_len)
  568.                 goto fail;      /* shut up; it's expedient. */
  569.  
  570.             /* 'P' must be at least 22 bytes from end of zipfile */
  571.             for (inptr = inbuf+(int)tail_len-22;  inptr >= inbuf;  --inptr)
  572.                 if ((ascii_to_native(*inptr) == 'P')  &&
  573.                      !strncmp((char *)inptr, end_central_sig, 4)) {
  574.                     incnt -= inptr - inbuf;
  575.                     return 0;   /* found it */
  576.                 }               /* ...otherwise search next block */
  577.             /* sig may span block boundary: */
  578.             strncpy((char *)hold, (char *)inbuf, 3);
  579.         } else
  580.             cur_zipfile_bufstart = ziplen - tail_len;
  581.  
  582.         /*
  583.          * Loop through blocks of zipfile data, starting at the end and going
  584.          * toward the beginning.  Need only check last 65557 bytes of zipfile:
  585.          * comment may be up to 65535 bytes long, end-of-central-directory rec-
  586.          * ord is 18 bytes (shouldn't hardcode this number, but what the hell:
  587.          * already did so above (22=18+4)), and sig itself is 4 bytes.
  588.          * 
  589.          * zipinfo:  check the whole file, just in case some transfer protocol
  590.          * has appended a whole bunch of garbage at the end of the archive.
  591.          *
  592.          *                =todo=   ==done==   ==rounding==    =blksiz=      */
  593.         numblks = (int) ((ziplen - tail_len + (INBUFSIZ-1)) / INBUFSIZ);
  594.  
  595.         for (i = 1;  i <= numblks;  ++i) {
  596.             cur_zipfile_bufstart -= INBUFSIZ;
  597.             lseek(zipfd, cur_zipfile_bufstart, SEEK_SET);
  598.             if ((incnt = read(zipfd,inbuf,INBUFSIZ)) != INBUFSIZ)
  599.                 break;          /* fall through and fail */
  600.  
  601.             for (inptr = inbuf+INBUFSIZ-1;  inptr >= inbuf;  --inptr)
  602.                 if ((ascii_to_native(*inptr) == 'P')  &&
  603.                      !strncmp((char *)inptr, end_central_sig, 4)) {
  604.                     incnt -= inptr - inbuf;
  605.                     return 0;   /* found it */
  606.                 }
  607.             /* sig may span block boundary: */
  608.             strncpy((char *)hold, (char *)inbuf, 3);
  609.         }
  610.  
  611.     } /* end if (ziplen > INBUFSIZ) */
  612.  
  613. /*---------------------------------------------------------------------------
  614.     Searched through whole region where signature should be without finding
  615.     it.  Print informational message and die a horrible death.
  616.   ---------------------------------------------------------------------------*/
  617.  
  618. fail:
  619.  
  620.     fprintf(stderr, "\n\
  621.      %s:\n\n\
  622.      End-of-central-directory signature not found.  Either this file is not\n\
  623.      a zipfile, or it constitutes one disk of a multi-part archive.  In the\n\
  624.      latter case the central directory and zipfile comment will be found on\n\
  625.      the last disk(s) of this archive.\n", zipfn);
  626.     return 1;   /* failed */
  627.  
  628. } /* end function find_end_central_dir() */
  629.  
  630.  
  631.  
  632.  
  633.  
  634. /****************************************/
  635. /*  Function process_end_central_dir()  */
  636. /****************************************/
  637.  
  638. int process_end_central_dir()   /* return PK-type error code */
  639. {
  640.     ec_byte_rec   byterec;
  641.     int           error=0;
  642.  
  643.  
  644. /*--------------------------------------------------------------------------
  645.     Read the end-of-central-directory record and do any necessary machine-
  646.     type conversions (byte ordering, structure padding compensation) by
  647.     copying character array to struct.
  648.   ---------------------------------------------------------------------------*/
  649.  
  650.     if (readbuf((char *)byterec, ECREC_SIZE+4) <= 0)
  651.         return 51;
  652.  
  653.     ecrec.number_this_disk =
  654.         makeword(&byterec[NUMBER_THIS_DISK]);
  655.     ecrec.num_disk_with_start_central_dir =
  656.         makeword(&byterec[NUM_DISK_WITH_START_CENTRAL_DIR]);
  657.     ecrec.num_entries_centrl_dir_ths_disk =
  658.         makeword(&byterec[NUM_ENTRIES_CENTRL_DIR_THS_DISK]);
  659.     ecrec.total_entries_central_dir =
  660.         makeword(&byterec[TOTAL_ENTRIES_CENTRAL_DIR]);
  661.     ecrec.size_central_directory =
  662.         makelong(&byterec[SIZE_CENTRAL_DIRECTORY]);
  663.     ecrec.offset_start_central_directory =
  664.         makelong(&byterec[OFFSET_START_CENTRAL_DIRECTORY]);
  665.     ecrec.zipfile_comment_length =
  666.         makeword(&byterec[ZIPFILE_COMMENT_LENGTH]);
  667.  
  668.     expect_ecrec_offset = ecrec.offset_start_central_directory +
  669.                            ecrec.size_central_directory;
  670.  
  671. /*---------------------------------------------------------------------------
  672.     Print out various interesting things about the zipfile.
  673.   ---------------------------------------------------------------------------*/
  674.  
  675.     /* header fits on one line, for anything up to 10GB and 10000 files: */
  676.     if (hflag)
  677.         printf((strlen(zipfn)<39)? "Archive:  %s   %ld bytes   %d file%s\n"
  678.           : "Archive:  %s   %ld   %d\n", zipfn, ziplen,
  679.           ecrec.total_entries_central_dir,
  680.           (ecrec.total_entries_central_dir==1)? "":"s");
  681.  
  682.     /* verbose format */
  683.     if (lflag > 9) {
  684.         printf("\nEnd-of-central-directory record:\n");
  685.         printf("-------------------------------\n\n");
  686.  
  687.         printf("\
  688.   Actual offset of end-of-central-dir record:   %9ld (%.8lXh)\n\
  689.   Expected offset of end-of-central-dir record: %9ld (%.8lXh)\n\
  690.   (based on the length of the central directory and its expected offset)\n\n",
  691.           expect_ecrec_offset, expect_ecrec_offset,
  692.           real_ecrec_offset, real_ecrec_offset);
  693.  
  694.         if (ecrec.number_this_disk == 0) {
  695.             printf("\
  696.   This zipfile constitutes the sole disk of a single-part archive; its\n\
  697.   central directory contains %u %s.  The central directory is %lu\n\
  698.   (%.8lXh) bytes long, and its (expected) offset in bytes from the\n\
  699.   beginning of the zipfile is %lu (%.8lXh).\n\n",
  700.               ecrec.total_entries_central_dir,
  701.               (ecrec.total_entries_central_dir == 1)? "entry" : "entries",
  702.               ecrec.size_central_directory, ecrec.size_central_directory,
  703.               ecrec.offset_start_central_directory,
  704.               ecrec.offset_start_central_directory);
  705.         } else {
  706.             printf("\
  707.   This zipfile constitutes disk %u of a multi-part archive.  The central\n\
  708.   directory starts on disk %u; %u of its entries %s contained within\n\
  709.   this zipfile, out of a total of %u %s.  The entire central\n\
  710.   directory is %lu (%.8lXh) bytes long, and its offset in bytes from\n\
  711.   the beginning of the zipfile in which it begins is %lu (%.8lXh).\n\n",
  712.               ecrec.number_this_disk,
  713.               ecrec.num_disk_with_start_central_dir,
  714.               ecrec.num_entries_centrl_dir_ths_disk,
  715.               (ecrec.num_entries_centrl_dir_ths_disk == 1)? "is" : "are",
  716.               ecrec.total_entries_central_dir,
  717.               (ecrec.total_entries_central_dir == 1) ? "entry" : "entries",
  718.               ecrec.size_central_directory, ecrec.size_central_directory,
  719.               ecrec.offset_start_central_directory,
  720.               ecrec.offset_start_central_directory);
  721.         }
  722.  
  723.     /*-----------------------------------------------------------------------
  724.         Get the zipfile comment, if any, and print it out.  (Comment may be
  725.         up to 64KB long.  May the fleas of a thousand camels infest the arm-
  726.         pits of anyone who actually takes advantage of this fact.)
  727.       -----------------------------------------------------------------------*/
  728.  
  729.         if (!ecrec.zipfile_comment_length)
  730.             printf("  There is no zipfile comment.\n");
  731.         else {
  732.             printf("  The zipfile comment is %u bytes long and contains the following text:\n\n",
  733.               ecrec.zipfile_comment_length );
  734.             printf("======================== zipfile comment begins ==========================\n");
  735.             if (do_string(ecrec.zipfile_comment_length, DISPLAY))
  736.                 error = 1;          /* 1:  warning error */
  737.             printf("\n========================= zipfile comment ends ===========================\n");
  738.             if (error)
  739.                 printf("\n  The zipfile comment is truncated.\n");
  740.         } /* endif (comment exists) */
  741.  
  742.     } /* endif (verbose) */
  743.  
  744.     return error;
  745.  
  746. } /* end function process_end_central_dir() */
  747.  
  748.  
  749.  
  750.  
  751.