Imported Upstream version 1.3.2
[platform/upstream/libzip.git] / src / ziptool.c
1 /*
2   ziptool.c -- tool for modifying zip archive in multiple ways
3   Copyright (C) 2012-2016 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 #include "config.h"
35
36 #include <errno.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <sys/stat.h>
40 #ifdef HAVE_UNISTD_H
41 #include <unistd.h>
42 #endif
43 #ifdef _WIN32
44 /* WIN32 needs <fcntl.h> for _O_BINARY */
45 #include <fcntl.h>
46 #ifndef STDIN_FILENO
47 #define STDIN_FILENO _fileno(stdin)
48 #endif
49 #endif
50
51 #ifndef HAVE_GETOPT
52 #include "getopt.h"
53 #endif
54 extern int optopt;
55
56 #include "compat.h"
57 #include "zip.h"
58
59 zip_source_t *source_hole_create(const char *, int flags, zip_error_t *);
60
61 typedef enum {
62     SOURCE_TYPE_NONE,
63     SOURCE_TYPE_IN_MEMORY,
64     SOURCE_TYPE_HOLE
65 } source_type_t;
66
67 typedef struct dispatch_table_s {
68     const char *cmdline_name;
69     int argument_count;
70     const char *arg_names;
71     const char *description;
72     int (*function)(int argc, char *argv[]);
73 } dispatch_table_t;
74
75 static zip_flags_t get_flags(const char *arg);
76 static zip_int32_t get_compression_method(const char *arg);
77 static zip_uint16_t get_encryption_method(const char *arg);
78 static void hexdump(const zip_uint8_t *data, zip_uint16_t len);
79 static zip_t *read_to_memory(const char *archive, int flags, zip_error_t *error, zip_source_t **srcp);
80 static zip_source_t *source_nul(zip_t *za, zip_uint64_t length);
81
82 zip_t *za, *z_in[16];
83 unsigned int z_in_count;
84 zip_flags_t stat_flags;
85
86 static int
87 add(int argc, char *argv[]) {
88     zip_source_t *zs;
89
90     if ((zs=zip_source_buffer(za, argv[1], strlen(argv[1]), 0)) == NULL) {
91         fprintf(stderr, "can't create zip_source from buffer: %s\n", zip_strerror(za));
92         return -1;
93     }
94
95     if (zip_add(za, argv[0], zs) == -1) {
96         zip_source_free(zs);
97         fprintf(stderr, "can't add file '%s': %s\n", argv[0], zip_strerror(za));
98         return -1;
99     }
100     return 0;
101 }
102
103 static int
104 add_dir(int argc, char *argv[]) {
105     /* add directory */
106     if (zip_add_dir(za, argv[0]) < 0) {
107         fprintf(stderr, "can't add directory '%s': %s\n", argv[0], zip_strerror(za));
108         return -1;
109     }
110     return 0;
111 }
112
113 static int
114 add_file(int argc, char *argv[]) {
115     zip_source_t *zs;
116     zip_uint64_t start = strtoull(argv[2], NULL, 10);
117     zip_int64_t len = strtoll(argv[3], NULL, 10);
118
119     if (strcmp(argv[1], "/dev/stdin") == 0) {
120         if ((zs=zip_source_filep(za, stdin, start, len)) == NULL) {
121             fprintf(stderr, "can't create zip_source from stdin: %s\n", zip_strerror(za));
122             return -1;
123         }
124     } else {
125         if ((zs=zip_source_file(za, argv[1], start, len)) == NULL) {
126             fprintf(stderr, "can't create zip_source from file: %s\n", zip_strerror(za));
127             return -1;
128         }
129     }
130
131     if (zip_add(za, argv[0], zs) == -1) {
132         zip_source_free(zs);
133         fprintf(stderr, "can't add file '%s': %s\n", argv[0], zip_strerror(za));
134         return -1;
135     }
136     return 0;
137 }
138
139 static int
140 add_from_zip(int argc, char *argv[]) {
141     zip_uint64_t idx, start;
142     zip_int64_t len;
143     int err;
144     zip_source_t *zs;
145     /* add from another zip file */
146     idx = strtoull(argv[2], NULL, 10);
147     start = strtoull(argv[3], NULL, 10);
148     len = strtoll(argv[4], NULL, 10);
149     if ((z_in[z_in_count]=zip_open(argv[1], ZIP_CHECKCONS, &err)) == NULL) {
150         zip_error_t error;
151         zip_error_init_with_code(&error, err);
152         fprintf(stderr, "can't open zip archive '%s': %s\n", argv[1], zip_error_strerror(&error));
153         zip_error_fini(&error);
154         return -1;
155     }
156     if ((zs=zip_source_zip(za, z_in[z_in_count], idx, 0, start, len)) == NULL) {
157         fprintf(stderr, "error creating file source from '%s' index '%" PRIu64 "': %s\n", argv[1], idx, zip_strerror(za));
158         zip_close(z_in[z_in_count]);
159         return -1;
160     }
161     if (zip_add(za, argv[0], zs) == -1) {
162         fprintf(stderr, "can't add file '%s': %s\n", argv[0], zip_strerror(za));
163         zip_source_free(zs);
164         zip_close(z_in[z_in_count]);
165         return -1;
166     }
167     z_in_count++;
168     return 0;
169 }
170
171 static int
172 add_nul(int argc, char *argv[]) {
173     zip_source_t *zs;
174     zip_uint64_t length = strtoull(argv[1], NULL, 10);
175
176     if ((zs=source_nul(za, length)) == NULL) {
177         fprintf(stderr, "can't create zip_source for length: %s\n", zip_strerror(za));
178         return -1;
179     }
180
181     if (zip_add(za, argv[0], zs) == -1) {
182         zip_source_free(zs);
183         fprintf(stderr, "can't add file '%s': %s\n", argv[0], zip_strerror(za));
184         return -1;
185     }
186     return 0;
187 }
188
189 static int
190 cat(int argc, char *argv[]) {
191     /* output file contents to stdout */
192     zip_uint64_t idx;
193     zip_int64_t n;
194     zip_file_t *zf;
195     char buf[8192];
196     int err;
197     idx = strtoull(argv[0], NULL, 10);
198
199 #ifdef _WIN32
200     /* Need to set stdout to binary mode for Windows */
201     setmode(fileno(stdout), _O_BINARY);
202 #endif
203     if ((zf=zip_fopen_index(za, idx, 0)) == NULL) {
204         fprintf(stderr, "can't open file at index '%" PRIu64 "': %s\n", idx, zip_strerror(za));
205         return -1;
206     }
207     while ((n=zip_fread(zf, buf, sizeof(buf))) > 0) {
208         if (fwrite(buf, (size_t)n, 1, stdout) != 1) {
209             zip_fclose(zf);
210             fprintf(stderr, "can't write file contents to stdout: %s\n", strerror(errno));
211             return -1;
212         }
213     }
214     if (n == -1) {
215         fprintf(stderr, "can't read file at index '%" PRIu64 "': %s\n", idx, zip_file_strerror(zf));
216         zip_fclose(zf);
217         return -1;
218     }
219     if ((err = zip_fclose(zf)) != 0) {
220         zip_error_t error;
221
222         zip_error_init_with_code(&error, err);
223         fprintf(stderr, "can't close file at index '%" PRIu64 "': %s\n", idx, zip_error_strerror(&error));
224         return -1;
225     }
226
227     return 0;
228 }
229
230 static int
231 count_extra(int argc, char *argv[]) {
232     zip_int16_t count;
233     zip_uint64_t idx;
234     zip_flags_t ceflags = 0;
235     idx = strtoull(argv[0], NULL, 10);
236     ceflags = get_flags(argv[1]);
237     if ((count=zip_file_extra_fields_count(za, idx, ceflags)) < 0) {
238         fprintf(stderr, "can't get extra field count for file at index '%" PRIu64 "': %s\n", idx, zip_strerror(za));
239         return -1;
240     } else {
241         printf("Extra field count: %d\n", count);
242     }
243     return 0;
244 }
245
246 static int
247 count_extra_by_id(int argc, char *argv[]) {
248     zip_int16_t count;
249     zip_uint16_t eid;
250     zip_flags_t ceflags = 0;
251     zip_uint64_t idx;
252     idx = strtoull(argv[0], NULL, 10);
253     eid = (zip_uint16_t)strtoull(argv[1], NULL, 10);
254     ceflags = get_flags(argv[2]);
255     if ((count=zip_file_extra_fields_count_by_id(za, idx, eid, ceflags)) < 0) {
256         fprintf(stderr, "can't get extra field count for file at index '%" PRIu64 "' and for id '%d': %s\n", idx, eid, zip_strerror(za));
257         return -1;
258     } else {
259         printf("Extra field count: %d\n", count);
260     }
261     return 0;
262 }
263
264 static int
265 delete(int argc, char *argv[]) {
266     zip_uint64_t idx;
267     idx = strtoull(argv[0], NULL, 10);
268     if (zip_delete(za, idx) < 0) {
269         fprintf(stderr, "can't delete file at index '%" PRIu64 "': %s\n", idx, zip_strerror(za));
270         return -1;
271     }
272     return 0;
273 }
274
275 static int
276 delete_extra(int argc, char *argv[]) {
277     zip_flags_t geflags;
278     zip_uint16_t eid;
279     zip_uint64_t idx;
280     idx = strtoull(argv[0], NULL, 10);
281     eid = (zip_uint16_t)strtoull(argv[1], NULL, 10);
282     geflags = get_flags(argv[2]);
283     if ((zip_file_extra_field_delete(za, idx, eid, geflags)) < 0) {
284         fprintf(stderr, "can't delete extra field data for file at index '%" PRIu64 "', extra field id '%d': %s\n", idx, eid, zip_strerror(za));
285         return -1;
286     }
287     return 0;
288 }
289
290 static int
291 delete_extra_by_id(int argc, char *argv[]) {
292     zip_flags_t geflags;
293     zip_uint16_t eid, eidx;
294     zip_uint64_t idx;
295     idx = strtoull(argv[0], NULL, 10);
296     eid = (zip_uint16_t)strtoull(argv[1], NULL, 10);
297     eidx = (zip_uint16_t)strtoull(argv[2], NULL, 10);
298     geflags = get_flags(argv[3]);
299     if ((zip_file_extra_field_delete_by_id(za, idx, eid, eidx, geflags)) < 0) {
300         fprintf(stderr, "can't delete extra field data for file at index '%" PRIu64 "', extra field id '%d', extra field idx '%d': %s\n", idx, eid, eidx, zip_strerror(za));
301         return -1;
302     }
303     return 0;
304 }
305
306 static int
307 get_archive_comment(int argc, char *argv[]) {
308     const char *comment;
309     int len;
310     /* get archive comment */
311     if ((comment=zip_get_archive_comment(za, &len, 0)) == NULL)
312         printf("No archive comment\n");
313     else
314         printf("Archive comment: %.*s\n", len, comment);
315     return 0;
316 }
317
318 static int
319 get_extra(int argc, char *argv[]) {
320     zip_flags_t geflags;
321     zip_uint16_t id, eidx, eflen;
322     const zip_uint8_t *efdata;
323     zip_uint64_t idx;
324     /* get extra field data */
325     idx = strtoull(argv[0], NULL, 10);
326     eidx = (zip_uint16_t)strtoull(argv[1], NULL, 10);
327     geflags = get_flags(argv[2]);
328     if ((efdata=zip_file_extra_field_get(za, idx, eidx, &id, &eflen, geflags)) == NULL) {
329         fprintf(stderr, "can't get extra field data for file at index %" PRIu64 ", extra field %d, flags %u: %s\n", idx, eidx, geflags, zip_strerror(za));
330         return -1;
331     }
332     printf("Extra field 0x%04x: len %d", id, eflen);
333     if (eflen > 0) {
334         printf(", data ");
335         hexdump(efdata, eflen);
336     }
337     printf("\n");
338     return 0;
339 }
340
341 static int
342 get_extra_by_id(int argc, char *argv[]) {
343     zip_flags_t geflags;
344     zip_uint16_t eid, eidx, eflen;
345     const zip_uint8_t *efdata;
346     zip_uint64_t idx;
347     idx = strtoull(argv[0], NULL, 10);
348     eid = (zip_uint16_t)strtoull(argv[1], NULL, 10);
349     eidx = (zip_uint16_t)strtoull(argv[2], NULL, 10);
350     geflags = get_flags(argv[3]);
351     if ((efdata=zip_file_extra_field_get_by_id(za, idx, eid, eidx, &eflen, geflags)) == NULL) {
352         fprintf(stderr, "can't get extra field data for file at index %" PRIu64 ", extra field id %d, ef index %d, flags %u: %s\n", idx, eid, eidx, geflags, zip_strerror(za));
353         return -1;
354     }
355     printf("Extra field 0x%04x: len %d", eid, eflen);
356     if (eflen > 0) {
357         printf(", data ");
358         hexdump(efdata, eflen);
359     }
360     printf("\n");
361     return 0;
362 }
363
364 static int
365 get_file_comment(int argc, char *argv[]) {
366     const char *comment;
367     int len;
368     zip_uint64_t idx;
369     /* get file comment */
370     idx = strtoull(argv[0], NULL, 10);
371     if ((comment=zip_get_file_comment(za, idx, &len, 0)) == NULL) {
372         fprintf(stderr, "can't get comment for '%s': %s\n", zip_get_name(za, idx, 0), zip_strerror(za));
373         return -1;
374     } else if (len == 0)
375         printf("No comment for '%s'\n", zip_get_name(za, idx, 0));
376     else
377         printf("File comment for '%s': %.*s\n", zip_get_name(za, idx, 0), len, comment);
378     return 0;
379 }
380
381 static int
382 get_num_entries(int argc, char *argv[]) {
383     zip_int64_t count;
384     zip_flags_t flags;
385     /* get number of entries in archive */
386     flags = get_flags(argv[0]);
387     count = zip_get_num_entries(za, flags);
388     printf("%" PRId64 " entr%s in archive\n", count, count == 1 ? "y" : "ies");
389     return 0;
390 }
391
392 static int
393 name_locate(int argc, char *argv[]) {
394     zip_flags_t flags;
395     zip_int64_t idx;
396     flags = get_flags(argv[1]);
397
398     if ((idx=zip_name_locate(za, argv[0], flags)) < 0) {
399         fprintf(stderr, "can't find entry with name '%s' using flags '%s'\n", argv[0], argv[1]);
400     } else {
401         printf("name '%s' using flags '%s' found at index %" PRId64 "\n", argv[0], argv[1], idx);
402     }
403
404     return 0;
405 }
406
407 static void
408 progress_callback(zip_t *za, double percentage, void *ud) {
409     printf("%.1lf%% done\n", percentage*100);
410 }
411
412 static int
413 print_progress(int argc, char *argv[]) {
414     zip_register_progress_callback_with_state(za, 0.001, progress_callback, NULL, NULL);
415     return 0;
416 }
417
418 static int
419 zrename(int argc, char *argv[]) {
420     zip_uint64_t idx;
421     idx = strtoull(argv[0], NULL, 10);
422     if (zip_rename(za, idx, argv[1]) < 0) {
423         fprintf(stderr, "can't rename file at index '%" PRIu64 "' to '%s': %s\n", idx, argv[1], zip_strerror(za));
424         return -1;
425     }
426     return 0;
427 }
428
429 static int
430 replace_file_contents(int argc, char *argv[]) {
431     /* replace file contents with data from command line */
432     const char *content;
433     zip_source_t *s;
434     zip_uint64_t idx;
435     idx = strtoull(argv[0], NULL, 10);
436     content = argv[1];
437     if ((s=zip_source_buffer(za, content, strlen(content), 0)) == NULL ||
438         zip_file_replace(za, idx, s, 0) < 0) {
439         zip_source_free(s);
440         fprintf(stderr, "error replacing file data: %s\n", zip_strerror(za));
441         return -1;
442     }
443     return 0;
444 }
445
446 static int
447 set_extra(int argc, char *argv[]) {
448     zip_flags_t geflags;
449     zip_uint16_t eid, eidx;
450     const zip_uint8_t *efdata;
451     zip_uint64_t idx;
452     idx = strtoull(argv[0], NULL, 10);
453     eid = (zip_uint16_t)strtoull(argv[1], NULL, 10);
454     eidx = (zip_uint16_t)strtoull(argv[2], NULL, 10);
455     geflags = get_flags(argv[3]);
456     efdata = (zip_uint8_t *)argv[4];
457     if ((zip_file_extra_field_set(za, idx, eid, eidx, efdata, (zip_uint16_t)strlen((const char *)efdata), geflags)) < 0) {
458         fprintf(stderr, "can't set extra field data for file at index '%" PRIu64 "', extra field id '%d', index '%d': %s\n", idx, eid, eidx, zip_strerror(za));
459         return -1;
460     }
461     return 0;
462 }
463
464 static int
465 set_archive_comment(int argc, char *argv[]) {
466     if (zip_set_archive_comment(za, argv[0], (zip_uint16_t)strlen(argv[0])) < 0) {
467         fprintf(stderr, "can't set archive comment to '%s': %s\n", argv[0], zip_strerror(za));
468         return -1;
469     }
470     return 0;
471 }
472
473 static int
474 set_file_comment(int argc, char *argv[]) {
475     zip_uint64_t idx;
476     idx = strtoull(argv[0], NULL, 10);
477     if (zip_file_set_comment(za, idx, argv[1], (zip_uint16_t)strlen(argv[1]), 0) < 0) {
478         fprintf(stderr, "can't set file comment at index '%" PRIu64 "' to '%s': %s\n", idx, argv[1], zip_strerror(za));
479         return -1;
480     }
481     return 0;
482 }
483
484 static int
485 set_file_compression(int argc, char *argv[]) {
486     zip_int32_t method;
487     zip_uint32_t flags;
488     zip_uint64_t idx;
489     idx = strtoull(argv[0], NULL, 10);
490     method = get_compression_method(argv[1]);
491     flags = (zip_uint32_t)strtoull(argv[2], NULL, 10);
492     if (zip_set_file_compression(za, idx, method, flags) < 0) {
493         fprintf(stderr, "can't set file compression method at index '%" PRIu64 "' to '%s', flags '%d': %s\n", idx, argv[1], flags, zip_strerror(za));
494         return -1;
495     }
496     return 0;
497 }
498
499 static int
500 set_file_encryption(int argc, char *argv[]) {
501     zip_int32_t method;
502     zip_uint64_t idx;
503     char *password;
504     idx = strtoull(argv[0], NULL, 10);
505     method = get_encryption_method(argv[1]);
506     password = argv[2];
507     if (strlen(password) == 0) {
508         password = NULL;
509     }
510     if (zip_file_set_encryption(za, idx, method, password) < 0) {
511         fprintf(stderr, "can't set file encryption method at index '%" PRIu64 "' to '%s': %s\n", idx, argv[1], zip_strerror(za));
512         return -1;
513     }
514     return 0;
515 }
516
517 static int
518 set_file_mtime(int argc, char *argv[]) {
519     /* set file last modification time (mtime) */
520     time_t mtime;
521     zip_uint64_t idx;
522     idx = strtoull(argv[0], NULL, 10);
523     mtime = (time_t)strtoull(argv[1], NULL, 10);
524     if (zip_file_set_mtime(za, idx, mtime, 0) < 0) {
525         fprintf(stderr, "can't set file mtime at index '%" PRIu64 "' to '%ld': %s\n", idx, mtime, zip_strerror(za));
526         return -1;
527     }
528     return 0;
529 }
530
531 static int
532 set_file_mtime_all(int argc, char *argv[]) {
533     /* set last modification time (mtime) for all files */
534     time_t mtime;
535     zip_int64_t num_entries;
536     zip_uint64_t idx;
537     mtime = (time_t)strtoull(argv[0], NULL, 10);
538
539     if ((num_entries = zip_get_num_entries(za, 0)) < 0) {
540         fprintf(stderr, "can't get number of entries: %s\n", zip_strerror(za));
541         return -1;
542     }
543     for (idx = 0; idx < (zip_uint64_t)num_entries; idx++) {
544         if (zip_file_set_mtime(za, idx, mtime, 0) < 0) {
545             fprintf(stderr, "can't set file mtime at index '%" PRIu64 "' to '%ld': %s\n", idx, mtime, zip_strerror(za));
546             return -1;
547         }
548     }
549     return 0;
550 }
551
552 static int
553 set_password(int argc, char *argv[]) {
554     /* set default password */
555     if (zip_set_default_password(za, argv[0]) < 0) {
556         fprintf(stderr, "can't set default password to '%s'\n", argv[0]);
557         return -1;
558     }
559     return 0;
560 }
561
562 static int
563 zstat(int argc, char *argv[]) {
564     zip_uint64_t idx;
565     char buf[100];
566     struct zip_stat sb;
567     idx = strtoull(argv[0], NULL, 10);
568
569     if (zip_stat_index(za, idx, stat_flags, &sb) < 0) {
570         fprintf(stderr, "zip_stat_index failed on '%" PRIu64 "' failed: %s\n", idx, zip_strerror(za));
571         return -1;
572     }
573
574     if (sb.valid & ZIP_STAT_NAME)
575         printf("name: '%s'\n", sb.name);
576     if (sb.valid & ZIP_STAT_INDEX)
577         printf("index: '%"PRIu64"'\n", sb.index);
578     if (sb.valid & ZIP_STAT_SIZE)
579         printf("size: '%"PRIu64"'\n", sb.size);
580     if (sb.valid & ZIP_STAT_COMP_SIZE)
581         printf("compressed size: '%"PRIu64"'\n", sb.comp_size);
582     if (sb.valid & ZIP_STAT_MTIME) {
583         struct tm *tpm;
584         tpm = localtime(&sb.mtime);
585         strftime(buf, sizeof(buf), "%a %b %d %Y %H:%M:%S", tpm);
586         printf("mtime: '%s'\n", buf);
587     }
588     if (sb.valid & ZIP_STAT_CRC)
589         printf("crc: '%0x'\n", sb.crc);
590     if (sb.valid & ZIP_STAT_COMP_METHOD)
591         printf("compression method: '%d'\n", sb.comp_method);
592     if (sb.valid & ZIP_STAT_ENCRYPTION_METHOD)
593         printf("encryption method: '%d'\n", sb.encryption_method);
594     if (sb.valid & ZIP_STAT_FLAGS)
595         printf("flags: '%ld'\n", (long)sb.flags);
596     printf("\n");
597
598     return 0;
599 }
600
601 static int
602 unchange_all(int argc, char *argv[]) {
603     if (zip_unchange_all(za) < 0) {
604         fprintf(stderr, "can't revert changes to archive: %s\n", zip_strerror(za));
605         return -1;
606     }
607     return 0;
608 }
609
610 static int
611 zin_close(int argc, char *argv[]) {
612     zip_uint64_t idx;
613
614     idx = strtoull(argv[0], NULL, 10);
615     if (idx >= z_in_count) {
616         fprintf(stderr, "invalid argument '%" PRIu64 "', only %d zip sources open\n", idx, z_in_count);
617         return -1;
618     }
619     if (zip_close(z_in[idx]) < 0) {
620         fprintf(stderr, "can't close source archive: %s\n", zip_strerror(z_in[idx]));
621         return -1;
622     }
623     z_in[idx] = z_in[z_in_count];
624     z_in_count--;
625
626     return 0;
627 }
628
629 static zip_flags_t
630 get_flags(const char *arg)
631 {
632     zip_flags_t flags = 0;
633     if (strchr(arg, 'C') != NULL)
634         flags |= ZIP_FL_NOCASE;
635     if (strchr(arg, 'c') != NULL)
636         flags |= ZIP_FL_CENTRAL;
637     if (strchr(arg, 'd') != NULL)
638         flags |= ZIP_FL_NODIR;
639     if (strchr(arg, 'l') != NULL)
640         flags |= ZIP_FL_LOCAL;
641     if (strchr(arg, 'u') != NULL)
642         flags |= ZIP_FL_UNCHANGED;
643     return flags;
644 }
645
646 static zip_int32_t
647 get_compression_method(const char *arg)
648 {
649     if (strcmp(arg, "default") == 0)
650         return ZIP_CM_DEFAULT;
651     else if (strcmp(arg, "store") == 0)
652         return ZIP_CM_STORE;
653     else if (strcmp(arg, "deflate") == 0)
654         return ZIP_CM_DEFLATE;
655 #if defined(HAVE_LIBBZ2)
656     else if (strcmp(arg, "bzip2") == 0)
657         return ZIP_CM_BZIP2;
658 #endif
659     else if (strcmp(arg, "unknown") == 0)
660         return 100;
661     return 0; /* TODO: error handling */
662 }
663
664 static zip_uint16_t
665 get_encryption_method(const char *arg)
666 {
667     if (strcmp(arg, "none") == 0)
668         return ZIP_EM_NONE;
669     else if (strcmp(arg, "AES-128") == 0)
670         return ZIP_EM_AES_128;
671     else if (strcmp(arg, "AES-192") == 0)
672         return ZIP_EM_AES_192;
673     else if (strcmp(arg, "AES-256") == 0)
674         return ZIP_EM_AES_256;
675     else if (strcmp(arg, "unknown") == 0)
676         return 100;
677     return (zip_uint16_t)-1; /* TODO: error handling */
678 }
679
680 static void
681 hexdump(const zip_uint8_t *data, zip_uint16_t len)
682 {
683     zip_uint16_t i;
684
685     if (len <= 0)
686         return;
687
688     printf("0x");
689
690     for (i=0; i<len; i++)
691         printf("%02x", data[i]);
692
693     return;
694 }
695
696
697 static zip_t *
698 read_from_file(const char *archive, int flags, zip_error_t *error, zip_uint64_t offset, zip_uint64_t length)
699 {
700     zip_t *zaa;
701     zip_source_t *source;
702     int err;
703
704     if (offset == 0 && length == 0) {
705         if (strcmp(archive, "/dev/stdin") == 0) {
706             zaa = zip_fdopen(STDIN_FILENO, flags & ~ZIP_CREATE, &err);
707         }
708         else {
709             zaa = zip_open(archive, flags, &err);
710         }
711         if (zaa == NULL) {
712             zip_error_set(error, err, errno);
713             return NULL;
714         }
715     }
716     else {
717         if (length > ZIP_INT64_MAX) {
718             zip_error_set(error, ZIP_ER_INVAL, 0);
719             return NULL;
720         }
721         if ((source = zip_source_file_create(archive, offset, (zip_int64_t)length, error)) == NULL
722             || (zaa = zip_open_from_source(source, flags, error)) == NULL) {
723             zip_source_free(source);
724             return NULL;
725         }
726     }
727
728     return zaa;
729 }
730
731
732 static zip_t *
733 read_hole(const char *archive, int flags, zip_error_t *error)
734 {
735     zip_source_t *src = NULL;
736     zip_t *zs = NULL;
737
738     if (strcmp(archive, "/dev/stdin") == 0) {
739         zip_error_set(error, ZIP_ER_OPNOTSUPP, 0);
740         return NULL;
741     }
742
743     if ((src = source_hole_create(archive, flags, error)) == NULL
744         || (zs = zip_open_from_source(src, flags, error)) == NULL) {
745         zip_source_free(src);
746     }
747
748     return zs;
749 }
750
751
752 static zip_t *
753 read_to_memory(const char *archive, int flags, zip_error_t *error, zip_source_t **srcp)
754 {
755     struct stat st;
756     zip_source_t *src;
757     zip_t *zb;
758
759     if (strcmp(archive, "/dev/stdin") == 0) {
760         zip_error_set(error, ZIP_ER_OPNOTSUPP, 0);
761         return NULL;
762     }
763
764     if (stat(archive, &st) < 0) {
765         if (errno == ENOENT) {
766             src = zip_source_buffer_create(NULL, 0, 0, error);
767         }
768         else {
769             zip_error_set(error, ZIP_ER_OPEN, errno);
770             return NULL;
771         }
772     }
773     else {
774         char *buf;
775         FILE *fp;
776         if ((buf=malloc((size_t)st.st_size)) == NULL) {
777             zip_error_set(error, ZIP_ER_MEMORY, 0);
778             return NULL;
779         }
780         if ((fp=fopen(archive, "r")) == NULL) {
781             free(buf);
782             zip_error_set(error, ZIP_ER_READ, errno);
783             return NULL;
784         }
785         if (fread(buf, (size_t)st.st_size, 1, fp) < 1) {
786             free(buf);
787             fclose(fp);
788             zip_error_set(error, ZIP_ER_READ, errno);
789             return NULL;
790         }
791         fclose(fp);
792         src = zip_source_buffer_create(buf, (zip_uint64_t)st.st_size, 1, error);
793         if (src == NULL) {
794             free(buf);
795         }
796     }
797     if (src == NULL) {
798         return NULL;
799     }
800     zb = zip_open_from_source(src, flags, error);
801     if (zb == NULL) {
802         zip_source_free(src);
803         return NULL;
804     }
805     zip_source_keep(src);
806     *srcp = src;
807     return zb;
808 }
809
810
811 typedef struct source_nul {
812     zip_error_t error;
813     zip_uint64_t length;
814     zip_uint64_t offset;
815 } source_nul_t;
816
817 static zip_int64_t
818 source_nul_cb(void *ud, void *data, zip_uint64_t length, zip_source_cmd_t command)
819 {
820     source_nul_t *ctx = (source_nul_t *)ud;
821
822     switch (command) {
823         case ZIP_SOURCE_CLOSE:
824             return 0;
825
826         case ZIP_SOURCE_ERROR:
827             return zip_error_to_data(&ctx->error, data, length);
828
829         case ZIP_SOURCE_FREE:
830             free(ctx);
831             return 0;
832
833         case ZIP_SOURCE_OPEN:
834             ctx->offset = 0;
835             return 0;
836
837         case ZIP_SOURCE_READ:
838             if (length > ZIP_INT64_MAX) {
839                 zip_error_set(&ctx->error, ZIP_ER_INVAL, 0);
840                 return -1;
841             }
842
843             if (length > ctx->length - ctx->offset) {
844                 length =ctx->length - ctx->offset;
845             }
846
847             memset(data, 0, length);
848             ctx->offset += length;
849             return (zip_int64_t)length;
850
851         case ZIP_SOURCE_STAT: {
852             zip_stat_t *st = ZIP_SOURCE_GET_ARGS(zip_stat_t, data, length, &ctx->error);
853
854             if (st == NULL) {
855                 return -1;
856             }
857
858             st->valid |= ZIP_STAT_SIZE;
859             st->size = ctx->length;
860
861             return 0;
862         }
863
864         case ZIP_SOURCE_SUPPORTS:
865             return zip_source_make_command_bitmap(ZIP_SOURCE_CLOSE, ZIP_SOURCE_ERROR, ZIP_SOURCE_FREE, ZIP_SOURCE_OPEN, ZIP_SOURCE_READ, ZIP_SOURCE_STAT, -1);
866
867         default:
868             zip_error_set(&ctx->error, ZIP_ER_OPNOTSUPP, 0);
869             return -1;
870     }
871 }
872
873 static zip_source_t *
874 source_nul(zip_t *zs, zip_uint64_t length)
875 {
876     source_nul_t *ctx;
877     zip_source_t *src;
878
879     if ((ctx = (source_nul_t *)malloc(sizeof(*ctx))) == NULL) {
880         zip_error_set(zip_get_error(zs), ZIP_ER_MEMORY, 0);
881         return NULL;
882     }
883
884     zip_error_init(&ctx->error);
885     ctx->length = length;
886     ctx->offset = 0;
887
888     if ((src = zip_source_function(zs, source_nul_cb, ctx)) == NULL) {
889         free(ctx);
890         return NULL;
891     }
892
893     return src;
894 }
895
896
897 static int
898 write_memory_src_to_file(const char *archive, zip_source_t *src)
899 {
900     zip_stat_t zst;
901     char *buf;
902     FILE *fp;
903
904     if (zip_source_stat(src, &zst) < 0) {
905         fprintf(stderr, "zip_source_stat on buffer failed: %s\n", zip_error_strerror(zip_source_error(src)));
906         return -1;
907     }
908     if (zip_source_open(src) < 0) {
909         if (zip_error_code_zip(zip_source_error(src)) == ZIP_ER_DELETED) {
910             if (unlink(archive) < 0 && errno != ENOENT) {
911                 fprintf(stderr, "unlink failed: %s\n", strerror(errno));
912                 return -1;
913             }
914             return 0;
915         }
916         fprintf(stderr, "zip_source_open on buffer failed: %s\n", zip_error_strerror(zip_source_error(src)));
917         return -1;
918     }
919     if ((buf=malloc(zst.size)) == NULL) {
920         fprintf(stderr, "malloc failed: %s\n", strerror(errno));
921         zip_source_close(src);
922         return -1;
923     }
924     if (zip_source_read(src, buf, zst.size) < (zip_int64_t)zst.size) {
925         fprintf(stderr, "zip_source_read on buffer failed: %s\n", zip_error_strerror(zip_source_error(src)));
926         zip_source_close(src);
927         free(buf);
928         return -1;
929     }
930     zip_source_close(src);
931     if ((fp=fopen(archive, "wb")) == NULL) {
932         fprintf(stderr, "fopen failed: %s\n", strerror(errno));
933         free(buf);
934         return -1;
935     }
936     if (fwrite(buf, zst.size, 1, fp) < 1) {
937         fprintf(stderr, "fwrite failed: %s\n", strerror(errno));
938         free(buf);
939         fclose(fp);
940         return -1;
941     }
942     free(buf);
943     if (fclose(fp) != 0) {
944         fprintf(stderr, "fclose failed: %s\n", strerror(errno));
945         return -1;
946     }
947     return 0;
948 }
949
950 dispatch_table_t dispatch_table[] = {
951     { "add", 2, "name content", "add file called name using content", add },
952     { "add_dir", 1, "name", "add directory", add_dir },
953     { "add_file", 4, "name file_to_add offset len", "add file to archive, len bytes starting from offset", add_file },
954     { "add_from_zip", 5, "name archivename index offset len", "add file from another archive, len bytes starting from offset", add_from_zip },
955     { "add_nul", 2, "name length", "add NUL bytes", add_nul },
956     { "cat", 1, "index", "output file contents to stdout", cat },
957     { "count_extra", 2, "index flags", "show number of extra fields for archive entry", count_extra },
958     { "count_extra_by_id", 3, "index extra_id flags", "show number of extra fields of type extra_id for archive entry", count_extra_by_id },
959     { "delete", 1, "index", "remove entry", delete },
960     { "delete_extra", 3, "index extra_idx flags", "remove extra field", delete_extra },
961     { "delete_extra_by_id", 4, "index extra_id extra_index flags", "remove extra field of type extra_id", delete_extra_by_id },
962     { "get_archive_comment", 0, "", "show archive comment", get_archive_comment },
963     { "get_extra", 3, "index extra_index flags", "show extra field", get_extra },
964     { "get_extra_by_id", 4, "index extra_id extra_index flags", "show extra field of type extra_id", get_extra_by_id },
965     { "get_file_comment", 1, "index", "get file comment", get_file_comment },
966     { "get_num_entries", 1, "flags", "get number of entries in archive", get_num_entries },
967     { "name_locate", 2, "name flags", "find entry in archive", name_locate },
968     { "print_progress", 0, "", "print progress during zip_close()", print_progress },
969     { "rename", 2, "index name", "rename entry", zrename },
970     { "replace_file_contents", 2, "index data", "replace entry with data", replace_file_contents },
971     { "set_archive_comment", 1, "comment", "set archive comment", set_archive_comment },
972     { "set_extra", 5, "index extra_id extra_index flags value", "set extra field", set_extra },
973     { "set_file_comment", 2, "index comment", "set file comment", set_file_comment },
974     { "set_file_compression", 3, "index method compression_flags", "set file compression method", set_file_compression },
975     { "set_file_encryption", 3, "index method password", "set file encryption method", set_file_encryption },
976     { "set_file_mtime", 2, "index timestamp", "set file modification time", set_file_mtime },
977     { "set_file_mtime_all", 1, "timestamp", "set file modification time for all files", set_file_mtime_all },
978     { "set_password", 1, "password", "set default password for encryption", set_password },
979     { "stat", 1, "index", "print information about entry", zstat },
980     { "unchange_all", 0, "", "revert all changes", unchange_all },
981     { "zin_close", 1, "index", "close input zip_source (for internal tests)", zin_close }
982 };
983
984 static int
985 dispatch(int argc, char *argv[])
986 {
987     unsigned int i;
988     for (i=0; i<sizeof(dispatch_table)/sizeof(dispatch_table_t); i++) {
989         if (strcmp(dispatch_table[i].cmdline_name, argv[0]) == 0) {
990             argc--;
991             argv++;
992             /* 1 for the command, argument_count for the arguments */
993             if (argc < dispatch_table[i].argument_count) {
994                 fprintf(stderr, "not enough arguments for command '%s': %d available, %d needed\n", dispatch_table[i].cmdline_name, argc, dispatch_table[i].argument_count);
995                 return -1;
996             }
997             if (dispatch_table[i].function(argc, argv) == 0)
998                 return 1 + dispatch_table[i].argument_count;
999             return -1;
1000         }
1001     }
1002
1003     fprintf(stderr, "unknown command '%s'\n", argv[0]);
1004     return -1;
1005 }
1006
1007
1008 static void
1009 usage(const char *progname, const char *reason)
1010 {
1011     unsigned int i;
1012     FILE *out;
1013     if (reason == NULL)
1014         out = stdout;
1015     else
1016         out = stderr;
1017     fprintf(out, "usage: %s [-cegHhmnrst] [-l len] [-o offset] archive command1 [args] [command2 [args] ...]\n", progname);
1018     if (reason != NULL) {
1019         fprintf(out, "%s\n", reason);
1020         exit(1);
1021     }
1022
1023     fprintf(out, "\nSupported options are:\n"
1024             "\t-c\t\tcheck consistency\n"
1025             "\t-e\t\terror if archive already exists (only useful with -n)\n"
1026             "\t-g\t\tguess file name encoding (for stat)\n"
1027             "\t-H\t\twrite files with holes compactly\n"
1028             "\t-h\t\tdisplay this usage\n"
1029             "\t-l len\t\tonly use len bytes of file\n"
1030             "\t-m\t\tread archive into memory, and modify there; write out at end\n"
1031             "\t-n\t\tcreate archive if it doesn't exist\n"
1032             "\t-o offset\tstart reading file at offset\n"
1033             "\t-r\t\tprint raw file name encoding without translation (for stat)\n"
1034             "\t-s\t\tfollow file name convention strictly (for stat)\n"
1035             "\t-t\t\tdisregard current archive contents, if any\n");
1036     fprintf(out, "\nSupported commands and arguments are:\n");
1037     for (i=0; i<sizeof(dispatch_table)/sizeof(dispatch_table_t); i++) {
1038         fprintf(out, "\t%s %s\n\t    %s\n\n", dispatch_table[i].cmdline_name, dispatch_table[i].arg_names, dispatch_table[i].description);
1039     }
1040     fprintf(out, "\nSupported flags are:\n"
1041             "\t0\t(no flags)\n"
1042             "\tC\tZIP_FL_NOCASE\n"
1043             "\tc\tZIP_FL_CENTRAL\n"
1044             "\td\tZIP_FL_NODIR\n"
1045             "\tl\tZIP_FL_LOCAL\n"
1046             "\tu\tZIP_FL_UNCHANGED\n");
1047     fprintf(out, "\nSupported compression methods are:\n"
1048             "\tdefault\n"
1049 #if defined(HAVE_LIBBZ2)
1050             "\tbzip2\n"
1051 #endif
1052             "\tdeflate\n"
1053             "\tstore\n");
1054     fprintf(out, "\nSupported compression methods are:\n"
1055             "\tnone\n"
1056             "\tAES-128\n"
1057             "\tAES-192\n"
1058             "\tAES-256\n");
1059     fprintf(out, "\nThe index is zero-based.\n");
1060     exit(0);
1061 }
1062
1063 int
1064 main(int argc, char *argv[])
1065 {
1066     const char *archive;
1067     zip_source_t *memory_src = NULL;
1068     unsigned int i;
1069     int c, arg, err, flags;
1070     const char *prg;
1071     source_type_t source_type = SOURCE_TYPE_NONE;
1072     zip_uint64_t len = 0, offset = 0;
1073     zip_error_t error;
1074
1075     flags = 0;
1076     prg = argv[0];
1077
1078     while ((c=getopt(argc, argv, "cegHhl:mno:rst")) != -1) {
1079         switch (c) {
1080         case 'c':
1081             flags |= ZIP_CHECKCONS;
1082             break;
1083         case 'e':
1084             flags |= ZIP_EXCL;
1085             break;
1086         case 'g':
1087             stat_flags = ZIP_FL_ENC_GUESS;
1088             break;
1089         case 'H':
1090             source_type = SOURCE_TYPE_HOLE;
1091             break;
1092         case 'h':
1093             usage(prg, NULL);
1094             break;
1095         case 'l':
1096             len = strtoull(optarg, NULL, 10);
1097             break;
1098         case 'm':
1099             source_type = SOURCE_TYPE_IN_MEMORY;
1100             break;
1101         case 'n':
1102             flags |= ZIP_CREATE;
1103             break;
1104         case 'o':
1105             offset = strtoull(optarg, NULL, 10);
1106             break;
1107         case 'r':
1108             stat_flags = ZIP_FL_ENC_RAW;
1109             break;
1110         case 's':
1111             stat_flags = ZIP_FL_ENC_STRICT;
1112             break;
1113         case 't':
1114             flags |= ZIP_TRUNCATE;
1115             break;
1116
1117         default:
1118         {
1119             char reason[128];
1120             snprintf(reason, sizeof(reason), "invalid option -%c", optopt);
1121             usage(prg, reason);
1122         }
1123         }
1124     }
1125
1126     if (optind >= argc-1)
1127         usage(prg, "too few arguments");
1128
1129     arg = optind;
1130
1131     archive = argv[arg++];
1132
1133     if (flags == 0)
1134         flags = ZIP_CREATE;
1135
1136     zip_error_init(&error);
1137     switch (source_type) {
1138         case SOURCE_TYPE_NONE:
1139             za = read_from_file(archive, flags, &error, offset, len);
1140             break;
1141
1142         case SOURCE_TYPE_IN_MEMORY:
1143             za = read_to_memory(archive, flags, &error, &memory_src);
1144             break;
1145
1146         case SOURCE_TYPE_HOLE:
1147             za = read_hole(archive, flags, &error);
1148             break;
1149     }
1150     if (za == NULL) {
1151         fprintf(stderr, "can't open zip archive '%s': %s\n", archive, zip_error_strerror(&error));
1152         zip_error_fini(&error);
1153         return 1;
1154     }
1155     zip_error_fini(&error);
1156
1157     err = 0;
1158     while (arg < argc) {
1159         int ret;
1160         ret = dispatch(argc-arg, argv+arg);
1161         if (ret > 0) {
1162             arg += ret;
1163         } else {
1164             err = 1;
1165             break;
1166         }
1167     }
1168
1169     if (zip_close(za) == -1) {
1170         fprintf(stderr, "can't close zip archive '%s': %s\n", archive, zip_strerror(za));
1171         return 1;
1172     }
1173     if (source_type == SOURCE_TYPE_IN_MEMORY) {
1174         if (write_memory_src_to_file(archive, memory_src) < 0) {
1175             err = 1;
1176         }
1177         zip_source_free(memory_src);
1178     }
1179
1180     for (i=0; i<z_in_count; i++) {
1181         if (zip_close(z_in[i]) < 0) {
1182             err = 1;
1183         }
1184     }
1185
1186     return err;
1187 }