Parse capture configuration file in library
authorEric Biggers <ebiggers3@gmail.com>
Thu, 24 Apr 2014 04:08:50 +0000 (23:08 -0500)
committerEric Biggers <ebiggers3@gmail.com>
Thu, 24 Apr 2014 05:18:02 +0000 (00:18 -0500)
14 files changed:
Makefile.am
NEWS
include/wimlib.h
include/wimlib/capture.h
include/wimlib/textfile.h [new file with mode: 0644]
include/wimlib_tchar.h
programs/imagex.c
src/add_image.c
src/capture_common.c
src/pathlist.c
src/textfile.c [new file with mode: 0644]
src/update_image.c
src/wimboot.c
src/win32_capture.c

index 2dc7528..a9206fd 100644 (file)
@@ -15,7 +15,7 @@ endif
 
 lib_LTLIBRARIES = libwim.la
 
-libwim_la_LDFLAGS = -version-info 14:0:5 $(WINDOWS_LDFLAGS)
+libwim_la_LDFLAGS = -version-info 15:0:0 $(WINDOWS_LDFLAGS)
 
 libwim_la_SOURCES =            \
        src/add_image.c         \
@@ -65,6 +65,7 @@ libwim_la_SOURCES =           \
        src/split.c             \
        src/reparse.c           \
        src/template.c          \
+       src/textfile.c          \
        src/timestamp.c         \
        src/update_image.c      \
        src/util.c              \
@@ -113,6 +114,7 @@ libwim_la_SOURCES =         \
        include/wimlib/security.h       \
        include/wimlib/security_descriptor.h    \
        include/wimlib/sha1.h           \
+       include/wimlib/textfile.h       \
        include/wimlib/timestamp.h      \
        include/wimlib/types.h          \
        include/wimlib/unix_data.h      \
diff --git a/NEWS b/NEWS
index c415e0a..f48dc54 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -8,6 +8,12 @@ Version 1.6.3-BETA:
        archive.  See the documentation for the new '--wimboot' option to
        wimcapture and wimapply, respectively, for more information.
 
+       The shared library version has been bumped up; however, there are only
+       small incompatibilities:
+
+               - wimlib_capture_config has been removed.  The library now takes
+                 the configuration file directly.
+
 Version 1.6.2:
        Case-insensitive comparisons of strings (e.g. filenames) containing
        UTF-16 codepoints above 32767 are now done correctly.
index 31fab04..e880182 100644 (file)
@@ -959,46 +959,6 @@ struct wimlib_capture_source {
        long reserved;
 };
 
-/** Structure that specifies a list of path patterns. */
-struct wimlib_pattern_list {
-       /** Array of patterns.  The patterns may be modified by library code,
-        * but the @p pats pointer itself will not.  See the man page for
-        * <b>wimlib-imagex capture</b> for more information about allowed
-        * patterns. */
-       wimlib_tchar **pats;
-
-       /** Number of patterns in the @p pats array. */
-       size_t num_pats;
-
-       /** Ignored; may be used by the calling code. */
-       size_t num_allocated_pats;
-};
-
-/** A structure that contains lists of wildcards that match paths to treat
- * specially when capturing a WIM image. */
-struct wimlib_capture_config {
-       /** Paths matching any pattern this list are excluded from being
-        * captured, except if the same path appears in @p
-        * exclusion_exception_pats. */
-       struct wimlib_pattern_list exclusion_pats;
-
-       /** Paths matching any pattern in this list are never excluded from
-        * being captured. */
-       struct wimlib_pattern_list exclusion_exception_pats;
-
-       /** Reserved for future capture configuration options. */
-       struct wimlib_pattern_list reserved1;
-
-       /** Reserved for future capture configuration options. */
-       struct wimlib_pattern_list reserved2;
-
-       /** Library internal use only. */
-       wimlib_tchar *_prefix;
-
-       /** Library internal use only. */
-       size_t _prefix_num_tchars;
-};
-
 /** Set or unset the WIM header flag that marks it read-only
  * (WIM_HDR_FLAG_READONLY in Microsoft's documentation), based on the
  * ::wimlib_wim_info.is_marked_readonly member of the @p info parameter.  This
@@ -1362,19 +1322,13 @@ typedef int (*wimlib_iterate_lookup_table_callback_t)(const struct wimlib_resour
  * such a file is encountered.  */
 #define WIMLIB_ADD_FLAG_NO_UNSUPPORTED_EXCLUDE 0x00000400
 
-/** Automatically select a capture configuration appropriate for capturing
- * filesystems containing Windows operating systems.  When this flag is
- * specified, the corresponding @p config parameter or member must be @c NULL.
- *
- * Currently, selecting this capture configuration will cause the following
- * files and directories to be excluded from capture:
+/**
+ * Automatically select a capture configuration appropriate for capturing
+ * filesystems containing Windows operating systems.  For example,
+ * "pagefile.sys" and "System Volume Information" will be excluded.
  *
- * - "\$ntfs.log"
- * - "\hiberfil.sys"
- * - "\pagefile.sys"
- * - "\System Volume Information"
- * - "\RECYCLER"
- * - "\Windows\CSC"
+ * When this flag is specified, the corresponding @p config parameter or member
+ * must be @c NULL.
  *
  * Note that the default behavior--- that is, when this flag is not specified
  * and @p config is @c NULL--- is to use no capture configuration, meaning that
@@ -1383,7 +1337,9 @@ typedef int (*wimlib_iterate_lookup_table_callback_t)(const struct wimlib_resour
 #define WIMLIB_ADD_FLAG_WINCONFIG              0x00000800
 
 /**
- * Capture image as WIMBoot compatible.
+ * Capture image as WIMBoot compatible.  In addition, use the capture
+ * configuration file <c>$SOURCE/Windows/System32/WimBootCompress.ini</c> if it
+ * exists, where <c>$SOURCE</c> is the directory being captured.
  *
  * Note: this will not by itself change the compression type.  Before writing
  * the WIM file, it's recommended to also do:
@@ -1841,11 +1797,9 @@ struct wimlib_add_command {
         * WIM image. */
        wimlib_tchar *wim_target_path;
 
-       /** Configuration for excluded files.  @c NULL means
-        * exclude no files (use no configuration), unless
-        * ::WIMLIB_ADD_FLAG_WINCONFIG is specified in @p
-        * add_flags.  */
-       struct wimlib_capture_config *config;
+       /** Path to capture configuration file to use, or @c NULL for default.
+        */
+       wimlib_tchar *config_file;
 
        /** Bitwise OR of WIMLIB_ADD_FLAG_* flags. */
        int add_flags;
