181f94798fd57f32306c15a268a585f8921a86f0
[platform/upstream/libzip.git] / src / zipmerge.c
1 /*
2   zipmerge.c -- merge zip archives
3   Copyright (C) 2004-2017 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 <ctype.h>
36 #include <errno.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40
41 #include "config.h"
42
43 #ifdef HAVE_UNISTD_H
44 #include <unistd.h>
45 #endif
46
47 #ifndef HAVE_GETOPT
48 #include "getopt.h"
49 #endif
50
51 #include "compat.h"
52 #include "zip.h"
53
54 char *prg;
55
56 #define PROGRAM "zipmerge"
57
58 #define USAGE "usage: %s [-DhIiSsV] target-zip zip...\n"
59
60 char help_head[] =
61     PROGRAM " (" PACKAGE ") by Dieter Baron and Thomas Klausner\n\n";
62
63 char help[] = "\n\
64   -h       display this help message\n\
65   -V       display version number\n\
66   -D       ignore directory component in file names\n\
67   -I       ignore case in file names\n\
68   -i       ask before overwriting files\n\
69   -S       don't overwrite identical files\n\
70   -s       overwrite identical files without asking\n\
71 \n\
72 Report bugs to <libzip@nih.at>.\n";
73
74 char version_string[] = PROGRAM " (" PACKAGE " " VERSION ")\n\
75 Copyright (C) 2004-2017 Dieter Baron and Thomas Klausner\n\
76 " PACKAGE " comes with ABSOLUTELY NO WARRANTY, to the extent permitted by law.\n";
77
78 #define OPTIONS "hVDiIsS"
79
80 #define CONFIRM_ALL_YES         0x001
81 #define CONFIRM_ALL_NO          0x002
82 #define CONFIRM_SAME_YES        0x010
83 #define CONFIRM_SAME_NO         0x020
84
85 int confirm;
86 zip_flags_t name_flags;
87
88 static int confirm_replace(zip_t *, const char *, zip_uint64_t,
89                            zip_t *, const char *, zip_uint64_t);
90 static zip_t *merge_zip(zip_t *, const char *, const char *);
91
92
93 int
94 main(int argc, char *argv[])
95 {
96     zip_t *za;
97     zip_t **zs;
98     int c, err;
99     unsigned int i, n;
100     char *tname;
101
102     prg = argv[0];
103
104     confirm = CONFIRM_ALL_YES;
105     name_flags = 0;
106
107     while ((c=getopt(argc, argv, OPTIONS)) != -1) {
108         switch (c) {
109         case 'D':
110             name_flags |= ZIP_FL_NODIR;
111             break;
112         case 'i':
113             confirm &= ~CONFIRM_ALL_YES;
114             break;
115         case 'I':
116             name_flags |= ZIP_FL_NOCASE;
117             break;
118         case 's':
119             confirm &= ~CONFIRM_SAME_NO;
120             confirm |= CONFIRM_SAME_YES;
121             break;
122         case 'S':
123             confirm &= ~CONFIRM_SAME_YES;
124             confirm |= CONFIRM_SAME_NO;
125             break;
126
127         case 'h':
128             fputs(help_head, stdout);
129             printf(USAGE, prg);
130             fputs(help, stdout);
131             exit(0);
132         case 'V':
133             fputs(version_string, stdout);
134             exit(0);
135
136         default:
137             fprintf(stderr, USAGE, prg);
138             exit(2);
139         }
140     }
141
142     if (argc < optind+2) {
143         fprintf(stderr, USAGE, prg);
144         exit(2);
145     }
146
147     tname = argv[optind++];
148     argv += optind;
149
150     n = (unsigned int)(argc-optind);
151     if ((zs=(zip_t **)malloc(sizeof(zs[0])*n)) == NULL) {
152         fprintf(stderr, "%s: out of memory\n", prg);
153         exit(1);
154     }
155
156     if ((za=zip_open(tname, ZIP_CREATE, &err)) == NULL) {
157         zip_error_t error;
158         zip_error_init_with_code(&error, err);
159         fprintf(stderr, "%s: can't open zip archive '%s': %s\n", prg, tname, zip_error_strerror(&error));
160         zip_error_fini(&error);
161         exit(1);
162     }
163
164     for (i=0; i<n; i++) {
165         if ((zs[i]=merge_zip(za, tname, argv[i])) == NULL)
166             exit(1);
167     }
168
169     if (zip_close(za) < 0) {
170         fprintf(stderr, "%s: cannot write zip archive '%s': %s\n",
171                 prg, tname, zip_strerror(za));
172         exit(1);
173     }
174
175     for (i=0; i<n; i++)
176         zip_close(zs[i]);
177
178     exit(0);
179 }
180
181
182 static int
183 confirm_replace(zip_t *za, const char *tname, zip_uint64_t it,
184                 zip_t *zs, const char *sname, zip_uint64_t is)
185 {
186     char line[1024];
187     struct zip_stat st, ss;
188
189     if (confirm & CONFIRM_ALL_YES)
190         return 1;
191     else if (confirm & CONFIRM_ALL_NO)
192         return 0;
193
194     if (zip_stat_index(za, it, ZIP_FL_UNCHANGED, &st) < 0) {
195         fprintf(stderr, "%s: cannot stat file %"PRIu64" in '%s': %s\n",
196                 prg, it, tname, zip_strerror(za));
197         return -1;
198     }
199     if (zip_stat_index(zs, is, 0, &ss) < 0) {
200         fprintf(stderr, "%s: cannot stat file %"PRIu64" in '%s': %s\n",
201                 prg, is, sname, zip_strerror(zs));
202         return -1;
203     }
204
205     if (st.size == ss.size && st.crc == ss.crc) {
206         if (confirm & CONFIRM_SAME_YES)
207             return 1;
208         else if (confirm & CONFIRM_SAME_NO)
209             return 0;
210     }
211
212     printf("replace '%s' (%"PRIu64" / %08x) in `%s'\n"
213            "   with '%s' (%"PRIu64" / %08x) from `%s'? ",
214            st.name, st.size, st.crc, tname,
215            ss.name, ss.size, ss.crc, sname);
216     fflush(stdout);
217
218     if (fgets(line, sizeof(line), stdin) == NULL) {
219         fprintf(stderr, "%s: read error from stdin: %s\n",
220                 prg, strerror(errno));
221         return -1;
222     }
223
224     if (tolower((unsigned char)line[0]) == 'y')
225         return 1;
226
227     return 0;
228 }
229
230
231 static zip_t *
232 merge_zip(zip_t *za, const char *tname, const char *sname)
233 {
234     zip_t *zs;
235     zip_source_t *source;
236     zip_int64_t ret, idx;
237     zip_uint64_t i;
238     int err;
239     const char *fname;
240     
241     if ((zs=zip_open(sname, 0, &err)) == NULL) {
242         zip_error_t error;
243         zip_error_init_with_code(&error, err);
244         fprintf(stderr, "%s: can't open zip archive '%s': %s\n", prg, sname, zip_error_strerror(&error));
245         zip_error_fini(&error);
246         return NULL;
247     }
248
249     ret = zip_get_num_entries(zs, 0);
250     if (ret < 0) {
251         fprintf(stderr, "%s: cannot get number of entries for '%s': %s\n", prg, sname, zip_strerror(za));
252         return NULL;
253     }
254     for (i=0; i<(zip_uint64_t)ret; i++) {
255         fname = zip_get_name(zs, i, 0);
256
257         if ((idx=zip_name_locate(za, fname, name_flags)) >= 0) {
258             switch (confirm_replace(za, tname, (zip_uint64_t)idx, zs, sname, i)) {
259             case 0:
260                 break;
261                 
262             case 1:
263                 if ((source=zip_source_zip(za, zs, i, 0, 0, 0)) == NULL
264                     || zip_replace(za, (zip_uint64_t)idx, source) < 0) {
265                     zip_source_free(source);
266                     fprintf(stderr,
267                             "%s: cannot replace '%s' in `%s': %s\n",
268                             prg, fname, tname, zip_strerror(za));
269                     zip_close(zs);
270                     return NULL;
271                 }
272                 break;
273
274             case -1:
275                 zip_close(zs);
276                 return NULL;
277                 
278             default:
279                 fprintf(stderr, "%s: internal error: "
280                         "unexpected return code from confirm (%d)\n",
281                         prg, err);
282                 zip_close(zs);
283                 return NULL;
284             }
285         }
286         else {
287             if ((source=zip_source_zip(za, zs, i, 0, 0, 0)) == NULL
288                 || zip_add(za, fname, source) < 0) {
289                 zip_source_free(source);
290                 fprintf(stderr,
291                         "%s: cannot add '%s' to `%s': %s\n",
292                         prg, fname, tname, zip_strerror(za));
293                 zip_close(zs);
294                 return NULL;
295             }
296         }
297     }
298
299     return zs;
300 }