    less the end-of-central-directory record was on this disk, and we would
    not have gotten to this routine unless this is also the disk on which
    the central directory starts.  In practice, this had better be the ONLY
    disk in the archive, but maybe someday we'll add multi-disk support.
  ---------------------------------------------------------------------------*/

    members_remaining = ecrec.total_entries_central_dir;
    while (members_remaining) {
        j = 0;

        /*
         * Loop through files in central directory, storing offsets, file
         * attributes, and case-conversion flags until block size is reached.
         */

        while (members_remaining && (j < DIR_BLKSIZ)) {
            --members_remaining;

            if (readbuf(sig, 4) <= 0) {
                error_in_archive = 51;  /* 51:  unexpected EOF */
                members_remaining = 0;  /* ...so no more left to do */
                break;
            }
            if (strncmp(sig, CENTRAL_HDR_SIG, 4)) {  /* just to make sure */
                fprintf(stderr, CentSigMsg, j);  /* sig not found */
                fprintf(stderr, ReportMsg);   /* report to info-zip */
                error_in_archive = 3;   /* 3:  error in zipfile */
                members_remaining = 0;  /* ...so no more left to do */
                break;
            }
            /* (sets lcflag)-------v  */
            if ((error = process_central_file_header()) != 0) {
                error_in_archive = error;   /* only 51 (EOF) defined */
                members_remaining = 0;  /* ...so no more left to do */
                break;
            }
            if ((error = do_string(crec.filename_length, FILENAME)) != 0) {
                if (error > error_in_archive)
                    error_in_archive = error;
                if (error > 1) {  /* fatal:  no more left to do */
                    fprintf(stderr, FilNamMsg, filename, "central");
                    members_remaining = 0;
                    break;
                }
            }
            if ((error = do_string(crec.extra_field_length, SKIP)) != 0) {
                if (error > error_in_archive)
                    error_in_archive = error;
                if (error > 1) {  /* fatal */
                    fprintf(stderr, ExtFieldMsg, filename, "central");
                    members_remaining = 0;
                    break;
                }
            }
            if ((error = do_string(crec.file_comment_length, SKIP)) != 0) {
                if (error > error_in_archive)
                    error_in_archive = error;
                if (error > 1) {  /* fatal */
                    fprintf(stderr, "\n%s:  bad file comment length\n",
                            filename);
                    members_remaining = 0;
                    break;
                }
            }
            if (process_all_files) {
                if (crec.general_purpose_bit_flag & 1) {
                    fprintf(stderr, CryptMsg, filename);  /* encrypted: skip */
                    error_in_archive = 1;       /* 1:  warning error */
                } else {
                    ULONG tmp = ULONG_(crec.external_file_attributes);

                    switch (hostnum) {
                        case UNIX_:
                          info[j].f_attr = tmp >> 16;
                          break;
                        case DOS_OS2_FAT_:
                          tmp = (!(tmp & 1)) << 1;   /* read-only bit */
                          info[j].f_attr = 0444 | tmp<<6 | tmp<<3 | tmp;
#ifdef UNIX
                          umask((int)(tmp = umask(0)));
                          info[j].f_attr &= ~tmp;
#endif
                          break;
                        case MAC_:
                          tmp &= 1;   /* read-only bit */
                          info[j].f_attr = tmp;
                          break;
                        default:
                          info[j].f_attr = 0666;
                          break;
                    }
                    info[j].lcflag = lcflag;
                    info[j++].offset = (longint)
                        ULONG_(crec.relative_offset_local_header);
                }
            } else {
                fnamev = fnv;   /* don't destroy permanent filename pointer */
                for (--fnamev; *++fnamev;)
                    if (match(filename, *fnamev)) {
                        if (crec.version_needed_to_extract[0] > UNZIP_VERSION) {
                            fprintf(stderr, "%s:  requires compatibility with\
 version %u.%u to extract\n       (this handles %u.%u)--skipping.\n", filename,
                                crec.version_needed_to_extract[0] / 10,
                                crec.version_needed_to_extract[0] % 10,
                                UNZIP_VERSION / 10, UNZIP_VERSION % 10);
                            error_in_archive = 1;       /* 1:  warning error */
                        } else if (crec.general_purpose_bit_flag & 1) {
                            fprintf(stderr, CryptMsg, filename);  /* encrypt */
                            error_in_archive = 1;       /* 1:  warning error */
                        } else {
                            ULONG tmp = ULONG_(crec.external_file_attributes);

                            switch (hostnum) {
                                case UNIX_:
                                  info[j].f_attr = tmp >> 16;
                                  break;
                                case DOS_OS2_FAT_:
                                  tmp = (!(tmp & 1)) << 1;  /* read-only bit */
                                  info[j].f_attr = 0444 | tmp<<6 | tmp<<3 | tmp;
#ifdef UNIX
                                  umask((int)(tmp = umask(0)));
                                  info[j].f_attr &= ~tmp;
#endif
                                  break;
                                case MAC_:
                                  tmp &= 1;   /* read-only bit */
                                  info[j].f_attr = tmp;
                                  break;
                                default:
                                  info[j].f_attr = 0666;
                                  break;
                            }
                            info[j].lcflag = lcflag;
                            info[j++].offset = (longint)
                                ULONG_(crec.relative_offset_local_header);
                        }
                        break;  /* found match for filename, so stop looping */

                    }   /* end if (match), for-loop (fnamev) */
            }           /* end if (process_all_files) */
        }               /* end while-loop (adding files to current block) */

        /* save position in central directory so can come back later */
        cd_bufstart = cur_zipfile_bufstart;
        cd_inptr = inptr;
        cd_incnt = incnt;

        /*
         * Loop through files in block, extracting or testing each one.
         */

        for (i = 0; i < j; ++i) {
            /*
             * if the target position is not within the current input buffer
             * (either haven't yet read far enough, or (maybe) skipping back-
             * ward) skip to the target position and reset readbuf().
             */

            /* LSEEK(info[i].offset):  */
            inbuf_offset = info[i].offset % INBUFSIZ;
            bufstart = info[i].offset - inbuf_offset;

            if (bufstart != cur_zipfile_bufstart) {
                cur_zipfile_bufstart = lseek(zipfd, bufstart, SEEK_SET);
                if ((incnt = read(zipfd,inbuf,INBUFSIZ)) <= 0) {
                    fprintf(stderr, OffsetMsg, filename, "lseek");
                    error_in_archive = 3;   /* 3:  error in zipfile, but */
                    continue;               /*  can still do next file   */
                }
                inptr = inbuf + inbuf_offset;
                incnt -= inbuf_offset;
            } else {
                incnt += (inptr-inbuf) - inbuf_offset;
                inptr = inbuf + inbuf_offset;
            }
            lcflag = info[i].lcflag;
            f_attr = info[i].f_attr;

            /* should be in proper position now, so check for sig */
            if (readbuf(sig, 4) <= 0) {
                fprintf(stderr, OffsetMsg, filename, "EOF");  /* bad offset */
                error_in_archive = 3;   /* 3:  error in zipfile */
                continue;       /* but can still do next one */
            }
            if (strncmp(sig, LOCAL_HDR_SIG, 4)) {
                fprintf(stderr, OffsetMsg, filename,
                        "can't find local header sig");   /* bad offset */
                error_in_archive = 3;
                continue;
            }
            if ((error = process_local_file_header()) != 0) {
                fprintf(stderr, "\n%s:  bad local header\n", filename);
                error_in_archive = error;       /* only 51 (EOF) defined */
                continue;       /* can still do next one */
            }
            if ((error = do_string(lrec.filename_length, FILENAME)) != 0) {
                if (error > error_in_archive)
                    error_in_archive = error;
                if (error > 1) {
                    fprintf(stderr, FilNamMsg, filename, "local");
                    continue;   /* go on to next one */
                }
            }
            if ((error = do_string(lrec.extra_field_length, SKIP)) != 0) {
                if (error > error_in_archive)
                    error_in_archive = error;
                if (error > 1) {
                    fprintf(stderr, ExtFieldMsg, filename, "local");
                    continue;   /* go on */
                }
            }
            if ((error = extract_or_test_member()) != 0)
                if (error > error_in_archive)
                    error_in_archive = error;       /* ...and keep going */

        }                       /* end for-loop (i:  files in current block) */

        /*
         * Jump back to where we were in the central directory, then go and do
         * the next batch of files.
         */

        cur_zipfile_bufstart = lseek(zipfd, cd_bufstart, SEEK_SET);
        read(zipfd, inbuf, INBUFSIZ);   /* were already there ==> no error */
        inptr = cd_inptr;
        incnt = cd_incnt;

#ifdef TEST
        printf("\ncd_bufstart = %ld (%.8lXh)\n", cd_bufstart, cd_bufstart);
        printf("cur_zipfile_bufstart = %ld (%.8lXh)\n", cur_zipfile_bufstart,
          cur_zipfile_bufstart);
        printf("inptr-inbuf = %d\n", inptr-inbuf);
        printf("incnt = %d\n\n", incnt);
#endif

    }           /* end while-loop (blocks of files in central directory) */

