| /* |
| zipmerge.c -- merge zip archives |
| Copyright (C) 2004-2019 Dieter Baron and Thomas Klausner |
| |
| This file is part of libzip, a library to manipulate ZIP archives. |
| The authors can be contacted at <libzip@nih.at> |
| |
| Redistribution and use in source and binary forms, with or without |
| modification, are permitted provided that the following conditions |
| are met: |
| 1. Redistributions of source code must retain the above copyright |
| notice, this list of conditions and the following disclaimer. |
| 2. Redistributions in binary form must reproduce the above copyright |
| notice, this list of conditions and the following disclaimer in |
| the documentation and/or other materials provided with the |
| distribution. |
| 3. The names of the authors may not be used to endorse or promote |
| products derived from this software without specific prior |
| written permission. |
| |
| THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS |
| OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY |
| DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE |
| GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER |
| IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR |
| OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN |
| IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| |
| #include <ctype.h> |
| #include <errno.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #include "config.h" |
| |
| #ifndef HAVE_GETOPT |
| #include "getopt.h" |
| #endif |
| |
| #include "zip.h" |
| |
| char *progname; |
| |
| #define PROGRAM "zipmerge" |
| |
| #define USAGE "usage: %s [-DhIiSsV] target-zip zip...\n" |
| |
| char help_head[] = PROGRAM " (" PACKAGE ") by Dieter Baron and Thomas Klausner\n\n"; |
| |
| char help[] = "\n\ |
| -h display this help message\n\ |
| -V display version number\n\ |
| -D ignore directory component in file names\n\ |
| -I ignore case in file names\n\ |
| -i ask before overwriting files\n\ |
| -S don't overwrite identical files\n\ |
| -s overwrite identical files without asking\n\ |
| \n\ |
| Report bugs to <libzip@nih.at>.\n"; |
| |
| char version_string[] = PROGRAM " (" PACKAGE " " VERSION ")\n\ |
| Copyright (C) 2004-2019 Dieter Baron and Thomas Klausner\n\ |
| " PACKAGE " comes with ABSOLUTELY NO WARRANTY, to the extent permitted by law.\n"; |
| |
| #define OPTIONS "hVDiIsS" |
| |
| #define CONFIRM_ALL_YES 0x001 |
| #define CONFIRM_ALL_NO 0x002 |
| #define CONFIRM_SAME_YES 0x010 |
| #define CONFIRM_SAME_NO 0x020 |
| |
| int confirm; |
| zip_flags_t name_flags; |
| |
| static int confirm_replace(zip_t *, const char *, zip_uint64_t, zip_t *, const char *, zip_uint64_t); |
| static zip_t *merge_zip(zip_t *, const char *, const char *); |
| |
| |
| int |
| main(int argc, char *argv[]) { |
| zip_t *za; |
| zip_t **zs; |
| int c, err; |
| unsigned int i, n; |
| char *tname; |
| |
| progname = argv[0]; |
| |
| confirm = CONFIRM_ALL_YES; |
| name_flags = 0; |
| |
| while ((c = getopt(argc, argv, OPTIONS)) != -1) { |
| switch (c) { |
| case 'D': |
| name_flags |= ZIP_FL_NODIR; |
| break; |
| case 'i': |
| confirm &= ~CONFIRM_ALL_YES; |
| break; |
| case 'I': |
| name_flags |= ZIP_FL_NOCASE; |
| break; |
| case 's': |
| confirm &= ~CONFIRM_SAME_NO; |
| confirm |= CONFIRM_SAME_YES; |
| break; |
| case 'S': |
| confirm &= ~CONFIRM_SAME_YES; |
| confirm |= CONFIRM_SAME_NO; |
| break; |
| |
| case 'h': |
| fputs(help_head, stdout); |
| printf(USAGE, progname); |
| fputs(help, stdout); |
| exit(0); |
| case 'V': |
| fputs(version_string, stdout); |
| exit(0); |
| |
| default: |
| fprintf(stderr, USAGE, progname); |
| exit(2); |
| } |
| } |
| |
| if (argc < optind + 2) { |
| fprintf(stderr, USAGE, progname); |
| exit(2); |
| } |
| |
| tname = argv[optind++]; |
| argv += optind; |
| |
| n = (unsigned int)(argc - optind); |
| if ((zs = (zip_t **)malloc(sizeof(zs[0]) * n)) == NULL) { |
| fprintf(stderr, "%s: out of memory\n", progname); |
| exit(1); |
| } |
| |
| if ((za = zip_open(tname, ZIP_CREATE, &err)) == NULL) { |
| zip_error_t error; |
| zip_error_init_with_code(&error, err); |
| fprintf(stderr, "%s: can't open zip archive '%s': %s\n", progname, tname, zip_error_strerror(&error)); |
| zip_error_fini(&error); |
| exit(1); |
| } |
| |
| for (i = 0; i < n; i++) { |
| if ((zs[i] = merge_zip(za, tname, argv[i])) == NULL) |
| exit(1); |
| } |
| |
| if (zip_close(za) < 0) { |
| fprintf(stderr, "%s: cannot write zip archive '%s': %s\n", progname, tname, zip_strerror(za)); |
| exit(1); |
| } |
| |
| for (i = 0; i < n; i++) |
| zip_close(zs[i]); |
| |
| exit(0); |
| } |
| |
| |
| static int |
| confirm_replace(zip_t *za, const char *tname, zip_uint64_t it, zip_t *zs, const char *sname, zip_uint64_t is) { |
| char line[1024]; |
| struct zip_stat st, ss; |
| |
| if (confirm & CONFIRM_ALL_YES) |
| return 1; |
| else if (confirm & CONFIRM_ALL_NO) |
| return 0; |
| |
| if (zip_stat_index(za, it, ZIP_FL_UNCHANGED, &st) < 0) { |
| fprintf(stderr, "%s: cannot stat file %" PRIu64 " in '%s': %s\n", progname, it, tname, zip_strerror(za)); |
| return -1; |
| } |
| if (zip_stat_index(zs, is, 0, &ss) < 0) { |
| fprintf(stderr, "%s: cannot stat file %" PRIu64 " in '%s': %s\n", progname, is, sname, zip_strerror(zs)); |
| return -1; |
| } |
| |
| if (st.size == ss.size && st.crc == ss.crc) { |
| if (confirm & CONFIRM_SAME_YES) |
| return 1; |
| else if (confirm & CONFIRM_SAME_NO) |
| return 0; |
| } |
| |
| printf("replace '%s' (%" PRIu64 " / %08x) in `%s'\n" |
| " with '%s' (%" PRIu64 " / %08x) from `%s'? ", |
| st.name, st.size, st.crc, tname, ss.name, ss.size, ss.crc, sname); |
| fflush(stdout); |
| |
| if (fgets(line, sizeof(line), stdin) == NULL) { |
| fprintf(stderr, "%s: read error from stdin: %s\n", progname, strerror(errno)); |
| return -1; |
| } |
| |
| if (tolower((unsigned char)line[0]) == 'y') |
| return 1; |
| |
| return 0; |
| } |
| |
| |
| static zip_t * |
| merge_zip(zip_t *za, const char *tname, const char *sname) { |
| zip_t *zs; |
| zip_source_t *source; |
| zip_int64_t ret, idx; |
| zip_uint64_t i; |
| int err; |
| const char *fname; |
| |
| if ((zs = zip_open(sname, 0, &err)) == NULL) { |
| zip_error_t error; |
| zip_error_init_with_code(&error, err); |
| fprintf(stderr, "%s: can't open zip archive '%s': %s\n", progname, sname, zip_error_strerror(&error)); |
| zip_error_fini(&error); |
| return NULL; |
| } |
| |
| ret = zip_get_num_entries(zs, 0); |
| if (ret < 0) { |
| fprintf(stderr, "%s: cannot get number of entries for '%s': %s\n", progname, sname, zip_strerror(za)); |
| return NULL; |
| } |
| for (i = 0; i < (zip_uint64_t)ret; i++) { |
| fname = zip_get_name(zs, i, 0); |
| |
| if ((idx = zip_name_locate(za, fname, name_flags)) >= 0) { |
| switch (confirm_replace(za, tname, (zip_uint64_t)idx, zs, sname, i)) { |
| case 0: |
| break; |
| |
| case 1: |
| if ((source = zip_source_zip(za, zs, i, 0, 0, 0)) == NULL || zip_replace(za, (zip_uint64_t)idx, source) < 0) { |
| zip_source_free(source); |
| fprintf(stderr, "%s: cannot replace '%s' in `%s': %s\n", progname, fname, tname, zip_strerror(za)); |
| zip_close(zs); |
| return NULL; |
| } |
| break; |
| |
| case -1: |
| zip_close(zs); |
| return NULL; |
| |
| default: |
| fprintf(stderr, |
| "%s: internal error: " |
| "unexpected return code from confirm (%d)\n", |
| progname, err); |
| zip_close(zs); |
| return NULL; |
| } |
| } |
| else { |
| if ((source = zip_source_zip(za, zs, i, 0, 0, 0)) == NULL || zip_add(za, fname, source) < 0) { |
| zip_source_free(source); |
| fprintf(stderr, "%s: cannot add '%s' to `%s': %s\n", progname, fname, tname, zip_strerror(za)); |
| zip_close(zs); |
| return NULL; |
| } |
| } |
| } |
| |
| return zs; |
| } |