From bcbf7a2a72fd7bc950fee089f504206db497ebb6 Mon Sep 17 00:00:00 2001 From: Tim Kientzle Date: Sat, 6 Mar 2010 12:14:06 -0500 Subject: [PATCH] Rework archive_write_set_options: * 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 | 1 + libarchive/CMakeLists.txt | 1 + libarchive/archive.h | 17 +- libarchive/archive_write.c | 111 --------- libarchive/archive_write_set_options.c | 302 +++++++++++++++++++++++++ 5 files changed, 316 insertions(+), 116 deletions(-) create mode 100644 libarchive/archive_write_set_options.c diff --git a/Makefile.am b/Makefile.am index 9df66c4b9..7f8ef5e5f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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 diff --git a/libarchive/CMakeLists.txt b/libarchive/CMakeLists.txt index 051f916be..556baf83f 100644 --- a/libarchive/CMakeLists.txt +++ b/libarchive/CMakeLists.txt @@ -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 ) diff --git a/libarchive/archive.h b/libarchive/archive.h index 935a8a005..84f9ea147 100644 --- a/libarchive/archive.h +++ b/libarchive/archive.h @@ -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 diff --git a/libarchive/archive_write.c b/libarchive/archive_write.c index 11b384ec5..8c8aca57e 100644 --- a/libarchive/archive_write.c +++ b/libarchive/archive_write.c @@ -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 index 000000000..fe0ce67cb --- /dev/null +++ b/libarchive/archive_write_set_options.c @@ -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 +#endif +#ifdef HAVE_STRING_H +#include +#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)); +} -- 2.47.3