@@ -2072,10 +2026,15 @@ wimlib_add_empty_image(WIMStruct *wim,
  *     Name to give the new image.  If @c NULL or empty, the new image is given
  *     no name.  If nonempty, it must specify a name that does not already
  *     exist in @p wim.
- * @param config
- *     Capture configuration that specifies files, directories, or path globs
- *     to exclude from being captured.  If @c NULL, a dummy configuration where
- *     no paths are treated specially is used.
+ * @param config_file
+ *     Path to capture configuration file, or @c NULL.  This file may specify,
+ *     among other things, which files to exclude from capture.  See the man
+ *     page for <b>wimlib-imagex capture</b> (<b>--config</b> option) for
+ *     details of the file format.  If @c NULL, the default capture
+ *     configuration shall be used.  Ordinarily, the default capture
+ *     configuration will result in no files being excluded from capture purely
+ *     based on name; however, the ::WIMLIB_ADD_FLAG_WINCONFIG and
+ *     ::WIMLIB_ADD_FLAG_WIMBOOT flags modify the default.
  * @param add_flags
  *     Bitwise OR of flags prefixed with WIMLIB_ADD_FLAG.
  * @param progress_func
@@ -2099,7 +2058,7 @@ extern int
 wimlib_add_image(WIMStruct *wim,
                 const wimlib_tchar *source,
                 const wimlib_tchar *name,
-                const struct wimlib_capture_config *config,
+                const wimlib_tchar *config_file,
                 int add_flags,
                 wimlib_progress_func_t progress_func);
 
@@ -2128,7 +2087,7 @@ wimlib_add_image_multisource(WIMStruct *wim,
                             const struct wimlib_capture_source *sources,
                             size_t num_sources,
                             const wimlib_tchar *name,
-                            const struct wimlib_capture_config *config,
+                            const wimlib_tchar *config_file,
                             int add_flags,
                             wimlib_progress_func_t progress_func);
 
index 7ff82db..995a60f 100644 (file)
@@ -5,12 +5,21 @@
 #include "wimlib/inode_table.h"
 #include "wimlib/list.h"
 #include "wimlib/security.h"
+#include "wimlib/textfile.h"
 #include "wimlib/util.h"
 
 struct wim_lookup_table;
 struct wim_dentry;
 struct wim_inode;
 
+struct capture_config {
+       struct string_set exclusion_pats;
+       struct string_set exclusion_exception_pats;
+       tchar *prefix;
+       size_t prefix_num_tchars;
+       tchar *buf;
+};
+
 /* Common parameters to implementations of building an in-memory dentry tree
  * from an on-disk directory structure. */
 struct add_image_params {
@@ -28,9 +37,8 @@ struct add_image_params {
         * image so far. */
        struct wim_sd_set sd_set;
 
-       /* Pointer to the capture configuration, which indicates whether any
-        * files should be excluded from capture or not. */
-       struct wimlib_capture_config *config;
+       /* Pointer to the capture configuration.  */
+       struct capture_config *config;
 
        /* Flags that affect the capture operation (WIMLIB_ADD_FLAG_*) */
        int add_flags;
@@ -56,20 +64,17 @@ extern void
 do_capture_progress(struct add_image_params *params, int status,
                    const struct wim_inode *inode);
 
-extern bool
-exclude_path(const tchar *path, size_t path_len,
-            const struct wimlib_capture_config *config,
-            bool exclude_prefix);
-
-extern struct wimlib_capture_config *
-copy_capture_config(const struct wimlib_capture_config *config);
-
 extern int
-copy_and_canonicalize_capture_config(const struct wimlib_capture_config *config,
-                                    struct wimlib_capture_config **config_copy_ret);
+do_read_capture_config_file(const tchar *config_file, tchar *buf, size_t buflen,
+                           struct capture_config *config);
 
 extern void
-free_capture_config(struct wimlib_capture_config *config);
+destroy_capture_config(struct capture_config *config);
+
+extern bool
+exclude_path(const tchar *path, size_t path_len,
+            const struct capture_config *config,
+            bool exclude_prefix);
 
 
 #ifdef WITH_NTFS_3G
diff --git a/include/wimlib/textfile.h b/include/wimlib/textfile.h
new file mode 100644 (file)
index 0000000..4c4d122
--- /dev/null
@@ -0,0 +1,49 @@
+#ifndef _WIMLIB_TEXTFILE_H_
+#define _WIMLIB_TEXTFILE_H_
+
+#include <wimlib/types.h>
+
+struct string_set {
+       tchar **strings;
+       size_t num_strings;
+       size_t num_alloc_strings;
+};
+
+#define STRING_SET_INITIALIZER \
+       { .strings = NULL, .num_strings = 0, .num_alloc_strings = 0, }
+
+#define STRING_SET(_strings) \
+       struct string_set _strings = STRING_SET_INITIALIZER
+
+typedef int (*line_mangle_t)(tchar *line, const tchar *filename,
+                            unsigned long line_no);
+
+struct text_file_section {
+       const tchar *name;
+       struct string_set *strings;
+};
+
+extern int
+do_load_text_file(const tchar *path, tchar *buf, size_t buflen, tchar **buf_ret,
+                 const struct text_file_section *pos_sections,
+                 int num_pos_sections, line_mangle_t mangle_line);
+
+static inline int
+load_text_file(const tchar *path, tchar **buf_ret,
+              const struct text_file_section *pos_sections,
+              int num_pos_sections, line_mangle_t mangle_line)
+{
+       return do_load_text_file(path, NULL, 0, buf_ret,
+                                pos_sections, num_pos_sections, mangle_line);
+}
+
+static inline int
+load_text_buffer(tchar *buf, size_t buflen,
+                const struct text_file_section *pos_sections,
+                int num_pos_sections, line_mangle_t mangle_line)
+{
+       return do_load_text_file(NULL, buf, buflen, &buf,
+                                pos_sections, num_pos_sections, mangle_line);
+}
+
+#endif /* _WIMLIB_TEXTFILE_H_ */
index ecaae5d..519c425 100644 (file)
@@ -21,6 +21,7 @@ typedef wchar_t tchar;
  * with the "wide-character" functions. */
 #  define tmemchr      wmemchr
 #  define tmemcpy      wmemcpy
+#  define tmemmove     wmemmove
 #  define tmempcpy     wmempcpy
 #  define tstrcpy      wcscpy
 #  define tprintf      wprintf
@@ -84,6 +85,7 @@ typedef char tchar;
  * string functions. */
 #  define tmemchr      memchr
 #  define tmemcpy      memcpy
+#  define tmemmove     memmove
 #  define tmempcpy     mempcpy
 #  define tstrcpy      strcpy
 #  define tprintf      printf
index e80d3e6..753206d 100644 (file)
@@ -417,17 +417,6 @@ imagex_error(const tchar *format, ...)
        va_end(va);
 }
 
-static void _format_attribute(printf, 1, 2)
-imagex_warning(const tchar *format, ...)
-{
-       va_list va;
-       va_start(va, format);
-       tfputs(T("WARNING: "), stderr);
-       tvfprintf(stderr, format, va);
-       tputc(T('\n'), stderr);
-       va_end(va);
-}
-
 /* Print formatted error message to stderr. */
 static void _format_attribute(printf, 1, 2)
 imagex_error_with_errno(const tchar *format, ...)
@@ -827,147 +816,6 @@ parse_source_list(tchar **source_list_contents_p, size_t source_list_nchars,
        return sources;
 }
 
-
-enum capture_config_section {
-       CAPTURE_CONFIG_NO_SECTION,
-       CAPTURE_CONFIG_EXCLUSION_SECTION,
-       CAPTURE_CONFIG_EXCLUSION_EXCEPTION_SECTION,
-       CAPTURE_CONFIG_IGNORE_SECTION,
-};
-
-enum {
-       CAPTURE_CONFIG_INVALID_SECTION,
-       CAPTURE_CONFIG_CHANGED_SECTION,
-       CAPTURE_CONFIG_SAME_SECTION,
-};
-
-static int
-check_config_section(tchar *line, size_t len,
-                    enum capture_config_section *cur_section)
-{
-       while (istspace(*line))
-               line++;
-
-       if (*line != T('['))
-               return CAPTURE_CONFIG_SAME_SECTION;
-
-       line++;
-       tchar *endbrace = tstrrchr(line, T(']'));
-       if (!endbrace)
-               return CAPTURE_CONFIG_SAME_SECTION;
-
-       if (!tmemcmp(line, T("ExclusionList"), endbrace - line)) {
-               *cur_section = CAPTURE_CONFIG_EXCLUSION_SECTION;
-       } else if (!tmemcmp(line, T("ExclusionException"), endbrace - line)) {
-               *cur_section = CAPTURE_CONFIG_EXCLUSION_EXCEPTION_SECTION;
-       } else if (!tmemcmp(line, T("CompressionExclusionList"), endbrace - line)) {
-               *cur_section = CAPTURE_CONFIG_IGNORE_SECTION;
-               tfputs(T("WARNING: Ignoring [CompressionExclusionList] section "
-                        "of capture config file\n"),
-                      stderr);
-       } else if (!tmemcmp(line, T("AlignmentList"), endbrace - line)) {
-               *cur_section = CAPTURE_CONFIG_IGNORE_SECTION;
-               tfputs(T("WARNING: Ignoring [AlignmentList] section "
-                        "of capture config file\n"),
-                      stderr);
-       } else {
-               imagex_warning(T("Unknown capture config file section \"%"TS"\""),
-                              line - 1);
-               return CAPTURE_CONFIG_INVALID_SECTION;
-       }
-       return CAPTURE_CONFIG_CHANGED_SECTION;
-}
-
-
-static bool
-pattern_list_add_pattern(struct wimlib_pattern_list *pat_list,
-                        tchar *pat)
-{
-       if (pat_list->num_pats == pat_list->num_allocated_pats) {
-               tchar **pats;
-               size_t num_allocated_pats = pat_list->num_pats + 8;
-
-               pats = realloc(pat_list->pats,
-                              num_allocated_pats * sizeof(pat_list->pats[0]));
-               if (!pats) {
-                       imagex_error(T("Out of memory!"));
-                       return false;
-               }
-               pat_list->pats = pats;
-               pat_list->num_allocated_pats = num_allocated_pats;
-       }
-       pat_list->pats[pat_list->num_pats++] = pat;
-       return true;
-}
-
-static bool
-parse_capture_config_line(tchar *line, size_t len,
-                         enum capture_config_section *cur_section,
-                         struct wimlib_capture_config *config)
-{
-       tchar *filename;
-       int ret;
-
-       ret = check_config_section(line, len, cur_section);
-       if (ret == CAPTURE_CONFIG_CHANGED_SECTION ||
-           ret == CAPTURE_CONFIG_INVALID_SECTION)
-               return true;
-
-       switch (*cur_section) {
-       case CAPTURE_CONFIG_NO_SECTION:
-               imagex_error(T("Line \"%"TS"\" is not in a section "
-                              "(such as [ExclusionList]"), line);
-               return false;
-       case CAPTURE_CONFIG_EXCLUSION_SECTION:
-               if (parse_string(&line, &len, &filename) != PARSE_STRING_SUCCESS)
-                       return false;
-               return pattern_list_add_pattern(&config->exclusion_pats,
-                                               filename);
-       case CAPTURE_CONFIG_EXCLUSION_EXCEPTION_SECTION:
-               if (parse_string(&line, &len, &filename) != PARSE_STRING_SUCCESS)
-                       return false;
-               return pattern_list_add_pattern(&config->exclusion_exception_pats,
-                                               filename);
-       case CAPTURE_CONFIG_IGNORE_SECTION:
-               return true;
-       }
-       return false;
-}
-
-static int
-parse_capture_config(tchar **contents_p, size_t nchars,
-                    struct wimlib_capture_config *config)
-{
-       ssize_t nlines;
-       tchar *p;
-       size_t i;
-       enum capture_config_section cur_section;
-
-       memset(config, 0, sizeof(*config));
-
-       nlines = text_file_count_lines(contents_p, &nchars);
-       if (nlines < 0)
-               return -1;
-
-       cur_section = CAPTURE_CONFIG_NO_SECTION;
-       p = *contents_p;
-       for (i = 0; i < nlines; i++) {
-               tchar *endp = tmemchr(p, T('\n'), nchars);
-               size_t len = endp - p + 1;
-               *endp = T('\0');
-               if (p != endp && *(endp - 1) == T('\r')) {
-                       *(endp - 1) = '\0';
-                       len--;
-               }
-               if (!is_comment_line(p, len))
-                       if (!parse_capture_config_line(p, len, &cur_section, config))
-                               return -1;
-               p = endp + 1;
-
-       }
-       return 0;
-}
-
 /* Reads the contents of a file into memory. */
 static char *
 file_get_contents(const tchar *filename, size_t *len_ret)
@@ -1842,8 +1690,6 @@ imagex_capture_or_append(int argc, tchar **argv, int cmd)
        tchar *source_copy;
 
        tchar *config_file = NULL;
-       tchar *config_str;
-       struct wimlib_capture_config *config;
 
        bool source_list = false;
        size_t source_list_nchars = 0;
@@ -2002,26 +1848,6 @@ imagex_capture_or_append(int argc, tchar **argv, int cmd)
        if (compress_slow)
                set_compress_slow();
 
-       /* Set default configuration file  */
-#ifdef __WIN32__
-       if ((add_image_flags & WIMLIB_ADD_IMAGE_FLAG_WIMBOOT) && !config) {
-               struct stat st;
-
-               config_file = alloca(wcslen(source) * sizeof(wchar_t) + 100);
-               swprintf(config_file, L"%ls\\%ls",
-                        source,  L"Windows\\System32\\WimBootCompress.ini");
-
-               if (tstat(config_file, &st)) {
-                       imagex_printf(L"\"%ls\" does not exist; using "
-                                     "default configuration\n",
-                                     config_file);
-                       config_file = NULL;
-               } else {
-                       add_image_flags &= ~WIMLIB_ADD_IMAGE_FLAG_WINCONFIG;
-               }
-       }
-#endif
-
        if (!tstrcmp(wimfile, T("-"))) {
                /* Writing captured WIM to standard output.  */
        #if 0
@@ -2126,27 +1952,6 @@ imagex_capture_or_append(int argc, tchar **argv, int cmd)
                source_list_contents = NULL;
        }
 
-       if (config_file) {
-               /* Read and parse capture configuration file.  */
-               size_t config_len;
-
-               config_str = file_get_text_contents(config_file, &config_len);
-               if (!config_str) {
-                       ret = -1;
-                       goto out_free_capture_sources;
-               }
-
-               config = alloca(sizeof(*config));
-               ret = parse_capture_config(&config_str, config_len, config);
-               if (ret)
-                       goto out_free_config;
-       } else {
-               /* No capture configuration file specified; use default
-                * configuration for capturing Windows operating systems.  */
-               config = NULL;
-               add_image_flags |= WIMLIB_ADD_FLAG_WINCONFIG;
-       }
-
        /* Open the existing WIM, or create a new one.  */
        if (cmd == CMD_APPEND)
                ret = wimlib_open_wim(wimfile, open_flags, &wim,
@@ -2154,7 +1959,7 @@ imagex_capture_or_append(int argc, tchar **argv, int cmd)
        else
                ret = wimlib_create_new_wim(compression_type, &wim);
        if (ret)
-               goto out_free_config;
+               goto out_free_capture_sources;
 
        /* Set chunk size if non-default.  */
        if (chunk_size != UINT32_MAX) {
@@ -2294,7 +2099,7 @@ imagex_capture_or_append(int argc, tchar **argv, int cmd)
                                           capture_sources,
                                           num_sources,
                                           name,
-                                          config,
+                                          config_file,
                                           add_image_flags,
                                           imagex_progress_func);
        if (ret)
@@ -2364,12 +2169,6 @@ out_free_base_wims:
        free(base_wims);
 out_free_wim:
        wimlib_free(wim);
-out_free_config:
-       if (config) {
-               free(config->exclusion_pats.pats);
-               free(config->exclusion_exception_pats.pats);
-               free(config_str);
-       }
 out_free_capture_sources:
        if (capture_sources_malloced)
                free(capture_sources);
@@ -3863,7 +3662,8 @@ imagex_update(int argc, tchar **argv, int cmd)
        int write_flags = 0;
        int update_flags = WIMLIB_UPDATE_FLAG_SEND_PROGRESS;
        int default_add_flags = WIMLIB_ADD_FLAG_EXCLUDE_VERBOSE |
-                               WIMLIB_ADD_FLAG_VERBOSE;
+                               WIMLIB_ADD_FLAG_VERBOSE |
+                               WIMLIB_ADD_FLAG_WINCONFIG;
        int default_delete_flags = 0;
        unsigned num_threads = 0;
        int c;
@@ -3872,10 +3672,7 @@ imagex_update(int argc, tchar **argv, int cmd)
        struct wimlib_update_command *cmds;
        size_t num_cmds;
        tchar *command_str = NULL;
-
-       const tchar *config_file = NULL;
-       tchar *config_str;
-       struct wimlib_capture_config *config;
+       tchar *config_file = NULL;
 
        for_opt(c, update_options) {
                switch (c) {
@@ -3973,25 +3770,6 @@ imagex_update(int argc, tchar **argv, int cmd)
                image = 1;
        }
 
-       /* Parse capture configuration file if specified */
-       if (config_file) {
-               size_t config_len;
-
-               config_str = file_get_text_contents(config_file, &config_len);
-               if (!config_str) {
-                       ret = -1;
-                       goto out_wimlib_free;
-               }
-
-               config = alloca(sizeof(*config));
-               ret = parse_capture_config(&config_str, config_len, config);
-               if (ret)
-                       goto out_free_config;
-       } else {
-               config = NULL;
-               default_add_flags |= WIMLIB_ADD_FLAG_WINCONFIG;
-       }
-
        /* Read update commands from standard input, or the command string if
         * specified.  */
        if (command_str) {
@@ -4006,7 +3784,7 @@ imagex_update(int argc, tchar **argv, int cmd)
                cmd_file_contents = stdin_get_text_contents(&cmd_file_nchars);
                if (!cmd_file_contents) {
                        ret = -1;
-                       goto out_free_config;
+                       goto out_wimlib_free;
                }
 
                /* Parse the update commands */
@@ -4023,7 +3801,7 @@ imagex_update(int argc, tchar **argv, int cmd)
                switch (cmds[i].op) {
                case WIMLIB_UPDATE_OP_ADD:
                        cmds[i].add.add_flags |= default_add_flags;
-                       cmds[i].add.config = config;
+                       cmds[i].add.config_file = config_file;
                        break;
                case WIMLIB_UPDATE_OP_DELETE:
                        cmds[i].delete_.delete_flags |= default_delete_flags;
@@ -4046,12 +3824,6 @@ out_free_cmds:
        free(cmds);
 out_free_cmd_file_contents:
        free(cmd_file_contents);
-out_free_config:
-       if (config) {
-               free(config->exclusion_pats.pats);
-               free(config->exclusion_exception_pats.pats);
-               free(config_str);
-       }
 out_wimlib_free:
        wimlib_free(wim);
 out_free_command_str:
index 1d826fb..c8aeaa6 100644 (file)
@@ -124,7 +124,7 @@ static struct wimlib_update_command *
 capture_sources_to_add_cmds(const struct wimlib_capture_source *sources,
                            size_t num_sources,
                            int add_flags,
-                           const struct wimlib_capture_config *config)
+                           const tchar *config_file)
 {
        struct wimlib_update_command *add_cmds;
 
@@ -139,9 +139,8 @@ capture_sources_to_add_cmds(const struct wimlib_capture_source *sources,
                              sources[i].fs_source_path,
                              sources[i].wim_target_path);
                        add_cmds[i].op = WIMLIB_UPDATE_OP_ADD;
-                       add_cmds[i].add.add_flags = add_flags & ~(WIMLIB_ADD_FLAG_BOOT |
-                                                                 WIMLIB_ADD_FLAG_WIMBOOT);
-                       add_cmds[i].add.config = (struct wimlib_capture_config*)config;
+                       add_cmds[i].add.add_flags = add_flags & ~WIMLIB_ADD_FLAG_BOOT;
+                       add_cmds[i].add.config_file = (tchar *)config_file;
                        add_cmds[i].add.fs_source_path = sources[i].fs_source_path;
                        add_cmds[i].add.wim_target_path = sources[i].wim_target_path;
                }
@@ -155,7 +154,7 @@ wimlib_add_image_multisource(WIMStruct *wim,
                             const struct wimlib_capture_source *sources,
                             size_t num_sources,
                             const tchar *name,
-                            const struct wimlib_capture_config *config,
+                            const tchar *config_file,
                             int add_flags,
                             wimlib_progress_func_t progress_func)
 {
@@ -176,7 +175,7 @@ wimlib_add_image_multisource(WIMStruct *wim,
 
        /* Translate the "capture sources" into generic update commands. */
        add_cmds = capture_sources_to_add_cmds(sources, num_sources,
-                                              add_flags, config);
+                                              add_flags, config_file);
        if (add_cmds == NULL) {
                ret = WIMLIB_ERR_NOMEM;
                goto out_delete_image;
@@ -211,7 +210,7 @@ WIMLIBAPI int
 wimlib_add_image(WIMStruct *wim,
                 const tchar *source,
                 const tchar *name,
-                const struct wimlib_capture_config *config,
+                const tchar *config_file,
                 int add_flags,
                 wimlib_progress_func_t progress_func)
 {
@@ -223,6 +222,6 @@ wimlib_add_image(WIMStruct *wim,
                .reserved = 0,
        };
        return wimlib_add_image_multisource(wim, &capture_src, 1, name,
-                                           config, add_flags,
+                                           config_file, add_flags,
                                            progress_func);
 }
index 4197861..d955c36 100644 (file)
 #include "wimlib/error.h"
 #include "wimlib/lookup_table.h"
 #include "wimlib/paths.h"
+#include "wimlib/textfile.h"
 #include "wimlib/wildcard.h"
 
 #include <string.h>
 
-static int
-canonicalize_pattern(const tchar *pat, tchar **canonical_pat_ret)
+void
+do_capture_progress(struct add_image_params *params, int status,
+                   const struct wim_inode *inode)
 {
-       tchar *canonical_pat;
+       switch (status) {
+       case WIMLIB_SCAN_DENTRY_OK:
+               if (!(params->add_flags & WIMLIB_ADD_FLAG_VERBOSE))
+                       return;
+       case WIMLIB_SCAN_DENTRY_UNSUPPORTED:
+       case WIMLIB_SCAN_DENTRY_EXCLUDED:
+       case WIMLIB_SCAN_DENTRY_EXCLUDED_SYMLINK:
+               if (!(params->add_flags & WIMLIB_ADD_FLAG_EXCLUDE_VERBOSE))
+                       return;
+       }
+       params->progress.scan.status = status;
+       if (status == WIMLIB_SCAN_DENTRY_OK && inode->i_nlink == 1) {
+               const struct wim_lookup_table_entry *lte;
+               for (unsigned i = 0; i <= inode->i_num_ads; i++) {
+                       lte = inode_stream_lte_resolved(inode, i);
+                       if (lte != NULL)
+                               params->progress.scan.num_bytes_scanned += lte->size;
+               }
+               if (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY)
+                       params->progress.scan.num_dirs_scanned++;
+               else
+                       params->progress.scan.num_nondirs_scanned++;
+       }
+       if (params->progress_func) {
+               params->progress_func(WIMLIB_PROGRESS_MSG_SCAN_DENTRY,
+                                     &params->progress);
+       }
+}
 
+static int
+mangle_pat(tchar *pat, const tchar *path, unsigned long line_no)
+{
        if (!is_any_path_separator(pat[0]) &&
            pat[0] != T('\0') && pat[1] == T(':'))
        {
@@ -49,103 +81,66 @@ canonicalize_pattern(const tchar *pat, tchar **canonical_pat_ret)
                         * relative to the current working directory on the c:
                         * drive.  We require paths with drive letters to be
                         * absolute. */
-                       ERROR("Invalid path \"%"TS"\"; paths including drive letters "
-                             "must be absolute!", pat);
-                       ERROR("Maybe try \"%"TC":\\%"TS"\"?",
-                             pat[0], pat + 2);
+                       ERROR("%"TS":%lu: Invalid path \"%"TS"\"; paths including "
+                             "drive letters must be absolute!\n"
+                             "        Maybe try \"%"TC":\\%"TS"\"?",
+                             path, line_no, pat, pat[0], &pat[2]);
                        return WIMLIB_ERR_INVALID_CAPTURE_CONFIG;
                }
 
-               WARNING("Pattern \"%"TS"\" starts with a drive letter, which is "
-                       "being removed.", pat);
-               /* Strip the drive letter */
-               pat += 2;
+               WARNING("%"TS":%lu: Pattern \"%"TS"\" starts with a drive "
+                       "letter, which is being removed.",
+                       path, line_no, pat);
+
+               /* Strip the drive letter.  */
+               tmemmove(pat, pat + 2, tstrlen(pat + 2) + 1);
        }
-       canonical_pat = canonicalize_fs_path(pat);
-       if (!canonical_pat)
-               return WIMLIB_ERR_NOMEM;
 
        /* Translate all possible path separators into the operating system's
-        * preferred path separator. */
-       for (tchar *p = canonical_pat; *p; p++)
+        * preferred path separator.  */
+       for (tchar *p = pat; *p; p++)
                if (is_any_path_separator(*p))
                        *p = OS_PREFERRED_PATH_SEPARATOR;
-       *canonical_pat_ret = canonical_pat;
        return 0;
 }
 
-static int
-copy_and_canonicalize_pattern_list(const struct wimlib_pattern_list *list,
-                                  struct wimlib_pattern_list *copy)
-{
-       int ret = 0;
-
-       copy->pats = CALLOC(list->num_pats, sizeof(list->pats[0]));
-       if (!copy->pats)
-               return WIMLIB_ERR_NOMEM;
-       copy->num_pats = list->num_pats;
-       for (size_t i = 0; i < list->num_pats; i++) {
-               ret = canonicalize_pattern(list->pats[i], &copy->pats[i]);
-               if (ret)
-                       break;
-       }
-       return ret;
-}
-
 int
-copy_and_canonicalize_capture_config(const struct wimlib_capture_config *config,
-                                    struct wimlib_capture_config **config_copy_ret)
+do_read_capture_config_file(const tchar *config_file, tchar *buf, size_t buflen,
+                           struct capture_config *config)
 {
-       struct wimlib_capture_config *config_copy;
        int ret;
-
-       config_copy = CALLOC(1, sizeof(struct wimlib_capture_config));
-       if (!config_copy) {
-               ret = WIMLIB_ERR_NOMEM;
-               goto out_free_capture_config;
-       }
-       ret = copy_and_canonicalize_pattern_list(&config->exclusion_pats,
-                                                &config_copy->exclusion_pats);
-       if (ret)
-               goto out_free_capture_config;
-       ret = copy_and_canonicalize_pattern_list(&config->exclusion_exception_pats,
-                                                &config_copy->exclusion_exception_pats);
+       struct text_file_section sections[] = {
+               {T("ExclusionList"),
+                       &config->exclusion_pats},
+               {T("ExclusionException"),
+                       &config->exclusion_exception_pats},
+       };
+
+       ret = do_load_text_file(config_file, buf, buflen, &buf,
+                               sections, ARRAY_LEN(sections), mangle_pat);
        if (ret)
-               goto out_free_capture_config;
-       *config_copy_ret = config_copy;
-       goto out;
-out_free_capture_config:
-       free_capture_config(config_copy);
-out:
-       return ret;
-}
+               return ret;
 
-static void
-destroy_pattern_list(struct wimlib_pattern_list *list)
-{
-       for (size_t i = 0; i < list->num_pats; i++)
-               FREE(list->pats[i]);
-       FREE(list->pats);
+       config->buf = buf;
+       return 0;
 }
 
 void
-free_capture_config(struct wimlib_capture_config *config)
+destroy_capture_config(struct capture_config *config)
 {
-       if (config) {
-               destroy_pattern_list(&config->exclusion_pats);
-               destroy_pattern_list(&config->exclusion_exception_pats);
-               FREE(config);
-       }
+       FREE(config->exclusion_pats.strings);
+       FREE(config->exclusion_exception_pats.strings);
+       FREE(config->buf);
 }
 
 static bool
 match_pattern(const tchar *path,
              const tchar *path_basename,
-             const struct wimlib_pattern_list *list)
+             const struct string_set *list)
 {
-       for (size_t i = 0; i < list->num_pats; i++) {
+       for (size_t i = 0; i < list->num_strings; i++) {
 
-               const tchar *pat = list->pats[i];
+               const tchar *pat = list->strings[i];
                const tchar *string;
 
                if (*pat == OS_PREFERRED_PATH_SEPARATOR) {
@@ -176,39 +171,6 @@ match_pattern(const tchar *path,
        return false;
 }
 
-void
-do_capture_progress(struct add_image_params *params, int status,
-                   const struct wim_inode *inode)
-{
-       switch (status) {
-       case WIMLIB_SCAN_DENTRY_OK:
-               if (!(params->add_flags & WIMLIB_ADD_FLAG_VERBOSE))
-                       return;
-       case WIMLIB_SCAN_DENTRY_UNSUPPORTED:
-       case WIMLIB_SCAN_DENTRY_EXCLUDED:
-       case WIMLIB_SCAN_DENTRY_EXCLUDED_SYMLINK:
-               if (!(params->add_flags & WIMLIB_ADD_FLAG_EXCLUDE_VERBOSE))
-                       return;
-       }
-       params->progress.scan.status = status;
-       if (status == WIMLIB_SCAN_DENTRY_OK && inode->i_nlink == 1) {
-               const struct wim_lookup_table_entry *lte;
-               for (unsigned i = 0; i <= inode->i_num_ads; i++) {
-                       lte = inode_stream_lte_resolved(inode, i);
-                       if (lte != NULL)
-                               params->progress.scan.num_bytes_scanned += lte->size;
-               }
-               if (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY)
-                       params->progress.scan.num_dirs_scanned++;
-               else
-                       params->progress.scan.num_nondirs_scanned++;
-       }
-       if (params->progress_func) {
-               params->progress_func(WIMLIB_PROGRESS_MSG_SCAN_DENTRY,
-                                     &params->progress);
-       }
-}
-
 /* Return true if the image capture configuration file indicates we should
  * exclude the filename @path from capture.
  *
@@ -220,17 +182,17 @@ do_capture_progress(struct add_image_params *params, int status,
  */
 bool
 exclude_path(const tchar *path, size_t path_len,
-            const struct wimlib_capture_config *config, bool exclude_prefix)
+            const struct capture_config *config, bool exclude_prefix)
 {
        if (!config)
                return false;
        const tchar *basename = path_basename_with_len(path, path_len);
        if (exclude_prefix) {
-               wimlib_assert(path_len >= config->_prefix_num_tchars);
-               if (!tmemcmp(config->_prefix, path, config->_prefix_num_tchars) &&
-                   path[config->_prefix_num_tchars] == OS_PREFERRED_PATH_SEPARATOR)
+               wimlib_assert(path_len >= config->prefix_num_tchars);
+               if (!tmemcmp(config->prefix, path, config->prefix_num_tchars) &&
+                   path[config->prefix_num_tchars] == OS_PREFERRED_PATH_SEPARATOR)
                {
-                       path += config->_prefix_num_tchars;
+                       path += config->prefix_num_tchars;
                }
        }
        return match_pattern(path, basename, &config->exclusion_pats) &&
index e14c187..c366ed7 100644 (file)
 #  include "config.h"
 #endif
 
-#include "wimlib/encoding.h"
-#include "wimlib/error.h"
-#include "wimlib/file_io.h"
 #include "wimlib/pathlist.h"
-#include "wimlib/util.h"
-
-#include <ctype.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-static int
-read_file_contents(const tchar *path, char **buf_ret, size_t *bufsize_ret)
-{
-       int raw_fd;
-       struct filedes fd;
-       struct stat st;
-       void *buf;
-       int ret;
-       int errno_save;
-
-       raw_fd = topen(path, O_RDONLY | O_BINARY);
-       if (raw_fd < 0) {
-               ERROR_WITH_ERRNO("Can't open \"%"TS"\"", path);
-               return WIMLIB_ERR_OPEN;
-       }
-       if (fstat(raw_fd, &st)) {
-               ERROR_WITH_ERRNO("Can't stat \"%"TS"\"", path);
-               close(raw_fd);
-               return WIMLIB_ERR_STAT;
-       }
-       if ((size_t)st.st_size != st.st_size ||
-           (buf = MALLOC(st.st_size)) == NULL)
-       {
-               close(raw_fd);
-               ERROR("Not enough memory to read \"%"TS"\"", path);
-               return WIMLIB_ERR_NOMEM;
-       }
-
-       filedes_init(&fd, raw_fd);
-       ret = full_read(&fd, buf, st.st_size);
-       errno_save = errno;
-       filedes_close(&fd);
-       errno = errno_save;
-       if (ret) {
-               ERROR_WITH_ERRNO("Error reading \"%"TS"\"", path);
-               FREE(buf);
-               return ret;
-       }
-
-       *buf_ret = buf;
-       *bufsize_ret = st.st_size;
-       return 0;
-}
-
-static int
-read_utf8_file_contents(const tchar *path, tchar **buf_ret, size_t *buflen_ret)
-{
-       int ret;
-       char *buf_utf8;
-       size_t bufsize_utf8;
-       size_t offset_utf8;
-       tchar *buf_tstr;
-       size_t bufsize_tstr;
-
-       ret = read_file_contents(path, &buf_utf8, &bufsize_utf8);
-       if (ret)
-               return ret;
-
-       /* Ignore UTF-8 BOM.  */
-       if (bufsize_utf8 >= 3 && (u8)buf_utf8[0] == 0xef &&
-           (u8)buf_utf8[1] == 0xbb && (u8)buf_utf8[2] == 0xbf)
-               offset_utf8 = 3;
-       else
-               offset_utf8 = 0;
-
-       ret = utf8_to_tstr(buf_utf8 + offset_utf8, bufsize_utf8 - offset_utf8,
-                          &buf_tstr, &bufsize_tstr);
-       FREE(buf_utf8);
-       if (ret)
-               return ret;
-
-       *buf_ret = buf_tstr;
-       *buflen_ret = bufsize_tstr / sizeof(tchar);
-       return 0;
-}
-
-static int
-parse_path_list_file(tchar *buf, size_t buflen,
-                    tchar ***paths_ret, size_t *num_paths_ret)
-{
-       tchar **paths = NULL;
-       size_t num_paths = 0;
-       size_t num_alloc_paths = 0;
-       tchar *nl;
-       tchar *p;
-
-       for (p = buf; p != buf + buflen; p = nl + 1) {
-               tchar *line_begin, *line_end;
-               size_t line_len;
-
-               nl = tmemchr(p, T('\n'), buf + buflen - p);
-               if (nl == NULL)
-                       break;
-
-               line_begin = p;
-               line_end = nl;
-
-               /* Ignore leading whitespace.  */
-               while (line_begin < nl && istspace(*line_begin))
-                       line_begin++;
-
-               /* Ignore trailing whitespace.  */
-               while (line_end > line_begin && istspace(*(line_end - 1)))
-                       line_end--;
-
-               line_len = line_end - line_begin;
-
-               /* Ignore comments and empty lines.  */
-               if (line_len == 0 || *line_begin == T(';'))
-                       continue;
-
-               if (num_paths == num_alloc_paths) {
-                       tchar **new_paths;
-                       size_t new_num_alloc_paths = max(num_alloc_paths + 8,
-                                                        num_alloc_paths * 3 / 2);
-
-                       new_paths = REALLOC(paths, new_num_alloc_paths *
-                                                  sizeof(paths[0]));
-                       if (new_paths == NULL)
-                               goto oom;
-                       paths = new_paths;
-                       num_alloc_paths = new_num_alloc_paths;
-               }
-
-               *line_end = T('\0');
-               paths[num_paths++] = line_begin;
-       }
-
-       *paths_ret = paths;
-       *num_paths_ret = num_paths;
-       return 0;
-
-oom:
-       FREE(paths);
-       return WIMLIB_ERR_NOMEM;
-}
+#include "wimlib/textfile.h"
 
 int
 read_path_list_file(const tchar *listfile,
                    tchar ***paths_ret, size_t *num_paths_ret,
                    void **mem_ret)
 {
-       int ret;
+       STRING_SET(paths);
+       struct text_file_section tmp = {
+               .name = T(""),
+               .strings = &paths,
+       };
        tchar *buf;
-       size_t buflen;
+       int ret;
 
-       ret = read_utf8_file_contents(listfile, &buf, &buflen);
+       ret = load_text_file(listfile, &buf, &tmp, 1, NULL);
        if (ret)
                return ret;
 
-       buf[buflen++] = T('\n');
-
-       ret = parse_path_list_file(buf, buflen, paths_ret, num_paths_ret);
-       if (ret) {
-               FREE(buf);
-               return ret;
-       }
-       *mem_ret = buf;
+       *paths_ret = paths.strings;
+       *num_paths_ret = paths.num_strings;
+       *mem_ret = (void *)buf;
        return 0;
 }
diff --git a/src/textfile.c b/src/textfile.c
new file mode 100644 (file)
index 0000000..8e1bb6c
--- /dev/null
@@ -0,0 +1,345 @@
+/*
+ * textfile.c
+ */
+
+/*
+ * Copyright (C) 2014 Eric Biggers
+ *
+ * This file is part of wimlib, a library for working with WIM files.
+ *
+ * wimlib is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * wimlib is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ * A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with wimlib; if not, see http://www.gnu.org/licenses/.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include "wimlib/assert.h"
+#include "wimlib/encoding.h"
+#include "wimlib/error.h"
+#include "wimlib/file_io.h"
+#include "wimlib/textfile.h"
+#include "wimlib/util.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+static int
+read_file_contents(const tchar *path, u8 **buf_ret, size_t *bufsize_ret)
+{
+       int raw_fd;
+       struct filedes fd;
+       struct stat st;
+       u8 *buf;
+       int ret;
+       int errno_save;
+
+       if (!path || !*path)
+               return WIMLIB_ERR_INVALID_PARAM;
+
+       raw_fd = topen(path, O_RDONLY | O_BINARY);
+       if (raw_fd < 0) {
+               ERROR_WITH_ERRNO("Can't open \"%"TS"\"", path);
+               return WIMLIB_ERR_OPEN;
+       }
+       if (fstat(raw_fd, &st)) {
+               ERROR_WITH_ERRNO("Can't stat \"%"TS"\"", path);
+               close(raw_fd);
+               return WIMLIB_ERR_STAT;
+       }
+       if ((size_t)st.st_size != st.st_size ||
+           (buf = MALLOC(st.st_size)) == NULL)
+       {
+               close(raw_fd);
+               ERROR("Not enough memory to read \"%"TS"\"", path);
+               return WIMLIB_ERR_NOMEM;
+       }
+
+       filedes_init(&fd, raw_fd);
+       ret = full_read(&fd, buf, st.st_size);
+       errno_save = errno;
+       filedes_close(&fd);
+       errno = errno_save;
+       if (ret) {
+               ERROR_WITH_ERRNO("Error reading \"%"TS"\"", path);
+               FREE(buf);
+               return ret;
+       }
+
+       *buf_ret = buf;
+       *bufsize_ret = st.st_size;
+       return 0;
+}
+
+static int
+read_text_file_contents(const tchar *path,
+                       tchar **buf_ret, size_t *buflen_ret)
+{
+       int ret;
+       u8 *buf_raw;
+       size_t bufsize_raw;
+       size_t offset_raw;
+       bool utf8;
+       tchar *buf_tstr;
+       size_t bufsize_tstr;
+
+       ret = read_file_contents(path, &buf_raw, &bufsize_raw);
+       if (ret)
+               return ret;
+
+       /* Guess the encoding: UTF-8 or UTF-16LE.  (Something weirder and you're
+        * out of luck, sorry...)  */
+       if (bufsize_raw >= 2 &&
+           buf_raw[0] == 0xFF &&
+           buf_raw[1] == 0xFE)
+       {
+               utf8 = false;
+               offset_raw = 2;
+       }
+       else if (bufsize_raw >= 2 &&
+                buf_raw[0] <= 0x7F &&
+                buf_raw[1] == 0x00)
+       {
+               utf8 = false;
+               offset_raw = 0;
+       }
+       else if (bufsize_raw >= 3 &&
+                buf_raw[0] == 0xEF &&
+                buf_raw[1] == 0xBB &&
+                buf_raw[2] == 0xBF)
+       {
+               utf8 = true;
+               offset_raw = 3;
+       }
+       else
+       {
+               utf8 = true;
+               offset_raw = 0;
+       }
+
+       if (utf8) {
+               ret = utf8_to_tstr((const char *)(buf_raw + offset_raw),
+                                  bufsize_raw - offset_raw,
+                                  &buf_tstr, &bufsize_tstr);
+       } else {
+       #if TCHAR_IS_UTF16LE
+               bufsize_tstr = bufsize_raw - offset_raw;
+               buf_tstr = MALLOC(bufsize_tstr + 2);
+               if (buf_tstr) {
+                       memcpy(buf_tstr, buf_raw + offset_raw, bufsize_tstr);
+                       ((u8*)buf_tstr)[bufsize_tstr + 0] = 0;
+                       ((u8*)buf_tstr)[bufsize_tstr + 1] = 0;
+               } else {
+                       ret = WIMLIB_ERR_NOMEM;
+               }
+       #else
+               ret = utf16le_to_tstr((const utf16lechar *)(buf_raw + offset_raw),
+                                     bufsize_raw - offset_raw,
+                                     &buf_tstr, &bufsize_tstr);
+       #endif
+       }
+       FREE(buf_raw);
+       if (ret)
+               return ret;
+
+       *buf_ret = buf_tstr;
+       *buflen_ret = bufsize_tstr / sizeof(tchar);
+       return 0;
+}
+
+static int
+string_set_append(struct string_set *set, tchar *str)
+{
+       size_t num_alloc_strings = set->num_alloc_strings;
+
+       if (set->num_strings == num_alloc_strings) {
+               tchar **new_strings;
+
+               num_alloc_strings = max(num_alloc_strings * 3 / 2,
+                                       num_alloc_strings + 4);
+               new_strings = REALLOC(set->strings,
+                                     sizeof(set->strings[0]) * num_alloc_strings);
+               if (!new_strings)
+                       return WIMLIB_ERR_NOMEM;
+               set->strings = new_strings;
+               set->num_alloc_strings = num_alloc_strings;
+       }
+       set->strings[set->num_strings++] = str;
+       return 0;
+}
+
+#define NOT_IN_SECTION         -1
+#define IN_UNKNOWN_SECTION     -2
+
+static int
+parse_text_file(const tchar *path, tchar *buf, size_t buflen,
+               const struct text_file_section *pos_sections,
+               int num_pos_sections, line_mangle_t mangle_line)
+{
+       int current_section = NOT_IN_SECTION;
+       bool have_named_sections = false;
+       tchar *p;
+       tchar *nl;
+       unsigned long line_no = 1;
+
+       for (int i = 0; i < num_pos_sections; i++) {
+               if (*pos_sections[i].name)
+                       have_named_sections = true;
+               else
+                       current_section = i;
+       }
+
+       for (p = buf; p != buf + buflen; p = nl + 1, line_no++) {
+               tchar *line_begin, *line_end;
+               size_t line_len;
+               int ret;
+
+               nl = tmemchr(p, T('\n'), buf + buflen - p);
+               if (!nl)
+                       break;
+
+               line_begin = p;
+               line_end = nl;
+
+               /* Ignore leading whitespace.  */
+               while (line_begin < nl && istspace(*line_begin))
+                       line_begin++;
+
+               /* Ignore trailing whitespace.  */
+               while (line_end > line_begin && istspace(*(line_end - 1)))
+                       line_end--;
+
+               line_len = line_end - line_begin;
+
+               /* Ignore comments and empty lines.  */
+               if (line_len == 0 || *line_begin == T(';') || *line_begin == T('#'))
+                       continue;
+
+               line_begin[line_len] = T('\0');
+
+               /* Check for beginning of new section.  */
+               if (line_begin[0] == T('[') &&
+                   line_begin[line_len - 1] == T(']') &&
+                   have_named_sections)
+               {
+                       line_begin[line_len - 1] = T('\0');
+                       current_section = IN_UNKNOWN_SECTION;
+                       for (int i = 0; i < num_pos_sections; i++) {
+                               if (!tstrcmp(line_begin + 1,
+                                            pos_sections[i].name))
+                               {
+                                       current_section = i;
+                                       break;
+                               }
+                       }
+                       line_begin[line_len - 1] = T(']');
+                       if (current_section < 0)
+                               WARNING("%"TS":%lu: Unrecognized section \"%"TS"\"",
+                                       path, line_no, line_begin);
+                       continue;
+               }
+
+               if (current_section < 0) {
+                       if (current_section == NOT_IN_SECTION)
+                               WARNING("%"TS":%lu: Not in a bracketed section!",
+                                       path, line_no);
+                       continue;
+               }
+
+               if (mangle_line) {
+                       ret = (*mangle_line)(line_begin, path, line_no);
+                       if (ret)
+                               return ret;
+               }
+
+               ret = string_set_append(pos_sections[current_section].strings,
+                                       line_begin);
+               if (ret)
+                       return ret;
+       }
+       return 0;
+}
+
+/**
+ * do_load_text_file -
+ *
+ * Read and parse lines from a text file from an on-disk file or a buffer.
+ * The file may contain sections, like in an INI file.
+ *
+ * @path
+ *     Path to the file on disk to read, or a dummy name for the buffer.
+ * @buf
+ *     If NULL, the data will be read from the @path file.  Otherwise the data
+ *     will be read from this buffer, which must be newline-terminated.
+ * @buflen
+ *     Length of buffer in 'tchars'; ignored if @buf is NULL.
+ * @buf_ret
+ *     On success, a pointer to a buffer backing the parsed lines is stored
+ *     here.  If @buf is not NULL, this will be @buf.  Otherwise, this will be
+ *     an allocated buffer that must be freed when finished with the lines.
+ * @pos_sections
+ *     Specifications of allowed sections in the file.  Each such specification
+ *     consists of the name of the section (e.g. [ExclusionList], like in the
+ *     INI file format), along with a pointer to the list of lines parsed for
+ *     that section.  Use an empty name to indicate the destination of lines
+ *     not in any section.
+ * @num_pos_sections
+ *     Length of @pos_sections array.
+ * @mangle_line
+ *     Optional callback to modify each line being read.
+ *
+ * Returns 0 on success or a positive error code on failure.
+ *
+ * Unknown sections are ignored (warning printed).
+ */
+int
+do_load_text_file(const tchar *path,
+                 tchar *buf, size_t buflen,
+                 tchar **buf_ret,
+                 const struct text_file_section *pos_sections,
+                 int num_pos_sections,
+                 line_mangle_t mangle_line)
+{
+       int ret;
+       bool pathmode = (buf == NULL);
+
+       if (pathmode) {
+               ret = read_text_file_contents(path, &buf, &buflen);
+               if (ret)
+                       return ret;
+
+               /* Overwrite '\0' with '\n' to avoid special case of last line
+                * not terminated with '\n'.  */
+               buf[buflen++] = T('\n');
+       } else {
+               wimlib_assert(buflen > 0 && buf[buflen - 1] == T('\n'));
+       }
+
+       ret = parse_text_file(path, buf, buflen, pos_sections,
+                             num_pos_sections, mangle_line);
+       if (ret) {
+               for (int i = 0; i < num_pos_sections; i++)
+                       FREE(pos_sections[i].strings->strings);
+               if (pathmode)
+                       FREE(buf);
+               return ret;
+       }
+
+       *buf_ret = buf;
+       return 0;
+}
index 9e6262c..82aac7f 100644 (file)
@@ -3,7 +3,7 @@
  */
 
 /*
- * Copyright (C) 2013 Eric Biggers
+ * Copyright (C) 2013, 2014 Eric Biggers
  *
  * This file is part of wimlib, a library for working with WIM files.
  *
@@ -37,6 +37,7 @@
 #include "wimlib/xml.h"
 
 #include <errno.h>
+#include <sys/stat.h>
 
 /* Overlays @branch onto @target, both of which must be directories. */
 static int
@@ -164,6 +165,82 @@ attach_branch(struct wim_dentry **root_p, struct wim_dentry *branch,
        }
 }
 
+const tchar wincfg[] =
+T(
+"[ExclusionList]\n"
+"/$ntfs.log\n"
+"/hiberfil.sys\n"
+"/pagefile.sys\n"
+"/System Volume Information\n"
+"/RECYCLER\n"
+"/Windows/CSC\n"
+);
+
+static int
+get_capture_config(const tchar *config_file, struct capture_config *config,
+                  int add_flags, const tchar *fs_source_path)
+{
+       int ret;
+       tchar *tmp_config_file = NULL;
+
+       memset(config, 0, sizeof(*config));
+
+       /* For WIMBoot capture, check for default capture configuration file
+        * unless one was explicitly specified.  */
+       if (!config_file && (add_flags & WIMLIB_ADD_FLAG_WIMBOOT)) {
+
+               /* XXX: Handle loading file correctly when in NTFS volume.  */
+
+               const tchar *wimboot_cfgfile =
+                       T("/Windows/System32/WimBootCompress.ini");
+               size_t len = tstrlen(fs_source_path) +
+                            tstrlen(wimboot_cfgfile);
+               tmp_config_file = MALLOC((len + 1) * sizeof(tchar));
+               struct stat st;
+
+               tsprintf(tmp_config_file, T("%"TS"%"TS),
+                        fs_source_path, wimboot_cfgfile);
+               if (!tstat(tmp_config_file, &st)) {
+                       config_file = tmp_config_file;
+                       add_flags &= ~WIMLIB_ADD_FLAG_WINCONFIG;
+               } else {
+                       WARNING("\"%"TS"\" does not exist.\n"
+                               "          Using default capture configuration!",
+                               tmp_config_file);
+               }
+       }
+
+       if (add_flags & WIMLIB_ADD_FLAG_WINCONFIG) {
+
+               /* Use Windows default.  */
+
+               tchar *wincfg_copy;
+               const size_t wincfg_len = ARRAY_LEN(wincfg) - 1;
+
+               if (config_file)
+                       return WIMLIB_ERR_INVALID_PARAM;
+
+               wincfg_copy = memdup(wincfg, wincfg_len * sizeof(wincfg[0]));
+               if (!wincfg_copy)
+                       return WIMLIB_ERR_NOMEM;
+
+               ret = do_read_capture_config_file(T("wincfg"), wincfg_copy,
+                                                 wincfg_len, config);
+               if (ret)
+                       FREE(wincfg_copy);
+       } else if (config_file) {
+               /* Use the specified configuration file.  */
+               ret = do_read_capture_config_file(config_file, NULL, 0, config);
+       } else {
+               /* ... Or don't use any configuration file at all.  No files
+                * will be excluded from capture, all files will be compressed,
+                * etc.  */
+               ret = 0;
+       }
+       FREE(tmp_config_file);
+       return ret;
+}
+
 static int
 execute_add_command(WIMStruct *wim,
                    const struct wimlib_update_command *add_cmd,
@@ -179,7 +256,8 @@ execute_add_command(WIMStruct *wim,
        int (*capture_tree)(struct wim_dentry **,
                            const tchar *,
                            struct add_image_params *);
-       struct wimlib_capture_config *config;
+       const tchar *config_file;
+       struct capture_config config;
 #ifdef WITH_NTFS_3G
        struct _ntfs_volume *ntfs_vol = NULL;
 #endif
@@ -192,7 +270,8 @@ execute_add_command(WIMStruct *wim,
        add_flags = add_cmd->add.add_flags;
        fs_source_path = add_cmd->add.fs_source_path;
        wim_target_path = add_cmd->add.wim_target_path;
-       config = add_cmd->add.config;
+       config_file = add_cmd->add.config_file;
+
        DEBUG("fs_source_path=\"%"TS"\", wim_target_path=\"%"TS"\", add_flags=%#x",
              fs_source_path, wim_target_path, add_flags);
 
@@ -222,10 +301,14 @@ execute_add_command(WIMStruct *wim,
                extra_arg = NULL;
        }
 
+       ret = get_capture_config(config_file, &config,
+                                add_flags, fs_source_path);
+       if (ret)
+               goto out;
 
        ret = init_inode_table(&params.inode_table, 9001);
        if (ret)
-               goto out;
+               goto out_destroy_config;
 
        ret = init_sd_set(&params.sd_set, imd->security_data);
        if (ret)
@@ -234,7 +317,7 @@ execute_add_command(WIMStruct *wim,
        INIT_LIST_HEAD(&unhashed_streams);
        params.lookup_table = wim->lookup_table;
        params.unhashed_streams = &unhashed_streams;
-       params.config = config;
+       params.config = &config;
        params.add_flags = add_flags;
        params.extra_arg = extra_arg;
 
@@ -243,10 +326,9 @@ execute_add_command(WIMStruct *wim,
        params.progress.scan.wim_target_path = wim_target_path;
        if (progress_func)
                progress_func(WIMLIB_PROGRESS_MSG_SCAN_BEGIN, &params.progress);
-       if (config) {
-               config->_prefix = fs_source_path;
-               config->_prefix_num_tchars = tstrlen(fs_source_path);
-       }
+
+       config.prefix = fs_source_path;
+       config.prefix_num_tchars = tstrlen(fs_source_path);
 
        if (wim_target_path[0] == T('\0'))
                params.add_flags |= WIMLIB_ADD_FLAG_ROOT;
@@ -291,6 +373,8 @@ out_destroy_sd_set:
        destroy_sd_set(&params.sd_set, rollback_sd);
 out_destroy_inode_table:
        destroy_inode_table(&params.inode_table);
+out_destroy_config:
+       destroy_capture_config(&config);
 out:
        return ret;
 }
@@ -440,22 +524,6 @@ execute_update_commands(WIMStruct *wim,
 }
 
 
-tchar *winpats[] = {
-       T("/$ntfs.log"),
-       T("/hiberfil.sys"),
-       T("/pagefile.sys"),
-       T("/System Volume Information"),
-       T("/RECYCLER"),
-       T("/Windows/CSC"),
-};
-
-static const struct wimlib_capture_config winconfig = {
-       .exclusion_pats = {
-               .num_pats = ARRAY_LEN(winpats),
-               .pats = winpats,
-       },
-};
-
 static int
 check_add_command(struct wimlib_update_command *cmd,
                  const struct wim_header *hdr)
@@ -465,10 +533,9 @@ check_add_command(struct wimlib_update_command *cmd,
        if (add_flags & ~(WIMLIB_ADD_FLAG_NTFS |
                          WIMLIB_ADD_FLAG_DEREFERENCE |
                          WIMLIB_ADD_FLAG_VERBOSE |
-                         /* BOOT doesn't make sense for wimlib_update_image().
-                          * Same with WIMBOOT.  */
+                         /* BOOT doesn't make sense for wimlib_update_image().  */
                          /*WIMLIB_ADD_FLAG_BOOT |*/
-                         /*WIMLIB_ADD_FLAG_WIMBOOT |*/
+                         WIMLIB_ADD_FLAG_WIMBOOT |
                          WIMLIB_ADD_FLAG_UNIX_DATA |
                          WIMLIB_ADD_FLAG_NO_ACLS |
                          WIMLIB_ADD_FLAG_STRICT_ACLS |
@@ -603,7 +670,7 @@ free_update_commands(struct wimlib_update_command *cmds, size_t num_cmds)
                        case WIMLIB_UPDATE_OP_ADD:
                                FREE(cmds[i].add.fs_source_path);
                                FREE(cmds[i].add.wim_target_path);
-                               free_capture_config(cmds[i].add.config);
+                               FREE(cmds[i].add.config_file);
                                break;
                        case WIMLIB_UPDATE_OP_DELETE:
                                FREE(cmds[i].delete_.wim_path);
@@ -625,7 +692,6 @@ copy_update_commands(const struct wimlib_update_command *cmds,
 {
        int ret;
        struct wimlib_update_command *cmds_copy;
-       const struct wimlib_capture_config *config;
 
        cmds_copy = CALLOC(num_cmds, sizeof(cmds[0]));
        if (!cmds_copy)
@@ -642,14 +708,10 @@ copy_update_commands(const struct wimlib_update_command *cmds,
                        if (!cmds_copy[i].add.fs_source_path ||
                            !cmds_copy[i].add.wim_target_path)
                                goto oom;
-                       config = cmds[i].add.config;
-                       if (cmds[i].add.add_flags & WIMLIB_ADD_FLAG_WINCONFIG)
-                               config = &winconfig;
-                       if (config) {
-                               ret = copy_and_canonicalize_capture_config(config,
-                                                                          &cmds_copy[i].add.config);
-                               if (ret)
-                                       goto err;
+                       if (cmds[i].add.config_file) {
+                               cmds_copy[i].add.config_file = TSTRDUP(cmds[i].add.config_file);
+                               if (!cmds_copy[i].add.config_file)
+                                       goto oom;
                        }
                        cmds_copy[i].add.add_flags = cmds[i].add.add_flags;
                        break;
index beb4eab..2d3ab08 100644 (file)
  * along with wimlib; if not, see http://www.gnu.org/licenses/.
  */
 
-#ifndef __WIN32__
-#  error "This file contains Windows code!"
-#endif
 
 #ifdef HAVE_CONFIG_H
 #  include "config.h"
 #endif
 
+#ifdef __WIN32__
+
 #include "wimlib/win32_common.h"
 #include "wimlib/win32.h"
 #include "wimlib/assert.h"
@@ -293,3 +292,5 @@ out_close_handle:
 out:
        return ret;
 }
+
+#endif /* __WIN32__ */
index cf5c65c..6f748a3 100644 (file)
@@ -1424,9 +1424,9 @@ win32_build_dentry_tree(struct wim_dentry **root_ret,
        /* Update pattern prefix.  */
        if (params->config != NULL)
        {
-               params->config->_prefix = TSTRDUP(path);
-               params->config->_prefix_num_tchars = path_nchars;
-               if (params->config->_prefix == NULL)
+               params->config->prefix = TSTRDUP(path);
+               params->config->prefix_num_tchars = path_nchars;
+               if (params->config->prefix == NULL)
                {
                        ret = WIMLIB_ERR_NOMEM;
                        goto out_free_path;
@@ -1438,7 +1438,7 @@ win32_build_dentry_tree(struct wim_dentry **root_ret,
                                                path_nchars, params,
                                                &state, vol_flags);
        if (params->config != NULL)
-               FREE(params->config->_prefix);
+               FREE(params->config->prefix);
 out_free_path:
        FREE(path);
        if (ret == 0)