]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
Rework archive_write_set_options:
authorTim Kientzle <kientzle@gmail.com>
Sat, 6 Mar 2010 17:14:06 +0000 (12:14 -0500)
committerTim Kientzle <kientzle@gmail.com>
Sat, 6 Mar 2010 17:14:06 +0000 (12:14 -0500)
 * Move the code into a separate source file for easier maintenance
 * Report all failed options in the error message

I plan to do a similar refactor on archive_read_set_options soon.

SVN-Revision: 2010

Makefile.am
libarchive/CMakeLists.txt
libarchive/archive.h
libarchive/archive_write.c
libarchive/archive_write_set_options.c [new file with mode: 0644]

index 9df66c4b97c689fbf91141f408919054366954f5..7f8ef5e5f91265915c55b26e4e5e31805a82965d 100644 (file)
@@ -168,6 +168,7 @@ libarchive_la_SOURCES=                                              \
        libarchive/archive_write_set_format_shar.c              \
        libarchive/archive_write_set_format_ustar.c             \
        libarchive/archive_write_set_format_zip.c               \
+       libarchive/archive_write_set_options.c                  \
        libarchive/config_freebsd.h                             \
        libarchive/filter_fork.c                                \
        libarchive/filter_fork.h
index 051f916bed1768e3c0ecca8a0567a4827149537d..556baf83f85747b644724cd015673e0484c180da 100644 (file)
@@ -89,6 +89,7 @@ SET(libarchive_SOURCES
   archive_write_set_format_shar.c
   archive_write_set_format_ustar.c
   archive_write_set_format_zip.c
+  archive_write_set_options.c
   filter_fork.c
   filter_fork.h
 )
index 935a8a005bf34fdf0ec7f2270237d72e1fb77b05..84f9ea14765190bf8bc10386e3836c989b92b2eb 100644 (file)
@@ -630,17 +630,24 @@ __LA_DECL int              archive_write_finish(struct archive *);
 #endif
 
 /*
- * Set write options.
+ * Set write options.  Note that there's really no reason to use
+ * anything but archive_write_set_options().  The others should probably
+ * all be deprecated and eventually removed.
  */
+/* Apply option string to both the format and all filters. */
+__LA_DECL int          archive_write_set_options(struct archive *_a,
+                           const char *s);
 /* Apply option string to the format only. */
 __LA_DECL int          archive_write_set_format_options(struct archive *_a,
                            const char *s);
-/* Apply option string to the compressor only. */
-__LA_DECL int          archive_write_set_compressor_options(struct archive *_a,
+/* Apply option string to all matching filters. */
+__LA_DECL int          archive_write_set_filter_options(struct archive *_a,
                            const char *s);
-/* Apply option string to both the format and the compressor. */
-__LA_DECL int          archive_write_set_options(struct archive *_a,
+#if ARCHIVE_VERSION_NUMBER < 4000000
+/* Deprecated synonym for archive_write_set_filter_options. */
+__LA_DECL int          archive_write_set_compressor_options(struct archive *_a,
                            const char *s);
+#endif
 
 /*-
  * ARCHIVE_WRITE_DISK API
index 11b384ec5bfe22c46b836ca6c548a4db09ce1763..8c8aca57ea77b00ff5811ab83956848d827fdf7c 100644 (file)
@@ -133,117 +133,6 @@ archive_write_new(void)
        return (&a->archive);
 }
 
-/*
- * Set write options for the format. Returns 0 if successful.
- */
-int
-archive_write_set_format_options(struct archive *_a, const char *s)
-{
-       struct archive_write *a = (struct archive_write *)_a;
-       char key[64], val[64];
-       int len, r, ret = ARCHIVE_OK;
-
-       archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
-           ARCHIVE_STATE_NEW, "archive_write_set_format_options");
-       archive_clear_error(&a->archive);
-
-       if (s == NULL || *s == '\0')
-               return (ARCHIVE_OK);
-       if (a->format_options == NULL)
-               /* This format does not support option. */
-               return (ARCHIVE_OK);
-
-       while ((len = __archive_parse_options(s, a->format_name,
-           sizeof(key), key, sizeof(val), val)) > 0) {
-               if (val[0] == '\0')
-                       r = a->format_options(a, key, NULL);
-               else
-                       r = a->format_options(a, key, val);
-               if (r == ARCHIVE_FATAL)
-                       return (r);
-               if (r < ARCHIVE_OK) { /* This key was not handled. */
-                       archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
-                           "Unsupported option ``%s''", key);
-                       ret = ARCHIVE_WARN;
-               }
-               s += len;
-       }
-       if (len < 0) {
-               archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
-                   "Malformed options string.");
-               return (ARCHIVE_WARN);
-       }
-       return (ret);
-}
-
-/*
- * Set write options for the compressor. Returns 0 if successful.
- */
-int
-archive_write_set_compressor_options(struct archive *_a, const char *s)
-{
-       struct archive_write *a = (struct archive_write *)_a;
-       struct archive_write_filter *filter;
-       char key[64], val[64];
-       int len, r;
-       int ret = ARCHIVE_OK;
-
-       archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
-           ARCHIVE_STATE_NEW, "archive_write_set_compressor_options");
-       archive_clear_error(&a->archive);
-
-       if (s == NULL || *s == '\0')
-               return (ARCHIVE_OK);
-       len = 0;
-       for (filter = a->filter_first; filter != NULL; filter = filter->next_filter) {
-               if (filter->options == NULL)
-                       /* This filter does not have any options */
-                       continue;
-               while ((len = __archive_parse_options(s,
-                           a->archive.compression_name,
-                           sizeof(key), key, sizeof(val), val)) > 0) {
-                       if (val[0] == '\0')
-                               r = filter->options(filter, key, NULL);
-                       else
-                               r = filter->options(filter, key, val);
-                       if (r == ARCHIVE_FATAL)
-                               return (r);
-                       if (r < ARCHIVE_OK) {
-                               archive_set_error(&a->archive,
-                                   ARCHIVE_ERRNO_MISC,
-                                   "Unsupported option ``%s''", key);
-                               ret = ARCHIVE_WARN;
-                       }
-                       s += len;
-               }
-       }
-       if (len < 0) {
-               archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
-                   "Illegal format options.");
-               return (ARCHIVE_WARN);
-       }
-       return (ret);
-}
-
-/*
- * Set write options for the format and the compressor. Returns 0 if successful.
- */
-int
-archive_write_set_options(struct archive *_a, const char *s)
-{
-       int r1, r2;
-
-       r1 = archive_write_set_format_options(_a, s);
-       if (r1 < ARCHIVE_WARN)
-               return (r1);
-       r2 = archive_write_set_compressor_options(_a, s);
-       if (r2 < ARCHIVE_WARN)
-               return (r2);
-       if (r1 == ARCHIVE_WARN && r2 == ARCHIVE_WARN)
-               return (ARCHIVE_WARN);
-       return (ARCHIVE_OK);
-}
-
 /*
  * Set the block size.  Returns 0 if successful.
  */
