]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
Make a filter program be called with its arguments.
authorMichihiro NAKAJIMA <ggcueroad@gmail.com>
Thu, 4 Oct 2012 00:18:30 +0000 (09:18 +0900)
committerMichihiro NAKAJIMA <ggcueroad@gmail.com>
Fri, 5 Oct 2012 00:45:05 +0000 (09:45 +0900)
12 files changed:
Makefile.am
libarchive/archive.h
libarchive/archive_read_private.h
libarchive/archive_read_support_filter_program.c
libarchive/archive_write_add_filter_program.c
libarchive/filter_fork.c
libarchive/filter_fork.h
libarchive/filter_fork_windows.c
libarchive/test/CMakeLists.txt
libarchive/test/test_read_compress_program.c
libarchive/test/test_read_filter_program_signature.c [new file with mode: 0644]
libarchive/test/test_write_compress_program.c

index 3e277aba530f882409fb7fdec8cae159e4855621..9ae870f41ef1708b014d264eceba6db5fd21f355 100644 (file)
@@ -331,6 +331,7 @@ libarchive_test_SOURCES=                                    \
        libarchive/test/test_read_disk_entry_from_file.c        \
        libarchive/test/test_read_extract.c                     \
        libarchive/test/test_read_file_nonexistent.c            \
+       libarchive/test/test_read_filter_program_signature.c    \
        libarchive/test/test_read_format_7zip.c                 \
        libarchive/test/test_read_format_ar.c                   \
        libarchive/test/test_read_format_cab.c                  \
index 7b7e75eebc31e1a2ab496678afb31d0ba0ecda92..d914ed43cfaf3da1fce9ee971b2b61b615676af5 100644 (file)
@@ -328,9 +328,22 @@ __LA_DECL int archive_read_support_filter_lzma(struct archive *);
 __LA_DECL int archive_read_support_filter_none(struct archive *);
 __LA_DECL int archive_read_support_filter_program(struct archive *,
                     const char *command);
+__LA_DECL int archive_read_support_filter_programl(struct archive *,
+                    const char *command, const char *arg,
+                    .../* (char *)0 */);
+__LA_DECL int archive_read_support_filter_programv(struct archive *,
+                    const char *command, char * const argv[]);
 __LA_DECL int archive_read_support_filter_program_signature
