"Fossies" - the Fresh Open Source Software Archive

Member "libzip-1.6.0/src/zipcmp.c" (24 Jan 2020, 17499 Bytes) of package /linux/misc/libzip-1.6.0.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "zipcmp.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.5.2_vs_1.6.0.

    1 /*
    2   zipcmp.c -- compare zip files
    3   Copyright (C) 2003-2019 Dieter Baron and Thomas Klausner
    4 
    5   This file is part of libzip, a library to manipulate ZIP archives.
    6   The authors can be contacted at <libzip@nih.at>
    7 
    8   Redistribution and use in source and binary forms, with or without
    9   modification, are permitted provided that the following conditions
   10   are met:
   11   1. Redistributions of source code must retain the above copyright
   12      notice, this list of conditions and the following disclaimer.
   13   2. Redistributions in binary form must reproduce the above copyright
   14      notice, this list of conditions and the following disclaimer in
   15      the documentation and/or other materials provided with the
   16      distribution.
   17   3. The names of the authors may not be used to endorse or promote
   18      products derived from this software without specific prior
   19      written permission.
   20 
   21   THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
   22   OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
   23   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   24   ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
   25   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   26   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
   27   GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   28   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
   29   IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
   30   OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
   31   IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   32 */
   33 
   34 
   35 #include "config.h"
   36 
   37 #include <errno.h>
   38 #include <stdio.h>
   39 #include <stdlib.h>
   40 #include <string.h>
   41 #include <sys/stat.h>
   42 #ifdef HAVE_STRINGS_H
   43 #include <strings.h>
   44 #endif
   45 #ifdef HAVE_UNISTD_H
   46 #include <unistd.h>
   47 #endif
   48 #ifdef HAVE_FTS_H
   49 #include <fts.h>
   50 #endif
   51 #include <zlib.h>
   52 
   53 #ifndef HAVE_GETOPT
   54 #include "getopt.h"
   55 #endif
   56 
   57 #include "compat.h"
   58 #include "zip.h"
   59 
   60 struct archive {
   61     const char *name;
   62     zip_t *za;
   63     zip_uint64_t nentry;
   64     struct entry *entry;
   65     const char *comment;
   66     size_t comment_length;
   67 };
   68 
   69 struct ef {
   70     const char *name;
   71     zip_uint16_t flags;
   72     zip_uint16_t id;
   73     zip_uint16_t size;
   74     const zip_uint8_t *data;
   75 };
   76 
   77 struct entry {
   78     char *name;
   79     zip_uint64_t size;
   80     zip_uint32_t crc;
   81     zip_uint32_t comp_method;
   82     struct ef *extra_fields;
   83     zip_uint16_t n_extra_fields;
   84     const char *comment;
   85     zip_uint32_t comment_length;
   86 };
   87 
   88 
   89 const char *progname;
   90 
   91 #define PROGRAM "zipcmp"
   92 
   93 #define USAGE "usage: %s [-hipqtVv] archive1 archive2\n"
   94 
   95 char help_head[] = PROGRAM " (" PACKAGE ") by Dieter Baron and Thomas Klausner\n\n";
   96 
   97 char help[] = "\n\
   98   -h       display this help message\n\
   99   -i       compare names ignoring case distinctions\n\
  100   -p       compare as many details as possible\n\
  101   -q       be quiet\n\
  102   -t       test zip files (compare file contents to checksum)\n\
  103   -V       display version number\n\
  104   -v       be verbose (print differences, default)\n\
  105 \n\
  106 Report bugs to <libzip@nih.at>.\n";
  107 
  108 char version_string[] = PROGRAM " (" PACKAGE " " VERSION ")\n\
  109 Copyright (C) 2003-2019 Dieter Baron and Thomas Klausner\n\
  110 " PACKAGE " comes with ABSOLUTELY NO WARRANTY, to the extent permitted by law.\n";
  111 
  112 #define OPTIONS "hVipqtv"
  113 
  114 
  115 #define BOTH_ARE_ZIPS(a) (a[0].za && a[1].za)
  116 
  117 static int comment_compare(const char *c1, size_t l1, const char *c2, size_t l2);
  118 static int compare_list(char *const name[], const void *l[], const zip_uint64_t n[], int size, int (*cmp)(const void *, const void *), int (*checks)(char *const name[2], const void *, const void *), void (*print)(const void *));
  119 static int compare_zip(char *const zn[]);
  120 static int ef_compare(char *const name[2], const struct entry *e1, const struct entry *e2);
  121 static int ef_order(const void *a, const void *b);
  122 static void ef_print(const void *p);
  123 static int ef_read(zip_t *za, zip_uint64_t idx, struct entry *e);
  124 static int entry_cmp(const void *p1, const void *p2);
  125 static int entry_paranoia_checks(char *const name[2], const void *p1, const void *p2);
  126 static void entry_print(const void *p);
  127 static int is_directory(const char *name);
  128 #ifdef HAVE_FTS_H
  129 static int list_directory(const char *name, struct archive *a);
  130 #endif
  131 static int list_zip(const char *name, struct archive *a);
  132 static int test_file(zip_t *za, zip_uint64_t idx, zip_uint64_t size, zip_uint32_t crc);
  133 
  134 int ignore_case, test_files, paranoid, verbose;
  135 int header_done;
  136 
  137 
  138 int
  139 main(int argc, char *const argv[]) {
  140     int c;
  141 
  142     progname = argv[0];
  143 
  144     ignore_case = 0;
  145     test_files = 0;
  146     paranoid = 0;
  147     verbose = 1;
  148 
  149     while ((c = getopt(argc, argv, OPTIONS)) != -1) {
  150     switch (c) {
  151     case 'i':
  152         ignore_case = 1;
  153         break;
  154     case 'p':
  155         paranoid = 1;
  156         break;
  157     case 'q':
  158         verbose = 0;
  159         break;
  160     case 't':
  161         test_files = 1;
  162         break;
  163     case 'v':
  164         verbose = 1;
  165         break;
  166 
  167     case 'h':
  168         fputs(help_head, stdout);
  169         printf(USAGE, progname);
  170         fputs(help, stdout);
  171         exit(0);
  172     case 'V':
  173         fputs(version_string, stdout);
  174         exit(0);
  175 
  176     default:
  177         fprintf(stderr, USAGE, progname);
  178         exit(2);
  179     }
  180     }
  181 
  182     if (argc != optind + 2) {
  183     fprintf(stderr, USAGE, progname);
  184     exit(2);
  185     }
  186 
  187     exit((compare_zip(argv + optind) == 0) ? 0 : 1);
  188 }
  189 
  190 
  191 static int
  192 compare_zip(char *const zn[]) {
  193     struct archive a[2];
  194     struct entry *e[2];
  195     zip_uint64_t n[2];
  196     int i;
  197     int res;
  198 
  199     for (i = 0; i < 2; i++) {
  200     a[i].name = zn[i];
  201     a[i].entry = NULL;
  202     a[i].nentry = 0;
  203     a[i].za = NULL;
  204     a[i].comment = NULL;
  205     a[i].comment_length = 0;
  206 
  207     if (is_directory(zn[i])) {
  208 #ifndef HAVE_FTS_H
  209         fprintf(stderr, "%s: reading directories not supported\n", progname);
  210         exit(2);
  211 #else
  212         if (list_directory(zn[i], a + i) < 0)
  213         exit(2);
  214         paranoid = 0; /* paranoid checks make no sense for directories, since they compare zip metadata */
  215 #endif
  216     }
  217     else {
  218         if (list_zip(zn[i], a + i) < 0)
  219         exit(2);
  220     }
  221     if (a[i].nentry > 0)
  222         qsort(a[i].entry, a[i].nentry, sizeof(a[i].entry[0]), entry_cmp);
  223     }
  224 
  225     header_done = 0;
  226 
  227     e[0] = a[0].entry;
  228     e[1] = a[1].entry;
  229     n[0] = a[0].nentry;
  230     n[1] = a[1].nentry;
  231     res = compare_list(zn, (const void **)e, n, sizeof(e[i][0]), entry_cmp, paranoid ? entry_paranoia_checks : NULL, entry_print);
  232 
  233     if (paranoid) {
  234     if (comment_compare(a[0].comment, a[0].comment_length, a[1].comment, a[1].comment_length) != 0) {
  235         if (verbose) {
  236         printf("--- archive comment (%zd)\n", a[0].comment_length);
  237         printf("+++ archive comment (%zd)\n", a[1].comment_length);
  238         }
  239         res = 1;
  240     }
  241     }
  242 
  243     for (i = 0; i < 2; i++) {
  244     zip_uint64_t j;
  245 
  246     if (a[i].za) {
  247         zip_close(a[i].za);
  248     }
  249     for (j = 0; j < a[i].nentry; j++) {
  250         free(a[i].entry[j].name);
  251         free(a[i].entry[j].extra_fields);
  252     }
  253     free(a[i].entry);
  254     }
  255 
  256     switch (res) {
  257     case 0:
  258     exit(0);
  259 
  260     case 1:
  261     exit(1);
  262 
  263     default:
  264     exit(2);
  265     }
  266 }
  267 
  268 #ifdef HAVE_FTS_H
  269 static zip_int64_t
  270 compute_crc(const char *fname) {
  271     FILE *f;
  272     uLong crc = crc32(0L, Z_NULL, 0);
  273     size_t n;
  274     Bytef buffer[8192];
  275 
  276 
  277     if ((f = fopen(fname, "r")) == NULL) {
  278     fprintf(stderr, "%s: can't open %s: %s\n", progname, fname, strerror(errno));
  279     return -1;
  280     }
  281 
  282     while ((n = fread(buffer, 1, sizeof(buffer), f)) > 0) {
  283     crc = crc32(crc, buffer, (unsigned int)n);
  284     }
  285 
  286     if (ferror(f)) {
  287     fprintf(stderr, "%s: read error on %s: %s\n", progname, fname, strerror(errno));
  288     fclose(f);
  289     return -1;
  290     }
  291 
  292     fclose(f);
  293 
  294     return (zip_int64_t)crc;
  295 }
  296 #endif
  297 
  298 
  299 static int
  300 is_directory(const char *name) {
  301     struct stat st;
  302 
  303     if (stat(name, &st) < 0)
  304     return 0;
  305 
  306     return S_ISDIR(st.st_mode);
  307 }
  308 
  309 
  310 #ifdef HAVE_FTS_H
  311 static int
  312 list_directory(const char *name, struct archive *a) {
  313     FTS *fts;
  314     FTSENT *ent;
  315     zip_uint64_t nalloc;
  316     size_t prefix_length;
  317 
  318     char *const names[2] = {(char *)name, NULL};
  319 
  320 
  321     if ((fts = fts_open(names, FTS_NOCHDIR | FTS_LOGICAL, NULL)) == NULL) {
  322     fprintf(stderr, "%s: can't open directory '%s': %s\n", progname, name, strerror(errno));
  323     return -1;
  324     }
  325     prefix_length = strlen(name) + 1;
  326 
  327     nalloc = 0;
  328 
  329     while ((ent = fts_read(fts))) {
  330     zip_int64_t crc;
  331 
  332     switch (ent->fts_info) {
  333     case FTS_D:
  334     case FTS_DOT:
  335     case FTS_DP:
  336     case FTS_DEFAULT:
  337     case FTS_SL:
  338     case FTS_NSOK:
  339         break;
  340 
  341     case FTS_DC:
  342     case FTS_DNR:
  343     case FTS_ERR:
  344     case FTS_NS:
  345     case FTS_SLNONE:
  346         /* TODO: error */
  347         fts_close(fts);
  348         return -1;
  349 
  350     case FTS_F:
  351         if (a->nentry >= nalloc) {
  352         nalloc += 16;
  353         if (nalloc > SIZE_MAX / sizeof(a->entry[0])) {
  354             fprintf(stderr, "%s: malloc failure\n", progname);
  355             exit(1);
  356         }
  357         a->entry = realloc(a->entry, sizeof(a->entry[0]) * nalloc);
  358         if (a->entry == NULL) {
  359             fprintf(stderr, "%s: malloc failure\n", progname);
  360             exit(1);
  361         }
  362         }
  363 
  364         a->entry[a->nentry].name = strdup(ent->fts_path + prefix_length);
  365         a->entry[a->nentry].size = (zip_uint64_t)ent->fts_statp->st_size;
  366         if ((crc = compute_crc(ent->fts_accpath)) < 0) {
  367         fts_close(fts);
  368         return -1;
  369         }
  370 
  371         a->entry[a->nentry].crc = (zip_uint32_t)crc;
  372         a->nentry++;
  373         break;
  374     }
  375     }
  376 
  377     if (fts_close(fts)) {
  378     fprintf(stderr, "%s: error closing directory '%s': %s\n", progname, a->name, strerror(errno));
  379     return -1;
  380     }
  381 
  382     return 0;
  383 }
  384 #endif
  385 
  386 
  387 static int
  388 list_zip(const char *name, struct archive *a) {
  389     zip_t *za;
  390     int err;
  391     struct zip_stat st;
  392     unsigned int i;
  393 
  394     if ((za = zip_open(name, paranoid ? ZIP_CHECKCONS : 0, &err)) == NULL) {
  395     zip_error_t error;
  396     zip_error_init_with_code(&error, err);
  397     fprintf(stderr, "%s: cannot open zip archive '%s': %s\n", progname, name, zip_error_strerror(&error));
  398     zip_error_fini(&error);
  399     return -1;
  400     }
  401 
  402     a->za = za;
  403     a->nentry = (zip_uint64_t)zip_get_num_entries(za, 0);
  404 
  405     if (a->nentry == 0)
  406     a->entry = NULL;
  407     else {
  408     if ((a->nentry > SIZE_MAX / sizeof(a->entry[0])) || (a->entry = (struct entry *)malloc(sizeof(a->entry[0]) * a->nentry)) == NULL) {
  409         fprintf(stderr, "%s: malloc failure\n", progname);
  410         exit(1);
  411     }
  412 
  413     for (i = 0; i < a->nentry; i++) {
  414         zip_stat_index(za, i, 0, &st);
  415         a->entry[i].name = strdup(st.name);
  416         a->entry[i].size = st.size;
  417         a->entry[i].crc = st.crc;
  418         if (test_files)
  419         test_file(za, i, st.size, st.crc);
  420         if (paranoid) {
  421         a->entry[i].comp_method = st.comp_method;
  422         ef_read(za, i, a->entry + i);
  423         a->entry[i].comment = zip_file_get_comment(za, i, &a->entry[i].comment_length, 0);
  424         }
  425         else {
  426         a->entry[i].comp_method = 0;
  427         a->entry[i].n_extra_fields = 0;
  428         }
  429     }
  430 
  431     if (paranoid) {
  432         int length;
  433         a->comment = zip_get_archive_comment(za, &length, 0);
  434         a->comment_length = (size_t)length;
  435     }
  436     else {
  437         a->comment = NULL;
  438         a->comment_length = 0;
  439     }
  440     }
  441 
  442     return 0;
  443 }
  444 
  445 
  446 static int
  447 comment_compare(const char *c1, size_t l1, const char *c2, size_t l2) {
  448     if (l1 != l2)
  449     return 1;
  450 
  451     if (l1 == 0)
  452     return 0;
  453 
  454     if (c1 == NULL || c2 == NULL)
  455     return c1 == c2;
  456 
  457     return memcmp(c1, c2, (size_t)l2);
  458 }
  459 
  460 
  461 static int
  462 compare_list(char *const name[2], const void *l[2], const zip_uint64_t n[2], int size, int (*cmp)(const void *, const void *), int (*check)(char *const name[2], const void *, const void *), void (*print)(const void *)) {
  463     unsigned int i[2];
  464     int j, c;
  465     int diff;
  466 
  467 #define INC(k) (i[k]++, l[k] = ((const char *)l[k]) + size)
  468 #define PRINT(k)                                          \
  469     do {                                                  \
  470     if (header_done == 0 && verbose) {                \
  471         printf("--- %s\n+++ %s\n", name[0], name[1]); \
  472         header_done = 1;                              \
  473     }                                                 \
  474     if (verbose) {                                    \
  475         printf("%c ", (k) ? '+' : '-');               \
  476         print(l[k]);                                  \
  477     }                                                 \
  478     diff = 1;                                         \
  479     } while (0)
  480 
  481     i[0] = i[1] = 0;
  482     diff = 0;
  483     while (i[0] < n[0] && i[1] < n[1]) {
  484     c = cmp(l[0], l[1]);
  485 
  486     if (c == 0) {
  487         if (check)
  488         diff |= check(name, l[0], l[1]);
  489         INC(0);
  490         INC(1);
  491     }
  492     else if (c < 0) {
  493         PRINT(0);
  494         INC(0);
  495     }
  496     else {
  497         PRINT(1);
  498         INC(1);
  499     }
  500     }
  501 
  502     for (j = 0; j < 2; j++) {
  503     while (i[j] < n[j]) {
  504         PRINT(j);
  505         INC(j);
  506     }
  507     }
  508 
  509     return diff;
  510 }
  511 
  512 
  513 static int
  514 ef_read(zip_t *za, zip_uint64_t idx, struct entry *e) {
  515     zip_int16_t n_local, n_central;
  516     zip_uint16_t i;
  517 
  518     if ((n_local = zip_file_extra_fields_count(za, idx, ZIP_FL_LOCAL)) < 0 || (n_central = zip_file_extra_fields_count(za, idx, ZIP_FL_CENTRAL)) < 0) {
  519     return -1;
  520     }
  521 
  522     e->n_extra_fields = (zip_uint16_t)(n_local + n_central);
  523 
  524     if ((e->extra_fields = (struct ef *)malloc(sizeof(e->extra_fields[0]) * e->n_extra_fields)) == NULL)
  525     return -1;
  526 
  527     for (i = 0; i < n_local; i++) {
  528     e->extra_fields[i].name = e->name;
  529     e->extra_fields[i].data = zip_file_extra_field_get(za, idx, i, &e->extra_fields[i].id, &e->extra_fields[i].size, ZIP_FL_LOCAL);
  530     if (e->extra_fields[i].data == NULL)
  531         return -1;
  532     e->extra_fields[i].flags = ZIP_FL_LOCAL;
  533     }
  534     for (; i < e->n_extra_fields; i++) {
  535     e->extra_fields[i].name = e->name;
  536     e->extra_fields[i].data = zip_file_extra_field_get(za, idx, (zip_uint16_t)(i - n_local), &e->extra_fields[i].id, &e->extra_fields[i].size, ZIP_FL_CENTRAL);
  537     if (e->extra_fields[i].data == NULL)
  538         return -1;
  539     e->extra_fields[i].flags = ZIP_FL_CENTRAL;
  540     }
  541 
  542     qsort(e->extra_fields, e->n_extra_fields, sizeof(e->extra_fields[0]), ef_order);
  543 
  544     return 0;
  545 }
  546 
  547 
  548 static int
  549 ef_compare(char *const name[2], const struct entry *e1, const struct entry *e2) {
  550     struct ef *ef[2];
  551     zip_uint64_t n[2];
  552 
  553     ef[0] = e1->extra_fields;
  554     ef[1] = e2->extra_fields;
  555     n[0] = e1->n_extra_fields;
  556     n[1] = e2->n_extra_fields;
  557 
  558     return compare_list(name, (const void **)ef, n, sizeof(struct ef), ef_order, NULL, ef_print);
  559 }
  560 
  561 
  562 static int
  563 ef_order(const void *ap, const void *bp) {
  564     const struct ef *a, *b;
  565 
  566     a = (struct ef *)ap;
  567     b = (struct ef *)bp;
  568 
  569     if (a->flags != b->flags)
  570     return a->flags - b->flags;
  571     if (a->id != b->id)
  572     return a->id - b->id;
  573     if (a->size != b->size)
  574     return a->size - b->size;
  575     return memcmp(a->data, b->data, a->size);
  576 }
  577 
  578 
  579 static void
  580 ef_print(const void *p) {
  581     const struct ef *ef = (struct ef *)p;
  582     int i;
  583 
  584     printf("                    %s  ", ef->name);
  585     printf("%04x %c <", ef->id, ef->flags == ZIP_FL_LOCAL ? 'l' : 'c');
  586     for (i = 0; i < ef->size; i++)
  587     printf("%s%02x", i ? " " : "", ef->data[i]);
  588     printf(">\n");
  589 }
  590 
  591 
  592 static int
  593 entry_cmp(const void *p1, const void *p2) {
  594     const struct entry *e1, *e2;
  595     int c;
  596 
  597     e1 = (struct entry *)p1;
  598     e2 = (struct entry *)p2;
  599 
  600     if ((c = (ignore_case ? strcasecmp : strcmp)(e1->name, e2->name)) != 0)
  601     return c;
  602     if (e1->size != e2->size) {
  603     if (e1->size > e2->size)
  604         return 1;
  605     else
  606         return -1;
  607     }
  608     if (e1->crc != e2->crc)
  609     return (int)e1->crc - (int)e2->crc;
  610 
  611     return 0;
  612 }
  613 
  614 
  615 static int
  616 entry_paranoia_checks(char *const name[2], const void *p1, const void *p2) {
  617     const struct entry *e1, *e2;
  618     int ret;
  619 
  620     e1 = (struct entry *)p1;
  621     e2 = (struct entry *)p2;
  622 
  623     ret = 0;
  624 
  625     if (ef_compare(name, e1, e2) != 0)
  626     ret = 1;
  627 
  628     if (e1->comp_method != e2->comp_method) {
  629     if (verbose) {
  630         if (header_done == 0) {
  631         printf("--- %s\n+++ %s\n", name[0], name[1]);
  632         header_done = 1;
  633         }
  634         printf("---                     %s  ", e1->name);
  635         printf("method %u\n", e1->comp_method);
  636         printf("+++                     %s  ", e1->name);
  637         printf("method %u\n", e2->comp_method);
  638     }
  639     ret = 1;
  640     }
  641     if (comment_compare(e1->comment, e1->comment_length, e2->comment, e2->comment_length) != 0) {
  642     if (verbose) {
  643         if (header_done == 0) {
  644         printf("--- %s\n+++ %s\n", name[0], name[1]);
  645         header_done = 1;
  646         }
  647         printf("---                     %s  ", e1->name);
  648         printf("comment %d\n", e1->comment_length);
  649         printf("+++                     %s  ", e1->name);
  650         printf("comment %d\n", e2->comment_length);
  651     }
  652     ret = 1;
  653     }
  654 
  655     return ret;
  656 }
  657 
  658 
  659 static void
  660 entry_print(const void *p) {
  661     const struct entry *e;
  662 
  663     e = (struct entry *)p;
  664 
  665     /* TODO PRId64 */
  666     printf("%10lu %08x %s\n", (unsigned long)e->size, e->crc, e->name);
  667 }
  668 
  669 
  670 static int
  671 test_file(zip_t *za, zip_uint64_t idx, zip_uint64_t size, zip_uint32_t crc) {
  672     zip_file_t *zf;
  673     char buf[8192];
  674     zip_uint64_t nsize;
  675     zip_int64_t n;
  676     zip_uint32_t ncrc;
  677 
  678     if ((zf = zip_fopen_index(za, idx, 0)) == NULL) {
  679     fprintf(stderr, "%s: cannot open file %" PRIu64 " in archive: %s\n", progname, idx, zip_strerror(za));
  680     return -1;
  681     }
  682 
  683     ncrc = (zip_uint32_t)crc32(0, NULL, 0);
  684     nsize = 0;
  685 
  686     while ((n = zip_fread(zf, buf, sizeof(buf))) > 0) {
  687     nsize += (zip_uint64_t)n;
  688     ncrc = (zip_uint32_t)crc32(ncrc, (const Bytef *)buf, (unsigned int)n);
  689     }
  690 
  691     if (n < 0) {
  692     fprintf(stderr, "%s: error reading file %" PRIu64 " in archive: %s\n", progname, idx, zip_file_strerror(zf));
  693     zip_fclose(zf);
  694     return -1;
  695     }
  696 
  697     zip_fclose(zf);
  698 
  699     if (nsize != size) {
  700     fprintf(stderr, "%s: file %" PRIu64 ": unexpected length %" PRId64 " (should be %" PRId64 ")\n", progname, idx, nsize, size);
  701     return -2;
  702     }
  703     if (ncrc != crc) {
  704     fprintf(stderr, "%s: file %" PRIu64 ": unexpected length %x (should be %x)\n", progname, idx, ncrc, crc);
  705     return -2;
  706     }
  707 
  708     return 0;
  709 }