/*---------------------------------------------------------------------------
    Double check that we're back at the end-of-central-directory record, and
    print quick summary of results, if we were just testing the archive.  We
    send the summary to stdout so that people doing the testing in the back-
    ground and redirecting to a file can just do a "tail" on the output file.
  ---------------------------------------------------------------------------*/

    readbuf(sig, 4);
    if (strncmp(sig, END_CENTRAL_SIG, 4)) {     /* just to make sure again */
        fprintf(stderr, EndSigMsg);  /* didn't find end-of-central-dir sig */
        fprintf(stderr, ReportMsg);  /* report to info-zip */
        if (!error_in_archive)       /* don't overwrite stronger error */
            error_in_archive = 1;    /* 1:  warning error */
    }
    if (tflag && (quietflg == 1)) {
        if (error_in_archive)
            printf("At least one error was detected in the archive.\n");
        else if (process_all_files)
            printf("No errors detected.\n");
        else
            printf("No errors detected in the tested files.\n");
    }
    return (error_in_archive);

}       /* end function extract_or_test_files() */





/**************************************/
/*  Function extract_or_test_member() */
/**************************************/

int extract_or_test_member()
/* return PK-type error code */
{
    int error = 0;
    UWORD b;



/*---------------------------------------------------------------------------
    Initialize variables, buffers, etc.
  ---------------------------------------------------------------------------*/

    bits_left = 0;
    bitbuf = 0;
    outpos = 0L;
    outcnt = 0;
    outptr = outbuf;
    zipeof = 0;
    crc32val = 0xFFFFFFFFL;

    memset(outbuf, 0, OUTBUFSIZ);
    if (aflag)                  /* if we have a scratchpad, clear it out */
        memset(outout, 0, OUTBUFSIZ);

    if (tflag) {
        if (!quietflg) {
            fprintf(stdout, "  Testing: %-12s ", filename);
            fflush(stdout);
        }
    } else {
        if (cflag)              /* output to stdout (copy of it) */
#ifdef MACOS
            outfd = 1;
#else
            outfd = dup(1);
#endif
        else {
            if ((error = mapped_name()) > 1)  /* member name conversion error */
                return (2);     /* 2:  (weak) error in zipfile */
            if (create_output_file())   /* output to file:  read/write perms */
                return (50);    /* 50:  disk full */
        }
    }                           /* endif (!tflag) */

/*---------------------------------------------------------------------------
    Unpack the file.
  ---------------------------------------------------------------------------*/

    switch (lrec.compression_method) {

    case STORED:
        if (!tflag && QCOND) {
            fprintf(stdout, " Extracting: %-12s ", filename);
            if (cflag)
                fprintf(stdout, "\n");
            fflush(stdout);
        }
        while (ReadByte(&b))
            OUTB(b);
        break;

    case SHRUNK:
        if (!tflag && QCOND) {
            fprintf(stdout, "UnShrinking: %-12s ", filename);
            if (cflag)
                fprintf(stdout, "\n");
            fflush(stdout);
        }
        unShrink();
        break;

    case REDUCED1:
    case REDUCED2:
    case REDUCED3:
    case REDUCED4:
        if (!tflag && QCOND) {
            fprintf(stdout, "  Expanding: %-12s ", filename);
            if (cflag)
                fprintf(stdout, "\n");
            fflush(stdout);
        }
        unReduce();
        break;

    case IMPLODED:
        if (!tflag && QCOND) {
            fprintf(stdout, "  Exploding: %-12s ", filename);
            if (cflag)
                fprintf(stdout, "\n");
            fflush(stdout);
        }
        unImplode();
        break;

    default:
        fprintf(stderr, "%s:  unknown compression method\n", filename);
        /* close and delete file before return?? */
        return (1);             /* 1:  warning error */
    }

/*---------------------------------------------------------------------------
    Write the last partial buffer, if any; set the file date and time; and
    close the file (not necessarily in that order).  Then make sure CRC came
    out OK and print result.  [Note:  crc32val must be logical-ANDed with
    32 bits of 1's, or else machines whose longs are bigger than 32 bits will
    report bad CRCs (because of the upper bits being filled with 1's instead
    of 0's).]
  ---------------------------------------------------------------------------*/

    if (FlushOutput())
        return (50);            /* 50:  disk full */

    if (!tflag)
#if defined(VMS) || defined(MTS)/* VMS already set file time; MTS can't */
        close(outfd);
#else
        set_file_time_and_close();
#endif

    if ((crc32val = ((~crc32val) & 0xFFFFFFFFL)) != ULONG_(lrec.crc32)) {
        /* if quietflg is set we haven't output the filename yet, do it */
        if (quietflg)
            printf("%-12s: ", filename);
        fprintf(stdout, " Bad CRC %08lx  (should be %08lx)\n", crc32val,
                ULONG_(lrec.crc32));
        return (1);             /* 1:  warning error */
    } else if (tflag) {
        if (!quietflg)
            fprintf(stdout, " OK\n");
    } else {
        if (QCOND)
            fprintf(stdout, "\n");
    }

    return (error);

}       /* end function extract_or_test_member() */