-               (struct archive *, const char *,
+               (struct archive *, const char * /* cmd */,
                                    const void * /* match */, size_t);
+__LA_DECL int archive_read_support_filter_programl_signature
+               (struct archive *, const char * /* cmd */,
+                const char * /* arg */,
+                .../* , (char *)0, const void *, size_t */);
+__LA_DECL int archive_read_support_filter_programv_signature
+               (struct archive *, const char * /* cmd */,
+                char * const [] /* argv */,
+                const void * /* match */, size_t);
 
 __LA_DECL int archive_read_support_filter_rpm(struct archive *);
 __LA_DECL int archive_read_support_filter_uu(struct archive *);
@@ -601,6 +614,10 @@ __LA_DECL int archive_write_add_filter_lzma(struct archive *);
 __LA_DECL int archive_write_add_filter_none(struct archive *);
 __LA_DECL int archive_write_add_filter_program(struct archive *,
                     const char *cmd);
+__LA_DECL int archive_write_add_filter_programl(struct archive *,
+                    const char *cmd, const char *arg, .../*, (char *)0 */);
+__LA_DECL int archive_write_add_filter_programv(struct archive *,
+                    const char *cmd, char * const argv[]);
 __LA_DECL int archive_write_add_filter_xz(struct archive *);
 
 
index 4d956a08012d4a35077d8b681d171a59284e5890..03a7733f10a6edb6a9b6da5c5e684773e51ce726 100644 (file)
@@ -226,4 +226,8 @@ int64_t     __archive_read_filter_seek(struct archive_read_filter *, int64_t, int);
 int64_t        __archive_read_consume(struct archive_read *, int64_t);
 int64_t        __archive_read_filter_consume(struct archive_read_filter *, int64_t);
 int __archive_read_program(struct archive_read_filter *, const char *);
+int __archive_read_programl(struct archive_read_filter *, const char *,
+    const char *, .../*, (char *)0 */);
+int __archive_read_programv(struct archive_read_filter *, const char *,
+    char * const argv[]);
 #endif
index fe6048e89eb2acba8d03b4add68dc31f228a763e..2b5540abab46ef96d1f1f27e9cf20e5f4ca81f35 100644 (file)
@@ -1,5 +1,6 @@
 /*-
  * Copyright (c) 2007 Joerg Sonnenberger
+ * Copyright (c) 2012 Michihiro NAKAJIMA
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -53,6 +54,7 @@ __FBSDID("$FreeBSD$");
 
 #include "archive.h"
 #include "archive_private.h"
+#include "archive_string.h"
 #include "archive_read_private.h"
 #include "filter_fork.h"
 
@@ -80,6 +82,71 @@ archive_read_support_filter_program(struct archive *a, const char *cmd)
        return (archive_read_support_filter_program_signature(a, cmd, NULL, 0));
 }
 
+static void
+free_argv(char **argv)
+{
+       int i;
+
+       if (argv) {
+               for (i = 0; argv[i] != NULL; i++)
+                       free(argv[i]);
+               free(argv);
+       }
+}
+
+int
+archive_read_support_filter_programl(struct archive *a, const char *cmd,
+    const char *arg, ...)
+{
+       va_list ap;
+       char **argv;
+       int i, r;
+
+       i = 2;
+       if (arg != NULL) {
+               va_start(ap, arg);
+               while (va_arg(ap, char *) != NULL)
+                       i++;
+               va_end(ap);
+       }
+       argv = calloc(i, sizeof(char *));
+       if (argv == NULL)
+               goto memerr;
+       argv[0] = strdup((arg)?arg:cmd);
+       if (argv[0] == NULL)
+               goto memerr;
+       i = 1;
+       va_start(ap, arg);
+       if (arg) {
+               char *val;
+               while ((val = va_arg(ap, char *)) != NULL) {
+                       argv[i] = strdup(val);
+                       if (argv[i] == NULL) {
+                               va_end(ap);
+                               goto memerr;
+                       }
+                       i++;
+               }
+       }
+       va_end(ap);
+       argv[i] = NULL;
+       r = archive_read_support_filter_programv_signature(a, cmd, argv,
+               NULL, 0);
+       free_argv(argv);
+       return (r);
+memerr:
+       free_argv(argv);
+       archive_set_error(a, ENOMEM, "Can't allocate memory");
+       return (ARCHIVE_FATAL);
+}
+
+int
+archive_read_support_filter_programv(struct archive *a, const char *cmd,
+    char * const argv[])
+{
+       return (archive_read_support_filter_programv_signature(a, cmd, argv,
+               NULL, 0));
+}
 
 /*
  * The bidder object stores the command and the signature to watch for.
@@ -88,6 +155,7 @@ archive_read_support_filter_program(struct archive *a, const char *cmd)
  */
 struct program_bidder {
        char *cmd;
+       char **argv;
        void *signature;
        size_t signature_len;
        int inhibit;
@@ -102,7 +170,7 @@ static int  program_bidder_free(struct archive_read_filter_bidder *);
  * The actual filter needs to track input and output data.
  */
 struct program_filter {
-       char            *description;
+       struct archive_string description;
        pid_t            child;
        int              exit_status;
        int              waitpid_return;
@@ -115,6 +183,29 @@ struct program_filter {
 static ssize_t program_filter_read(struct archive_read_filter *,
                    const void **);
 static int     program_filter_close(struct archive_read_filter *);
+static void    free_state(struct program_bidder *);
+
+static int
+set_bidder_signature(struct archive_read_filter_bidder *bidder,
+    struct program_bidder *state, const void *signature, size_t signature_len)
+{
+
+       if (signature != NULL && signature_len > 0) {
+               state->signature_len = signature_len;
+               state->signature = malloc(signature_len);
+               memcpy(state->signature, signature, signature_len);
+       }
+
+       /*
+        * Fill in the bidder object.
+        */
+       bidder->data = state;
+       bidder->bid = program_bidder_bid;
+       bidder->init = program_bidder_init;
+       bidder->options = NULL;
+       bidder->free = program_bidder_free;
+       return (ARCHIVE_OK);
+}
 
 int
 archive_read_support_filter_program_signature(struct archive *_a,
@@ -133,37 +224,159 @@ archive_read_support_filter_program_signature(struct archive *_a,
        /*
         * Allocate our private state.
         */
-       state = (struct program_bidder *)calloc(sizeof (*state), 1);
+       state = (struct program_bidder *)calloc(1, sizeof (*state));
        if (state == NULL)
+               goto memerr;
+       state->cmd = strdup(cmd);
+       if (state->cmd == NULL)
+               goto memerr;
+       state->argv = calloc(2, sizeof(char *));
+       if (state->argv == NULL)
+               goto memerr;
+       state->argv[0] = strdup(cmd);
+       if (state->argv[0] == NULL)
+               goto memerr;
+       state->argv[1] = NULL;
+
+       return set_bidder_signature(bidder, state, signature, signature_len);
+memerr:
+       free_state(state);
+       archive_set_error(_a, ENOMEM, "Can't allocate memory");
+       return (ARCHIVE_FATAL);
+}
+
+int
+archive_read_support_filter_programl_signature(struct archive *_a,
+    const char *cmd, const char *arg, .../* , signature, signature_len */)
+{
+       struct archive_read *a = (struct archive_read *)_a;
+       struct archive_read_filter_bidder *bidder;
+       struct program_bidder *state;
+       const void *signature;
+       size_t signature_len;
+       va_list ap;
+       int i;
+
+       /*
+        * Get a bidder object from the read core.
+        */
+       if (__archive_read_get_bidder(a, &bidder) != ARCHIVE_OK)
                return (ARCHIVE_FATAL);
+
+       /*
+        * Allocate our private state.
+        */
+       state = (struct program_bidder *)calloc(1, sizeof (*state));
+       if (state == NULL)
+               goto memerr;
        state->cmd = strdup(cmd);
-       if (signature != NULL && signature_len > 0) {
-               state->signature_len = signature_len;
-               state->signature = malloc(signature_len);
-               memcpy(state->signature, signature, signature_len);
+       if (state->cmd == NULL)
+               goto memerr;
+
+       i = 2;
+       if (arg != NULL) {
+               va_start(ap, arg);
+               while (va_arg(ap, char *) != NULL)
+                       i++;
+               va_end(ap);
        }
+       state->argv = calloc(i, sizeof(char *));
+       if (state->argv == NULL)
+               goto memerr;
+       state->argv[0] = strdup((arg)?arg:cmd);
+       if (state->argv[0] == NULL)
+               goto memerr;
+       i = 1;
+       va_start(ap, arg);
+       if (arg) {
+               char *val;
+               while ((val = va_arg(ap, char *)) != NULL) {
+                       state->argv[i] = strdup(val);
+                       if (state->argv[i] == NULL) {
+                               va_end(ap);
+                               goto memerr;
+                       }
+                       i++;
+               }
+       }
+       signature = va_arg(ap, const void *);
+       signature_len = va_arg(ap, size_t);
+       va_end(ap);
+       state->argv[i] = NULL;
+
+       return set_bidder_signature(bidder, state, signature, signature_len);
+memerr:
+       free_state(state);
+       archive_set_error(_a, ENOMEM, "Can't allocate memory");
+       return (ARCHIVE_FATAL);
+}
+
+int
+archive_read_support_filter_programv_signature(struct archive *_a,
+    const char *cmd, char * const argv[], const void *signature,
+    size_t signature_len)
+{
+       struct archive_read *a = (struct archive_read *)_a;
+       struct archive_read_filter_bidder *bidder;
+       struct program_bidder *state;
+       int i;
 
        /*
-        * Fill in the bidder object.
+        * Get a bidder object from the read core.
         */
-       bidder->data = state;
-       bidder->bid = program_bidder_bid;
-       bidder->init = program_bidder_init;
-       bidder->options = NULL;
-       bidder->free = program_bidder_free;
-       return (ARCHIVE_OK);
+       if (__archive_read_get_bidder(a, &bidder) != ARCHIVE_OK)
+               return (ARCHIVE_FATAL);
+
+       /*
+        * Allocate our private state.
+        */
+       state = (struct program_bidder *)calloc(1, sizeof (*state));
+       if (state == NULL)
+               goto memerr;
+       state->cmd = strdup(cmd);
+       if (state->cmd == NULL)
+               goto memerr;
+
+       for (i = 0; argv[i] != NULL; i++)
+               ;
+       state->argv = calloc(i + 1, sizeof(char *));
+       if (state->argv == NULL)
+               goto memerr;
+       for (i = 0; argv[i] != NULL; i++) {
+               state->argv[i] = strdup(argv[i]);
+               if (state->argv[i] == NULL)
+                       goto memerr;
+       }
+       state->argv[i] = NULL;
+
+       return set_bidder_signature(bidder, state, signature, signature_len);
+memerr:
+       free_state(state);
+       archive_set_error(_a, ENOMEM, "Can't allocate memory");
+       return (ARCHIVE_FATAL);
 }
 
 static int
 program_bidder_free(struct archive_read_filter_bidder *self)
 {
        struct program_bidder *state = (struct program_bidder *)self->data;
-       free(state->cmd);
-       free(state->signature);
-       free(self->data);
+
+       free_state(state);
        return (ARCHIVE_OK);
 }
 
+static void
+free_state(struct program_bidder *state)
+{
+
+       if (state) {
+               free(state->cmd);
+               free_argv(state->argv);
+               free(state->signature);
+               free(state);
+       }
+}
+
 /*
  * If we do have a signature, bid only if that matches.
  *
@@ -336,40 +549,118 @@ child_read(struct archive_read_filter *self, char *buf, size_t buf_len)
 
 int
 __archive_read_program(struct archive_read_filter *self, const char *cmd)
+{
+       char *argv[2];
+       int r;
+
+       argv[0] = strdup(cmd);
+       if (argv[0] == NULL) {
+               archive_set_error(&self->archive->archive, ENOMEM,
+                   "Can't allocate input data");
+               return (ARCHIVE_FATAL);
+       }
+       argv[1] = NULL;
+       r = __archive_read_programv(self, cmd, argv);
+       free(argv[0]);
+       return (r);
+}
+
+int
+__archive_read_programl(struct archive_read_filter *self, const char *cmd,
+    const char *arg, ...)
+{
+       va_list ap;
+       char **argv;
+       char *val;
+       int i, r;
+
+       i = 2;
+       if (arg != NULL) {
+               va_start(ap, arg);
+               while (va_arg(ap, char *) != NULL)
+                       i++;
+               va_end(ap);
+       }
+       argv = malloc(i * sizeof(char *));
+       if (argv == NULL)
+               goto memerr;
+
+       if (arg != NULL) {
+               argv[0] = strdup(arg);
+               if (argv[0] == NULL)
+                       goto memerr;
+               i = 1;
+               va_start(ap, arg);
+               while ((val = va_arg(ap, char *)) != NULL) {
+                       argv[i] = strdup(val);
+                       if (argv[i] == NULL)
+                               goto memerr;
+                       i++;
+               }
+               va_end(ap);
+               argv[i] = NULL;
+       } else {
+               argv[0] = strdup(cmd);
+               if (argv[0] == NULL)
+                       goto memerr;
+               argv[1] = NULL;
+       }
+
+       r = __archive_read_programv(self, cmd, argv);
+       free_argv(argv);
+       return (r);
+memerr:
+       free_argv(argv);
+       archive_set_error(&self->archive->archive, ENOMEM,
+           "Can't allocate input data");
+       return (ARCHIVE_FATAL);
+}
+
+int
+__archive_read_programv(struct archive_read_filter *self, const char *cmd,
+    char * const argv[])
 {
        struct program_filter   *state;
        static const size_t out_buf_len = 65536;
        char *out_buf;
-       char *description;
        const char *prefix = "Program: ";
+       int i;
+       size_t l;
 
+       l = strlen(prefix) + strlen(cmd) + 1;
+       for (i = 0; argv[i] != NULL; i++)
+               l += strlen(argv[i]) + 1;
        state = (struct program_filter *)calloc(1, sizeof(*state));
        out_buf = (char *)malloc(out_buf_len);
-       description = (char *)malloc(strlen(prefix) + strlen(cmd) + 1);
-       if (state == NULL || out_buf == NULL || description == NULL) {
+       if (state == NULL || out_buf == NULL ||
+           archive_string_ensure(&state->description, l) == NULL) {
                archive_set_error(&self->archive->archive, ENOMEM,
                    "Can't allocate input data");
+               archive_string_free(&state->description);
                free(state);
                free(out_buf);
-               free(description);
                return (ARCHIVE_FATAL);
        }
+       archive_strcpy(&state->description, prefix);
+       archive_strcat(&state->description, cmd);
+       for (i = 0; argv[i] != NULL; i++) {
+               archive_strappend_char(&state->description, ' ');
+               archive_strcat(&state->description, argv[i]);
+       }
 
        self->code = ARCHIVE_COMPRESSION_PROGRAM;
-       state->description = description;
-       strcpy(state->description, prefix);
-       strcat(state->description, cmd);
-       self->name = state->description;
+       self->name = state->description.s;
 
        state->out_buf = out_buf;
        state->out_buf_len = out_buf_len;
 
-       if ((state->child = __archive_create_child(cmd,
+       if ((state->child = __archive_create_child(cmd, argv,
                 &state->child_stdin, &state->child_stdout)) == -1) {
                free(state->out_buf);
                free(state);
                archive_set_error(&self->archive->archive, EINVAL,
-                   "Can't initialize filter; unable to run program \"%s\"", cmd);
+                   "Can't initialize filter; unable to run program \"%s\"",
+                   cmd);
                return (ARCHIVE_FATAL);
        }
 
@@ -388,7 +679,8 @@ program_bidder_init(struct archive_read_filter *self)
        struct program_bidder   *bidder_state;
 
        bidder_state = (struct program_bidder *)self->bidder->data;
-       return (__archive_read_program(self, bidder_state->cmd));
+       return (__archive_read_programv(self, bidder_state->cmd,
+                   bidder_state->argv));
 }
 
 static ssize_t
@@ -431,7 +723,7 @@ program_filter_close(struct archive_read_filter *self)
 
        /* Release our private data. */
        free(state->out_buf);
-       free(state->description);
+       archive_string_free(&state->description);
        free(state);
 
        return (e);
index 77896700380ee75ed3bbdff65a7eefd5e28a7a3f..7ccc059c198d2a6536b22920416dfcbd641b9452 100644 (file)
@@ -1,5 +1,6 @@
 /*-
  * Copyright (c) 2007 Joerg Sonnenberger
+ * Copyright (c) 2012 Michihiro NAKAJIMA
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -44,6 +45,7 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_compression_program.c
 
 #include "archive.h"
 #include "archive_private.h"
+#include "archive_string.h"
 #include "archive_write_private.h"
 #include "filter_fork.h"
 
@@ -58,7 +60,8 @@ archive_write_set_compression_program(struct archive *a, const char *cmd)
 
 struct private_data {
        char            *cmd;
-       char            *description;
+       char            **argv;
+       struct archive_string description;
        pid_t            child;
        int              child_stdin, child_stdout;
 
@@ -71,6 +74,8 @@ static int archive_compressor_program_write(struct archive_write_filter *,
                    const void *, size_t);
 static int archive_compressor_program_close(struct archive_write_filter *);
 static int archive_compressor_program_free(struct archive_write_filter *);
+static int write_add_filter_programv(struct archive *, const char *,
+    char * const *);
 
 /*
  * Add a filter to this write handle that passes all data through an
@@ -79,28 +84,148 @@ static int archive_compressor_program_free(struct archive_write_filter *);
 int
 archive_write_add_filter_program(struct archive *_a, const char *cmd)
 {
+       char *argv[2];
+       int r;
+
+       archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+           ARCHIVE_STATE_NEW, "archive_write_add_filter_program");
+
+       argv[0] = strdup(cmd);
+       if (argv[0] == NULL) {
+               archive_set_error(_a, ENOMEM,
+                   "Can't allocate memory for filter program");
+               return (ARCHIVE_FATAL);
+       }
+       argv[1] = NULL;
+       r = write_add_filter_programv(_a, cmd, argv);
+       free(argv[0]);
+       return (r);
+}
+
+int
+archive_write_add_filter_programl(struct archive *_a, const char *cmd,
+    const char *arg, ...)
+{
+       va_list ap;
+       char **argv;
+       char *val;
+       int i, r;
+
+       archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+           ARCHIVE_STATE_NEW, "archive_write_add_filter_programl");
+
+       i = 2;
+       if (arg != NULL) {
+               va_start(ap, arg);
+               while (va_arg(ap, char *) != NULL)
+                       i++;
+               va_end(ap);
+       }
+       argv = malloc(i * sizeof(char *));
+       if (argv == NULL)
+               goto memerr;
+
+       if (arg != NULL) {
+               argv[0] = strdup(arg);
+               if (argv[0] == NULL)
+                       goto memerr;
+               i = 1;
+               va_start(ap, arg);
+               while ((val = va_arg(ap, char *)) != NULL) {
+                       argv[i] = strdup(val);
+                       if (argv[i] == NULL)
+                               goto memerr;
+                       i++;    
+               }
+               va_end(ap);
+               argv[i] = NULL;
+       } else {
+               argv[0] = strdup(cmd);
+               if (argv[0] == NULL)
+                       goto memerr;
+               argv[1] = NULL;
+       }
+
+       r = write_add_filter_programv(_a, cmd, argv);
+       for (i = 0; argv[i] != NULL; i++)
+               free(argv[i]);
+       free(argv);
+       return (r);
+memerr:
+       if (argv) {
+               for (i = 0; argv[i] != NULL; i++)
+                       free(argv[i]);
+               free(argv);
+       }
+       archive_set_error(_a, ENOMEM,
+           "Can't allocate memory for filter program");
+       return (ARCHIVE_FATAL);
+}
+
+int
+archive_write_add_filter_programv(struct archive *_a, const char *cmd,
+    char * const argv[])
+{
+
+       archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+           ARCHIVE_STATE_NEW, "archive_write_add_filter_programv");
+
+       return write_add_filter_programv(_a, cmd, argv);
+}
+
+static int
+write_add_filter_programv(struct archive *_a, const char *cmd,
+    char * const argv[])
+{
+
        struct archive_write_filter *f = __archive_write_allocate_filter(_a);
-       struct archive_write *a = (struct archive_write *)_a;
        struct private_data *data;
        static const char *prefix = "Program: ";
-       archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
-           ARCHIVE_STATE_NEW, "archive_write_add_filter_program");
+       int i;
+       size_t l;
+
        data = calloc(1, sizeof(*data));
-       if (data == NULL) {
-               archive_set_error(&a->archive, ENOMEM, "Out of memory");
-               return (ARCHIVE_FATAL);
-       }
+       if (data == NULL)
+               goto memerr;
        data->cmd = strdup(cmd);
-       data->description = (char *)malloc(strlen(prefix) + strlen(cmd) + 1);
-       strcpy(data->description, prefix);
-       strcat(data->description, cmd);
+       if (data->cmd == NULL)
+               goto memerr;
+
+       /* Reproduce argv. */
+       for (i = 0; argv[i] != NULL; i++)
+               ;
+       data->argv = calloc(i + 1, sizeof(char *));
+       if (data->argv == NULL)
+               goto memerr;
+       l = strlen(prefix) + strlen(cmd) + 1;
+       for (i = 0; argv[i] != NULL; i++) {
+               data->argv[i] = strdup(argv[i]);
+               if (data->argv[i] == NULL)
+                       goto memerr;
+               l += strlen(data->argv[i]) + 1;
+       }
+
+       /* Make up a description string. */
+       if (archive_string_ensure(&data->description, l) == NULL)
+               goto memerr;
+       archive_strcpy(&data->description, prefix);
+       archive_strcpy(&data->description, cmd);
+       for (i = 0; argv[i] != NULL; i++) {
+               archive_strappend_char(&data->description, ' ');
+               archive_strcat(&data->description, data->argv[i]);
+       }
 
-       f->name = data->description;
+       f->name = data->description.s;
        f->data = data;
        f->open = &archive_compressor_program_open;
        f->code = ARCHIVE_COMPRESSION_PROGRAM;
        f->free = archive_compressor_program_free;
        return (ARCHIVE_OK);
+memerr:
+       archive_compressor_program_free(f);
+       archive_set_error(_a, ENOMEM,
+           "Can't allocate memory for filter program");
+       return (ARCHIVE_FATAL);
 }
 
 /*
@@ -129,6 +254,7 @@ archive_compressor_program_open(struct archive_write_filter *f)
        }
 
        if ((data->child = __archive_create_child(data->cmd,
+                (char * const *)data->argv,
                 &data->child_stdin, &data->child_stdout)) == -1) {
                archive_set_error(f->archive, EINVAL,
                    "Can't initialise filter");
@@ -299,10 +425,19 @@ static int
 archive_compressor_program_free(struct archive_write_filter *f)
 {
        struct private_data *data = (struct private_data *)f->data;
-       free(data->cmd);
-       free(data->description);
-       free(data->child_buf);
-       free(data);
-       f->data = NULL;
+
+       if (data) {
+               if (data->argv != NULL) {
+                       int i;
+                       for (i = 0; data->argv[i] != NULL; i++)
+                               free(data->argv[i]);
+                       free(data->argv);
+               }
+               free(data->cmd);
+               archive_string_free(&data->description);
+               free(data->child_buf);
+               free(data);
+               f->data = NULL;
+       }
        return (ARCHIVE_OK);
 }
index 95796ac2b1c805e64e0ae8defdb8398a8e4f3226..bfaa5adce0ba5c8abb6fe6cb89549e692d457f48 100644 (file)
@@ -70,13 +70,13 @@ __FBSDID("$FreeBSD: head/lib/libarchive/filter_fork.c 182958 2008-09-12 05:33:00
 #include "filter_fork.h"
 
 pid_t
-__archive_create_child(const char *path, int *child_stdin, int *child_stdout)
+__archive_create_child(const char *path, char * const argv[], int *child_stdin,
+    int *child_stdout)
 {
        pid_t child;
        int stdin_pipe[2], stdout_pipe[2], tmp;
 #if HAVE_POSIX_SPAWNP
        posix_spawn_file_actions_t actions;
-       char *argv[2];
        int r;
 #endif
 
@@ -128,13 +128,7 @@ __archive_create_child(const char *path, int *child_stdin, int *child_stdout)
                if (r != 0)
                        goto actions_inited;
        }
-       argv[0] = strdup(path);
-       if (argv[0] == NULL)
-               goto actions_inited2;
-       argv[1] = NULL;
-       r = posix_spawnp(&child, path, &actions, NULL,
-               (char ** const)argv, NULL);
-       free(argv[0]);
+       r = posix_spawnp(&child, path, &actions, NULL, argv, NULL);
        if (r != 0)
                goto actions_inited;
        posix_spawn_file_actions_destroy(&actions);
@@ -159,7 +153,7 @@ __archive_create_child(const char *path, int *child_stdin, int *child_stdout)
                        _exit(254);
                if (stdout_pipe[1] != 1 /* stdout */)
                        close(stdout_pipe[1]);
-               execlp(path, path, (char *)NULL);
+               execvp(path, argv);
                _exit(254);
        }
 #endif /* HAVE_POSIX_SPAWNP */
@@ -177,7 +171,6 @@ __archive_create_child(const char *path, int *child_stdin, int *child_stdout)
 #if HAVE_POSIX_SPAWNP
 actions_inited:
        errno = r;
-actions_inited2:
        posix_spawn_file_actions_destroy(&actions);
 #endif
 stdout_opened:
index 453d032d1c811638793065e614b93e0c065ba15b..1652c85d08a16d6099043b4595e7648db9de7f81 100644 (file)
@@ -33,7 +33,8 @@
 #define FILTER_FORK_H
 
 pid_t
-__archive_create_child(const char *path, int *child_stdin, int *child_stdout);
+__archive_create_child(const char *path, char * const argv[], int *child_stdin,
+       int *child_stdout);
 
 void
 __archive_check_child(int in, int out);
index 8d8beb01eb2584769ecdf81097eac3e5858f05bf..c18ddff344f55bb5cb90416a349b017305943617 100644 (file)
  */
 
 #include "archive_platform.h"
+#include "archive_string.h"
 
 #if defined(_WIN32) && !defined(__CYGWIN__)
 
 #include "filter_fork.h"
 
 pid_t
-__archive_create_child(const char *path, int *child_stdin, int *child_stdout)
+__archive_create_child(const char *path, char * const argv[], int *child_stdin,
+    int *child_stdout)
 {
        HANDLE childStdout[2], childStdin[2],childStderr;
        SECURITY_ATTRIBUTES secAtts;
        STARTUPINFO staInfo;
        PROCESS_INFORMATION childInfo;
-       char cmd[MAX_PATH];
+       struct archive_string appname;
+       struct archive_string cmdline;
+       int i, l;
+
+       archive_string_init(&appname);
+       archive_string_init(&cmdline);
+       if (archive_string_ensure(&appname, strlen(path) + 1) == NULL)
+               goto fail;
+       archive_strcpy(&appname, path);
+       for (l = 0, i = 0;  argv[i] != NULL; i++) {
+               if (i == 0)
+                       continue;
+               l += strlen(argv[i]) + 1;
+       }
+       if (archive_string_ensure(&cmdline, l + 1) == NULL)
+               goto fail;
+       for (i = 0;  argv[i] != NULL; i++) {
+               if (i == 0)
+                       continue;
+               if (i > 1)
+                       archive_strappend_char(&cmdline, ' ');
+               archive_strcat(&cmdline, argv[i]);
+       }
 
        secAtts.nLength = sizeof(SECURITY_ATTRIBUTES);
        secAtts.bInheritHandle = TRUE;
@@ -79,10 +103,8 @@ __archive_create_child(const char *path, int *child_stdin, int *child_stdout)
        staInfo.hStdInput = childStdin[0];
        staInfo.wShowWindow = SW_HIDE;
        staInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
-       strncpy(cmd, path, sizeof(cmd)-1);
-       cmd[sizeof(cmd)-1] = '\0';
-       if (CreateProcessA(NULL, cmd, NULL, NULL, TRUE, 0, NULL, NULL,
-           &staInfo, &childInfo) == 0) {
+       if (CreateProcessA(appname.s, cmdline.s, NULL, NULL, TRUE, 0,
+             NULL, NULL, &staInfo, &childInfo) == 0) {
                CloseHandle(childStdout[0]);
                CloseHandle(childStdout[1]);
                CloseHandle(childStdin[0]);
@@ -100,9 +122,13 @@ __archive_create_child(const char *path, int *child_stdin, int *child_stdout)
        CloseHandle(childStdout[1]);
        CloseHandle(childStdin[0]);
 
+       archive_string_free(&appname);
+       archive_string_free(&cmdline);
        return (childInfo.dwProcessId);
 
 fail:
+       archive_string_free(&appname);
+       archive_string_free(&cmdline);
        return (-1);
 }
 
index 0ae41ae637351d420af3228153ae4f52c5afc588..30846588ffbc9b052349c655c75ad12080208db3 100644 (file)
@@ -76,6 +76,7 @@ IF(ENABLE_TEST)
     test_read_disk_entry_from_file.c
     test_read_extract.c
     test_read_file_nonexistent.c
+    test_read_filter_program_signature.c
     test_read_format_7zip.c
     test_read_format_ar.c
     test_read_format_cab.c
index 751abd54fc91eb447838cb6f457724e1624f3de5..92f7aed309c903657265d6263180ea8c0a42a529 100644 (file)
@@ -1,5 +1,6 @@
 /*-
  * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2012 Michhiro NAKAJIMA
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -82,3 +83,96 @@ DEFINE_TEST(test_read_compress_program)
 }
 
 
+DEFINE_TEST(test_read_compress_programl)
+{
+       int r;
+       struct archive_entry *ae;
+       struct archive *a;
+
+       /*
+        * First, test handling when a non-existent compression
+        * program is requested.
+        */
+       assert((a = archive_read_new()) != NULL);
+       r = archive_read_support_filter_program(a, "nonexistent");
+       if (r == ARCHIVE_FATAL) {
+               skipping("archive_read_support_filter_program() "
+                   "unsupported on this platform");
+               return;
+       }
+       assertEqualIntA(a, ARCHIVE_OK, r);
+       assertEqualIntA(a, ARCHIVE_OK,
+           archive_read_support_format_all(a));
+       assertEqualIntA(a, ARCHIVE_FATAL,
+           archive_read_open_memory(a, archive, sizeof(archive)));
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+       assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+
+       if (!canGzip()) {
+               skipping("Can't run gzip program on this platform");
+               return;
+       }
+       assert((a = archive_read_new()) != NULL);
+       assertEqualIntA(a, ARCHIVE_OK,
+           archive_read_support_filter_none(a));
+       assertEqualIntA(a, ARCHIVE_OK,
+           archive_read_support_filter_programl(a, "gzip", "gzip",
+               "-d", NULL));
+       assertEqualIntA(a, ARCHIVE_OK,
+           archive_read_support_format_all(a));
+       assertEqualIntA(a, ARCHIVE_OK,
+           archive_read_open_memory(a, archive, sizeof(archive)));
+       assertEqualIntA(a, ARCHIVE_OK,
+           archive_read_next_header(a, &ae));
+       assertEqualInt(archive_compression(a), ARCHIVE_COMPRESSION_PROGRAM);
+       assertEqualInt(archive_format(a), ARCHIVE_FORMAT_TAR_USTAR);
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+       assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
+
+DEFINE_TEST(test_read_compress_programv)
+{
+       int r;
+       struct archive_entry *ae;
+       struct archive *a;
+       char * const argv[] = {"gzip", "-d", NULL};
+
+       /*
+        * First, test handling when a non-existent compression
+        * program is requested.
+        */
+       assert((a = archive_read_new()) != NULL);
+       r = archive_read_support_filter_program(a, "nonexistent");
+       if (r == ARCHIVE_FATAL) {
+               skipping("archive_read_support_filter_program() "
+                   "unsupported on this platform");
+               return;
+       }
+       assertEqualIntA(a, ARCHIVE_OK, r);
+       assertEqualIntA(a, ARCHIVE_OK,
+           archive_read_support_format_all(a));
+       assertEqualIntA(a, ARCHIVE_FATAL,
+           archive_read_open_memory(a, archive, sizeof(archive)));
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+       assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+
+       if (!canGzip()) {
+               skipping("Can't run gzip program on this platform");
+               return;
+       }
+       assert((a = archive_read_new()) != NULL);
+       assertEqualIntA(a, ARCHIVE_OK,
+           archive_read_support_filter_none(a));
+       assertEqualIntA(a, ARCHIVE_OK,
+           archive_read_support_filter_programv(a, "gzip", argv));
+       assertEqualIntA(a, ARCHIVE_OK,
+           archive_read_support_format_all(a));
+       assertEqualIntA(a, ARCHIVE_OK,
+           archive_read_open_memory(a, archive, sizeof(archive)));
+       assertEqualIntA(a, ARCHIVE_OK,
+           archive_read_next_header(a, &ae));
+       assertEqualInt(archive_compression(a), ARCHIVE_COMPRESSION_PROGRAM);
+       assertEqualInt(archive_format(a), ARCHIVE_FORMAT_TAR_USTAR);
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+       assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
diff --git a/libarchive/test/test_read_filter_program_signature.c b/libarchive/test/test_read_filter_program_signature.c
new file mode 100644 (file)
index 0000000..0512560
--- /dev/null
@@ -0,0 +1,231 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2012 Michhiro NAKAJIMA
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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 AUTHOR(S) 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 "test.h"
+__FBSDID("$FreeBSD$");
+
+static unsigned char archive[] = {
+31,139,8,0,222,'C','p','C',0,3,211,'c',160,'=','0','0','0','0','7','5','U',
+0,210,134,230,166,6,200,'4',28,'(',24,26,24,27,155,24,152,24,154,27,155,')',
+24,24,26,152,154,25,'2','(',152,210,193,'m',12,165,197,'%',137,'E','@',167,
+148,'d',230,226,'U','G','H',30,234,15,'8','=',10,'F',193,'(',24,5,131,28,
+0,0,29,172,5,240,0,6,0,0};
+#define signature archive
+#define badsignature (&archive[1])
+#define signature_len 4
+
+DEFINE_TEST(test_read_filter_program_signature)
+{
+       int r;
+       struct archive_entry *ae;
+       struct archive *a;
+
+       /*
+        * First, test handling when a non-existent compression
+        * program is requested.
+        */
+       assert((a = archive_read_new()) != NULL);
+       r = archive_read_support_filter_program(a, "nonexistent");
+       if (r == ARCHIVE_FATAL) {
+               skipping("archive_read_support_filter_program() "
+                   "unsupported on this platform");
+               return;
+       }
+       assertEqualIntA(a, ARCHIVE_OK, r);
+       assertEqualIntA(a, ARCHIVE_OK,
+           archive_read_support_format_all(a));
+       assertEqualIntA(a, ARCHIVE_FATAL,
+           archive_read_open_memory(a, archive, sizeof(archive)));
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+       assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+
+       /*
+        * If we have "gzip -d", try using that.
+        */
+       if (!canGunzip()) {
+               skipping("Can't run gunzip program on this platform");
+               return;
+       }
+       assert((a = archive_read_new()) != NULL);
+       assertEqualIntA(a, ARCHIVE_OK,
+           archive_read_support_filter_none(a));
+       assertEqualIntA(a, ARCHIVE_OK,
+           archive_read_support_filter_program_signature(a, "gunzip",
+               signature, signature_len));
+       assertEqualIntA(a, ARCHIVE_OK,
+           archive_read_support_format_all(a));
+       assertEqualIntA(a, ARCHIVE_OK,
+           archive_read_open_memory(a, archive, sizeof(archive)));
+       assertEqualIntA(a, ARCHIVE_OK,
+           archive_read_next_header(a, &ae));
+       assertEqualInt(archive_compression(a), ARCHIVE_COMPRESSION_PROGRAM);
+       assertEqualInt(archive_format(a), ARCHIVE_FORMAT_TAR_USTAR);
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+       assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+
+       /*
+        * Test bad signature.
+        */
+       assert((a = archive_read_new()) != NULL);
+       assertEqualIntA(a, ARCHIVE_OK,
+           archive_read_support_filter_none(a));
+       assertEqualIntA(a, ARCHIVE_OK,
+           archive_read_support_filter_program_signature(a, "gunzip",
+               badsignature, signature_len));
+       assertEqualIntA(a, ARCHIVE_OK,
+           archive_read_support_format_all(a));
+       assertEqualIntA(a, ARCHIVE_FATAL,
+           archive_read_open_memory(a, archive, sizeof(archive)));
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+       assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
+
+
+DEFINE_TEST(test_read_filter_programl_signature)
+{
+       int r;
+       struct archive_entry *ae;
+       struct archive *a;
+
+       /*
+        * First, test handling when a non-existent compression
+        * program is requested.
+        */
+       assert((a = archive_read_new()) != NULL);
+       r = archive_read_support_filter_program(a, "nonexistent");
+       if (r == ARCHIVE_FATAL) {
+               skipping("archive_read_support_filter_program() "
+                   "unsupported on this platform");
+               return;
+       }
+       assertEqualIntA(a, ARCHIVE_OK, r);
+       assertEqualIntA(a, ARCHIVE_OK,
+           archive_read_support_format_all(a));
+       assertEqualIntA(a, ARCHIVE_FATAL,
+           archive_read_open_memory(a, archive, sizeof(archive)));
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+       assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+
+       if (!canGzip()) {
+               skipping("Can't run gzip program on this platform");
+               return;
+       }
+       assert((a = archive_read_new()) != NULL);
+       assertEqualIntA(a, ARCHIVE_OK,
+           archive_read_support_filter_none(a));
+       assertEqualIntA(a, ARCHIVE_OK,
+           archive_read_support_filter_programl_signature(a, "gzip", "gzip",
+               "-d", NULL, signature, signature_len));
+       assertEqualIntA(a, ARCHIVE_OK,
+           archive_read_support_format_all(a));
+       assertEqualIntA(a, ARCHIVE_OK,
+           archive_read_open_memory(a, archive, sizeof(archive)));
+       assertEqualIntA(a, ARCHIVE_OK,
+           archive_read_next_header(a, &ae));
+       assertEqualInt(archive_compression(a), ARCHIVE_COMPRESSION_PROGRAM);
+       assertEqualInt(archive_format(a), ARCHIVE_FORMAT_TAR_USTAR);
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+       assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+
+       /*
+        * Test bad signature.
+        */
+       assert((a = archive_read_new()) != NULL);
+       assertEqualIntA(a, ARCHIVE_OK,
+           archive_read_support_filter_none(a));
+       assertEqualIntA(a, ARCHIVE_OK,
+           archive_read_support_filter_programl_signature(a, "gzip", "gzip",
+               "-d", NULL, badsignature, signature_len));
+       assertEqualIntA(a, ARCHIVE_OK,
+           archive_read_support_format_all(a));
+       assertEqualIntA(a, ARCHIVE_FATAL,
+           archive_read_open_memory(a, archive, sizeof(archive)));
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+       assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
+
+DEFINE_TEST(test_read_filter_programv_signature)
+{
+       int r;
+       struct archive_entry *ae;
+       struct archive *a;
+       char * const argv[] = {"gzip", "-d", NULL};
+
+       /*
+        * First, test handling when a non-existent compression
+        * program is requested.
+        */
+       assert((a = archive_read_new()) != NULL);
+       r = archive_read_support_filter_program(a, "nonexistent");
+       if (r == ARCHIVE_FATAL) {
+               skipping("archive_read_support_filter_program() "
+                   "unsupported on this platform");
+               return;
+       }
+       assertEqualIntA(a, ARCHIVE_OK, r);
+       assertEqualIntA(a, ARCHIVE_OK,
+           archive_read_support_format_all(a));
+       assertEqualIntA(a, ARCHIVE_FATAL,
+           archive_read_open_memory(a, archive, sizeof(archive)));
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+       assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+
+       if (!canGzip()) {
+               skipping("Can't run gzip program on this platform");
+               return;
+       }
+       assert((a = archive_read_new()) != NULL);
+       assertEqualIntA(a, ARCHIVE_OK,
+           archive_read_support_filter_none(a));
+       assertEqualIntA(a, ARCHIVE_OK,
+           archive_read_support_filter_programv_signature(a, "gzip", argv,
+               signature, signature_len));
+       assertEqualIntA(a, ARCHIVE_OK,
+           archive_read_support_format_all(a));
+       assertEqualIntA(a, ARCHIVE_OK,
+           archive_read_open_memory(a, archive, sizeof(archive)));
+       assertEqualIntA(a, ARCHIVE_OK,
+           archive_read_next_header(a, &ae));
+       assertEqualInt(archive_compression(a), ARCHIVE_COMPRESSION_PROGRAM);
+       assertEqualInt(archive_format(a), ARCHIVE_FORMAT_TAR_USTAR);
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+       assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+
+       /*
+        * Test bad signature.
+        */
+       assert((a = archive_read_new()) != NULL);
+       assertEqualIntA(a, ARCHIVE_OK,
+           archive_read_support_filter_none(a));
+       assertEqualIntA(a, ARCHIVE_OK,
+           archive_read_support_filter_programv_signature(a, "gzip", argv,
+               badsignature, signature_len));
+       assertEqualIntA(a, ARCHIVE_OK,
+           archive_read_support_format_all(a));
+       assertEqualIntA(a, ARCHIVE_FATAL,
+           archive_read_open_memory(a, archive, sizeof(archive)));
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+       assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
index 4956e1a6fb6a88fe3ad99f4388c99625846c51a5..63908aca6423bc956ab550900231de4c2b810a6f 100644 (file)
@@ -1,5 +1,6 @@
 /*-
  * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2012 Michihiro NAKAJIMA
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -54,7 +55,196 @@ DEFINE_TEST(test_write_compress_program)
        /* Write it through an external "gzip" program. */
        assert((a = archive_write_new()) != NULL);
        assertA(0 == archive_write_set_format_ustar(a));
-       r = archive_write_set_compression_program(a, "gzip");
+       r = archive_write_add_filter_program(a, "gzip");
+       if (r == ARCHIVE_FATAL) {
+               skipping("Write compression via external "
+                   "program unsupported on this platform");
+               archive_write_free(a);
+               return;
+       }
+       assertA(0 == archive_write_set_bytes_per_block(a, blocksize));
+       assertA(0 == archive_write_set_bytes_in_last_block(a, blocksize));
+       assertA(blocksize == archive_write_get_bytes_in_last_block(a));
+       assertA(0 == archive_write_open_memory(a, buff, sizeof(buff), &used));
+       assertA(blocksize == archive_write_get_bytes_in_last_block(a));
+
+       /*
+        * Write a file to it.
+        */
+       assert((ae = archive_entry_new()) != NULL);
+       archive_entry_set_mtime(ae, 1, 10);
+       archive_entry_copy_pathname(ae, "file");
+       archive_entry_set_mode(ae, S_IFREG | 0755);
+       archive_entry_set_size(ae, 8);
+
+       assertA(0 == archive_write_header(a, ae));
+       archive_entry_free(ae);
+       assertA(8 == archive_write_data(a, "12345678", 9));
+
+       /* Close out the archive. */
+       assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a));
+       assertEqualInt(ARCHIVE_OK, archive_write_free(a));
+
+       /*
+        * Now, read the data back through the built-in gzip support.
+        */
+       assert((a = archive_read_new()) != NULL);
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
+       r = archive_read_support_filter_gzip(a);
+       /* The compression_gzip() handler will fall back to gunzip
+        * automatically, but if we know gunzip isn't available, then
+        * skip the rest. */
+       if (r != ARCHIVE_OK && !canGunzip()) {
+               skipping("No libz and no gunzip program, "
+                   "unable to verify gzip compression");
+               assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+               return;
+       }
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_open_memory(a, buff, used));
+
+       if (!assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae))) {
+               archive_read_free(a);
+               return;
+       }
+
+       assertEqualInt(1, archive_entry_mtime(ae));
+       assertEqualInt(0, archive_entry_atime(ae));
+       assertEqualInt(0, archive_entry_ctime(ae));
+       assertEqualString("file", archive_entry_pathname(ae));
+       assertEqualInt((S_IFREG | 0755), archive_entry_mode(ae));
+       assertEqualInt(8, archive_entry_size(ae));
+       assertEqualIntA(a, 8, archive_read_data(a, buff2, 10));
+       assertEqualMem(buff2, "12345678", 8);
+
+       /* Verify the end of the archive. */
+       assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+       assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
+
+DEFINE_TEST(test_write_compress_programl)
+{
+       struct archive_entry *ae;
+       struct archive *a;
+       size_t used;
+       int blocksize = 1024;
+       int r;
+
+       if (!canGzip()) {
+               skipping("Cannot run 'gzip'");
+               return;
+       }
+       /* NOTE: Setting blocksize=1024 will cause gunzip failure because
+        * it add extra bytes that gunzip ignores with its warning and
+        * exit code 1. So we should set blocksize=1 in order not to
+        * yield the extra bytes when using gunzip. */
+       assert((a = archive_read_new()) != NULL);
+       r = archive_read_support_filter_gzip(a);
+       if (r != ARCHIVE_OK && canGunzip())
+               blocksize = 1;
+       assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+
+       /* Create a new archive in memory. */
+       /* Write it through an external "gzip" program. */
+       assert((a = archive_write_new()) != NULL);
+       assertA(0 == archive_write_set_format_ustar(a));
+       r = archive_write_add_filter_programl(a, "gzip", "gzip", NULL);
+       if (r == ARCHIVE_FATAL) {
+               skipping("Write compression via external "
+                   "program unsupported on this platform");
+               archive_write_free(a);
+               return;
+       }
+       assertA(0 == archive_write_set_bytes_per_block(a, blocksize));
+       assertA(0 == archive_write_set_bytes_in_last_block(a, blocksize));
+       assertA(blocksize == archive_write_get_bytes_in_last_block(a));
+       assertA(0 == archive_write_open_memory(a, buff, sizeof(buff), &used));
+       assertA(blocksize == archive_write_get_bytes_in_last_block(a));
+
+       /*
+        * Write a file to it.
+        */
+       assert((ae = archive_entry_new()) != NULL);
+       archive_entry_set_mtime(ae, 1, 10);
+       archive_entry_copy_pathname(ae, "file");
+       archive_entry_set_mode(ae, S_IFREG | 0755);
+       archive_entry_set_size(ae, 8);
+
+       assertA(0 == archive_write_header(a, ae));
+       archive_entry_free(ae);
+       assertA(8 == archive_write_data(a, "12345678", 9));
+
+       /* Close out the archive. */
+       assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a));
+       assertEqualInt(ARCHIVE_OK, archive_write_free(a));
+
+       /*
+        * Now, read the data back through the built-in gzip support.
+        */
+       assert((a = archive_read_new()) != NULL);
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
+       r = archive_read_support_filter_gzip(a);
+       /* The compression_gzip() handler will fall back to gunzip
+        * automatically, but if we know gunzip isn't available, then
+        * skip the rest. */
+       if (r != ARCHIVE_OK && !canGunzip()) {
+               skipping("No libz and no gunzip program, "
+                   "unable to verify gzip compression");
+               assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+               return;
+       }
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_open_memory(a, buff, used));
+
+       if (!assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae))) {
+               archive_read_free(a);
+               return;
+       }
+
+       assertEqualInt(1, archive_entry_mtime(ae));
+       assertEqualInt(0, archive_entry_atime(ae));
+       assertEqualInt(0, archive_entry_ctime(ae));
+       assertEqualString("file", archive_entry_pathname(ae));
+       assertEqualInt((S_IFREG | 0755), archive_entry_mode(ae));
+       assertEqualInt(8, archive_entry_size(ae));
+       assertEqualIntA(a, 8, archive_read_data(a, buff2, 10));
+       assertEqualMem(buff2, "12345678", 8);
+
+       /* Verify the end of the archive. */
+       assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+       assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
+
+DEFINE_TEST(test_write_compress_programv)
+{
+       struct archive_entry *ae;
+       struct archive *a;
+       size_t used;
+       int blocksize = 1024;
+       int r;
+       char * const argv[] = {"gzip", NULL};
+
+       if (!canGzip()) {
+               skipping("Cannot run 'gzip'");
+               return;
+       }
+       /* NOTE: Setting blocksize=1024 will cause gunzip failure because
+        * it add extra bytes that gunzip ignores with its warning and
+        * exit code 1. So we should set blocksize=1 in order not to
+        * yield the extra bytes when using gunzip. */
+       assert((a = archive_read_new()) != NULL);
+       r = archive_read_support_filter_gzip(a);
+       if (r != ARCHIVE_OK && canGunzip())
+               blocksize = 1;
+       assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+
+       /* Create a new archive in memory. */
+       /* Write it through an external "gzip" program. */
+       assert((a = archive_write_new()) != NULL);
+       assertA(0 == archive_write_set_format_ustar(a));
+       r = archive_write_add_filter_programv(a, "gzip", argv);
        if (r == ARCHIVE_FATAL) {
                skipping("Write compression via external "
                    "program unsupported on this platform");