2 zipcmp.c -- compare zip files
3 Copyright (C) 2003-2015 Dieter Baron and Thomas Klausner
5 This file is part of libzip, a library to manipulate ZIP archives.
6 The authors can be contacted at <libzip@nih.at>
8 Redistribution and use in source and binary forms, with or without
9 modification, are permitted provided that the following conditions
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
17 3. The names of the authors may not be used to endorse or promote
18 products derived from this software without specific prior
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.
66 size_t comment_length;
74 const zip_uint8_t *data;
81 zip_uint32_t comp_method;
82 struct ef *extra_fields;
83 zip_uint16_t n_extra_fields;
85 zip_uint32_t comment_length;
91 #define PROGRAM "zipcmp"
93 #define USAGE "usage: %s [-hipqtVv] archive1 archive2\n"
96 PROGRAM " (" PACKAGE ") by Dieter Baron and Thomas Klausner\n\n";
99 -h display this help message\n\
100 -i compare names ignoring case distinctions\n\
101 -p compare as many details as possible\n\
103 -t test zip files (compare file contents to checksum)\n\
104 -V display version number\n\
105 -v be verbose (print differences, default)\n\
107 Report bugs to <libzip@nih.at>.\n";
109 char version_string[] = PROGRAM " (" PACKAGE " " VERSION ")\n\
110 Copyright (C) 2003-2015 Dieter Baron and Thomas Klausner\n\
111 " PACKAGE " comes with ABSOLUTELY NO WARRANTY, to the extent permitted by law.\n";
113 #define OPTIONS "hVipqtv"
116 #define BOTH_ARE_ZIPS(a) (a[0].za && a[1].za)
118 static int comment_compare(const char *c1, size_t l1, const char *c2, size_t l2);
119 static int compare_list(char * const name[],
120 const void *l[], const zip_uint64_t n[], int size,
121 int (*cmp)(const void *, const void *),
122 int (*checks)(char *const name[2], const void *, const void *),
123 void (*print)(const void *));
124 static int compare_zip(char * const zn[]);
125 static int ef_compare(char *const name[2], const struct entry *e1, const struct entry *e2);
126 static int ef_order(const void *a, const void *b);
127 static void ef_print(const void *p);
128 static int ef_read(zip_t *za, zip_uint64_t idx, struct entry *e);
129 static int entry_cmp(const void *p1, const void *p2);
130 static int entry_paranoia_checks(char *const name[2], const void *p1, const void *p2);
131 static void entry_print(const void *p);
132 static int is_directory(const char *name);
134 static int list_directory(const char *name, struct archive *a);
136 static int list_zip(const char *name, struct archive *a);
137 static int test_file(zip_t *za, zip_uint64_t idx, zip_uint64_t size, zip_uint32_t crc);
139 int ignore_case, test_files, paranoid, verbose;
144 main(int argc, char * const argv[])
155 while ((c=getopt(argc, argv, OPTIONS)) != -1) {
174 fputs(help_head, stdout);
179 fputs(version_string, stdout);
183 fprintf(stderr, USAGE, prg);
188 if (argc != optind+2) {
189 fprintf(stderr, USAGE, prg);
193 exit((compare_zip(argv+optind) == 0) ? 0 : 1);
198 compare_zip(char * const zn[])
206 for (i=0; i<2; i++) {
212 a[i].comment_length =0;
214 if (is_directory(zn[i])) {
216 fprintf(stderr, "%s: reading directories not supported\n", prg);
219 if (list_directory(zn[i], a+i) < 0)
221 paranoid = 0; /* paranoid checks make no sense for directories, since they compare zip metadata */
225 if (list_zip(zn[i], a+i) < 0)
228 qsort(a[i].entry, a[i].nentry, sizeof(a[i].entry[0]), entry_cmp);
237 res = compare_list(zn, (const void **)e, n, sizeof(e[i][0]), entry_cmp, paranoid ? entry_paranoia_checks : NULL, entry_print);
240 if (comment_compare(a[0].comment, a[0].comment_length, a[1].comment, a[1].comment_length) != 0) {
242 printf("--- archive comment (%zd)\n", a[0].comment_length);
243 printf("+++ archive comment (%zd)\n", a[1].comment_length);
267 compute_crc(const char *fname)
270 uLong crc = crc32(0L, Z_NULL, 0);
275 if ((f=fopen(fname, "r")) == NULL) {
276 fprintf(stderr, "%s: can't open %s: %s\n", prg, fname, strerror(errno));
280 while ((n=fread(buffer, 1, sizeof(buffer), f)) > 0) {
281 crc = crc32(crc, buffer, (unsigned int)n);
285 fprintf(stderr, "%s: read error on %s: %s\n", prg, fname, strerror(errno));
292 return (zip_int64_t)crc;
298 is_directory(const char *name)
302 if (stat(name, &st) < 0)
305 return S_ISDIR(st.st_mode);
311 list_directory(const char *name, struct archive *a)
317 char * const names[2] = { (char *)name, NULL };
320 if ((fts = fts_open(names, FTS_NOCHDIR|FTS_LOGICAL, NULL)) == NULL) {
321 fprintf(stderr, "%s: can't open directory '%s': %s\n", prg, name, strerror(errno));
324 size_t prefix_length = strlen(name)+1;
328 while ((ent = fts_read(fts))) {
331 switch (ent->fts_info) {
350 if (a->nentry >= nalloc) {
352 if (nalloc > SIZE_MAX/sizeof(a->entry[0])) {
353 fprintf(stderr, "%s: malloc failure\n", prg);
356 a->entry = realloc(a->entry, sizeof(a->entry[0])*nalloc);
357 if (a->entry == NULL) {
358 fprintf(stderr, "%s: malloc failure\n", prg);
363 a->entry[a->nentry].name = strdup(ent->fts_path+prefix_length);
364 a->entry[a->nentry].size = (zip_uint64_t)ent->fts_statp->st_size;
365 if ((crc = compute_crc(ent->fts_accpath)) < 0) {
370 a->entry[a->nentry].crc = (zip_uint32_t)crc;
376 if (fts_close(fts)) {
377 fprintf(stderr, "%s: error closing directory '%s': %s\n", prg, a->name, strerror(errno));
387 list_zip(const char *name, struct archive *a)
394 if ((za=zip_open(name, paranoid ? ZIP_CHECKCONS : 0, &err)) == NULL) {
396 zip_error_init_with_code(&error, err);
397 fprintf(stderr, "%s: cannot open zip archive '%s': %s\n", prg, name, zip_error_strerror(&error));
398 zip_error_fini(&error);
403 a->nentry = (zip_uint64_t)zip_get_num_entries(za, 0);
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", prg);
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;
419 test_file(za, i, st.size, st.crc);
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);
426 a->entry[i].comp_method = 0;
427 a->entry[i].n_extra_fields = 0;
433 a->comment = zip_get_archive_comment(za, &length, 0);
434 a->comment_length = (size_t)length;
438 a->comment_length = 0;
447 comment_compare(const char *c1, size_t l1, const char *c2, size_t l2) {
454 if (c1 == NULL || c2 == NULL)
457 return memcmp(c1, c2, (size_t)l2);
462 compare_list(char * const name[2],
463 const void *l[2], const zip_uint64_t n[2], int size,
464 int (*cmp)(const void *, const void *),
465 int (*check)(char *const name[2], const void *, const void *),
466 void (*print)(const void *))
472 #define INC(k) (i[k]++, l[k]=((const char *)l[k])+size)
473 #define PRINT(k) do { \
474 if (header_done==0 && verbose) { \
475 printf("--- %s\n+++ %s\n", name[0], name[1]); \
479 printf("%c ", (k)?'+':'-'); \
487 while (i[0]<n[0] && i[1]<n[1]) {
492 diff |= check(name, l[0], l[1]);
506 for (j=0; j<2; j++) {
518 ef_read(zip_t *za, zip_uint64_t idx, struct entry *e)
520 zip_int16_t n_local, n_central;
523 if ((n_local = zip_file_extra_fields_count(za, idx, ZIP_FL_LOCAL)) < 0
524 || (n_central = zip_file_extra_fields_count(za, idx, ZIP_FL_CENTRAL)) < 0) {
528 e->n_extra_fields = (zip_uint16_t)(n_local + n_central);
530 if ((e->extra_fields=(struct ef *)malloc(sizeof(e->extra_fields[0])*e->n_extra_fields)) == NULL)
533 for (i=0; i<n_local; i++) {
534 e->extra_fields[i].name = e->name;
535 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);
536 if (e->extra_fields[i].data == NULL)
538 e->extra_fields[i].flags = ZIP_FL_LOCAL;
540 for (; i<e->n_extra_fields; i++) {
541 e->extra_fields[i].name = e->name;
542 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);
543 if (e->extra_fields[i].data == NULL)
545 e->extra_fields[i].flags = ZIP_FL_CENTRAL;
548 qsort(e->extra_fields, e->n_extra_fields, sizeof(e->extra_fields[0]), ef_order);
555 ef_compare(char *const name[2], const struct entry *e1, const struct entry *e2)
560 ef[0] = e1->extra_fields;
561 ef[1] = e2->extra_fields;
562 n[0] = e1->n_extra_fields;
563 n[1] = e2->n_extra_fields;
565 return compare_list(name, (const void **)ef, n, sizeof(struct ef), ef_order, NULL, ef_print);
571 ef_order(const void *ap, const void *bp) {
572 const struct ef *a, *b;
577 if (a->flags != b->flags)
578 return a->flags - b->flags;
580 return a->id - b->id;
581 if (a->size != b->size)
582 return a->size - b->size;
583 return memcmp(a->data, b->data, a->size);
588 ef_print(const void *p)
590 const struct ef *ef = (struct ef *)p;
593 printf(" %s ", ef->name);
594 printf("%04x %c <", ef->id, ef->flags == ZIP_FL_LOCAL ? 'l' : 'c');
595 for (i=0; i<ef->size; i++)
596 printf("%s%02x", i ? " " : "", ef->data[i]);
602 entry_cmp(const void *p1, const void *p2)
604 const struct entry *e1, *e2;
607 e1 = (struct entry *)p1;
608 e2 = (struct entry *)p2;
610 if ((c=(ignore_case ? strcasecmp : strcmp)(e1->name, e2->name)) != 0)
612 if (e1->size != e2->size) {
613 if (e1->size > e2->size)
618 if (e1->crc != e2->crc)
619 return (int)e1->crc - (int)e2->crc;
626 entry_paranoia_checks(char *const name[2], const void *p1, const void *p2) {
627 const struct entry *e1, *e2;
630 e1 = (struct entry *)p1;
631 e2 = (struct entry *)p2;
635 if (ef_compare(name, e1, e2) != 0)
638 if (e1->comp_method != e2->comp_method) {
640 if (header_done==0) {
641 printf("--- %s\n+++ %s\n", name[0], name[1]);
644 printf("--- %s ", e1->name);
645 printf("method %u\n", e1->comp_method);
646 printf("+++ %s ", e1->name);
647 printf("method %u\n", e2->comp_method);
651 if (comment_compare(e1->comment, e1->comment_length, e2->comment, e2->comment_length) != 0) {
653 if (header_done==0) {
654 printf("--- %s\n+++ %s\n", name[0], name[1]);
657 printf("--- %s ", e1->name);
658 printf("comment %d\n", e1->comment_length);
659 printf("+++ %s ", e1->name);
660 printf("comment %d\n", e2->comment_length);
671 entry_print(const void *p)
673 const struct entry *e;
675 e = (struct entry *)p;
678 printf("%10lu %08x %s\n", (unsigned long)e->size, e->crc, e->name);
683 test_file(zip_t *za, zip_uint64_t idx, zip_uint64_t size, zip_uint32_t crc)
691 if ((zf=zip_fopen_index(za, idx, 0)) == NULL) {
692 fprintf(stderr, "%s: cannot open file %" PRIu64 " in archive: %s\n", prg, idx, zip_strerror(za));
696 ncrc = (zip_uint32_t)crc32(0, NULL, 0);
699 while ((n=zip_fread(zf, buf, sizeof(buf))) > 0) {
700 nsize += (zip_uint64_t)n;
701 ncrc = (zip_uint32_t)crc32(ncrc, (const Bytef *)buf, (unsigned int)n);
705 fprintf(stderr, "%s: error reading file %" PRIu64 " in archive: %s\n", prg, idx, zip_file_strerror(zf));
713 fprintf(stderr, "%s: file %" PRIu64 ": unexpected length %" PRId64 " (should be %" PRId64 ")\n", prg, idx, nsize, size);
717 fprintf(stderr, "%s: file %" PRIu64 ": unexpected length %x (should be %x)\n", prg, idx, ncrc, crc);