/*******************************************/
/*  Function process_central_file_header() */
/*******************************************/

int process_central_file_header()
/* return PK-type error code */
{
#ifdef NOTINT16
    central_directory_byte_header byterec;
#endif



/*---------------------------------------------------------------------------
    Read the next central directory entry and do any necessary machine-type
    conversions (byte ordering, structure padding compensation--in the latter
    case, copy the data from the array into which it was read (byterec) to
    the usable struct (crec)).
  ---------------------------------------------------------------------------*/

#ifndef NOTINT16
    if (readbuf((char *) &crec, CREC_SIZE) <= 0)
        return (51);

#else  /* NOTINT16 */
    if (readbuf((char *) byterec, CREC_SIZE) <= 0)
        return (51);            /* 51:  unexpected EOF */

    crec.version_made_by[0] = byterec[C_VERSION_MADE_BY_0];
    crec.version_made_by[1] = byterec[C_VERSION_MADE_BY_1];
    crec.version_needed_to_extract[0] = byterec[C_VERSION_NEEDED_TO_EXTRACT_0];
    crec.version_needed_to_extract[1] = byterec[C_VERSION_NEEDED_TO_EXTRACT_1];

    crec.general_purpose_bit_flag =
        makeword(&byterec[C_GENERAL_PURPOSE_BIT_FLAG]);
    crec.compression_method =
        makeword(&byterec[C_COMPRESSION_METHOD]);
    crec.last_mod_file_time =
        makeword(&byterec[C_LAST_MOD_FILE_TIME]);
    crec.last_mod_file_date =
        makeword(&byterec[C_LAST_MOD_FILE_DATE]);
    crec.crc32 =
        makelong(&byterec[C_CRC32]);
    crec.compressed_size =
        makelong(&byterec[C_COMPRESSED_SIZE]);
    crec.uncompressed_size =
        makelong(&byterec[C_UNCOMPRESSED_SIZE]);
    crec.filename_length =
        makeword(&byterec[C_FILENAME_LENGTH]);
    crec.extra_field_length =
        makeword(&byterec[C_EXTRA_FIELD_LENGTH]);
    crec.file_comment_length =
        makeword(&byterec[C_FILE_COMMENT_LENGTH]);
    crec.disk_number_start =
        makeword(&byterec[C_DISK_NUMBER_START]);
    crec.internal_file_attributes =
        makeword(&byterec[C_INTERNAL_FILE_ATTRIBUTES]);
    crec.external_file_attributes =
        makelong(&byterec[C_EXTERNAL_FILE_ATTRIBUTES]); /* LONG, not word! */
    crec.relative_offset_local_header =
        makelong(&byterec[C_RELATIVE_OFFSET_LOCAL_HEADER]);
#endif  /* NOTINT16 */

    hostnum = min(crec.version_made_by[1], NUM_HOSTS);
/*  extnum = min( crec.version_needed_to_extract[1], NUM_HOSTS ); */
    methnum = min(crec.compression_method, NUM_METHODS);

/*---------------------------------------------------------------------------
    Set flag for lowercase conversion of filename, depending on which OS the
    file is coming from.  This section could be ifdef'd if some people have
    come to love DOS uppercase filenames under Unix...but really, guys, get
    a life. :)  NOTE THAT ALL SYSTEM NAMES NOW HAVE TRAILING UNDERSCORES!!!
    This is to prevent interference with compiler command-line defines such
    as -DUNIX, for example, which are then used in "#ifdef UNIX" constructs.
  ---------------------------------------------------------------------------*/

    lcflag = FALSE;
    if (!Uflag)                 /* as long as user hasn't specified
                                 * case-preservation */
        switch (hostnum) {
        case DOS_OS2_FAT_:
        case VMS_:
        case VM_CMS_:           /* all caps? */
        case CPM_:              /* like DOS, right? */
/*      case ATARI_:            ??? */
/*      case Z_SYSTEM_:         ??? */
/*      case TOPS20_:           (if we had such a thing...) */

            lcflag = TRUE;      /* convert filename to lowercase */
            break;

        default:                /* AMIGA_, UNIX_, (ATARI_), OS2_HPFS_, */
            break;              /*   MAC_, (Z_SYSTEM_):  no conversion */
        }

    return (0);

}       /* end function process_central_file_header() */