diff --git a/libarchive/archive_write_set_options.c b/libarchive/archive_write_set_options.c
new file mode 100644 (file)
index 0000000..fe0ce67
--- /dev/null
@@ -0,0 +1,302 @@
+/*-
+ * Copyright (c) 2003-2010 Tim Kientzle
+ * 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 "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive_string.h"
+#include "archive_write_private.h"
+
+/*
+ * Like strdup, but takes start and end pointers.
+ */
+static char *
+strsave(const char *start, const char *end)
+{
+       char *s = malloc(end - start + 1);
+       if (s == NULL)
+               return (NULL);
+       memcpy(s, start, end - start);
+       s[end - start] = '\0';
+       return (s);
+}
+
+/*
+ * Parse the next option from the string.  Returns the length of
+ * the recognized option string.  Sets name, key, value to malloc-ed
+ * strings holding the components of this option.  Caller is
+ * responsible for freeing the name, key, and value.
+ */
+static int
+next_option(const char *_p, char **name, char **key, char **value)
+{
+       static const char *set_value = "1";
+       const char *p, *start;
+       char sep;
+
+       *name = *key = NULL;
+       *value = strsave(set_value, set_value + 1);
+
+       /* The first separator determines whether the first word is a
+        * name (':'), bare key (\0' or ','), or key followed by value
+        * ('=').
+        */
+       p = _p;
+       while (*p == ' ' || *p == '\t')
+               ++p;
+       if (*p == '\0')
+               return (0);
+       if (*p == '!') {
+               free(*value);
+               *value = NULL;
+               ++p;
+       }
+       start = p;
+       if (*p < 'a' || *p > 'z')
+               return (-1);
+       while (*p != ':' && *p != ',' && *p != '=' && *p != '\0') {
+               if ((*p < 'a' || *p > 'z')
+                   && (*p < '0' || *p > '9')
+                   && (*p != '-'))
+                       return (-1);
+               ++p;
+       }
+       sep = *p;
+       if (sep == ':')
+               *name = strsave(start, p);
+       else
+               *key = strsave(start, p);
+       if (sep != '\0')
+               ++p;
+       switch (sep) {
+       case '\0': case ',':
+               return p - _p;
+       case ':':
+               goto key;
+       case '=':
+               goto value;
+       }
+
+key:
+       if (*p == '!') {
+               free(*value);
+               *value = NULL;
+               ++p;
+       }
+       start = p;
+       while (*p != ',' && *p != '=' && *p != '\0') {
+               if ((*p < 'a' || *p > 'z')
+                   && (*p < '0' || *p > '9')
+                   && (*p != '-'))
+                       return (-1);
+               ++p;
+       }
+       *key = strsave(start, p);
+       sep = *p;
+       if (*p != '\0')
+               ++p;
+       if (sep != '=')
+               return p - _p;
+
+value:
+       start = p;
+       while (*p != ',' && *p != '\0')
+               ++p;
+       free(*value);
+       *value = strsave(start, p);
+       if (*p == ',')
+               ++p;
+       return p - _p;
+}
+
+
+/*
+ * See if the format can handle this option.
+ */
+static int
+apply_format_option(struct archive_write *a, const char *name,
+    const char *key, const char *value)
+{
+       int r;
+       if (name != NULL && strcmp(name, a->format_name) != 0)
+               return (ARCHIVE_WARN);
+       if (a->format_options == NULL)
+               return (ARCHIVE_WARN);
+       r = a->format_options(a, key, value);
+       if (r == ARCHIVE_FATAL)
+               return (r);
+       return (r < ARCHIVE_OK ? ARCHIVE_WARN : ARCHIVE_OK);
+}
+
+/*
+ * See if a filter can handle this option.
+ */
+static int
+apply_filter_option(struct archive_write *a, const char *name,
+    const char *key, const char *value)
+{
+       struct archive_write_filter *filter;
+       int r, handled = 0;
+
+       for (filter = a->filter_first; filter != NULL; filter = filter->next_filter) {
+               if (filter->options == NULL)
+                       continue;
+               if (name != NULL && strcmp(name, filter->name) != 0)
+                       continue;
+               r = filter->options(filter, key, value);
+               if (r == ARCHIVE_FATAL)
+                       return (r);
+               if (r == ARCHIVE_OK)
+                       ++handled;
+       }
+       return (handled > 0 ? ARCHIVE_OK : ARCHIVE_WARN);
+}
+
+
+/*
+ * See if a format or filter can handle this option.
+ * Returns ARCHIVE_OK if it is handled by anyone.
+ */
+static int
+apply_option(struct archive_write *a, const char *name,
+    const char *key, const char *value)
+{
+       int r1, r2;
+
+       r1 = apply_format_option(a, name, key, value);
+       if (r1 == ARCHIVE_FATAL)
+               return (r1);
+       r2 = apply_filter_option(a, name, key, value);
+       if (r2 == ARCHIVE_FATAL)
+               return (r2);
+       if (r1 < ARCHIVE_OK && r2 < ARCHIVE_OK)
+               return (ARCHIVE_WARN);
+       return (ARCHIVE_OK);
+}
+
+/*
+ * Walk through all of the options and try applying each one.
+ * Construct an error message listing all of the unhandled options.
+ */
+static int
+process_options(struct archive *_a, const char *s,
+    int(*apply)(struct archive_write *, const char *, const char *, const char *))
+{
+       struct archive_write *a = (struct archive_write *)_a;
+       struct archive_string unhandled;
+       char *name, *key, *value;
+       int len, r;
+
+       archive_clear_error(&a->archive);
+       archive_string_init(&unhandled);
+
+       if (s == NULL || *s == '\0')
+               return (ARCHIVE_OK);
+
+       while ((len = next_option(s, &name, &key, &value)) > 0) {
+               r = (*apply)(a, name, key, value);
+               if (r == ARCHIVE_FATAL) {
+                       free(name);
+                       free(key);
+                       free(value);
+                       return (r);
+               }
+               if (r < ARCHIVE_OK) { /* This key was not handled. */
+                       if (archive_strlen(&unhandled) > 0)
+                               archive_strcat(&unhandled, ", ");
+                       archive_strcat(&unhandled, key);
+               }
+               free(name);
+               free(key);
+               free(value);
+               s += len;
+       }
+       if (len < 0) {
+               archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+                   "Malformed options string: %s", s);
+               archive_string_free(&unhandled);
+               return (ARCHIVE_WARN);
+       }
+       if (archive_strlen(&unhandled) > 0) {
+               archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+                   "Unsupported options: %s", unhandled.s);
+               archive_string_free(&unhandled);
+               return (ARCHIVE_WARN);
+       }
+       return (ARCHIVE_OK);
+}
+
+/*
+ * Set write options for the format. Returns 0 if successful.
+ */
+int
+archive_write_set_format_options(struct archive *a, const char *s)
+{
+       archive_check_magic(a, ARCHIVE_WRITE_MAGIC,
+           ARCHIVE_STATE_NEW, "archive_write_set_format_options");
+       return (process_options(a, s, apply_format_option));
+}
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+/*
+ * Set write options for the filters. Returns 0 if successful.
+ */
+int
+archive_write_set_compressor_options(struct archive *a, const char *s)
+{
+       archive_check_magic(a, ARCHIVE_WRITE_MAGIC,
+           ARCHIVE_STATE_NEW, "archive_write_set_compressor_options");
+       return (process_options(a, s, apply_filter_option));
+}
+#endif
+
+/*
+ * Set write options for the filters. Returns 0 if successful.
+ */
+int
+archive_write_set_filter_options(struct archive *a, const char *s)
+{
+       archive_check_magic(a, ARCHIVE_WRITE_MAGIC,
+           ARCHIVE_STATE_NEW, "archive_write_set_compressor_options");
+       return (process_options(a, s, apply_filter_option));
+}
+
+/*
+ * Set write options. Returns 0 if successful.
+ */
+int
+archive_write_set_options(struct archive *a, const char *s)
+{
+       archive_check_magic(a, ARCHIVE_WRITE_MAGIC,
+           ARCHIVE_STATE_NEW, "archive_write_set_compressor_options");
+       return (process_options(a, s, apply_option));
+}