2 zipcmp.c -- compare zip files
3 Copyright (C) 2003-2016 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-2016 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)
229 qsort(a[i].entry, a[i].nentry, sizeof(a[i].entry[0]), entry_cmp);
238 res = compare_list(zn, (const void **)e, n, sizeof(e[i][0]), entry_cmp, paranoid ? entry_paranoia_checks : NULL, entry_print);
241 if (comment_compare(a[0].comment, a[0].comment_length, a[1].comment, a[1].comment_length) != 0) {
243 printf("--- archive comment (%zd)\n", a[0].comment_length);
244 printf("+++ archive comment (%zd)\n", a[1].comment_length);
268 compute_crc(const char *fname)
271 uLong crc = crc32(0L, Z_NULL, 0);
276 if ((f=fopen(fname, "r")) == NULL) {
277 fprintf(stderr, "%s: can't open %s: %s\n", prg, fname, strerror(errno));
281 while ((n=fread(buffer, 1, sizeof(buffer), f)) > 0) {
282 crc = crc32(crc, buffer, (unsigned int)n);
286 fprintf(stderr, "%s: read error on %s: %s\n", prg, fname, strerror(errno));
293 return (zip_int64_t)crc;
299 is_directory(const char *name)
303 if (stat(name, &st) < 0)
306 return S_ISDIR(st.st_mode);
312 list_directory(const char *name, struct archive *a)
317 size_t prefix_length;
319 char * const names[2] = { (char *)name, NULL };
322 if ((fts = fts_open(names, FTS_NOCHDIR|FTS_LOGICAL, NULL)) == NULL) {
323 fprintf(stderr, "%s: can't open directory '%s': %s\n", prg, name, strerror(errno));
326 prefix_length = strlen(name)+1;
330 while ((ent = fts_read(fts))) {
333 switch (ent->fts_info) {
352 if (a->nentry >= nalloc) {
354 if (nalloc > SIZE_MAX/sizeof(a->entry[0])) {
355 fprintf(stderr, "%s: malloc failure\n", prg);
358 a->entry = realloc(a->entry, sizeof(a->entry[0])*nalloc);
359 if (a->entry == NULL) {
360 fprintf(stderr, "%s: malloc failure\n", prg);
365 a->entry[a->nentry].name = strdup(ent->fts_path+prefix_length);
366 a->entry[a->nentry].size = (zip_uint64_t)ent->fts_statp->st_size;
367 if ((crc = compute_crc(ent->fts_accpath)) < 0) {
372 a->entry[a->nentry].crc = (zip_uint32_t)crc;
378 if (fts_close(fts)) {
379 fprintf(stderr, "%s: error closing directory '%s': %s\n", prg, a->name, strerror(errno));
389 list_zip(const char *name, struct archive *a)
396 if ((za=zip_open(name, paranoid ? ZIP_CHECKCONS : 0, &err)) == NULL) {
398 zip_error_init_with_code(&error, err);
399 fprintf(stderr, "%s: cannot open zip archive '%s': %s\n", prg, name, zip_error_strerror(&error));
400 zip_error_fini(&error);
405 a->nentry = (zip_uint64_t)zip_get_num_entries(za, 0);
410 if ((a->nentry > SIZE_MAX/sizeof(a->entry[0])) || (a->entry=(struct entry *)malloc(sizeof(a->entry[0]) * a->nentry)) == NULL) {
411 fprintf(stderr, "%s: malloc failure\n", prg);
415 for (i=0; i<a->nentry; i++) {
416 zip_stat_index(za, i, 0, &st);
417 a->entry[i].name = strdup(st.name);
418 a->entry[i].size = st.size;
419 a->entry[i].crc = st.crc;
421 test_file(za, i, st.size, st.crc);
423 a->entry[i].comp_method = st.comp_method;
424 ef_read(za, i, a->entry+i);
425 a->entry[i].comment = zip_file_get_comment(za, i, &a->entry[i].comment_length, 0);
428 a->entry[i].comp_method = 0;
429 a->entry[i].n_extra_fields = 0;
435 a->comment = zip_get_archive_comment(za, &length, 0);
436 a->comment_length = (size_t)length;
440 a->comment_length = 0;
449 comment_compare(const char *c1, size_t l1, const char *c2, size_t l2) {
456 if (c1 == NULL || c2 == NULL)
459 return memcmp(c1, c2, (size_t)l2);
464 compare_list(char * const name[2],
465 const void *l[2], const zip_uint64_t n[2], int size,
466 int (*cmp)(const void *, const void *),
467 int (*check)(char *const name[2], const void *, const void *),
468 void (*print)(const void *))
474 #define INC(k) (i[k]++, l[k]=((const char *)l[k])+size)
475 #define PRINT(k) do { \
476 if (header_done==0 && verbose) { \
477 printf("--- %s\n+++ %s\n", name[0], name[1]); \
481 printf("%c ", (k)?'+':'-'); \
489 while (i[0]<n[0] && i[1]<n[1]) {
494 diff |= check(name, l[0], l[1]);
508 for (j=0; j<2; j++) {
520 ef_read(zip_t *za, zip_uint64_t idx, struct entry *e)
522 zip_int16_t n_local, n_central;
525 if ((n_local = zip_file_extra_fields_count(za, idx, ZIP_FL_LOCAL)) < 0
526 || (n_central = zip_file_extra_fields_count(za, idx, ZIP_FL_CENTRAL)) < 0) {
530 e->n_extra_fields = (zip_uint16_t)(n_local + n_central);
532 if ((e->extra_fields=(struct ef *)malloc(sizeof(e->extra_fields[0])*e->n_extra_fields)) == NULL)
535 for (i=0; i<n_local; i++) {
536 e->extra_fields[i].name = e->name;
537 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);
538 if (e->extra_fields[i].data == NULL)
540 e->extra_fields[i].flags = ZIP_FL_LOCAL;
542 for (; i<e->n_extra_fields; i++) {
543 e->extra_fields[i].name = e->name;
544 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);
545 if (e->extra_fields[i].data == NULL)
547 e->extra_fields[i].flags = ZIP_FL_CENTRAL;
550 qsort(e->extra_fields, e->n_extra_fields, sizeof(e->extra_fields[0]), ef_order);
557 ef_compare(char *const name[2], const struct entry *e1, const struct entry *e2)
562 ef[0] = e1->extra_fields;
563 ef[1] = e2->extra_fields;
564 n[0] = e1->n_extra_fields;
565 n[1] = e2->n_extra_fields;
567 return compare_list(name, (const void **)ef, n, sizeof(struct ef), ef_order, NULL, ef_print);
573 ef_order(const void *ap, const void *bp) {
574 const struct ef *a, *b;
579 if (a->flags != b->flags)
580 return a->flags - b->flags;
582 return a->id - b->id;
583 if (a->size != b->size)
584 return a->size - b->size;
585 return memcmp(a->data, b->data, a->size);
590 ef_print(const void *p)
592 const struct ef *ef = (struct ef *)p;
595 printf(" %s ", ef->name);
596 printf("%04x %c <", ef->id, ef->flags == ZIP_FL_LOCAL ? 'l' : 'c');
597 for (i=0; i<ef->size; i++)
598 printf("%s%02x", i ? " " : "", ef->data[i]);
604 entry_cmp(const void *p1, const void *p2)
606 const struct entry *e1, *e2;
609 e1 = (struct entry *)p1;
610 e2 = (struct entry *)p2;
612 if ((c=(ignore_case ? strcasecmp : strcmp)(e1->name, e2->name)) != 0)
614 if (e1->size != e2->size) {
615 if (e1->size > e2->size)
620 if (e1->crc != e2->crc)
621 return (int)e1->crc - (int)e2->crc;
628 entry_paranoia_checks(char *const name[2], const void *p1, const void *p2) {
629 const struct entry *e1, *e2;
632 e1 = (struct entry *)p1;
633 e2 = (struct entry *)p2;
637 if (ef_compare(name, e1, e2) != 0)
640 if (e1->comp_method != e2->comp_method) {
642 if (header_done==0) {
643 printf("--- %s\n+++ %s\n", name[0], name[1]);
646 printf("--- %s ", e1->name);
647 printf("method %u\n", e1->comp_method);
648 printf("+++ %s ", e1->name);
649 printf("method %u\n", e2->comp_method);
653 if (comment_compare(e1->comment, e1->comment_length, e2->comment, e2->comment_length) != 0) {
655 if (header_done==0) {
656 printf("--- %s\n+++ %s\n", name[0], name[1]);
659 printf("--- %s ", e1->name);
660 printf("comment %d\n", e1->comment_length);
661 printf("+++ %s ", e1->name);
662 printf("comment %d\n", e2->comment_length);
673 entry_print(const void *p)
675 const struct entry *e;
677 e = (struct entry *)p;
680 printf("%10lu %08x %s\n", (unsigned long)e->size, e->crc, e->name);
685 test_file(zip_t *za, zip_uint64_t idx, zip_uint64_t size, zip_uint32_t crc)
693 if ((zf=zip_fopen_index(za, idx, 0)) == NULL) {
694 fprintf(stderr, "%s: cannot open file %" PRIu64 " in archive: %s\n", prg, idx, zip_strerror(za));
698 ncrc = (zip_uint32_t)crc32(0, NULL, 0);
701 while ((n=zip_fread(zf, buf, sizeof(buf))) > 0) {
702 nsize += (zip_uint64_t)n;
703 ncrc = (zip_uint32_t)crc32(ncrc, (const Bytef *)buf, (unsigned int)n);
707 fprintf(stderr, "%s: error reading file %" PRIu64 " in archive: %s\n", prg, idx, zip_file_strerror(zf));
715 fprintf(stderr, "%s: file %" PRIu64 ": unexpected length %" PRId64 " (should be %" PRId64 ")\n", prg, idx, nsize, size);
719 fprintf(stderr, "%s: file %" PRIu64 ": unexpected length %x (should be %x)\n", prg, idx, ncrc, crc);