2 ziptool.c -- tool for modifying zip archive in multiple ways
3 Copyright (C) 2012-2022 Dieter Baron and Thomas Klausner
5 This file is part of libzip, a library to manipulate ZIP archives.
6 The authors can be contacted at <libzip@nih.at>
8 Redistribution and use in source and binary forms, with or without
9 modification, are permitted provided that the following conditions
11 1. Redistributions of source code must retain the above copyright
12 notice, this list of conditions and the following disclaimer.
13 2. Redistributions in binary form must reproduce the above copyright
14 notice, this list of conditions and the following disclaimer in
15 the documentation and/or other materials provided with the
17 3. The names of the authors may not be used to endorse or promote
18 products derived from this software without specific prior
21 THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
22 OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
25 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
27 GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
29 IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
30 OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
31 IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
44 /* WIN32 needs <fcntl.h> for _O_BINARY */
47 #define STDIN_FILENO _fileno(stdin)
60 typedef struct dispatch_table_s {
61 const char *cmdline_name;
63 const char *arg_names;
64 const char *description;
65 int (*function)(char *argv[]);
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);
75 #define OPTIONS_REGRESS ""
76 #define USAGE_REGRESS ""
80 unsigned int z_in_count;
81 zip_flags_t stat_flags;
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));
92 if (zip_add(za, argv[0], zs) == -1) {
94 fprintf(stderr, "can't add file '%s': %s\n", argv[0], zip_strerror(za));
101 add_dir(char *argv[]) {
103 if (zip_add_dir(za, argv[0]) < 0) {
104 fprintf(stderr, "can't add directory '%s': %s\n", argv[0], zip_strerror(za));
111 add_file(char *argv[]) {
113 zip_uint64_t start = strtoull(argv[2], NULL, 10);
114 zip_int64_t len = strtoll(argv[3], NULL, 10);
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));
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));
129 if (zip_add(za, argv[0], zs) == -1) {
131 fprintf(stderr, "can't add file '%s': %s\n", argv[0], zip_strerror(za));
138 add_from_zip(char *argv[]) {
139 zip_uint64_t idx, start;
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) {
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);
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]);
159 if (zip_add(za, argv[0], zs) == -1) {
160 fprintf(stderr, "can't add file '%s': %s\n", argv[0], zip_strerror(za));
162 zip_close(z_in[z_in_count]);
171 /* output file contents to stdout */
177 idx = strtoull(argv[0], NULL, 10);
180 /* Need to set stdout to binary mode for Windows */
181 setmode(fileno(stdout), _O_BINARY);
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));
187 while ((n = zip_fread(zf, buf, sizeof(buf))) > 0) {
188 if (fwrite(buf, (size_t)n, 1, stdout) != 1) {
190 fprintf(stderr, "can't write file contents to stdout: %s\n", strerror(errno));
195 fprintf(stderr, "can't read file at index '%" PRIu64 "': %s\n", idx, zip_file_strerror(zf));
199 if ((err = zip_fclose(zf)) != 0) {
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));
211 count_extra(char *argv[]) {
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));
222 printf("Extra field count: %d\n", count);
228 count_extra_by_id(char *argv[]) {
231 zip_flags_t ceflags = 0;
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));
241 printf("Extra field count: %d\n", count);
246 static int delete (char *argv[]) {
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));
257 delete_extra(char *argv[]) {
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));
272 delete_extra_by_id(char *argv[]) {
274 zip_uint16_t eid, eidx;
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));
288 get_archive_comment(char *argv[]) {
291 /* get archive comment */
292 if ((comment = zip_get_archive_comment(za, &len, 0)) == NULL)
293 printf("No archive comment\n");
295 printf("Archive comment: %.*s\n", len, comment);
300 get_extra(char *argv[]) {
302 zip_uint16_t id, eidx, eflen;
303 const zip_uint8_t *efdata;
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));
313 printf("Extra field 0x%04x: len %d", id, eflen);
316 hexdump(efdata, eflen);
323 get_extra_by_id(char *argv[]) {
325 zip_uint16_t eid, eidx, eflen;
326 const zip_uint8_t *efdata;
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));
336 printf("Extra field 0x%04x: len %d", eid, eflen);
339 hexdump(efdata, eflen);
346 get_file_comment(char *argv[]) {
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));
357 printf("No comment for '%s'\n", zip_get_name(za, idx, 0));
359 printf("File comment for '%s': %.*s\n", zip_get_name(za, idx, 0), len, comment);
364 get_num_entries(char *argv[]) {
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");
375 name_locate(char *argv[]) {
378 flags = get_flags(argv[1]);
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]);
384 printf("name '%s' using flags '%s' found at index %" PRId64 "\n", argv[0], argv[1], idx);
390 struct progress_userdata_s {
395 struct progress_userdata_s progress_userdata;
398 progress_callback(zip_t *archive, double percentage, void *ud) {
399 printf("%.1f%% done\n", percentage * 100);
400 progress_userdata.percentage = percentage;
404 print_progress(char *argv[]) {
405 zip_register_progress_callback_with_state(za, 0.001, progress_callback, NULL, NULL);
410 zrename(char *argv[]) {
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));
421 replace_file_contents(char *argv[]) {
422 /* replace file contents with data from command line */
426 idx = strtoull(argv[0], NULL, 10);
428 if ((s = zip_source_buffer(za, content, strlen(content), 0)) == NULL || zip_file_replace(za, idx, s, 0) < 0) {
430 fprintf(stderr, "error replacing file data: %s\n", zip_strerror(za));
437 set_extra(char *argv[]) {
439 zip_uint16_t eid, eidx;
440 const zip_uint8_t *efdata;
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));
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));
464 set_file_comment(char *argv[]) {
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));
475 set_file_compression(char *argv[]) {
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));
490 set_file_encryption(char *argv[]) {
494 idx = strtoull(argv[0], NULL, 10);
495 method = get_encryption_method(argv[1]);
497 if (strlen(password) == 0) {
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));
508 set_file_dostime(char *argv[]) {
509 /* set file last modification time (mtime) directly */
510 zip_uint16_t dostime, dosdate;
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));
523 set_file_mtime(char *argv[]) {
524 /* set file last modification time (mtime) */
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));
537 set_file_mtime_all(char *argv[]) {
538 /* set last modification time (mtime) for all files */
540 zip_int64_t num_entries;
542 mtime = (time_t)strtoull(argv[0], NULL, 10);
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));
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));
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]);
568 zstat(char *argv[]) {
572 idx = strtoull(argv[0], NULL, 10);
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));
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) {
589 #ifdef HAVE_LOCALTIME_R
591 tpm = localtime_r(&sb.mtime, &tm);
593 tpm = localtime(&sb.mtime);
596 printf("mtime: <not valid>\n");
599 strftime(buf, sizeof(buf), "%a %b %d %Y %H:%M:%S", tpm);
600 printf("mtime: '%s'\n", buf);
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);
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;
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)
646 else if (strcasecmp(arg, "deflate") == 0)
647 return ZIP_CM_DEFLATE;
648 #if defined(HAVE_LIBBZ2)
649 else if (strcasecmp(arg, "bzip2") == 0)
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.
656 else if (strcasecmp(arg, "lzma2") == 0)
659 else if (strcasecmp(arg, "lzma") == 0)
661 else if (strcasecmp(arg, "xz") == 0)
664 #if defined(HAVE_LIBZSTD)
665 else if (strcasecmp(arg, "zstd") == 0)
669 else if (strcasecmp(arg, "unknown") == 0)
671 return 0; /* TODO: error handling */
675 get_encryption_method(const char *arg) {
676 if (strcasecmp(arg, "none") == 0)
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)
688 return (zip_uint16_t)-1; /* TODO: error handling */
692 hexdump(const zip_uint8_t *data, zip_uint16_t len) {
700 for (i = 0; i < len; i++)
701 printf("%02x", data[i]);
706 read_from_file(const char *archive, int flags, zip_error_t *error, zip_uint64_t offset, zip_uint64_t length) {
708 zip_source_t *source;
711 if (offset == 0 && length == 0) {
712 if (strcmp(archive, "/dev/stdin") == 0) {
713 zaa = zip_fdopen(STDIN_FILENO, flags & ~ZIP_CREATE, &err);
716 zaa = zip_open(archive, flags, &err);
719 zip_error_set(error, err, errno);
724 if (length > ZIP_INT64_MAX) {
725 zip_error_set(error, ZIP_ER_INVAL, 0);
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);
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
773 dispatch(int argc, char *argv[]) {
775 for (i = 0; i < sizeof(dispatch_table) / sizeof(dispatch_table_t); i++) {
776 if (strcmp(dispatch_table[i].cmdline_name, argv[0]) == 0) {
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);
784 if (dispatch_table[i].function(argv) == 0)
785 return 1 + dispatch_table[i].argument_count;
790 fprintf(stderr, "unknown command '%s'\n", argv[0]);
796 usage(const char *progname, const char *reason) {
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);
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"
813 "\t-F size\t\tfragment size for in memory archive\n"
815 "\t-g\t\tguess file name encoding (for stat)\n"
817 "\t-H\t\twrite files with holes compactly\n"
819 "\t-h\t\tdisplay this usage\n"
820 "\t-l len\t\tonly use len bytes of file\n"
822 "\t-m\t\tread archive into memory, and modify there; write out at end\n"
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);
833 fprintf(out, "\nSupported flags are:\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"
846 if (zip_compression_method_supported(ZIP_CM_BZIP2, 1)) {
847 fprintf(out, "\tbzip2\n");
849 fprintf(out, "\tdeflate\n"
851 if (zip_compression_method_supported(ZIP_CM_XZ, 1)) {
852 fprintf(out, "\txz\n");
854 if (zip_compression_method_supported(ZIP_CM_ZSTD, 1)) {
855 fprintf(out, "\tzstd\n");
857 fprintf(out, "\nSupported encryption methods are:\n"
859 if (zip_encryption_method_supported(ZIP_EM_AES_128, 1)) {
860 fprintf(out, "\tAES-128\n");
862 if (zip_encryption_method_supported(ZIP_EM_AES_192, 1)) {
863 fprintf(out, "\tAES-192\n");
865 if (zip_encryption_method_supported(ZIP_EM_AES_256, 1)) {
866 fprintf(out, "\tAES-256\n");
868 fprintf(out, "\tPKWARE\n");
869 fprintf(out, "\nThe index is zero-based.\n");
874 #define ziptool_open read_from_file
876 ziptool_post_close(const char *archive) {
882 main(int argc, char *argv[]) {
885 int c, arg, err, flags;
887 zip_uint64_t len = 0, offset = 0;
893 while ((c = getopt(argc, argv, "ceghl:no:rst" OPTIONS_REGRESS)) != -1) {
896 flags |= ZIP_CHECKCONS;
902 stat_flags = ZIP_FL_ENC_GUESS;
908 len = strtoull(optarg, NULL, 10);
914 offset = strtoull(optarg, NULL, 10);
917 stat_flags = ZIP_FL_ENC_RAW;
920 stat_flags = ZIP_FL_ENC_STRICT;
923 flags |= ZIP_TRUNCATE;
925 #ifdef GETOPT_REGRESS
931 snprintf(reason, sizeof(reason), "invalid option -%c", optopt);
937 if (optind >= argc - 1)
938 usage(prg, "too few arguments");
942 archive = argv[arg++];
947 zip_error_init(&error);
948 za = ziptool_open(archive, flags, &error, offset, len);
950 fprintf(stderr, "can't open zip archive '%s': %s\n", archive, zip_error_strerror(&error));
951 zip_error_fini(&error);
954 zip_error_fini(&error);
959 ret = dispatch(argc - arg, argv + arg);
969 if (zip_close(za) == -1) {
970 fprintf(stderr, "can't close zip archive '%s': %s\n", archive, zip_strerror(za));
973 if (ziptool_post_close(archive) < 0) {
977 for (i = 0; i < z_in_count; i++) {
978 if (zip_close(z_in[i]) < 0) {