+static int write_archive_type(const char *format, const char *hex, const char *prefix)
+{
+ struct argv_array argv = ARGV_ARRAY_INIT;
+ const char **nargv;
+ int result;
+ argv_array_push(&argv, "snapshot");
+ argv_array_push(&argv, format);
+ if (prefix) {
+ struct strbuf buf = STRBUF_INIT;
+ strbuf_addstr(&buf, prefix);
+ strbuf_addch(&buf, '/');
+ argv_array_push(&argv, "--prefix");
+ argv_array_push(&argv, buf.buf);
+ strbuf_release(&buf);
+ }
+ argv_array_push(&argv, hex);
+ /*
+ * Now we need to copy the pointers to arguments into a new
+ * structure because write_archive will rearrange its arguments
+ * which may result in duplicated/missing entries causing leaks
+ * or double-frees in argv_array_clear.
+ */
+ nargv = xmalloc(sizeof(char *) * (argv.argc + 1));
+ /* argv_array guarantees a trailing NULL entry. */
+ memcpy(nargv, argv.argv, sizeof(char *) * (argv.argc + 1));
+
+ result = write_archive(argv.argc, nargv, NULL, 1, NULL, 0);
+ argv_array_clear(&argv);
+ free(nargv);
+ return result;
+}
+
+static int write_tar_archive(const char *hex, const char *prefix)
+{
+ return write_archive_type("--format=tar", hex, prefix);
+}
+
+static int write_zip_archive(const char *hex, const char *prefix)
+{
+ return write_archive_type("--format=zip", hex, prefix);
+}
+
+static int write_compressed_tar_archive(const char *hex,
+ const char *prefix,
+ char *filter_argv[])
+{
+ int rv;
+ struct cgit_filter f;
+
+ f.cmd = filter_argv[0];
+ f.argv = filter_argv;
+ cgit_open_filter(&f);
+ rv = write_tar_archive(hex, prefix);
+ cgit_close_filter(&f);
+ return rv;
+}
+
+static int write_tar_gzip_archive(const char *hex, const char *prefix)
+{
+ char *argv[] = { "gzip", "-n", NULL };
+ return write_compressed_tar_archive(hex, prefix, argv);
+}
+
+static int write_tar_bzip2_archive(const char *hex, const char *prefix)
+{
+ char *argv[] = { "bzip2", NULL };
+ return write_compressed_tar_archive(hex, prefix, argv);
+}
+
+static int write_tar_xz_archive(const char *hex, const char *prefix)
+{
+ char *argv[] = { "xz", NULL };
+ return write_compressed_tar_archive(hex, prefix, argv);
+}
+
+const struct cgit_snapshot_format cgit_snapshot_formats[] = {
+ { ".zip", "application/x-zip", write_zip_archive, 0x01 },
+ { ".tar.gz", "application/x-gzip", write_tar_gzip_archive, 0x02 },
+ { ".tar.bz2", "application/x-bzip2", write_tar_bzip2_archive, 0x04 },
+ { ".tar", "application/x-tar", write_tar_archive, 0x08 },
+ { ".tar.xz", "application/x-xz", write_tar_xz_archive, 0x10 },
+ { NULL }
+};
+
+static const struct cgit_snapshot_format *get_format(const char *filename)
+{
+ const struct cgit_snapshot_format *fmt;
+ int fl, sl;
+
+ fl = strlen(filename);
+ for (fmt = cgit_snapshot_formats; fmt->suffix; fmt++) {
+ sl = strlen(fmt->suffix);
+ if (sl >= fl)
+ continue;
+ if (!strcmp(fmt->suffix, filename + fl - sl))
+ return fmt;
+ }
+ return NULL;
+}
+
+static int make_snapshot(const struct cgit_snapshot_format *format,
+ const char *hex, const char *prefix,
+ const char *filename)