/*****************************************/
/*  Function process_local_file_header() */
/*****************************************/

int process_local_file_header()
/* return PK-type error code */
{
#ifdef NOTINT16
    local_byte_header byterec;
#endif



/*---------------------------------------------------------------------------
    Read the next local file header and do any necessary machine-type con-
    versions (byte ordering, structure padding compensation--in the latter
    case, copy the data from the array into which it was read (byterec) to
    the usable struct (lrec)).
  ---------------------------------------------------------------------------*/

#ifndef NOTINT16
    if (readbuf((char *) &lrec, LREC_SIZE) <= 0)
        return (51);

#else /* NOTINT16 */
    if (readbuf((char *) byterec, LREC_SIZE) <= 0)
        return (51);            /* 51:  unexpected EOF */

    lrec.version_needed_to_extract[0] = byterec[L_VERSION_NEEDED_TO_EXTRACT_0];
    lrec.version_needed_to_extract[1] = byterec[L_VERSION_NEEDED_TO_EXTRACT_1];

    lrec.general_purpose_bit_flag = makeword(&byterec[L_GENERAL_PURPOSE_BIT_FLAG]);
    lrec.compression_method = makeword(&byterec[L_COMPRESSION_METHOD]);
    lrec.last_mod_file_time = makeword(&byterec[L_LAST_MOD_FILE_TIME]);
    lrec.last_mod_file_date = makeword(&byterec[L_LAST_MOD_FILE_DATE]);
    lrec.crc32 = makelong(&byterec[L_CRC32]);
    lrec.compressed_size = makelong(&byterec[L_COMPRESSED_SIZE]);
    lrec.uncompressed_size = makelong(&byterec[L_UNCOMPRESSED_SIZE]);
    lrec.filename_length = makeword(&byterec[L_FILENAME_LENGTH]);
    lrec.extra_field_length = makeword(&byterec[L_EXTRA_FIELD_LENGTH]);
#endif  /* NOTINT16 */

    csize = (longint) ULONG_(lrec.compressed_size);
    ucsize = (longint) ULONG_(lrec.uncompressed_size);

    return (0);                 /* 0:  no error */

}       /* end function process_local_file_header() */
