Imported Upstream version 1.9.0
[platform/upstream/libzip.git] / src / ziptool.c
1 /*
2   ziptool.c -- tool for modifying zip archive in multiple ways
3   Copyright (C) 2012-2022 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 <time.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 "zip.h"
57
58 #include "compat.h"
59
60 typedef struct dispatch_table_s {
61     const char *cmdline_name;
62     int argument_count;
63     const char *arg_names;
64     const char *description;
65     int (*function)(char *argv[]);
66 } dispatch_table_t;
67
68 static zip_flags_t get_flags(const char *arg);
69 static zip_int32_t get_compression_method(const char *arg);
70 static zip_uint16_t get_encryption_method(const char *arg);
71 static void hexdump(const zip_uint8_t *data, zip_uint16_t len);
72 int ziptool_post_close(const char *archive);
73
74 #ifndef FOR_REGRESS
75 #define OPTIONS_REGRESS ""
76 #define USAGE_REGRESS ""
77 #endif
78
79 zip_t *za, *z_in[16];
80 unsigned int z_in_count;
81 zip_flags_t stat_flags;
82
83 static int
84 add(char *argv[]) {
85     zip_source_t *zs;
86
87     if ((zs = zip_source_buffer(za, argv[1], strlen(argv[1]), 0)) == NULL) {
88         fprintf(stderr, "can't create zip_source from buffer: %s\n", zip_strerror(za));
89         return -1;
90     }
91
92     if (zip_add(za, argv[0], zs) == -1) {
93         zip_source_free(zs);
94         fprintf(stderr, "can't add file '%s': %s\n", argv[0], zip_strerror(za));
95         return -1;
96     }
97     return 0;
98 }
99
100 static int
101 add_dir(char *argv[]) {
102     /* add directory */
103     if (zip_add_dir(za, argv[0]) < 0) {
104         fprintf(stderr, "can't add directory '%s': %s\n", argv[0], zip_strerror(za));
105         return -1;
106     }
107     return 0;
108 }
109
110 static int
111 add_file(char *argv[]) {
112     zip_source_t *zs;
113     zip_uint64_t start = strtoull(argv[2], NULL, 10);
114     zip_int64_t len = strtoll(argv[3], NULL, 10);
115
116     if (strcmp(argv[1], "/dev/stdin") == 0) {
117         if ((zs = zip_source_filep(za, stdin, start, len)) == NULL) {
118             fprintf(stderr, "can't create zip_source from stdin: %s\n", zip_strerror(za));
119             return -1;
120         }
121     }
122     else {
123         if ((zs = zip_source_file(za, argv[1], start, len)) == NULL) {
124             fprintf(stderr, "can't create zip_source from file: %s\n", zip_strerror(za));
125             return -1;
126         }
127     }
128
129     if (zip_add(za, argv[0], zs) == -1) {
130         zip_source_free(zs);
131         fprintf(stderr, "can't add file '%s': %s\n", argv[0], zip_strerror(za));
132         return -1;
133     }
134     return 0;
135 }
136
137 static int
138 add_from_zip(char *argv[]) {
139     zip_uint64_t idx, start;
140     zip_int64_t len;
141     int err;
142     zip_source_t *zs;
143     /* add from another zip file */
144     idx = strtoull(argv[2], NULL, 10);
145     start = strtoull(argv[3], NULL, 10);
146     len = strtoll(argv[4], NULL, 10);
147     if ((z_in[z_in_count] = zip_open(argv[1], ZIP_CHECKCONS, &err)) == NULL) {
148         zip_error_t error;
149         zip_error_init_with_code(&error, err);
150         fprintf(stderr, "can't open zip archive '%s': %s\n", argv[1], zip_error_strerror(&error));
151         zip_error_fini(&error);
152         return -1;
153     }
154     if ((zs = zip_source_zip(za, z_in[z_in_count], idx, 0, start, len)) == NULL) {
155         fprintf(stderr, "error creating file source from '%s' index '%" PRIu64 "': %s\n", argv[1], idx, zip_strerror(za));
156         zip_close(z_in[z_in_count]);
157         return -1;
158     }
159     if (zip_add(za, argv[0], zs) == -1) {
160         fprintf(stderr, "can't add file '%s': %s\n", argv[0], zip_strerror(za));
161         zip_source_free(zs);
162         zip_close(z_in[z_in_count]);
163         return -1;
164     }
165     z_in_count++;
166     return 0;
167 }
168
169 static int
170 cat(char *argv[]) {
171     /* output file contents to stdout */
172     zip_uint64_t idx;
173     zip_int64_t n;
174     zip_file_t *zf;
175     char buf[8192];
176     int err;
177     idx = strtoull(argv[0], NULL, 10);
178
179 #ifdef _WIN32
180     /* Need to set stdout to binary mode for Windows */
181     setmode(fileno(stdout), _O_BINARY);
182 #endif
183     if ((zf = zip_fopen_index(za, idx, 0)) == NULL) {
184         fprintf(stderr, "can't open file at index '%" PRIu64 "': %s\n", idx, zip_strerror(za));
185         return -1;
186     }
187     while ((n = zip_fread(zf, buf, sizeof(buf))) > 0) {
188         if (fwrite(buf, (size_t)n, 1, stdout) != 1) {
189             zip_fclose(zf);
190             fprintf(stderr, "can't write file contents to stdout: %s\n", strerror(errno));
191             return -1;
192         }
193     }
194     if (n == -1) {
195         fprintf(stderr, "can't read file at index '%" PRIu64 "': %s\n", idx, zip_file_strerror(zf));
196         zip_fclose(zf);
197         return -1;
198     }
199     if ((err = zip_fclose(zf)) != 0) {
200         zip_error_t error;
201
202         zip_error_init_with_code(&error, err);
203         fprintf(stderr, "can't close file at index '%" PRIu64 "': %s\n", idx, zip_error_strerror(&error));
204         return -1;
205     }
206
207     return 0;
208 }
209
210 static int
211 count_extra(char *argv[]) {
212     zip_int16_t count;
213     zip_uint64_t idx;
214     zip_flags_t ceflags = 0;
215     idx = strtoull(argv[0], NULL, 10);
216     ceflags = get_flags(argv[1]);
217     if ((count = zip_file_extra_fields_count(za, idx, ceflags)) < 0) {
218         fprintf(stderr, "can't get extra field count for file at index '%" PRIu64 "': %s\n", idx, zip_strerror(za));
219         return -1;
220     }
221     else {
222         printf("Extra field count: %d\n", count);
223     }
224     return 0;
225 }
226
227 static int
228 count_extra_by_id(char *argv[]) {
229     zip_int16_t count;
230     zip_uint16_t eid;
231     zip_flags_t ceflags = 0;
232     zip_uint64_t idx;
233     idx = strtoull(argv[0], NULL, 10);
234     eid = (zip_uint16_t)strtoull(argv[1], NULL, 10);
235     ceflags = get_flags(argv[2]);
236     if ((count = zip_file_extra_fields_count_by_id(za, idx, eid, ceflags)) < 0) {
237         fprintf(stderr, "can't get extra field count for file at index '%" PRIu64 "' and for id '%d': %s\n", idx, eid, zip_strerror(za));
238         return -1;
239     }
240     else {
241         printf("Extra field count: %d\n", count);
242     }
243     return 0;
244 }
245
246 static int delete (char *argv[]) {
247     zip_uint64_t idx;
248     idx = strtoull(argv[0], NULL, 10);
249     if (zip_delete(za, idx) < 0) {
250         fprintf(stderr, "can't delete file at index '%" PRIu64 "': %s\n", idx, zip_strerror(za));
251         return -1;
252     }
253     return 0;
254 }
255
256 static int
257 delete_extra(char *argv[]) {
258     zip_flags_t geflags;
259     zip_uint16_t eid;
260     zip_uint64_t idx;
261     idx = strtoull(argv[0], NULL, 10);
262     eid = (zip_uint16_t)strtoull(argv[1], NULL, 10);
263     geflags = get_flags(argv[2]);
264     if ((zip_file_extra_field_delete(za, idx, eid, geflags)) < 0) {
265         fprintf(stderr, "can't delete extra field data for file at index '%" PRIu64 "', extra field id '%d': %s\n", idx, eid, zip_strerror(za));
266         return -1;
267     }
268     return 0;
269 }
270
271 static int
272 delete_extra_by_id(char *argv[]) {
273     zip_flags_t geflags;
274     zip_uint16_t eid, eidx;
275     zip_uint64_t idx;
276     idx = strtoull(argv[0], NULL, 10);
277     eid = (zip_uint16_t)strtoull(argv[1], NULL, 10);
278     eidx = (zip_uint16_t)strtoull(argv[2], NULL, 10);
279     geflags = get_flags(argv[3]);
280     if ((zip_file_extra_field_delete_by_id(za, idx, eid, eidx, geflags)) < 0) {
281         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));
282         return -1;
283     }
284     return 0;
285 }
286
287 static int
288 get_archive_comment(char *argv[]) {
289     const char *comment;
290     int len;
291     /* get archive comment */
292     if ((comment = zip_get_archive_comment(za, &len, 0)) == NULL)
293         printf("No archive comment\n");
294     else
295         printf("Archive comment: %.*s\n", len, comment);
296     return 0;
297 }
298
299 static int
300 get_extra(char *argv[]) {
301     zip_flags_t geflags;
302     zip_uint16_t id, eidx, eflen;
303     const zip_uint8_t *efdata;
304     zip_uint64_t idx;
305     /* get extra field data */
306     idx = strtoull(argv[0], NULL, 10);
307     eidx = (zip_uint16_t)strtoull(argv[1], NULL, 10);
308     geflags = get_flags(argv[2]);
309     if ((efdata = zip_file_extra_field_get(za, idx, eidx, &id, &eflen, geflags)) == NULL) {
310         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));
311         return -1;
312     }
313     printf("Extra field 0x%04x: len %d", id, eflen);
314     if (eflen > 0) {
315         printf(", data ");
316         hexdump(efdata, eflen);
317     }
318     printf("\n");
319     return 0;
320 }
321
322 static int
323 get_extra_by_id(char *argv[]) {
324     zip_flags_t geflags;
325     zip_uint16_t eid, eidx, eflen;
326     const zip_uint8_t *efdata;
327     zip_uint64_t idx;
328     idx = strtoull(argv[0], NULL, 10);
329     eid = (zip_uint16_t)strtoull(argv[1], NULL, 10);
330     eidx = (zip_uint16_t)strtoull(argv[2], NULL, 10);
331     geflags = get_flags(argv[3]);
332     if ((efdata = zip_file_extra_field_get_by_id(za, idx, eid, eidx, &eflen, geflags)) == NULL) {
333         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));
334         return -1;
335     }
336     printf("Extra field 0x%04x: len %d", eid, eflen);
337     if (eflen > 0) {
338         printf(", data ");
339         hexdump(efdata, eflen);
340     }
341     printf("\n");
342     return 0;
343 }
344
345 static int
346 get_file_comment(char *argv[]) {
347     const char *comment;
348     int len;
349     zip_uint64_t idx;
350     /* get file comment */
351     idx = strtoull(argv[0], NULL, 10);
352     if ((comment = zip_get_file_comment(za, idx, &len, 0)) == NULL) {
353         fprintf(stderr, "can't get comment for '%s': %s\n", zip_get_name(za, idx, 0), zip_strerror(za));
354         return -1;
355     }
356     else if (len == 0)
357         printf("No comment for '%s'\n", zip_get_name(za, idx, 0));
358     else
359         printf("File comment for '%s': %.*s\n", zip_get_name(za, idx, 0), len, comment);
360     return 0;
361 }
362
363 static int
364 get_num_entries(char *argv[]) {
365     zip_int64_t count;
366     zip_flags_t flags;
367     /* get number of entries in archive */
368     flags = get_flags(argv[0]);
369     count = zip_get_num_entries(za, flags);
370     printf("%" PRId64 " entr%s in archive\n", count, count == 1 ? "y" : "ies");
371     return 0;
372 }
373
374 static int
375 name_locate(char *argv[]) {
376     zip_flags_t flags;
377     zip_int64_t idx;
378     flags = get_flags(argv[1]);
379
380     if ((idx = zip_name_locate(za, argv[0], flags)) < 0) {
381         fprintf(stderr, "can't find entry with name '%s' using flags '%s'\n", argv[0], argv[1]);
382     }
383     else {
384         printf("name '%s' using flags '%s' found at index %" PRId64 "\n", argv[0], argv[1], idx);
385     }
386
387     return 0;
388 }
389
390 struct progress_userdata_s {
391     double percentage;
392     double limit;
393 };
394
395 struct progress_userdata_s progress_userdata;
396
397 static void
398 progress_callback(zip_t *archive, double percentage, void *ud) {
399     printf("%.1f%% done\n", percentage * 100);
400     progress_userdata.percentage = percentage;
401 }
402
403 static int
404 print_progress(char *argv[]) {
405     zip_register_progress_callback_with_state(za, 0.001, progress_callback, NULL, NULL);
406     return 0;
407 }
408
409 static int
410 zrename(char *argv[]) {
411     zip_uint64_t idx;
412     idx = strtoull(argv[0], NULL, 10);
413     if (zip_rename(za, idx, argv[1]) < 0) {
414         fprintf(stderr, "can't rename file at index '%" PRIu64 "' to '%s': %s\n", idx, argv[1], zip_strerror(za));
415         return -1;
416     }
417     return 0;
418 }
419
420 static int
421 replace_file_contents(char *argv[]) {
422     /* replace file contents with data from command line */
423     const char *content;
424     zip_source_t *s;
425     zip_uint64_t idx;
426     idx = strtoull(argv[0], NULL, 10);
427     content = argv[1];
428     if ((s = zip_source_buffer(za, content, strlen(content), 0)) == NULL || zip_file_replace(za, idx, s, 0) < 0) {
429         zip_source_free(s);
430         fprintf(stderr, "error replacing file data: %s\n", zip_strerror(za));
431         return -1;
432     }
433     return 0;
434 }
435
436 static int
437 set_extra(char *argv[]) {
438     zip_flags_t geflags;
439     zip_uint16_t eid, eidx;
440     const zip_uint8_t *efdata;
441     zip_uint64_t idx;
442     idx = strtoull(argv[0], NULL, 10);
443     eid = (zip_uint16_t)strtoull(argv[1], NULL, 10);
444     eidx = (zip_uint16_t)strtoull(argv[2], NULL, 10);
445     geflags = get_flags(argv[3]);
446     efdata = (zip_uint8_t *)argv[4];
447     if ((zip_file_extra_field_set(za, idx, eid, eidx, efdata, (zip_uint16_t)strlen((const char *)efdata), geflags)) < 0) {
448         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));
449         return -1;
450     }
451     return 0;
452 }
453
454 static int
455 set_archive_comment(char *argv[]) {
456     if (zip_set_archive_comment(za, argv[0], (zip_uint16_t)strlen(argv[0])) < 0) {
457         fprintf(stderr, "can't set archive comment to '%s': %s\n", argv[0], zip_strerror(za));
458         return -1;
459     }
460     return 0;
461 }
462
463 static int
464 set_file_comment(char *argv[]) {
465     zip_uint64_t idx;
466     idx = strtoull(argv[0], NULL, 10);
467     if (zip_file_set_comment(za, idx, argv[1], (zip_uint16_t)strlen(argv[1]), 0) < 0) {
468         fprintf(stderr, "can't set file comment at index '%" PRIu64 "' to '%s': %s\n", idx, argv[1], zip_strerror(za));
469         return -1;
470     }
471     return 0;
472 }
473
474 static int
475 set_file_compression(char *argv[]) {
476     zip_int32_t method;
477     zip_uint32_t flags;
478     zip_uint64_t idx;
479     idx = strtoull(argv[0], NULL, 10);
480     method = get_compression_method(argv[1]);
481     flags = (zip_uint32_t)strtoull(argv[2], NULL, 10);
482     if (zip_set_file_compression(za, idx, method, flags) < 0) {
483         fprintf(stderr, "can't set file compression method at index '%" PRIu64 "' to '%s', flags '%" PRIu32 "': %s\n", idx, argv[1], flags, zip_strerror(za));
484         return -1;
485     }
486     return 0;
487 }
488
489 static int
490 set_file_encryption(char *argv[]) {
491     zip_uint16_t method;
492     zip_uint64_t idx;
493     char *password;
494     idx = strtoull(argv[0], NULL, 10);
495     method = get_encryption_method(argv[1]);
496     password = argv[2];
497     if (strlen(password) == 0) {
498         password = NULL;
499     }
500     if (zip_file_set_encryption(za, idx, method, password) < 0) {
501         fprintf(stderr, "can't set file encryption method at index '%" PRIu64 "' to '%s': %s\n", idx, argv[1], zip_strerror(za));
502         return -1;
503     }
504     return 0;
505 }
506
507 static int
508 set_file_dostime(char *argv[]) {
509     /* set file last modification time (mtime) directly */
510     zip_uint16_t dostime, dosdate;
511     zip_uint64_t idx;
512     idx = strtoull(argv[0], NULL, 10);
513     dostime = (zip_uint16_t)strtoull(argv[1], NULL, 10);
514     dosdate = (zip_uint16_t)strtoull(argv[2], NULL, 10);
515     if (zip_file_set_dostime(za, idx, dostime, dosdate, 0) < 0) {
516         fprintf(stderr, "can't set file dostime at index '%" PRIu64 "' to '%d'/'%d': %s\n", idx, (int)dostime, (int)dosdate, zip_strerror(za));
517         return -1;
518     }
519     return 0;
520 }
521
522 static int
523 set_file_mtime(char *argv[]) {
524     /* set file last modification time (mtime) */
525     time_t mtime;
526     zip_uint64_t idx;
527     idx = strtoull(argv[0], NULL, 10);
528     mtime = (time_t)strtoull(argv[1], NULL, 10);
529     if (zip_file_set_mtime(za, idx, mtime, 0) < 0) {
530         fprintf(stderr, "can't set file mtime at index '%" PRIu64 "' to '%lld': %s\n", idx, (long long)mtime, zip_strerror(za));
531         return -1;
532     }
533     return 0;
534 }
535
536 static int
537 set_file_mtime_all(char *argv[]) {
538     /* set last modification time (mtime) for all files */
539     time_t mtime;
540     zip_int64_t num_entries;
541     zip_uint64_t idx;
542     mtime = (time_t)strtoull(argv[0], NULL, 10);
543
544     if ((num_entries = zip_get_num_entries(za, 0)) < 0) {
545         fprintf(stderr, "can't get number of entries: %s\n", zip_strerror(za));
546         return -1;
547     }
548     for (idx = 0; idx < (zip_uint64_t)num_entries; idx++) {
549         if (zip_file_set_mtime(za, idx, mtime, 0) < 0) {
550             fprintf(stderr, "can't set file mtime at index '%" PRIu64 "' to '%lld': %s\n", idx, (long long)mtime, zip_strerror(za));
551             return -1;
552         }
553     }
554     return 0;
555 }
556
557 static int
558 set_password(char *argv[]) {
559     /* set default password */
560     if (zip_set_default_password(za, argv[0]) < 0) {
561         fprintf(stderr, "can't set default password to '%s'\n", argv[0]);
562         return -1;
563     }
564     return 0;
565 }
566
567 static int
568 zstat(char *argv[]) {
569     zip_uint64_t idx;
570     char buf[100];
571     struct zip_stat sb;
572     idx = strtoull(argv[0], NULL, 10);
573
574     if (zip_stat_index(za, idx, stat_flags, &sb) < 0) {
575         fprintf(stderr, "zip_stat_index failed on '%" PRIu64 "' failed: %s\n", idx, zip_strerror(za));
576         return -1;
577     }
578
579     if (sb.valid & ZIP_STAT_NAME)
580         printf("name: '%s'\n", sb.name);
581     if (sb.valid & ZIP_STAT_INDEX)
582         printf("index: '%" PRIu64 "'\n", sb.index);
583     if (sb.valid & ZIP_STAT_SIZE)
584         printf("size: '%" PRIu64 "'\n", sb.size);
585     if (sb.valid & ZIP_STAT_COMP_SIZE)
586         printf("compressed size: '%" PRIu64 "'\n", sb.comp_size);
587     if (sb.valid & ZIP_STAT_MTIME) {
588         struct tm *tpm;
589 #ifdef HAVE_LOCALTIME_R
590         struct tm tm;
591         tpm = localtime_r(&sb.mtime, &tm);
592 #else
593         tpm = localtime(&sb.mtime);
594 #endif
595         if (tpm == NULL) {
596             printf("mtime: <not valid>\n");
597         }
598         else {
599             strftime(buf, sizeof(buf), "%a %b %d %Y %H:%M:%S", tpm);
600             printf("mtime: '%s'\n", buf);
601         }
602     }
603     if (sb.valid & ZIP_STAT_CRC)
604         printf("crc: '%0x'\n", sb.crc);
605     if (sb.valid & ZIP_STAT_COMP_METHOD)
606         printf("compression method: '%d'\n", sb.comp_method);
607     if (sb.valid & ZIP_STAT_ENCRYPTION_METHOD)
608         printf("encryption method: '%d'\n", sb.encryption_method);
609     if (sb.valid & ZIP_STAT_FLAGS)
610         printf("flags: '%ld'\n", (long)sb.flags);
611     printf("\n");
612
613     return 0;
614 }
615
616 static zip_flags_t
617 get_flags(const char *arg) {
618     zip_flags_t flags = 0;
619     if (strchr(arg, 'C') != NULL)
620         flags |= ZIP_FL_NOCASE;
621     if (strchr(arg, 'c') != NULL)
622         flags |= ZIP_FL_CENTRAL;
623     if (strchr(arg, 'd') != NULL)
624         flags |= ZIP_FL_NODIR;
625     if (strchr(arg, 'l') != NULL)
626         flags |= ZIP_FL_LOCAL;
627     if (strchr(arg, 'u') != NULL)
628         flags |= ZIP_FL_UNCHANGED;
629     if (strchr(arg, '8') != NULL)
630         flags |= ZIP_FL_ENC_UTF_8;
631     if (strchr(arg, '4') != NULL)
632         flags |= ZIP_FL_ENC_CP437;
633     if (strchr(arg, 'r') != NULL)
634         flags |= ZIP_FL_ENC_RAW;
635     if (strchr(arg, 's') != NULL)
636         flags |= ZIP_FL_ENC_STRICT;
637     return flags;
638 }
639
640 static zip_int32_t
641 get_compression_method(const char *arg) {
642     if (strcasecmp(arg, "default") == 0)
643         return ZIP_CM_DEFAULT;
644     else if (strcasecmp(arg, "store") == 0)
645         return ZIP_CM_STORE;
646     else if (strcasecmp(arg, "deflate") == 0)
647         return ZIP_CM_DEFLATE;
648 #if defined(HAVE_LIBBZ2)
649     else if (strcasecmp(arg, "bzip2") == 0)
650         return ZIP_CM_BZIP2;
651 #endif
652 #if defined(HAVE_LIBLZMA)
653     /*  Disabled - because 7z isn't able to unpack ZIP+LZMA ZIP+LZMA2
654         archives made this way - and vice versa.
655
656         else if (strcasecmp(arg, "lzma2") == 0)
657           return ZIP_CM_LZMA2;
658     */
659     else if (strcasecmp(arg, "lzma") == 0)
660         return ZIP_CM_LZMA;
661     else if (strcasecmp(arg, "xz") == 0)
662         return ZIP_CM_XZ;
663 #endif
664 #if defined(HAVE_LIBZSTD)
665     else if (strcasecmp(arg, "zstd") == 0)
666         return ZIP_CM_ZSTD;
667
668 #endif
669     else if (strcasecmp(arg, "unknown") == 0)
670         return 100;
671     return 0; /* TODO: error handling */
672 }
673
674 static zip_uint16_t
675 get_encryption_method(const char *arg) {
676     if (strcasecmp(arg, "none") == 0)
677         return ZIP_EM_NONE;
678     else if (strcasecmp(arg, "PKWARE") == 0)
679         return ZIP_EM_TRAD_PKWARE;
680     else if (strcasecmp(arg, "AES-128") == 0)
681         return ZIP_EM_AES_128;
682     else if (strcasecmp(arg, "AES-192") == 0)
683         return ZIP_EM_AES_192;
684     else if (strcasecmp(arg, "AES-256") == 0)
685         return ZIP_EM_AES_256;
686     else if (strcasecmp(arg, "unknown") == 0)
687         return 100;
688     return (zip_uint16_t)-1; /* TODO: error handling */
689 }
690
691 static void
692 hexdump(const zip_uint8_t *data, zip_uint16_t len) {
693     zip_uint16_t i;
694
695     if (len <= 0)
696         return;
697
698     printf("0x");
699
700     for (i = 0; i < len; i++)
701         printf("%02x", data[i]);
702 }
703
704
705 static zip_t *
706 read_from_file(const char *archive, int flags, zip_error_t *error, zip_uint64_t offset, zip_uint64_t length) {
707     zip_t *zaa;
708     zip_source_t *source;
709     int err;
710
711     if (offset == 0 && length == 0) {
712         if (strcmp(archive, "/dev/stdin") == 0) {
713             zaa = zip_fdopen(STDIN_FILENO, flags & ~ZIP_CREATE, &err);
714         }
715         else {
716             zaa = zip_open(archive, flags, &err);
717         }
718         if (zaa == NULL) {
719             zip_error_set(error, err, errno);
720             return NULL;
721         }
722     }
723     else {
724         if (length > ZIP_INT64_MAX) {
725             zip_error_set(error, ZIP_ER_INVAL, 0);
726             return NULL;
727         }
728         if ((source = zip_source_file_create(archive, offset, (zip_int64_t)length, error)) == NULL || (zaa = zip_open_from_source(source, flags, error)) == NULL) {
729             zip_source_free(source);
730             return NULL;
731         }
732     }
733
734     return zaa;
735 }
736
737 dispatch_table_t dispatch_table[] = {{"add", 2, "name content", "add file called name using content", add},
738                                      {"add_dir", 1, "name", "add directory", add_dir},
739                                      {"add_file", 4, "name file_to_add offset len", "add file to archive, len bytes starting from offset", add_file},
740                                      {"add_from_zip", 5, "name archivename index offset len", "add file from another archive, len bytes starting from offset", add_from_zip},
741                                      {"cat", 1, "index", "output file contents to stdout", cat},
742                                      {"count_extra", 2, "index flags", "show number of extra fields for archive entry", count_extra},
743                                      {"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},
744                                      {"delete", 1, "index", "remove entry", delete},
745                                      {"delete_extra", 3, "index extra_idx flags", "remove extra field", delete_extra},
746                                      {"delete_extra_by_id", 4, "index extra_id extra_index flags", "remove extra field of type extra_id", delete_extra_by_id},
747                                      {"get_archive_comment", 0, "", "show archive comment", get_archive_comment},
748                                      {"get_extra", 3, "index extra_index flags", "show extra field", get_extra},
749                                      {"get_extra_by_id", 4, "index extra_id extra_index flags", "show extra field of type extra_id", get_extra_by_id},
750                                      {"get_file_comment", 1, "index", "get file comment", get_file_comment},
751                                      {"get_num_entries", 1, "flags", "get number of entries in archive", get_num_entries},
752                                      {"name_locate", 2, "name flags", "find entry in archive", name_locate},
753                                      {"print_progress", 0, "", "print progress during zip_close()", print_progress},
754                                      {"rename", 2, "index name", "rename entry", zrename},
755                                      {"replace_file_contents", 2, "index data", "replace entry with data", replace_file_contents},
756                                      {"set_archive_comment", 1, "comment", "set archive comment", set_archive_comment},
757                                      {"set_extra", 5, "index extra_id extra_index flags value", "set extra field", set_extra},
758                                      {"set_file_comment", 2, "index comment", "set file comment", set_file_comment},
759                                      {"set_file_compression", 3, "index method compression_flags", "set file compression method", set_file_compression},
760                                      {"set_file_dostime", 3, "index time date", "set file modification time and date (DOS format)", set_file_dostime},
761                                      {"set_file_encryption", 3, "index method password", "set file encryption method", set_file_encryption},
762                                      {"set_file_mtime", 2, "index timestamp", "set file modification time", set_file_mtime},
763                                      {"set_file_mtime_all", 1, "timestamp", "set file modification time for all files", set_file_mtime_all},
764                                      {"set_password", 1, "password", "set default password for encryption", set_password},
765                                      {"stat", 1, "index", "print information about entry", zstat}
766 #ifdef DISPATCH_REGRESS
767                                      ,
768                                      DISPATCH_REGRESS
769 #endif
770 };
771
772 static int
773 dispatch(int argc, char *argv[]) {
774     unsigned int i;
775     for (i = 0; i < sizeof(dispatch_table) / sizeof(dispatch_table_t); i++) {
776         if (strcmp(dispatch_table[i].cmdline_name, argv[0]) == 0) {
777             argc--;
778             argv++;
779             /* 1 for the command, argument_count for the arguments */
780             if (argc < dispatch_table[i].argument_count) {
781                 fprintf(stderr, "not enough arguments for command '%s': %d available, %d needed\n", dispatch_table[i].cmdline_name, argc, dispatch_table[i].argument_count);
782                 return -1;
783             }
784             if (dispatch_table[i].function(argv) == 0)
785                 return 1 + dispatch_table[i].argument_count;
786             return -1;
787         }
788     }
789
790     fprintf(stderr, "unknown command '%s'\n", argv[0]);
791     return -1;
792 }
793
794
795 static void
796 usage(const char *progname, const char *reason) {
797     unsigned int i;
798     FILE *out;
799     if (reason == NULL)
800         out = stdout;
801     else
802         out = stderr;
803     fprintf(out, "usage: %s [-ceghnrst]" USAGE_REGRESS " [-l len] [-o offset] archive command1 [args] [command2 [args] ...]\n", progname);
804     if (reason != NULL) {
805         fprintf(out, "%s\n", reason);
806         exit(1);
807     }
808
809     fprintf(out, "\nSupported options are:\n"
810                  "\t-c\t\tcheck consistency\n"
811                  "\t-e\t\terror if archive already exists (only useful with -n)\n"
812 #ifdef FOR_REGRESS
813                  "\t-F size\t\tfragment size for in memory archive\n"
814 #endif
815                  "\t-g\t\tguess file name encoding (for stat)\n"
816 #ifdef FOR_REGRESS
817                  "\t-H\t\twrite files with holes compactly\n"
818 #endif
819                  "\t-h\t\tdisplay this usage\n"
820                  "\t-l len\t\tonly use len bytes of file\n"
821 #ifdef FOR_REGRESS
822                  "\t-m\t\tread archive into memory, and modify there; write out at end\n"
823 #endif
824                  "\t-n\t\tcreate archive if it doesn't exist\n"
825                  "\t-o offset\tstart reading file at offset\n"
826                  "\t-r\t\tprint raw file name encoding without translation (for stat)\n"
827                  "\t-s\t\tfollow file name convention strictly (for stat)\n"
828                  "\t-t\t\tdisregard current archive contents, if any\n");
829     fprintf(out, "\nSupported commands and arguments are:\n");
830     for (i = 0; i < sizeof(dispatch_table) / sizeof(dispatch_table_t); i++) {
831         fprintf(out, "\t%s %s\n\t    %s\n\n", dispatch_table[i].cmdline_name, dispatch_table[i].arg_names, dispatch_table[i].description);
832     }
833     fprintf(out, "\nSupported flags are:\n"
834                  "\t0\t(no flags)\n"
835                  "\t4\tZIP_FL_ENC_CP437\n"
836                  "\t8\tZIP_FL_ENC_UTF_8\n"
837                  "\tC\tZIP_FL_NOCASE\n"
838                  "\tc\tZIP_FL_CENTRAL\n"
839                  "\td\tZIP_FL_NODIR\n"
840                  "\tl\tZIP_FL_LOCAL\n"
841                  "\tr\tZIP_FL_ENC_RAW\n"
842                  "\ts\tZIP_FL_ENC_STRICT\n"
843                  "\tu\tZIP_FL_UNCHANGED\n");
844     fprintf(out, "\nSupported compression methods are:\n"
845                  "\tdefault\n");
846     if (zip_compression_method_supported(ZIP_CM_BZIP2, 1)) {
847         fprintf(out, "\tbzip2\n");
848     }
849     fprintf(out, "\tdeflate\n"
850                  "\tstore\n");
851     if (zip_compression_method_supported(ZIP_CM_XZ, 1)) {
852         fprintf(out, "\txz\n");
853     }
854     if (zip_compression_method_supported(ZIP_CM_ZSTD, 1)) {
855         fprintf(out, "\tzstd\n");
856     }
857     fprintf(out, "\nSupported encryption methods are:\n"
858                  "\tnone\n");
859     if (zip_encryption_method_supported(ZIP_EM_AES_128, 1)) {
860         fprintf(out, "\tAES-128\n");
861     }
862     if (zip_encryption_method_supported(ZIP_EM_AES_192, 1)) {
863         fprintf(out, "\tAES-192\n");
864     }
865     if (zip_encryption_method_supported(ZIP_EM_AES_256, 1)) {
866         fprintf(out, "\tAES-256\n");
867     }
868     fprintf(out, "\tPKWARE\n");
869     fprintf(out, "\nThe index is zero-based.\n");
870     exit(0);
871 }
872
873 #ifndef FOR_REGRESS
874 #define ziptool_open read_from_file
875 int
876 ziptool_post_close(const char *archive) {
877     return 0;
878 }
879 #endif
880
881 int
882 main(int argc, char *argv[]) {
883     const char *archive;
884     unsigned int i;
885     int c, arg, err, flags;
886     const char *prg;
887     zip_uint64_t len = 0, offset = 0;
888     zip_error_t error;
889
890     flags = 0;
891     prg = argv[0];
892
893     while ((c = getopt(argc, argv, "ceghl:no:rst" OPTIONS_REGRESS)) != -1) {
894         switch (c) {
895         case 'c':
896             flags |= ZIP_CHECKCONS;
897             break;
898         case 'e':
899             flags |= ZIP_EXCL;
900             break;
901         case 'g':
902             stat_flags = ZIP_FL_ENC_GUESS;
903             break;
904         case 'h':
905             usage(prg, NULL);
906             break;
907         case 'l':
908             len = strtoull(optarg, NULL, 10);
909             break;
910         case 'n':
911             flags |= ZIP_CREATE;
912             break;
913         case 'o':
914             offset = strtoull(optarg, NULL, 10);
915             break;
916         case 'r':
917             stat_flags = ZIP_FL_ENC_RAW;
918             break;
919         case 's':
920             stat_flags = ZIP_FL_ENC_STRICT;
921             break;
922         case 't':
923             flags |= ZIP_TRUNCATE;
924             break;
925 #ifdef GETOPT_REGRESS
926             GETOPT_REGRESS
927 #endif
928
929         default: {
930             char reason[128];
931             snprintf(reason, sizeof(reason), "invalid option -%c", optopt);
932             usage(prg, reason);
933         }
934         }
935     }
936
937     if (optind >= argc - 1)
938         usage(prg, "too few arguments");
939
940     arg = optind;
941
942     archive = argv[arg++];
943
944     if (flags == 0)
945         flags = ZIP_CREATE;
946
947     zip_error_init(&error);
948     za = ziptool_open(archive, flags, &error, offset, len);
949     if (za == NULL) {
950         fprintf(stderr, "can't open zip archive '%s': %s\n", archive, zip_error_strerror(&error));
951         zip_error_fini(&error);
952         return 1;
953     }
954     zip_error_fini(&error);
955
956     err = 0;
957     while (arg < argc) {
958         int ret;
959         ret = dispatch(argc - arg, argv + arg);
960         if (ret > 0) {
961             arg += ret;
962         }
963         else {
964             err = 1;
965             break;
966         }
967     }
968
969     if (zip_close(za) == -1) {
970         fprintf(stderr, "can't close zip archive '%s': %s\n", archive, zip_strerror(za));
971         return 1;
972     }
973     if (ziptool_post_close(archive) < 0) {
974         err = 1;
975     }
976
977     for (i = 0; i < z_in_count; i++) {
978         if (zip_close(z_in[i]) < 0) {
979             err = 1;
980         }
981     }
982
983     return err;
984 }