Rename modify to ziptool and install it.
diff --git a/src/ziptool.c b/src/ziptool.c
new file mode 100644
index 0000000..f647adc
--- /dev/null
+++ b/src/ziptool.c
@@ -0,0 +1,1034 @@
+/*
+ ziptool.c -- tool for modifying zip archive in multiple ways
+ Copyright (C) 2012-2016 Dieter Baron and Thomas Klausner
+
+ This file is part of libzip, a library to manipulate ZIP archives.
+ The authors can be contacted at <libzip@nih.at>
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ 3. The names of the authors may not be used to endorse or promote
+ products derived from this software without specific prior
+ written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
+ OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "config.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef _WIN32
+/* WIN32 needs <fcntl.h> for _O_BINARY */
+#include <fcntl.h>
+#endif
+
+#ifndef HAVE_GETOPT
+#include "getopt.h"
+#endif
+
+#include "zip.h"
+#include "compat.h"
+
+zip_source_t *source_hole_create(const char *, int flags, zip_error_t *);
+
+typedef enum {
+ SOURCE_TYPE_NONE,
+ SOURCE_TYPE_IN_MEMORY,
+ SOURCE_TYPE_HOLE
+} source_type_t;
+
+typedef struct dispatch_table_s {
+ const char *cmdline_name;
+ int argument_count;
+ const char *arg_names;
+ const char *description;
+ int (*function)(int argc, char *argv[]);
+} dispatch_table_t;
+
+static zip_flags_t get_flags(const char *arg);
+static zip_int32_t get_compression_method(const char *arg);
+static void hexdump(const zip_uint8_t *data, zip_uint16_t len);
+static zip_t *read_to_memory(const char *archive, int flags, int *err, zip_source_t **srcp);
+static zip_source_t *source_nul(zip_t *za, zip_uint64_t length);
+
+zip_t *za, *z_in[16];
+unsigned int z_in_count;
+zip_flags_t stat_flags;
+
+static int
+add(int argc, char *argv[]) {
+ zip_source_t *zs;
+
+ if ((zs=zip_source_buffer(za, argv[1], strlen(argv[1]), 0)) == NULL) {
+ fprintf(stderr, "can't create zip_source from buffer: %s\n", zip_strerror(za));
+ return -1;
+ }
+
+ if (zip_add(za, argv[0], zs) == -1) {
+ zip_source_free(zs);
+ fprintf(stderr, "can't add file '%s': %s\n", argv[0], zip_strerror(za));
+ return -1;
+ }
+ return 0;
+}
+
+static int
+add_dir(int argc, char *argv[]) {
+ /* add directory */
+ if (zip_add_dir(za, argv[0]) < 0) {
+ fprintf(stderr, "can't add directory '%s': %s\n", argv[0], zip_strerror(za));
+ return -1;
+ }
+ return 0;
+}
+
+static int
+add_file(int argc, char *argv[]) {
+ zip_source_t *zs;
+ zip_uint64_t start = strtoull(argv[2], NULL, 10);
+ zip_int64_t len = strtoll(argv[3], NULL, 10);
+
+ if (strcmp(argv[1], "/dev/stdin") == 0) {
+ if ((zs=zip_source_filep(za, stdin, start, len)) == NULL) {
+ fprintf(stderr, "can't create zip_source from stdin: %s\n", zip_strerror(za));
+ return -1;
+ }
+ } else {
+ if ((zs=zip_source_file(za, argv[1], start, len)) == NULL) {
+ fprintf(stderr, "can't create zip_source from file: %s\n", zip_strerror(za));
+ return -1;
+ }
+ }
+
+ if (zip_add(za, argv[0], zs) == -1) {
+ zip_source_free(zs);
+ fprintf(stderr, "can't add file '%s': %s\n", argv[0], zip_strerror(za));
+ return -1;
+ }
+ return 0;
+}
+
+static int
+add_from_zip(int argc, char *argv[]) {
+ zip_uint64_t idx, start;
+ zip_int64_t len;
+ int err;
+ zip_source_t *zs;
+ /* add from another zip file */
+ idx = strtoull(argv[2], NULL, 10);
+ start = strtoull(argv[3], NULL, 10);
+ len = strtoll(argv[4], NULL, 10);
+ if ((z_in[z_in_count]=zip_open(argv[1], ZIP_CHECKCONS, &err)) == NULL) {
+ zip_error_t error;
+ zip_error_init_with_code(&error, err);
+ fprintf(stderr, "can't open zip archive '%s': %s\n", argv[1], zip_error_strerror(&error));
+ zip_error_fini(&error);
+ return -1;
+ }
+ if ((zs=zip_source_zip(za, z_in[z_in_count], idx, 0, start, len)) == NULL) {
+ fprintf(stderr, "error creating file source from '%s' index '%" PRIu64 "': %s\n", argv[1], idx, zip_strerror(za));
+ zip_close(z_in[z_in_count]);
+ return -1;
+ }
+ if (zip_add(za, argv[0], zs) == -1) {
+ fprintf(stderr, "can't add file '%s': %s\n", argv[0], zip_strerror(za));
+ zip_source_free(zs);
+ zip_close(z_in[z_in_count]);
+ return -1;
+ }
+ z_in_count++;
+ return 0;
+}
+
+static int
+add_nul(int argc, char *argv[]) {
+ zip_source_t *zs;
+ zip_uint64_t length = strtoull(argv[1], NULL, 10);
+
+ if ((zs=source_nul(za, length)) == NULL) {
+ fprintf(stderr, "can't create zip_source for length: %s\n", zip_strerror(za));
+ return -1;
+ }
+
+ if (zip_add(za, argv[0], zs) == -1) {
+ zip_source_free(zs);
+ fprintf(stderr, "can't add file '%s': %s\n", argv[0], zip_strerror(za));
+ return -1;
+ }
+ return 0;
+}
+
+static int
+cat(int argc, char *argv[]) {
+ /* output file contents to stdout */
+ zip_uint64_t idx;
+ zip_int64_t n;
+ zip_file_t *zf;
+ char buf[8192];
+ int err;
+ idx = strtoull(argv[0], NULL, 10);
+
+#ifdef _WIN32
+ /* Need to set stdout to binary mode for Windows */
+ setmode(fileno(stdout), _O_BINARY);
+#endif
+ if ((zf=zip_fopen_index(za, idx, 0)) == NULL) {
+ fprintf(stderr, "can't open file at index '%" PRIu64 "': %s\n", idx, zip_strerror(za));
+ return -1;
+ }
+ while ((n=zip_fread(zf, buf, sizeof(buf))) > 0) {
+ if (fwrite(buf, (size_t)n, 1, stdout) != 1) {
+ zip_fclose(zf);
+ fprintf(stderr, "can't write file contents to stdout: %s\n", strerror(errno));
+ return -1;
+ }
+ }
+ if (n == -1) {
+ zip_fclose(zf);
+ fprintf(stderr, "can't read file at index '%" PRIu64 "': %s\n", idx, zip_file_strerror(zf));
+ return -1;
+ }
+ if ((err = zip_fclose(zf)) != 0) {
+ zip_error_t error;
+
+ zip_error_init_with_code(&error, err);
+ fprintf(stderr, "can't close file at index '%" PRIu64 "': %s\n", idx, zip_error_strerror(&error));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+count_extra(int argc, char *argv[]) {
+ zip_int16_t count;
+ zip_uint64_t idx;
+ zip_flags_t ceflags = 0;
+ idx = strtoull(argv[0], NULL, 10);
+ ceflags = get_flags(argv[1]);
+ if ((count=zip_file_extra_fields_count(za, idx, ceflags)) < 0) {
+ fprintf(stderr, "can't get extra field count for file at index '%" PRIu64 "': %s\n", idx, zip_strerror(za));
+ return -1;
+ } else {
+ printf("Extra field count: %d\n", count);
+ }
+ return 0;
+}
+
+static int
+count_extra_by_id(int argc, char *argv[]) {
+ zip_int16_t count;
+ zip_uint16_t eid;
+ zip_flags_t ceflags = 0;
+ zip_uint64_t idx;
+ idx = strtoull(argv[0], NULL, 10);
+ eid = (zip_uint16_t)strtoull(argv[1], NULL, 10);
+ ceflags = get_flags(argv[2]);
+ if ((count=zip_file_extra_fields_count_by_id(za, idx, eid, ceflags)) < 0) {
+ fprintf(stderr, "can't get extra field count for file at index '%" PRIu64 "' and for id `%d': %s\n", idx, eid, zip_strerror(za));
+ return -1;
+ } else {
+ printf("Extra field count: %d\n", count);
+ }
+ return 0;
+}
+
+static int
+delete(int argc, char *argv[]) {
+ zip_uint64_t idx;
+ idx = strtoull(argv[0], NULL, 10);
+ if (zip_delete(za, idx) < 0) {
+ fprintf(stderr, "can't delete file at index '%" PRIu64 "': %s\n", idx, zip_strerror(za));
+ return -1;
+ }
+ return 0;
+}
+
+static int
+delete_extra(int argc, char *argv[]) {
+ zip_flags_t geflags;
+ zip_uint16_t eid;
+ zip_uint64_t idx;
+ idx = strtoull(argv[0], NULL, 10);
+ eid = (zip_uint16_t)strtoull(argv[1], NULL, 10);
+ geflags = get_flags(argv[2]);
+ if ((zip_file_extra_field_delete(za, idx, eid, geflags)) < 0) {
+ fprintf(stderr, "can't delete extra field data for file at index '%" PRIu64 "', extra field id `%d': %s\n", idx, eid, zip_strerror(za));
+ return -1;
+ }
+ return 0;
+}
+
+static int
+delete_extra_by_id(int argc, char *argv[]) {
+ zip_flags_t geflags;
+ zip_uint16_t eid, eidx;
+ zip_uint64_t idx;
+ idx = strtoull(argv[0], NULL, 10);
+ eid = (zip_uint16_t)strtoull(argv[1], NULL, 10);
+ eidx = (zip_uint16_t)strtoull(argv[2], NULL, 10);
+ geflags = get_flags(argv[3]);
+ if ((zip_file_extra_field_delete_by_id(za, idx, eid, eidx, geflags)) < 0) {
+ 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));
+ return -1;
+ }
+ return 0;
+}
+
+static int
+get_archive_comment(int argc, char *argv[]) {
+ const char *comment;
+ int len;
+ /* get archive comment */
+ if ((comment=zip_get_archive_comment(za, &len, 0)) == NULL)
+ printf("No archive comment\n");
+ else
+ printf("Archive comment: %.*s\n", len, comment);
+ return 0;
+}
+
+static int
+get_extra(int argc, char *argv[]) {
+ zip_flags_t geflags;
+ zip_uint16_t id, eidx, eflen;
+ const zip_uint8_t *efdata;
+ zip_uint64_t idx;
+ /* get extra field data */
+ idx = strtoull(argv[0], NULL, 10);
+ eidx = (zip_uint16_t)strtoull(argv[1], NULL, 10);
+ geflags = get_flags(argv[2]);
+ if ((efdata=zip_file_extra_field_get(za, idx, eidx, &id, &eflen, geflags)) == NULL) {
+ 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));
+ return -1;
+ }
+ printf("Extra field 0x%04x: len %d", id, eflen);
+ if (eflen > 0) {
+ printf(", data ");
+ hexdump(efdata, eflen);
+ }
+ printf("\n");
+ return 0;
+}
+
+static int
+get_extra_by_id(int argc, char *argv[]) {
+ zip_flags_t geflags;
+ zip_uint16_t eid, eidx, eflen;
+ const zip_uint8_t *efdata;
+ zip_uint64_t idx;
+ idx = strtoull(argv[0], NULL, 10);
+ eid = (zip_uint16_t)strtoull(argv[1], NULL, 10);
+ eidx = (zip_uint16_t)strtoull(argv[2], NULL, 10);
+ geflags = get_flags(argv[3]);
+ if ((efdata=zip_file_extra_field_get_by_id(za, idx, eid, eidx, &eflen, geflags)) == NULL) {
+ 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));
+ return -1;
+ }
+ printf("Extra field 0x%04x: len %d", eid, eflen);
+ if (eflen > 0) {
+ printf(", data ");
+ hexdump(efdata, eflen);
+ }
+ printf("\n");
+ return 0;
+}
+
+static int
+get_file_comment(int argc, char *argv[]) {
+ const char *comment;
+ int len;
+ zip_uint64_t idx;
+ /* get file comment */
+ idx = strtoull(argv[0], NULL, 10);
+ if ((comment=zip_get_file_comment(za, idx, &len, 0)) == NULL) {
+ fprintf(stderr, "can't get comment for '%s': %s\n", zip_get_name(za, idx, 0), zip_strerror(za));
+ return -1;
+ } else if (len == 0)
+ printf("No comment for '%s'\n", zip_get_name(za, idx, 0));
+ else
+ printf("File comment for '%s': %.*s\n", zip_get_name(za, idx, 0), len, comment);
+ return 0;
+}
+
+static int
+get_num_entries(int argc, char *argv[]) {
+ zip_int64_t count;
+ zip_flags_t flags;
+ /* get number of entries in archive */
+ flags = get_flags(argv[0]);
+ count = zip_get_num_entries(za, flags);
+ printf("%" PRId64 " entr%s in archive\n", count, count == 1 ? "y" : "ies");
+ return 0;
+}
+
+static int
+name_locate(int argc, char *argv[]) {
+ zip_flags_t flags;
+ zip_int64_t idx;
+ flags = get_flags(argv[1]);
+
+ if ((idx=zip_name_locate(za, argv[0], flags)) < 0) {
+ fprintf(stderr, "can't find entry with name '%s' using flags '%s'\n", argv[0], argv[1]);
+ } else {
+ printf("name '%s' using flags '%s' found at index %" PRId64 "\n", argv[0], argv[1], idx);
+ }
+
+ return 0;
+}
+
+static int
+zrename(int argc, char *argv[]) {
+ zip_uint64_t idx;
+ idx = strtoull(argv[0], NULL, 10);
+ if (zip_rename(za, idx, argv[1]) < 0) {
+ fprintf(stderr, "can't rename file at index '%" PRIu64 "' to `%s': %s\n", idx, argv[1], zip_strerror(za));
+ return -1;
+ }
+ return 0;
+}
+
+static int
+replace_file_contents(int argc, char *argv[]) {
+ /* replace file contents with data from command line */
+ const char *content;
+ zip_source_t *s;
+ zip_uint64_t idx;
+ idx = strtoull(argv[0], NULL, 10);
+ content = argv[1];
+ if ((s=zip_source_buffer(za, content, strlen(content), 0)) == NULL ||
+ zip_file_replace(za, idx, s, 0) < 0) {
+ zip_source_free(s);
+ fprintf(stderr, "error replacing file data: %s\n", zip_strerror(za));
+ return -1;
+ }
+ return 0;
+}
+
+static int
+set_extra(int argc, char *argv[]) {
+ zip_flags_t geflags;
+ zip_uint16_t eid, eidx;
+ const zip_uint8_t *efdata;
+ zip_uint64_t idx;
+ idx = strtoull(argv[0], NULL, 10);
+ eid = (zip_uint16_t)strtoull(argv[1], NULL, 10);
+ eidx = (zip_uint16_t)strtoull(argv[2], NULL, 10);
+ geflags = get_flags(argv[3]);
+ efdata = (zip_uint8_t *)argv[4];
+ if ((zip_file_extra_field_set(za, idx, eid, eidx, efdata, (zip_uint16_t)strlen((const char *)efdata), geflags)) < 0) {
+ 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));
+ return -1;
+ }
+ return 0;
+}
+
+static int
+set_archive_comment(int argc, char *argv[]) {
+ if (zip_set_archive_comment(za, argv[0], (zip_uint16_t)strlen(argv[0])) < 0) {
+ fprintf(stderr, "can't set archive comment to `%s': %s\n", argv[0], zip_strerror(za));
+ return -1;
+ }
+ return 0;
+}
+
+static int
+set_file_comment(int argc, char *argv[]) {
+ zip_uint64_t idx;
+ idx = strtoull(argv[0], NULL, 10);
+ if (zip_file_set_comment(za, idx, argv[1], (zip_uint16_t)strlen(argv[1]), 0) < 0) {
+ fprintf(stderr, "can't set file comment at index '%" PRIu64 "' to `%s': %s\n", idx, argv[1], zip_strerror(za));
+ return -1;
+ }
+ return 0;
+}
+
+static int
+set_file_compression(int argc, char *argv[]) {
+ zip_int32_t method;
+ zip_uint32_t flags;
+ zip_uint64_t idx;
+ idx = strtoull(argv[0], NULL, 10);
+ method = get_compression_method(argv[1]);
+ flags = (zip_uint32_t)strtoull(argv[2], NULL, 10);
+ if (zip_set_file_compression(za, idx, method, flags) < 0) {
+ fprintf(stderr, "can't set file compression method at index '%" PRIu64 "' to `%s', flags `%d': %s\n", idx, argv[1], flags, zip_strerror(za));
+ return -1;
+ }
+ return 0;
+}
+
+static int
+set_file_mtime(int argc, char *argv[]) {
+ /* set file last modification time (mtime) */
+ time_t mtime;
+ zip_uint64_t idx;
+ idx = strtoull(argv[0], NULL, 10);
+ mtime = (time_t)strtoull(argv[1], NULL, 10);
+ if (zip_file_set_mtime(za, idx, mtime, 0) < 0) {
+ fprintf(stderr, "can't set file mtime at index '%" PRIu64 "' to `%ld': %s\n", idx, mtime, zip_strerror(za));
+ return -1;
+ }
+ return 0;
+}
+
+static int
+set_password(int argc, char *argv[]) {
+ /* set default password */
+ if (zip_set_default_password(za, argv[0]) < 0) {
+ fprintf(stderr, "can't set default password to `%s'", argv[0]);
+ return -1;
+ }
+ return 0;
+}
+
+static int
+zstat(int argc, char *argv[]) {
+ zip_uint64_t idx;
+ char buf[100];
+ struct zip_stat sb;
+ idx = strtoull(argv[0], NULL, 10);
+
+ if (zip_stat_index(za, idx, stat_flags, &sb) < 0) {
+ fprintf(stderr, "zip_stat_index failed on '%" PRIu64 "' failed: %s\n", idx, zip_strerror(za));
+ return -1;
+ }
+
+ if (sb.valid & ZIP_STAT_NAME)
+ printf("name: '%s'\n", sb.name);
+ if (sb.valid & ZIP_STAT_INDEX)
+ printf("index: '%"PRIu64"'\n", sb.index);
+ if (sb.valid & ZIP_STAT_SIZE)
+ printf("size: '%"PRIu64"'\n", sb.size);
+ if (sb.valid & ZIP_STAT_COMP_SIZE)
+ printf("compressed size: '%"PRIu64"'\n", sb.comp_size);
+ if (sb.valid & ZIP_STAT_MTIME) {
+ struct tm *tpm;
+ tpm = localtime(&sb.mtime);
+ strftime(buf, sizeof(buf), "%a %b %d %Y %H:%M:%S", tpm);
+ printf("mtime: '%s'\n", buf);
+ }
+ if (sb.valid & ZIP_STAT_CRC)
+ printf("crc: '%0x'\n", sb.crc);
+ if (sb.valid & ZIP_STAT_COMP_METHOD)
+ printf("compression method: '%d'\n", sb.comp_method);
+ if (sb.valid & ZIP_STAT_ENCRYPTION_METHOD)
+ printf("encryption method: '%d'\n", sb.encryption_method);
+ if (sb.valid & ZIP_STAT_FLAGS)
+ printf("flags: '%ld'\n", (long)sb.flags);
+ printf("\n");
+
+ return 0;
+}
+
+static int
+unchange_all(int argc, char *argv[]) {
+ if (zip_unchange_all(za) < 0) {
+ fprintf(stderr, "can't revert changes to archive: %s\n", zip_strerror(za));
+ return -1;
+ }
+ return 0;
+}
+
+static int
+zin_close(int argc, char *argv[]) {
+ zip_uint64_t idx;
+
+ idx = strtoull(argv[0], NULL, 10);
+ if (idx >= z_in_count) {
+ fprintf(stderr, "invalid argument '%" PRIu64 "', only %d zip sources open\n", idx, z_in_count);
+ return -1;
+ }
+ if (zip_close(z_in[idx]) < 0) {
+ fprintf(stderr, "can't close source archive: %s\n", zip_strerror(z_in[idx]));
+ return -1;
+ }
+ z_in[idx] = z_in[z_in_count];
+ z_in_count--;
+
+ return 0;
+}
+
+static zip_flags_t
+get_flags(const char *arg)
+{
+ zip_flags_t flags = 0;
+ if (strchr(arg, 'C') != NULL)
+ flags |= ZIP_FL_NOCASE;
+ if (strchr(arg, 'c') != NULL)
+ flags |= ZIP_FL_CENTRAL;
+ if (strchr(arg, 'd') != NULL)
+ flags |= ZIP_FL_NODIR;
+ if (strchr(arg, 'l') != NULL)
+ flags |= ZIP_FL_LOCAL;
+ if (strchr(arg, 'u') != NULL)
+ flags |= ZIP_FL_UNCHANGED;
+ return flags;
+}
+
+static zip_int32_t
+get_compression_method(const char *arg)
+{
+ if (strcmp(arg, "default") == 0)
+ return ZIP_CM_DEFAULT;
+ else if (strcmp(arg, "store") == 0)
+ return ZIP_CM_STORE;
+ else if (strcmp(arg, "deflate") ==0)
+ return ZIP_CM_DEFLATE;
+ else if (strcmp(arg, "unknown") ==0)
+ return 99;
+ return 0; /* TODO: error handling */
+}
+
+static void
+hexdump(const zip_uint8_t *data, zip_uint16_t len)
+{
+ zip_uint16_t i;
+
+ if (len <= 0)
+ return;
+
+ printf("0x");
+
+ for (i=0; i<len; i++)
+ printf("%02x", data[i]);
+
+ return;
+}
+
+
+static zip_t *
+read_hole(const char *archive, int flags, int *err)
+{
+ zip_error_t error;
+ zip_source_t *src = NULL;
+ zip_t *zs = NULL;
+
+ zip_error_init(&error);
+
+ if ((src = source_hole_create(archive, flags, &error)) == NULL
+ || (zs = zip_open_from_source(src, flags, &error)) == NULL) {
+ zip_source_free(src);
+ *err = zip_error_code_zip(&error);
+ errno = zip_error_code_system(&error);
+ }
+
+ return zs;
+}
+
+
+static zip_t *
+read_to_memory(const char *archive, int flags, int *err, zip_source_t **srcp)
+{
+ struct stat st;
+ zip_source_t *src;
+ zip_t *zb;
+ zip_error_t error;
+
+ if (stat(archive, &st) < 0) {
+ if (errno == ENOENT) {
+ src = zip_source_buffer_create(NULL, 0, 0, &error);
+ }
+ else {
+ *err = ZIP_ER_OPEN;
+ return NULL;
+ }
+ }
+ else {
+ char *buf;
+ FILE *fp;
+ if ((buf=malloc((size_t)st.st_size)) == NULL) {
+ *err = ZIP_ER_MEMORY;
+ return NULL;
+ }
+ if ((fp=fopen(archive, "r")) == NULL) {
+ free(buf);
+ *err = ZIP_ER_READ;
+ return NULL;
+ }
+ if (fread(buf, (size_t)st.st_size, 1, fp) < 1) {
+ free(buf);
+ fclose(fp);
+ *err = ZIP_ER_READ;
+ return NULL;
+ }
+ fclose(fp);
+ src = zip_source_buffer_create(buf, (zip_uint64_t)st.st_size, 1, &error);
+ if (src == NULL) {
+ free(buf);
+ }
+ }
+ if (src == NULL) {
+ *err = zip_error_code_zip(&error);
+ errno = zip_error_code_system(&error);
+ return NULL;
+ }
+ zb = zip_open_from_source(src, flags, &error);
+ if (zb == NULL) {
+ *err = zip_error_code_zip(&error);
+ errno = zip_error_code_system(&error);
+ zip_source_free(src);
+ return NULL;
+ }
+ zip_source_keep(src);
+ *srcp = src;
+ return zb;
+}
+
+
+typedef struct source_nul {
+ zip_error_t error;
+ zip_uint64_t length;
+ zip_uint64_t offset;
+} source_nul_t;
+
+static zip_int64_t
+source_nul_cb(void *ud, void *data, zip_uint64_t length, zip_source_cmd_t command)
+{
+ source_nul_t *ctx = (source_nul_t *)ud;
+
+ switch (command) {
+ case ZIP_SOURCE_CLOSE:
+ return 0;
+
+ case ZIP_SOURCE_ERROR:
+ return zip_error_to_data(&ctx->error, data, length);
+
+ case ZIP_SOURCE_FREE:
+ free(ctx);
+ return 0;
+
+ case ZIP_SOURCE_OPEN:
+ ctx->offset = 0;
+ return 0;
+
+ case ZIP_SOURCE_READ:
+ if (length > ZIP_INT64_MAX) {
+ zip_error_set(&ctx->error, ZIP_ER_INVAL, 0);
+ return -1;
+ }
+
+ if (length > ctx->length - ctx->offset) {
+ length =ctx->length - ctx->offset;
+ }
+
+ memset(data, 0, length);
+ ctx->offset += length;
+ return (zip_int64_t)length;
+
+ case ZIP_SOURCE_STAT: {
+ zip_stat_t *st = ZIP_SOURCE_GET_ARGS(zip_stat_t, data, length, &ctx->error);
+
+ if (st == NULL) {
+ return -1;
+ }
+
+ st->valid |= ZIP_STAT_SIZE;
+ st->size = ctx->length;
+
+ return 0;
+ }
+
+ case ZIP_SOURCE_SUPPORTS:
+ 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);
+
+ default:
+ zip_error_set(&ctx->error, ZIP_ER_OPNOTSUPP, 0);
+ return -1;
+ }
+}
+
+static zip_source_t *
+source_nul(zip_t *zs, zip_uint64_t length)
+{
+ source_nul_t *ctx;
+ zip_source_t *src;
+
+ if ((ctx = (source_nul_t *)malloc(sizeof(*ctx))) == NULL) {
+ zip_error_set(zip_get_error(zs), ZIP_ER_MEMORY, 0);
+ return NULL;
+ }
+
+ zip_error_init(&ctx->error);
+ ctx->length = length;
+ ctx->offset = 0;
+
+ if ((src = zip_source_function(zs, source_nul_cb, ctx)) == NULL) {
+ free(ctx);
+ return NULL;
+ }
+
+ return src;
+}
+
+
+static int
+write_memory_src_to_file(const char *archive, zip_source_t *src)
+{
+ zip_stat_t zst;
+ char *buf;
+ FILE *fp;
+
+ if (zip_source_stat(src, &zst) < 0) {
+ fprintf(stderr, "zip_source_stat on buffer failed: %s\n", zip_error_strerror(zip_source_error(src)));
+ return -1;
+ }
+ if (zip_source_open(src) < 0) {
+ if (zip_error_code_zip(zip_source_error(src)) == ZIP_ER_DELETED) {
+ if (remove(archive) < 0 && errno != ENOENT) {
+ fprintf(stderr, "remove failed: %s\n", strerror(errno));
+ return -1;
+ }
+ return 0;
+ }
+ fprintf(stderr, "zip_source_open on buffer failed: %s\n", zip_error_strerror(zip_source_error(src)));
+ return -1;
+ }
+ if ((buf=malloc(zst.size)) == NULL) {
+ fprintf(stderr, "malloc failed: %s\n", strerror(errno));
+ zip_source_close(src);
+ return -1;
+ }
+ if (zip_source_read(src, buf, zst.size) < (zip_int64_t)zst.size) {
+ fprintf(stderr, "zip_source_read on buffer failed: %s\n", zip_error_strerror(zip_source_error(src)));
+ zip_source_close(src);
+ free(buf);
+ return -1;
+ }
+ zip_source_close(src);
+ if ((fp=fopen(archive, "wb")) == NULL) {
+ fprintf(stderr, "fopen failed: %s\n", strerror(errno));
+ free(buf);
+ return -1;
+ }
+ if (fwrite(buf, zst.size, 1, fp) < 1) {
+ fprintf(stderr, "fwrite failed: %s\n", strerror(errno));
+ free(buf);
+ fclose(fp);
+ return -1;
+ }
+ free(buf);
+ if (fclose(fp) != 0) {
+ fprintf(stderr, "fclose failed: %s\n", strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+dispatch_table_t dispatch_table[] = {
+ { "add", 2, "name content", "add file called name using content", add },
+ { "add_dir", 1, "name", "add directory", add_dir },
+ { "add_file", 4, "name file_to_add offset len", "add file to archive, len bytes starting from offset", add_file },
+ { "add_from_zip", 5, "name archivename index offset len", "add file from another archive, len bytes starting from offset", add_from_zip },
+ { "add_nul", 2, "name length", "add NUL bytes", add_nul },
+ { "cat", 1, "index", "output file contents to stdout", cat },
+ { "count_extra", 2, "index flags", "show number of extra fields for archive entry", count_extra },
+ { "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 },
+ { "delete", 1, "index", "remove entry", delete },
+ { "delete_extra", 3, "index extra_idx flags", "remove extra field", delete_extra },
+ { "delete_extra_by_id", 4, "index extra_id extra_index flags", "remove extra field of type extra_id", delete_extra_by_id },
+ { "get_archive_comment", 0, "", "show archive comment", get_archive_comment },
+ { "get_extra", 3, "index extra_index flags", "show extra field", get_extra },
+ { "get_extra_by_id", 4, "index extra_id extra_index flags", "show extra field of type extra_id", get_extra_by_id },
+ { "get_file_comment", 1, "index", "get file comment", get_file_comment },
+ { "get_num_entries", 1, "flags", "get number of entries in archive", get_num_entries },
+ { "name_locate", 2, "name flags", "find entry in archive", name_locate },
+ { "rename", 2, "index name", "rename entry", zrename },
+ { "replace_file_contents", 2, "index data", "replace entry with data", replace_file_contents },
+ { "set_archive_comment", 1, "comment", "set archive comment", set_archive_comment },
+ { "set_extra", 5, "index extra_id extra_index flags value", "set extra field", set_extra },
+ { "set_file_comment", 2, "index comment", "set file comment", set_file_comment },
+ { "set_file_compression", 3, "index method flags", "set file compression method", set_file_compression },
+ { "set_file_mtime", 2, "index timestamp", "set file modification time", set_file_mtime },
+ { "set_password", 1, "set default password", "set default password for encryption", set_password },
+ { "stat", 1, "index", "print information about entry", zstat },
+ { "unchange_all", 0, "", "revert all changes", unchange_all },
+ { "zin_close", 1, "index", "close input zip_source (for internal tests)", zin_close }
+};
+
+static int
+dispatch(int argc, char *argv[])
+{
+ unsigned int i;
+ for (i=0; i<sizeof(dispatch_table)/sizeof(dispatch_table_t); i++) {
+ if (strcmp(dispatch_table[i].cmdline_name, argv[0]) == 0) {
+ argc--;
+ argv++;
+ /* 1 for the command, argument_count for the arguments */
+ if (argc < dispatch_table[i].argument_count) {
+ fprintf(stderr, "not enough arguments for command '%s': %d available, %d needed\n", dispatch_table[i].cmdline_name, argc, dispatch_table[i].argument_count);
+ return -1;
+ }
+ if (dispatch_table[i].function(argc, argv) == 0)
+ return 1 + dispatch_table[i].argument_count;
+ return -1;
+ }
+ }
+
+ fprintf(stderr, "unknown command '%s'\n", argv[0]);
+ return -1;
+}
+
+
+static void
+usage(const char *progname)
+{
+ unsigned int i;
+ fprintf(stderr, "usage: %s [-cemnt] archive command1 [args] [command2 [args] ...]\n\n"
+ "Supported options are:\n"
+ "\t-c\tcheck consistency\n"
+ "\t-e\terror if archive already exists (only useful with -n)\n"
+ "\t-g\tguess file name encoding (for stat)\n"
+ "\t-H\twrite files with holes compactly\n"
+ "\t-m\tread archive into memory, and modify there; write out at end\n"
+ "\t-n\tcreate archive if it doesn't exist (default)\n"
+ "\t-r\tprint raw file name encoding without translation (for stat)\n"
+ "\t-s\tfollow file name convention strictly (for stat)\n"
+ "\t-t\tdisregard current archive contents, if any\n", progname);
+ fprintf(stderr, "\nSupported commands and arguments are:\n");
+ for (i=0; i<sizeof(dispatch_table)/sizeof(dispatch_table_t); i++) {
+ fprintf(stderr, "\t%s %s -- %s\n", dispatch_table[i].cmdline_name, dispatch_table[i].arg_names, dispatch_table[i].description);
+ }
+ fprintf(stderr, "\nSupported flags are:\n"
+ "\tC\tZIP_FL_NOCASE\n"
+ "\tc\tZIP_FL_CENTRAL\n"
+ "\td\tZIP_FL_NODIR\n"
+ "\tl\tZIP_FL_LOCAL\n"
+ "\tu\tZIP_FL_UNCHANGED\n");
+ fprintf(stderr, "\nThe index is zero-based.\n");
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ const char *archive;
+ zip_source_t *memory_src;
+ unsigned int i;
+ int c, arg, err, flags;
+ const char *prg;
+ source_type_t source_type = SOURCE_TYPE_NONE;
+
+ flags = 0;
+ prg = argv[0];
+
+ if (argc < 2)
+ usage(prg);
+
+ while ((c=getopt(argc, argv, "cegHmnrst")) != -1) {
+ switch (c) {
+ case 'c':
+ flags |= ZIP_CHECKCONS;
+ break;
+ case 'e':
+ flags |= ZIP_EXCL;
+ break;
+ case 'g':
+ stat_flags = ZIP_FL_ENC_GUESS;
+ break;
+ case 'H':
+ source_type = SOURCE_TYPE_HOLE;
+ break;
+ case 'm':
+ source_type = SOURCE_TYPE_IN_MEMORY;
+ break;
+ case 'n':
+ flags |= ZIP_CREATE;
+ break;
+ case 'r':
+ stat_flags = ZIP_FL_ENC_RAW;
+ break;
+ case 's':
+ stat_flags = ZIP_FL_ENC_STRICT;
+ break;
+ case 't':
+ flags |= ZIP_TRUNCATE;
+ break;
+
+ default:
+ usage(prg);
+ }
+ }
+
+ arg = optind;
+
+ archive = argv[arg++];
+
+ if (flags == 0)
+ flags = ZIP_CREATE;
+
+ switch (source_type) {
+ case SOURCE_TYPE_NONE:
+ za = zip_open(archive, flags, &err);
+ break;
+
+ case SOURCE_TYPE_IN_MEMORY:
+ za = read_to_memory(archive, flags, &err, &memory_src);
+ break;
+
+ case SOURCE_TYPE_HOLE: {
+ za = read_hole(archive, flags, &err);
+ break;
+ }
+ }
+ if (za == NULL) {
+ zip_error_t error;
+ zip_error_init_with_code(&error, err);
+ fprintf(stderr, "can't open zip archive '%s': %s\n", archive, zip_error_strerror(&error));
+ zip_error_fini(&error);
+ return 1;
+ }
+
+ err = 0;
+ while (arg < argc) {
+ int ret;
+ ret = dispatch(argc-arg, argv+arg);
+ if (ret > 0) {
+ arg += ret;
+ } else {
+ err = 1;
+ break;
+ }
+ }
+
+ if (zip_close(za) == -1) {
+ fprintf(stderr, "can't close zip archive '%s': %s\n", archive, zip_strerror(za));
+ return 1;
+ }
+ if (source_type == SOURCE_TYPE_IN_MEMORY) {
+ if (write_memory_src_to_file(archive, memory_src) < 0) {
+ err = 1;
+ }
+ zip_source_free(memory_src);
+ }
+
+ for (i=0; i<z_in_count; i++) {
+ if (zip_close(z_in[i]) < 0) {
+ err = 1;
+ }
+ }
+
+ return err;
+}