]> wimlib.net Git - wimlib/commitdiff
Allow 'wimextract' to use wildcard paths on command line
authorEric Biggers <ebiggers3@gmail.com>
Thu, 16 Jan 2014 07:45:57 +0000 (01:45 -0600)
committerEric Biggers <ebiggers3@gmail.com>
Thu, 16 Jan 2014 07:48:23 +0000 (01:48 -0600)
And a couple more changes, described in the NEWS.

NEWS
doc/imagex-extract.1.in
include/wimlib.h
programs/imagex-win32.c
programs/imagex.c
src/extract.c
tests/test-imagex-update_and_extract

diff --git a/NEWS b/NEWS
index 1fba3b55580bb72ae5416250737846b4ddd6bb98..23c3990354019279086e32a89ba692e71b2a70e1 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -11,6 +11,20 @@ Version 1.6.1:
        applying it with v1.6.1, decompress it with v1.6.0 then compress it with
        v1.6.1.
 
        applying it with v1.6.1, decompress it with v1.6.0 then compress it with
        v1.6.1.
 
+       wimextract now allows wildcard characters in paths specified on the
+       command line (not just in a list file, as was added in v1.6.0).
+
+       wimextract now allows a command line to contain multiple list files,
+       possibly interspersed with direct paths.
+
+       The '--strict-wildcards' option to wimextract has been removed and
+       replaced with the inverse option '--nullglob' --- that is, the default
+       behavior is now to issue an error when any path (possibly containing
+       wildcard characters) does not match any paths in the WIM image, and this
+       can be changed by specifying '--nullglob'.
+
+       Added '--preserve-dir-structure' option to wimextract.
+
        Fixed more permissions problems when extracting files on Windows.
 
        Memory usage for LZMS and LZX compression has been decreased.
        Fixed more permissions problems when extracting files on Windows.
 
        Memory usage for LZMS and LZX compression has been decreased.
index 9a9e1592032593a150be23fb4cf997fbe182494b..e059b2080b25e843277af4169f3181647efde3d4 100644 (file)
@@ -2,9 +2,7 @@
 .SH NAME
 @IMAGEX_PROGNAME@-extract \- Extract files or directories from a WIM image
 .SH SYNOPSIS
 .SH NAME
 @IMAGEX_PROGNAME@-extract \- Extract files or directories from a WIM image
 .SH SYNOPSIS
-\fB@IMAGEX_PROGNAME@ extract\fR \fIWIMFILE\fR \fIIMAGE\fR [\fIPATH\fR...]  [\fIOPTION\fR...]
-.br
-\fB@IMAGEX_PROGNAME@ extract\fR \fIWIMFILE\fR \fIIMAGE\fR @\fILISTFILE\fR  [\fIOPTION\fR...]
+\fB@IMAGEX_PROGNAME@ extract\fR \fIWIMFILE\fR \fIIMAGE\fR [(\fIPATH\fR | @\fILISTFILE\fR)...]  [\fIOPTION\fR...]
 .SH DESCRIPTION
 \fB@IMAGEX_PROGNAME@ extract\fR extracts one or more files or directory trees
 from the specified \fIIMAGE\fR contained in the Windows Imaging (WIM) file
 .SH DESCRIPTION
 \fB@IMAGEX_PROGNAME@ extract\fR extracts one or more files or directory trees
 from the specified \fIIMAGE\fR contained in the Windows Imaging (WIM) file
@@ -21,50 +19,63 @@ directory trees to extract.  It may be a 1-based index of an image in the WIM or
 the name of an image in the WIM.  Use the \fB@IMAGEX_PROGNAME@ info\fR (1)
 command to show what images a WIM file contains.
 .PP
 the name of an image in the WIM.  Use the \fB@IMAGEX_PROGNAME@ info\fR (1)
 command to show what images a WIM file contains.
 .PP
-Each \fIPATH\fR specifies a file or directory tree within the WIM image to
-extract.  Alternatively, a single \fILISTFILE\fR beginning with the '@'
-character is taken as a file that itself contains a list of files or directory
-trees to extract.  See \fBPATH_SPECIFICATIONS\fR.
+If no additional arguments are given, the entire WIM image is extracted.
+Otherwise, each additional argument is interpreted as a \fIPATH\fR if it does
+not begin with the '@' character, or a \fILISTFILE\fR if it does.  Each
+\fIPATH\fR specifies a file or directory tree within the WIM image to extract,
+whereas each \fILISTFILE\fR specifies a file that itself contains a list of
+paths to extract.  See \fBPATHS AND LISTFILES\fR for more details.
 .PP
 By default, files and directories are extracted to the current directory.  Use
 .PP
 By default, files and directories are extracted to the current directory.  Use
-\fB--dest-dir\fR to choose an alternate target directory.  Alternatively, use
-\fB--to-stdout\fR to extract a file to standard output to pipe into another
+\fB--dest-dir\fR to select a different destination directory.  Alternatively,
+use \fB--to-stdout\fR to extract a file to standard output to pipe into another
 program.
 .PP
 program.
 .PP
+A file or directory extracted from a \fIPATH\fR argument is by default extracted
+directly into the destination directory, whereas a file or directory extracted
+from a \fILISTFILE\fR argument is by default extracted into the destination
+directory in such a way that the archive's directory structure is
+preserved.  Use \fB--preserve-dir-structure\fR to always get the latter
+behavior.
+.PP
 \fB@IMAGEX_PROGNAME@ extract\fR supports extracting files and directory trees
 from stand-alone WIMs as well as split WIMs.  See \fBSPLIT WIMS\fR.
 \fB@IMAGEX_PROGNAME@ extract\fR supports extracting files and directory trees
 from stand-alone WIMs as well as split WIMs.  See \fBSPLIT WIMS\fR.
-.SH PATH SPECIFICATIONS
-Except when a single path is specified and prefixd by the '@' character, each
-\fIPATH\fR specifies a file or directory tree within the WIM image to extract.
-Each such path must be specified as an absolute path starting from the root of
-the WIM image, like those output by the \fB@IMAGEX_PROGNAME@ dir\fR (1) command.
-However, path separators may be either forward or backward slashes, and the
-leading slash is optional; also, on Windows, the paths are treated
-case-insensitively, while on UNIX, paths are treated case-sensitively, except
-when overridden through the \fBWIMLIB_IMAGEX_IGNORE_CASE\fR environmental
-variable, as documented in \fB@IMAGEX_PROGNAME@\fR (1).
-.PP
-If no \fIPATH\fRs are provided, the default behavior is to extract the full
-image, as if the path "/" had been provided.
-.PP
-If a single \fIPATH\fR is provided and is prefixed with the '@' character, it is
-interpreted as the path to a \fILISTFILE\fR which must be a UTF-8 text file that
-contains a list of paths (files or directories) to extract, one per line.  In
-each line, leading and trailing whitespace is ignored, and lines beginning with
-the ';' character and otherwise empty lines are ignored.  Each path must be
-unquoted and must specify a full path in the WIM image, as described above.
-However, unless \fB--no-wildcards\fR is specified, each path in the list file
-may also contain the wildcard characters '?' and '*'.  The '?' character matches
-any character other than a path separator, whereas the '*' character matches
-zero or more non-path-separator characters.  Consequently, a single wildcard
-pattern may expand to multiple actual files or directories.
-.PP
-In the \fILISTFILE\fR mode, by default a wildcard pattern that matches no files
-or directories in the WIM image only produces a warning; use
-\fB--strict-wildcards\fR if you want an error instead.  Also, when using a list
-file, files and directories not located at the root of the WIM image will be
-extracted to a corresponding subdirectory of the destination directory rather
-than directly to the destination directory itself.
+.SH PATHS AND LISTFILES
+Each path, including those on the command line and those in listfiles, must be
+specified as an absolute path starting from the root of the WIM image, like
+those output by the \fB@IMAGEX_PROGNAME@ dir\fR (1) command.  However, path
+separators may be either forward or backward slashes, and the leading slash is
+optional.  In addition, on Windows, the paths are treated case-insensitively,
+whereas on UNIX-like systems, paths are treated case-sensitively, except when
+overridden through the \fBWIMLIB_IMAGEX_IGNORE_CASE\fR environmental variable,
+as documented in \fB@IMAGEX_PROGNAME@\fR (1).
+.PP
+By default, each path may contain the wildcard characters '?' and '*'.  The '?'
+character matches any character other than a path separator, whereas the '*'
+character matches zero or more non-path-separator characters.  Consequently, a
+single wildcard pattern may expand to multiple actual files or directories.
+Use the \fB--no-wildcards\fR option to disable wildcard matching and search for
+each path literally.
+.PP
+Each \fILISTFILE\fR must be a UTF-8 text file that contains a list of paths to
+extract, one per line.  Wildcard characters are allowed by default.
+The following demonstrates an example listfile:
+.PP
+.RS
+.nf
+
+; This is a comment (begins with semicolon)
+/Users
+/Windows/explorer.exe
+/Windows/System32/en-US/*
+
+; Both forward and backslashes are valid.
+; Don't quote paths containing spaces.
+\\Program Files\\A*
+
+; Leading and trailing whitespace is ignored
+    \\Windows\\notepad*
+
 .SH SPLIT WIMS
 You may use \fB@IMAGEX_PROGNAME@ extract\fR to extract files or directory trees
 from a split WIM.  This uses the \fB--refs\fR="\fIGLOB\fR" option in the same
 .SH SPLIT WIMS
 You may use \fB@IMAGEX_PROGNAME@ extract\fR to extract files or directory trees
 from a split WIM.  This uses the \fB--refs\fR="\fIGLOB\fR" option in the same
@@ -82,6 +93,16 @@ See \fBSPLIT_WIMS\fR.  Note: \fIGLOB\fR is listed in quotes because it is
 interpreted by \fB@IMAGEX_PROGNAME@\fR and may need to be quoted to protect
 against shell expansion.
 .TP
 interpreted by \fB@IMAGEX_PROGNAME@\fR and may need to be quoted to protect
 against shell expansion.
 .TP
+\fB--dest-dir\fR=\fIDIR\fR
+Extract the files and directories to the directory \fIDIR\fR instead of to the
+current working directory.
+.TP
+\fB--to-stdout\fR
+Extract the files to standard output instead of to the filesystem.  This can
+only be provided if all the specified \fIPATH\fRs are to regular files (not
+directories or reparse points).  If present, alternate data streams are not
+extracted.
+.TP
 \fB--unix-data\fR
 See the documentation for this option in \fB@IMAGEX_PROGNAME@-apply\fR (1).
 .TP
 \fB--unix-data\fR
 See the documentation for this option in \fB@IMAGEX_PROGNAME@-apply\fR (1).
 .TP
@@ -97,25 +118,19 @@ See the documentation for this option in \fB@IMAGEX_PROGNAME@-apply\fR (1).
 \fB--include-invalid-names\fR
 See the documentation for this option in \fB@IMAGEX_PROGNAME@-apply\fR (1).
 .TP
 \fB--include-invalid-names\fR
 See the documentation for this option in \fB@IMAGEX_PROGNAME@-apply\fR (1).
 .TP
-\fB--to-stdout\fR
-Extract the files to standard output instead of to the filesystem.  This can
-only be provided if all the specified \fIPATH\fRs are to regular files (not
-directories or reparse points).  If present, alternate data streams are not
-extracted.
-.TP
-\fB--dest-dir\fR=\fIDIR\fR
-Extract the files and directories to the directory \fIDIR\fR instead of to the
-current working directory.
-.TP
 \fB--no-wildcards\fR
 \fB--no-wildcards\fR
-Do not interpret wildcard characters in paths in the \fILISTFILE\fR.
+Do not interpret wildcard characters in paths.
+.TP
+\fB--nullglob\fR
+If a wildcard pattern does not match any paths, ignore it instead of failing
+with an error.
 .TP
 .TP
-\fB--strict-wildcards\fR
-Fail if any wildcard patterns in \fILISTFILE\fR do not match any files or
-directories in the WIM image.  The default behavior is to warn only.  This
-option has no effect if \fB--no-wildcards\fR is also specified or if \fIPATH\fRs
-are specified instead of a \fILISTFILE\fR; in those cases, an error is issued if
-any file to extract does not exist.
+\fB--preserve-dir-structure\fR
+When extracting paths, preserve the archive directory structure instead of
+extracting the file or directory tree named by each path directly to the
+destination directory.  Note: \fB--preserve-dir-structure\fR is already the
+default behavior for paths in listfiles (but not paths directly specified on the
+command line).
 .SH NOTES
 See the documentation \fB@IMAGEX_PROGNAME@ apply\fR (1) for documentation about
 what data and metadata are extracted on UNIX-like systems versus on Windows.
 .SH NOTES
 See the documentation \fB@IMAGEX_PROGNAME@ apply\fR (1) for documentation about
 what data and metadata are extracted on UNIX-like systems versus on Windows.
index c2c32a8c164712ed6001e788d280676726e48c41..8e851aeb5f20cd312d5f9d7d064a73b6a98b5fe4 100644 (file)
@@ -1517,6 +1517,12 @@ typedef int (*wimlib_iterate_lookup_table_callback_t)(const struct wimlib_resour
 /** Do not extract Windows file attributes such as readonly, hidden, etc.  */
 #define WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES              0x00100000
 
 /** Do not extract Windows file attributes such as readonly, hidden, etc.  */
 #define WIMLIB_EXTRACT_FLAG_NO_ATTRIBUTES              0x00100000
 
+/** For wimlib_extract_paths() and wimlib_extract_pathlist() only:  Do not
+ * preserve the directory structure of the archive when extracting --- that is,
+ * place each extracted files or directory tree directly in the target
+ * directory.  */
+#define WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE  0x00200000
+
 /** @} */
 /** @ingroup G_mounting_wim_images
  * @{ */
 /** @} */
 /** @ingroup G_mounting_wim_images
  * @{ */
index 0d87bab37c87b7c835e751b68ad601e78b0caac0..c5c40d5f6f65bf34b0bc6023dcd472dcc5b57ee4 100644 (file)
@@ -56,28 +56,6 @@ L"       Maybe try converting your text file to UTF-16LE?\n"
        return NULL;
 }
 
        return NULL;
 }
 
-static inline bool
-is_path_separator(wchar_t c)
-{
-       return c == L'/' || c == L'\\';
-}
-
-/* basename() (modifying, trailing-slash stripping version) for wide-character
- * strings.  Understands both forward and backward slashes.  */
-wchar_t *
-win32_wbasename(wchar_t *path)
-{
-       wchar_t *p = wcschr(path, L'\0');
-
-       p--;
-       while (p >= path && is_path_separator(*p))
-               *p-- = '\0';
-       while (p >= path && !is_path_separator(*p))
-               p--;
-       p++;
-       return p;
-}
-
 /* Set a file descriptor to binary mode.  */
 void set_fd_to_binary_mode(int fd)
 {
 /* Set a file descriptor to binary mode.  */
 void set_fd_to_binary_mode(int fd)
 {
index 6044a93c0c23d9e01a013d89ae88607fb05a4a21..d0127b625747287b6d4e4d193f65c103975bc36b 100644 (file)
 
 #ifdef __WIN32__
 #  include "imagex-win32.h"
 
 #ifdef __WIN32__
 #  include "imagex-win32.h"
-#  define tbasename    win32_wbasename
 #  define OS_PREFERRED_PATH_SEPARATOR L'\\'
 #  define OS_PREFERRED_PATH_SEPARATOR_STRING L"\\"
 #  define print_security_descriptor     win32_print_security_descriptor
 #else /* __WIN32__ */
 #  define OS_PREFERRED_PATH_SEPARATOR L'\\'
 #  define OS_PREFERRED_PATH_SEPARATOR_STRING L"\\"
 #  define print_security_descriptor     win32_print_security_descriptor
 #else /* __WIN32__ */
-#  include <glob.h>
 #  include <getopt.h>
 #  include <langinfo.h>
 #  include <getopt.h>
 #  include <langinfo.h>
-#  define tbasename    basename
 #  define OS_PREFERRED_PATH_SEPARATOR '/'
 #  define OS_PREFERRED_PATH_SEPARATOR_STRING "/"
 #  define print_security_descriptor    default_print_security_descriptor
 #  define OS_PREFERRED_PATH_SEPARATOR '/'
 #  define OS_PREFERRED_PATH_SEPARATOR_STRING "/"
 #  define print_security_descriptor    default_print_security_descriptor
@@ -77,6 +74,34 @@ static inline void set_fd_to_binary_mode(int fd)
 
 #define ARRAY_LEN(array) (sizeof(array) / sizeof(array[0]))
 
 
 #define ARRAY_LEN(array) (sizeof(array) / sizeof(array[0]))
 
+static inline bool
+is_any_path_separator(tchar c)
+{
+       return c == T('/') || c == T('\\');
+}
+
+/* Like basename(), but handles both forward and backwards slashes.  */
+static tchar *
+tbasename(tchar *path)
+{
+       tchar *p = tstrchr(path, T('\0'));
+
+       for (;;) {
+               if (p == path)
+                       return path;
+               if (!is_any_path_separator(*--p))
+                       break;
+               *p = T('\0');
+       }
+
+       for (;;) {
+               if (p == path)
+                       return path;
+               if (is_any_path_separator(*--p))
+                       return ++p;
+       }
+}
+
 #define for_opt(c, opts) while ((c = getopt_long_only(argc, (tchar**)argv, T(""), \
                                opts, NULL)) != -1)
 
 #define for_opt(c, opts) while ((c = getopt_long_only(argc, (tchar**)argv, T(""), \
                                opts, NULL)) != -1)
 
@@ -147,12 +172,14 @@ enum {
        IMAGEX_NO_ACLS_OPTION,
        IMAGEX_NO_ATTRIBUTES_OPTION,
        IMAGEX_NO_WILDCARDS_OPTION,
        IMAGEX_NO_ACLS_OPTION,
        IMAGEX_NO_ATTRIBUTES_OPTION,
        IMAGEX_NO_WILDCARDS_OPTION,
+       IMAGEX_NULLGLOB_OPTION,
        IMAGEX_ONE_FILE_ONLY_OPTION,
        IMAGEX_NOT_PIPABLE_OPTION,
        IMAGEX_PACK_CHUNK_SIZE_OPTION,
        IMAGEX_PACK_STREAMS_OPTION,
        IMAGEX_PATH_OPTION,
        IMAGEX_PIPABLE_OPTION,
        IMAGEX_ONE_FILE_ONLY_OPTION,
        IMAGEX_NOT_PIPABLE_OPTION,
        IMAGEX_PACK_CHUNK_SIZE_OPTION,
        IMAGEX_PACK_STREAMS_OPTION,
        IMAGEX_PATH_OPTION,
        IMAGEX_PIPABLE_OPTION,
+       IMAGEX_PRESERVE_DIR_STRUCTURE_OPTION,
        IMAGEX_REBUILD_OPTION,
        IMAGEX_RECOMPRESS_OPTION,
        IMAGEX_RECURSIVE_OPTION,
        IMAGEX_REBUILD_OPTION,
        IMAGEX_RECOMPRESS_OPTION,
        IMAGEX_RECURSIVE_OPTION,
@@ -164,7 +191,6 @@ enum {
        IMAGEX_STAGING_DIR_OPTION,
        IMAGEX_STREAMS_INTERFACE_OPTION,
        IMAGEX_STRICT_ACLS_OPTION,
        IMAGEX_STAGING_DIR_OPTION,
        IMAGEX_STREAMS_INTERFACE_OPTION,
        IMAGEX_STRICT_ACLS_OPTION,
-       IMAGEX_STRICT_WILDCARDS_OPTION,
        IMAGEX_SYMLINK_OPTION,
        IMAGEX_THREADS_OPTION,
        IMAGEX_TO_STDOUT_OPTION,
        IMAGEX_SYMLINK_OPTION,
        IMAGEX_THREADS_OPTION,
        IMAGEX_TO_STDOUT_OPTION,
@@ -270,8 +296,9 @@ static const struct option extract_options[] = {
        {T("dest-dir"),    required_argument, NULL, IMAGEX_DEST_DIR_OPTION},
        {T("to-stdout"),   no_argument,       NULL, IMAGEX_TO_STDOUT_OPTION},
        {T("include-invalid-names"), no_argument, NULL, IMAGEX_INCLUDE_INVALID_NAMES_OPTION},
        {T("dest-dir"),    required_argument, NULL, IMAGEX_DEST_DIR_OPTION},
        {T("to-stdout"),   no_argument,       NULL, IMAGEX_TO_STDOUT_OPTION},
        {T("include-invalid-names"), no_argument, NULL, IMAGEX_INCLUDE_INVALID_NAMES_OPTION},
-       {T("strict-wildcards"), no_argument,  NULL, IMAGEX_STRICT_WILDCARDS_OPTION},
        {T("no-wildcards"), no_argument,      NULL, IMAGEX_NO_WILDCARDS_OPTION},
        {T("no-wildcards"), no_argument,      NULL, IMAGEX_NO_WILDCARDS_OPTION},
+       {T("nullglob"),     no_argument,      NULL, IMAGEX_NULLGLOB_OPTION},
+       {T("preserve-dir-structure"), no_argument, NULL, IMAGEX_PRESERVE_DIR_STRUCTURE_OPTION},
        {NULL, 0, NULL, 0},
 };
 
        {NULL, 0, NULL, 0},
 };
 
@@ -1250,16 +1277,14 @@ imagex_progress_func(enum wimlib_progress_msg msg,
                        info->extract.target);
                break;
        case WIMLIB_PROGRESS_MSG_EXTRACT_TREE_BEGIN:
                        info->extract.target);
                break;
        case WIMLIB_PROGRESS_MSG_EXTRACT_TREE_BEGIN:
-               if (info->extract.extract_root_wim_source_path[0] != T('\0')) {
-                       imagex_printf(T("Extracting "
-                                 "\""WIMLIB_WIM_PATH_SEPARATOR_STRING"%"TS"\" "
-                                 "from image %d (\"%"TS"\") "
-                                 "in \"%"TS"\" to \"%"TS"\"\n"),
-                               info->extract.extract_root_wim_source_path,
-                               info->extract.image,
-                               info->extract.image_name,
-                               info->extract.wimfile_name,
-                               info->extract.target);
+               if (info->extract.extract_root_wim_source_path[0]) {
+                       imagex_printf(T("Extracting \"%"TS"\" from image %d "
+                                       "(\"%"TS"\") in \"%"TS"\" to \"%"TS"\"\n"),
+                                     info->extract.extract_root_wim_source_path,
+                                     info->extract.image,
+                                     info->extract.image_name,
+                                     info->extract.wimfile_name,
+                                     info->extract.target);
                }
                break;
        case WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS:
                }
                break;
        case WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS:
@@ -2910,67 +2935,6 @@ out_err:
        goto out_free_refglobs;
 }
 
        goto out_free_refglobs;
 }
 
-static bool
-is_root_wim_path(const tchar *path)
-{
-       const tchar *p;
-       for (p = path; *p; p++)
-               if (*p != T('\\') && *p != T('/'))
-                       return false;
-       return true;
-}
-
-static void
-free_extract_commands(struct wimlib_extract_command *cmds, size_t num_cmds,
-                     const tchar *dest_dir)
-{
-       for (size_t i = 0; i < num_cmds; i++)
-               if (cmds[i].fs_dest_path != dest_dir)
-                       free(cmds[i].fs_dest_path);
-       free(cmds);
-}
-
-static struct wimlib_extract_command *
-prepare_extract_commands(tchar **paths, unsigned num_paths,
-                        int extract_flags, tchar *dest_dir,
-                        size_t *num_cmds_ret)
-{
-       struct wimlib_extract_command *cmds;
-       size_t num_cmds;
-       tchar *emptystr = T("");
-
-       if (num_paths == 0) {
-               num_paths = 1;
-               paths = &emptystr;
-       }
-       num_cmds = num_paths;
-       cmds = calloc(num_cmds, sizeof(cmds[0]));
-       if (!cmds) {
-               imagex_error(T("Out of memory!"));
-               return NULL;
-       }
-
-       for (size_t i = 0; i < num_cmds; i++) {
-               cmds[i].extract_flags = extract_flags;
-               cmds[i].wim_source_path = paths[i];
-               if (is_root_wim_path(paths[i])) {
-                       cmds[i].fs_dest_path = dest_dir;
-               } else {
-                       size_t len = tstrlen(dest_dir) + 1 + tstrlen(paths[i]);
-                       cmds[i].fs_dest_path = malloc((len + 1) * sizeof(tchar));
-                       if (!cmds[i].fs_dest_path) {
-                               free_extract_commands(cmds, num_cmds, dest_dir);
-                               return NULL;
-                       }
-                       tsprintf(cmds[i].fs_dest_path,
-                                T("%"TS""OS_PREFERRED_PATH_SEPARATOR_STRING"%"TS),
-                                dest_dir, tbasename(paths[i]));
-               }
-       }
-       *num_cmds_ret = num_cmds;
-       return cmds;
-}
-
 /* Extract files or directories from a WIM image */
 static int
 imagex_extract(int argc, tchar **argv, int cmd)
 /* Extract files or directories from a WIM image */
 static int
 imagex_extract(int argc, tchar **argv, int cmd)
@@ -2984,13 +2948,16 @@ imagex_extract(int argc, tchar **argv, int cmd)
        const tchar *image_num_or_name;
        const tchar *pathlist;
        tchar *dest_dir = T(".");
        const tchar *image_num_or_name;
        const tchar *pathlist;
        tchar *dest_dir = T(".");
-       int extract_flags = WIMLIB_EXTRACT_FLAG_NORPFIX;
-       int listfile_extract_flags = WIMLIB_EXTRACT_FLAG_GLOB_PATHS;
+       int extract_flags = WIMLIB_EXTRACT_FLAG_NORPFIX |
+                           WIMLIB_EXTRACT_FLAG_GLOB_PATHS |
+                           WIMLIB_EXTRACT_FLAG_STRICT_GLOB;
+       int notlist_extract_flags = WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE;
 
        STRING_SET(refglobs);
 
        struct wimlib_extract_command *cmds;
        size_t num_cmds;
 
        STRING_SET(refglobs);
 
        struct wimlib_extract_command *cmds;
        size_t num_cmds;
+       tchar *root_path = T("");
 
        for_opt(c, extract_options) {
                switch (c) {
 
        for_opt(c, extract_options) {
                switch (c) {
@@ -3030,10 +2997,13 @@ imagex_extract(int argc, tchar **argv, int cmd)
                        extract_flags |= WIMLIB_EXTRACT_FLAG_ALL_CASE_CONFLICTS;
                        break;
                case IMAGEX_NO_WILDCARDS_OPTION:
                        extract_flags |= WIMLIB_EXTRACT_FLAG_ALL_CASE_CONFLICTS;
                        break;
                case IMAGEX_NO_WILDCARDS_OPTION:
-                       listfile_extract_flags &= ~WIMLIB_EXTRACT_FLAG_GLOB_PATHS;
+                       extract_flags &= ~WIMLIB_EXTRACT_FLAG_GLOB_PATHS;
+                       break;
+               case IMAGEX_NULLGLOB_OPTION:
+                       extract_flags &= ~WIMLIB_EXTRACT_FLAG_STRICT_GLOB;
                        break;
                        break;
-               case IMAGEX_STRICT_WILDCARDS_OPTION:
-                       listfile_extract_flags |= WIMLIB_EXTRACT_FLAG_STRICT_GLOB;
+               case IMAGEX_PRESERVE_DIR_STRUCTURE_OPTION:
+                       notlist_extract_flags &= ~WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE;
                        break;
                default:
                        goto out_usage;
                        break;
                default:
                        goto out_usage;
@@ -3051,21 +3021,9 @@ imagex_extract(int argc, tchar **argv, int cmd)
        argc -= 2;
        argv += 2;
 
        argc -= 2;
        argv += 2;
 
-       if (argc == 1 && argv[0][0] == T('@')) {
-               pathlist = argv[0] + 1;
-               cmds = NULL;
-               num_cmds = 0;
-       } else {
-               cmds = prepare_extract_commands(argv, argc, extract_flags, dest_dir,
-                                               &num_cmds);
-               if (cmds == NULL)
-                       goto out_err;
-               pathlist = NULL;
-       }
-
        ret = wimlib_open_wim(wimfile, open_flags, &wim, imagex_progress_func);
        if (ret)
        ret = wimlib_open_wim(wimfile, open_flags, &wim, imagex_progress_func);
        if (ret)
-               goto out_free_cmds;
+               goto out_free_refglobs;
 
        image = wimlib_resolve_image(wim, image_num_or_name);
        ret = verify_image_exists_and_is_single(image,
 
        image = wimlib_resolve_image(wim, image_num_or_name);
        ret = verify_image_exists_and_is_single(image,
@@ -3080,17 +3038,38 @@ imagex_extract(int argc, tchar **argv, int cmd)
                        goto out_wimlib_free;
        }
 
                        goto out_wimlib_free;
        }
 
-       ret = 0;
-       if (ret == 0 && cmds != NULL) {
-               ret = wimlib_extract_files(wim, image, cmds, num_cmds, 0,
-                                          imagex_progress_func);
+       if (argc == 0) {
+               argv = &root_path;
+               argc = 1;
+               extract_flags &= ~WIMLIB_EXTRACT_FLAG_GLOB_PATHS;
        }
        }
-       if (ret == 0 && pathlist != NULL) {
-               ret = wimlib_extract_pathlist(wim, image, dest_dir,
-                                             pathlist,
-                                             extract_flags | listfile_extract_flags,
-                                             imagex_progress_func);
+
+       while (argc != 0 && ret == 0) {
+               int num_paths;
+
+               for (num_paths = 0;
+                    num_paths < argc && argv[num_paths][0] != T('@');
+                    num_paths++)
+                       ;
+
+               if (num_paths) {
+                       ret = wimlib_extract_paths(wim, image, dest_dir,
+                                                  (const tchar **)argv,
+                                                  num_paths,
+                                                  extract_flags | notlist_extract_flags,
+                                                  imagex_progress_func);
+                       argc -= num_paths;
+                       argv += num_paths;
+               } else {
+                       ret = wimlib_extract_pathlist(wim, image, dest_dir,
+                                                     argv[0] + 1,
+                                                     extract_flags,
+                                                     imagex_progress_func);
+                       argc--;
+                       argv++;
+               }
        }
        }
+
        if (ret == 0) {
                if (!imagex_be_quiet)
                        imagex_printf(T("Done extracting files.\n"));
        if (ret == 0) {
                if (!imagex_be_quiet)
                        imagex_printf(T("Done extracting files.\n"));
@@ -3107,8 +3086,6 @@ imagex_extract(int argc, tchar **argv, int cmd)
        }
 out_wimlib_free:
        wimlib_free(wim);
        }
 out_wimlib_free:
        wimlib_free(wim);
-out_free_cmds:
-       free_extract_commands(cmds, num_cmds, dest_dir);
 out_free_refglobs:
        string_set_destroy(&refglobs);
        return ret;
 out_free_refglobs:
        string_set_destroy(&refglobs);
        return ret;
@@ -4083,10 +4060,11 @@ T(
 ),
 [CMD_EXTRACT] =
 T(
 ),
 [CMD_EXTRACT] =
 T(
-"    %"TS" WIMFILE (IMAGE_NUM | IMAGE_NAME) ([PATH...] | @LISTFILE)\n"
-"                    [--check] [--ref=\"GLOB\"] [--no-acls] [--strict-acls]\n"
-"                    [--no-attributes] [--to-stdout] [--dest-dir=CMD_DIR]\n"
-"                    [--include-invalid-names]\n"
+"    %"TS" WIMFILE (IMAGE_NUM | IMAGE_NAME) [(PATH | @LISTFILE)...]\n"
+"                    [--check] [--ref=\"GLOB\"] [--dest-dir=CMD_DIR]\n"
+"                    [--to-stdout] [--no-acls] [--strict-acls]\n"
+"                    [--no-attributes] [--include-invalid-names]\n"
+"                    [--no-wildcards] [--nullglob]\n"
 ),
 [CMD_INFO] =
 T(
 ),
 [CMD_INFO] =
 T(
index 23c9305436a4ac744f635226677d955a35968b53..beb5a54277ac3e9e09ca51f5dd7e6d649c5a2a75 100644 (file)
@@ -69,8 +69,9 @@
 
 #define WIMLIB_EXTRACT_FLAG_MULTI_IMAGE 0x80000000
 #define WIMLIB_EXTRACT_FLAG_FROM_PIPE   0x40000000
 
 #define WIMLIB_EXTRACT_FLAG_MULTI_IMAGE 0x80000000
 #define WIMLIB_EXTRACT_FLAG_FROM_PIPE   0x40000000
-#define WIMLIB_EXTRACT_FLAG_PATHMODE    0x20000000
-#define WIMLIB_EXTRACT_MASK_PUBLIC      0x1fffffff
+#define WIMLIB_EXTRACT_FLAG_FILEMODE    0x20000000
+#define WIMLIB_EXTRACT_FLAG_IMAGEMODE   0x10000000
+#define WIMLIB_EXTRACT_MASK_PUBLIC      0x0fffffff
 
 /* Given a WIM dentry in the tree to be extracted, resolve all streams in the
  * corresponding inode and set 'out_refcnt' in each to 0.  */
 
 /* Given a WIM dentry in the tree to be extracted, resolve all streams in the
  * corresponding inode and set 'out_refcnt' in each to 0.  */
@@ -897,6 +898,9 @@ build_extraction_path(tchar path[], struct wim_dentry *dentry,
        path_nchars += target_prefix_nchars;
 
        for (d = dentry; d != ctx->extract_root; d = d->parent) {
        path_nchars += target_prefix_nchars;
 
        for (d = dentry; d != ctx->extract_root; d = d->parent) {
+               if (!d->in_extraction_tree || d->extraction_skipped)
+                       break;
+
                path_nchars += d->extraction_name_nchars + 1;
                list_add(&d->tmp_list, &ancestor_list);
        }
                path_nchars += d->extraction_name_nchars + 1;
                list_add(&d->tmp_list, &ancestor_list);
        }
@@ -1954,17 +1958,11 @@ do_feature_check(const struct wim_features *required_features,
                 const struct wim_features *supported_features,
                 int extract_flags,
                 const struct apply_operations *ops,
                 const struct wim_features *supported_features,
                 int extract_flags,
                 const struct apply_operations *ops,
-                const tchar *wim_source_path,
                 bool warn)
 {
                 bool warn)
 {
-       const tchar *loc;
+       const tchar *loc = T("the extraction operation");;
        const tchar *mode = T("this extraction mode");
 
        const tchar *mode = T("this extraction mode");
 
-       if (wim_source_path[0] == '\0')
-               loc = T("the WIM image");
-       else
-               loc = wim_source_path;
-
        if (warn) {
                /* We're an archive program, so theoretically we can do what we want
                 * with FILE_ATTRIBUTE_ARCHIVE (which is a dumb flag anyway).  Don't
        if (warn) {
                /* We're an archive program, so theoretically we can do what we want
                 * with FILE_ATTRIBUTE_ARCHIVE (which is a dumb flag anyway).  Don't
@@ -2210,20 +2208,14 @@ dentry_set_not_skipped(struct wim_dentry *dentry, void *_ignore)
 
 static int
 extract_trees(WIMStruct *wim, struct wim_dentry **trees, size_t num_trees,
 
 static int
 extract_trees(WIMStruct *wim, struct wim_dentry **trees, size_t num_trees,
-             const tchar *wim_source_path, const tchar *target,
-             int extract_flags, wimlib_progress_func_t progress_func)
+             const tchar *target, int extract_flags,
+             wimlib_progress_func_t progress_func)
 {
        struct wim_features required_features;
        struct apply_ctx ctx;
        int ret;
        struct wim_lookup_table_entry *lte;
 
 {
        struct wim_features required_features;
        struct apply_ctx ctx;
        int ret;
        struct wim_lookup_table_entry *lte;
 
-       wimlib_assert(num_trees != 0);
-       wimlib_assert(target != NULL);
-
-       if (num_trees > 1)
-               wimlib_assert((extract_flags & WIMLIB_EXTRACT_FLAG_PATHMODE));
-
        /* Start initializing the apply_ctx.  */
        memset(&ctx, 0, sizeof(struct apply_ctx));
        ctx.wim = wim;
        /* Start initializing the apply_ctx.  */
        memset(&ctx, 0, sizeof(struct apply_ctx));
        ctx.wim = wim;
@@ -2238,19 +2230,24 @@ extract_trees(WIMStruct *wim, struct wim_dentry **trees, size_t num_trees,
                                                      WIMLIB_EXTRACT_MASK_PUBLIC);
                ctx.progress.extract.image_name = wimlib_get_image_name(wim,
                                                                        wim->current_image);
                                                      WIMLIB_EXTRACT_MASK_PUBLIC);
                ctx.progress.extract.image_name = wimlib_get_image_name(wim,
                                                                        wim->current_image);
-               ctx.progress.extract.extract_root_wim_source_path = wim_source_path;
                ctx.progress.extract.target = target;
        }
        INIT_LIST_HEAD(&ctx.stream_list);
 
                ctx.progress.extract.target = target;
        }
        INIT_LIST_HEAD(&ctx.stream_list);
 
-       if (extract_flags & WIMLIB_EXTRACT_FLAG_PATHMODE) {
-               /* Path mode --- there may be multiple trees, and targets are
-                * set relative to the root of the image.
-                *
-                * Consider all dentries to be in the extraction tree, but
-                * assume all to be skipped unless in one of the subtrees being
-                * extracted or one of the ancestors of the subtrees up to the
-                * image root.  */
+       if (extract_flags & WIMLIB_EXTRACT_FLAG_FILEMODE) {
+               /* File mode --- target is explicit.  */
+               wimlib_assert(num_trees == 1);
+               ret = calculate_dentry_full_path(trees[0]);
+               if (ret)
+                       return ret;
+               ctx.progress.extract.extract_root_wim_source_path = trees[0]->_full_path;
+               ctx.extract_root = trees[0];
+               for_dentry_in_tree(ctx.extract_root, dentry_set_not_skipped, NULL);
+       } else {
+               /* Targets are to be set relative to the root of the image
+                * (preserving original directory structure).  */
+
+               ctx.progress.extract.extract_root_wim_source_path = T("");
                ctx.extract_root = wim_root_dentry(wim);
                for_dentry_in_tree(ctx.extract_root, dentry_set_skipped, NULL);
 
                ctx.extract_root = wim_root_dentry(wim);
                for_dentry_in_tree(ctx.extract_root, dentry_set_skipped, NULL);
 
@@ -2259,14 +2256,16 @@ extract_trees(WIMStruct *wim, struct wim_dentry **trees, size_t num_trees,
 
                        for_dentry_in_tree(trees[i], dentry_set_not_skipped, NULL);
                        d = trees[i];
 
                        for_dentry_in_tree(trees[i], dentry_set_not_skipped, NULL);
                        d = trees[i];
-                       while (d != ctx.extract_root) {
-                               d = d->parent;
-                               dentry_set_not_skipped(d, NULL);
+
+                       /* Extract directories up to image root if preserving
+                        * directory structure.  */
+                       if (!(extract_flags & WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE)) {
+                               while (d != ctx.extract_root) {
+                                       d = d->parent;
+                                       dentry_set_not_skipped(d, NULL);
+                               }
                        }
                }
                        }
                }
-       } else {
-               ctx.extract_root = trees[0];
-               for_dentry_in_tree(ctx.extract_root, dentry_set_not_skipped, NULL);
        }
 
        /* Select the appropriate apply_operations based on the
        }
 
        /* Select the appropriate apply_operations based on the
@@ -2298,9 +2297,7 @@ extract_trees(WIMStruct *wim, struct wim_dentry **trees, size_t num_trees,
        /* Get and check the features required to extract the dentry tree.  */
        dentry_tree_get_features(ctx.extract_root, &required_features);
        ret = do_feature_check(&required_features, &ctx.supported_features,
        /* Get and check the features required to extract the dentry tree.  */
        dentry_tree_get_features(ctx.extract_root, &required_features);
        ret = do_feature_check(&required_features, &ctx.supported_features,
-                              extract_flags, ctx.ops,
-                              wim_source_path,
-                              !(extract_flags & WIMLIB_EXTRACT_FLAG_PATHMODE));
+                              extract_flags, ctx.ops, true);
        if (ret)
                goto out_finish_or_abort_extract;
 
        if (ret)
                goto out_finish_or_abort_extract;
 
@@ -2382,10 +2379,10 @@ extract_trees(WIMStruct *wim, struct wim_dentry **trees, size_t num_trees,
 
        if (progress_func) {
                int msg;
 
        if (progress_func) {
                int msg;
-               if (*wim_source_path || (extract_flags & WIMLIB_EXTRACT_FLAG_PATHMODE))
-                       msg = WIMLIB_PROGRESS_MSG_EXTRACT_TREE_BEGIN;
-               else
+               if (extract_flags & WIMLIB_EXTRACT_FLAG_IMAGEMODE)
                        msg = WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_BEGIN;
                        msg = WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_BEGIN;
+               else
+                       msg = WIMLIB_PROGRESS_MSG_EXTRACT_TREE_BEGIN;
                progress_func(msg, &ctx.progress);
        }
 
                progress_func(msg, &ctx.progress);
        }
 
@@ -2494,10 +2491,10 @@ extract_trees(WIMStruct *wim, struct wim_dentry **trees, size_t num_trees,
 
        if (progress_func) {
                int msg;
 
        if (progress_func) {
                int msg;
-               if (*wim_source_path || (extract_flags & WIMLIB_EXTRACT_FLAG_PATHMODE))
-                       msg = WIMLIB_PROGRESS_MSG_EXTRACT_TREE_END;
-               else
+               if (extract_flags & WIMLIB_EXTRACT_FLAG_IMAGEMODE)
                        msg = WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_END;
                        msg = WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_END;
+               else
+                       msg = WIMLIB_PROGRESS_MSG_EXTRACT_TREE_END;
                progress_func(msg, &ctx.progress);
        }
 
                progress_func(msg, &ctx.progress);
        }
 
@@ -2526,68 +2523,12 @@ out_dentry_reset_needs_extraction:
        return ret;
 }
 
        return ret;
 }
 
-/*
- * extract_tree - Extract a file or directory tree from the currently selected
- *               WIM image.
- *
- * @wim:       WIMStruct for the WIM file, with the desired image selected
- *             (as wim->current_image).
- *
- * @wim_source_path:
- *             "Canonical" (i.e. no leading or trailing slashes, path
- *             separators WIM_PATH_SEPARATOR) path inside the WIM image to
- *             extract.  An empty string means the full image.
- *
- * @target:
- *             Filesystem path to extract the file or directory tree to.
- *             (Or, with WIMLIB_EXTRACT_FLAG_NTFS: the name of a NTFS volume.)
- *
- * @extract_flags:
- *             WIMLIB_EXTRACT_FLAG_*.  Also, the private flag
- *             WIMLIB_EXTRACT_FLAG_MULTI_IMAGE will be set if this is being
- *             called through wimlib_extract_image() with WIMLIB_ALL_IMAGES as
- *             the image.
- *
- * @progress_func:
- *             If non-NULL, progress function for the extraction.  The messages
- *             that may be sent in this function are:
- *
- *             WIMLIB_PROGRESS_MSG_EXTRACT_TREE_BEGIN or
- *                     WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_BEGIN;
- *             WIMLIB_PROGRESS_MSG_EXTRACT_DIR_STRUCTURE_BEGIN;
- *             WIMLIB_PROGRESS_MSG_EXTRACT_DIR_STRUCTURE_END;
- *             WIMLIB_PROGRESS_MSG_EXTRACT_DENTRY;
- *             WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS;
- *             WIMLIB_PROGRESS_MSG_APPLY_TIMESTAMPS;
- *             WIMLIB_PROGRESS_MSG_EXTRACT_TREE_END or
- *                     WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_END.
- *
- * Returns 0 on success; a positive WIMLIB_ERR_* code on failure.
- */
-static int
-extract_tree(WIMStruct *wim, const tchar *wim_source_path,
-            const tchar *target, int extract_flags,
-            wimlib_progress_func_t progress_func)
-{
-       struct wim_dentry *root;
-
-       root = get_dentry(wim, wim_source_path, WIMLIB_CASE_PLATFORM_DEFAULT);
-
-       if (root == NULL) {
-                 ERROR("Path \"%"TS"\" does not exist in WIM image %d",
-                       wim_source_path, wim->current_image);
-                 return WIMLIB_ERR_PATH_DOES_NOT_EXIST;
-       }
-       return extract_trees(wim, &root, 1, wim_source_path,
-                            target, extract_flags, progress_func);
-}
-
 /* Make sure the extraction flags make sense, and update them if needed.  */
 static int
 /* Make sure the extraction flags make sense, and update them if needed.  */
 static int
-check_extract_flags(int extract_flags,
-                   const u32 wim_header_flags,
-                   int *updated_extract_flags_ret)
+check_extract_flags(const WIMStruct *wim, int *extract_flags_p)
 {
 {
+       int extract_flags = *extract_flags_p;
+
        /* Check for invalid flag combinations  */
        if ((extract_flags &
             (WIMLIB_EXTRACT_FLAG_SYMLINK |
        /* Check for invalid flag combinations  */
        if ((extract_flags &
             (WIMLIB_EXTRACT_FLAG_SYMLINK |
@@ -2625,7 +2566,7 @@ check_extract_flags(int extract_flags,
        {
                /* Do reparse point fixups by default if the WIM header says
                 * they are enabled and we are extracting a full image. */
        {
                /* Do reparse point fixups by default if the WIM header says
                 * they are enabled and we are extracting a full image. */
-               if (wim_header_flags & WIM_HDR_FLAG_RP_FIX)
+               if (wim->hdr.flags & WIM_HDR_FLAG_RP_FIX)
                        extract_flags |= WIMLIB_EXTRACT_FLAG_RPFIX;
        }
 
                        extract_flags |= WIMLIB_EXTRACT_FLAG_RPFIX;
        }
 
@@ -2649,169 +2590,160 @@ check_extract_flags(int extract_flags,
                }
        }
 
                }
        }
 
-       if (updated_extract_flags_ret)
-               *updated_extract_flags_ret = extract_flags;
+       *extract_flags_p = extract_flags;
        return 0;
 }
 
        return 0;
 }
 
+static u32
+get_wildcard_flags(int extract_flags)
+{
+       u32 wildcard_flags = 0;
+
+       if (extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_GLOB)
+               wildcard_flags |= WILDCARD_FLAG_ERROR_IF_NO_MATCH;
+       else
+               wildcard_flags |= WILDCARD_FLAG_WARN_IF_NO_MATCH;
+
+       if (default_ignore_case)
+               wildcard_flags |= WILDCARD_FLAG_CASE_INSENSITIVE;
+
+       return wildcard_flags;
+}
+
+struct append_dentry_ctx {
+       struct wim_dentry **dentries;
+       size_t num_dentries;
+       size_t num_alloc_dentries;
+};
 
 
-/* Internal function to execute extraction commands for a WIM image.  The paths
- * in the extract commands are expected to be already "canonicalized".  */
 static int
 static int
-do_wimlib_extract_files(WIMStruct *wim,
+append_dentry_cb(struct wim_dentry *dentry, void *_ctx)
+{
+       struct append_dentry_ctx *ctx = _ctx;
+
+       if (ctx->num_dentries == ctx->num_alloc_dentries) {
+               struct wim_dentry **new_dentries;
+               size_t new_length;
+
+               new_length = max(ctx->num_alloc_dentries + 8,
+                                ctx->num_alloc_dentries * 3 / 2);
+               new_dentries = REALLOC(ctx->dentries,
+                                      new_length * sizeof(ctx->dentries[0]));
+               if (new_dentries == NULL)
+                       return WIMLIB_ERR_NOMEM;
+               ctx->dentries = new_dentries;
+               ctx->num_alloc_dentries = new_length;
+       }
+       ctx->dentries[ctx->num_dentries++] = dentry;
+       return 0;
+}
+
+static int
+do_wimlib_extract_paths(WIMStruct *wim,
                        int image,
                        int image,
-                       struct wimlib_extract_command *cmds,
-                       size_t num_cmds,
+                       const tchar *target,
+                       const tchar * const *paths,
+                       size_t num_paths,
+                       int extract_flags,
                        wimlib_progress_func_t progress_func)
 {
        int ret;
                        wimlib_progress_func_t progress_func)
 {
        int ret;
-       bool found_link_cmd = false;
-       bool found_nolink_cmd = false;
+       struct wim_dentry **trees;
+       size_t num_trees;
+
+       if (wim == NULL || target == NULL || target[0] == T('\0') ||
+           (num_paths != 0 && paths == NULL))
+               return WIMLIB_ERR_INVALID_PARAM;
+
+       ret = check_extract_flags(wim, &extract_flags);
+       if (ret)
+               return ret;
 
 
-       /* Select the image from which we are extracting files */
        ret = select_wim_image(wim, image);
        if (ret)
                return ret;
 
        ret = select_wim_image(wim, image);
        if (ret)
                return ret;
 
-       /* Make sure there are no streams in the WIM that have not been
-        * checksummed yet.  */
        ret = wim_checksum_unhashed_streams(wim);
        if (ret)
                return ret;
 
        ret = wim_checksum_unhashed_streams(wim);
        if (ret)
                return ret;
 
-       /* Check for problems with the extraction commands */
-       for (size_t i = 0; i < num_cmds; i++) {
+       if (extract_flags & WIMLIB_EXTRACT_FLAG_GLOB_PATHS) {
 
 
-               if (cmds[i].fs_dest_path[0] == T('\0'))
-                       return WIMLIB_ERR_INVALID_PARAM;
+               struct append_dentry_ctx append_dentry_ctx = {
+                       .dentries = NULL,
+                       .num_dentries = 0,
+                       .num_alloc_dentries = 0,
+               };
 
 
-               if (cmds[i].extract_flags & WIMLIB_EXTRACT_FLAG_GLOB_PATHS)
-                       return WIMLIB_ERR_INVALID_PARAM;
+               u32 wildcard_flags = get_wildcard_flags(extract_flags);
 
 
-               ret = check_extract_flags(cmds[i].extract_flags,
-                                         wim->hdr.flags,
-                                         &cmds[i].extract_flags);
-               if (ret)
-                       return ret;
-               if (cmds[i].extract_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK |
-                                            WIMLIB_EXTRACT_FLAG_HARDLINK)) {
-                       found_link_cmd = true;
-               } else {
-                       found_nolink_cmd = true;
-               }
-               if (found_link_cmd && found_nolink_cmd) {
-                       ERROR("Symlink or hardlink extraction mode must "
-                             "be set on all extraction commands");
-                       return WIMLIB_ERR_INVALID_PARAM;
+               for (size_t i = 0; i < num_paths; i++) {
+                       tchar *path = canonicalize_wim_path(paths[i]);
+                       if (path == NULL) {
+                               ret = WIMLIB_ERR_NOMEM;
+                               trees = append_dentry_ctx.dentries;
+                               goto out_free_trees;
+                       }
+                       ret = expand_wildcard(wim, path,
+                                             append_dentry_cb,
+                                             &append_dentry_ctx,
+                                             wildcard_flags);
+                       FREE(path);
+                       if (ret) {
+                               trees = append_dentry_ctx.dentries;
+                               goto out_free_trees;
+                       }
                }
                }
-       }
-
-       /* Execute the extraction commands */
-       for (size_t i = 0; i < num_cmds; i++) {
-               ret = extract_tree(wim,
-                                  cmds[i].wim_source_path,
-                                  cmds[i].fs_dest_path,
-                                  cmds[i].extract_flags,
-                                  progress_func);
-               if (ret)
-                       return ret;
-       }
-       return 0;
-}
-
-/* API function documented in wimlib.h  */
-WIMLIBAPI int
-wimlib_extract_files(WIMStruct *wim,
-                    int image,
-                    const struct wimlib_extract_command *cmds,
-                    size_t num_cmds,
-                    int default_extract_flags,
-                    wimlib_progress_func_t progress_func)
-{
-       int ret;
-       struct wimlib_extract_command *cmds_copy;
-       int all_flags = 0;
-
-       default_extract_flags &= WIMLIB_EXTRACT_MASK_PUBLIC;
-
-       if (num_cmds == 0)
-               return 0;
-
-       cmds_copy = CALLOC(num_cmds, sizeof(cmds[0]));
-       if (!cmds_copy)
-               return WIMLIB_ERR_NOMEM;
+               trees = append_dentry_ctx.dentries;
+               num_trees = append_dentry_ctx.num_dentries;
+       } else {
+               trees = MALLOC(num_paths * sizeof(trees[0]));
+               if (trees == NULL)
+                       return WIMLIB_ERR_NOMEM;
 
 
-       for (size_t i = 0; i < num_cmds; i++) {
-               cmds_copy[i].extract_flags = (default_extract_flags |
-                                                cmds[i].extract_flags)
-                                               & WIMLIB_EXTRACT_MASK_PUBLIC;
-               all_flags |= cmds_copy[i].extract_flags;
+               for (size_t i = 0; i < num_paths; i++) {
 
 
-               cmds_copy[i].wim_source_path = canonicalize_wim_path(cmds[i].wim_source_path);
-               if (!cmds_copy[i].wim_source_path) {
-                       ret = WIMLIB_ERR_NOMEM;
-                       goto out_free_cmds_copy;
-               }
+                       tchar *path = canonicalize_wim_path(paths[i]);
+                       if (path == NULL) {
+                               ret = WIMLIB_ERR_NOMEM;
+                               goto out_free_trees;
+                       }
 
 
-               cmds_copy[i].fs_dest_path = canonicalize_fs_path(cmds[i].fs_dest_path);
-               if (!cmds_copy[i].fs_dest_path) {
-                       ret = WIMLIB_ERR_NOMEM;
-                       goto out_free_cmds_copy;
+                       trees[i] = get_dentry(wim, path,
+                                             WIMLIB_CASE_PLATFORM_DEFAULT);
+                       FREE(path);
+                       if (trees[i] == NULL) {
+                                 ERROR("Path \"%"TS"\" does not exist "
+                                       "in WIM image %d",
+                                       paths[i], wim->current_image);
+                                 ret = WIMLIB_ERR_PATH_DOES_NOT_EXIST;
+                                 goto out_free_trees;
+                       }
                }
                }
-
+               num_trees = num_paths;
        }
        }
-       ret = do_wimlib_extract_files(wim, image,
-                                     cmds_copy, num_cmds,
-                                     progress_func);
 
 
-       if (all_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK |
-                        WIMLIB_EXTRACT_FLAG_HARDLINK))
-       {
-               for_lookup_table_entry(wim->lookup_table,
-                                      lte_free_extracted_file, NULL);
-       }
-out_free_cmds_copy:
-       for (size_t i = 0; i < num_cmds; i++) {
-               FREE(cmds_copy[i].wim_source_path);
-               FREE(cmds_copy[i].fs_dest_path);
+       if (num_trees == 0) {
+               ret = 0;
+               goto out_free_trees;
        }
        }
-       FREE(cmds_copy);
+
+       ret = extract_trees(wim, trees, num_trees,
+                           target, extract_flags, progress_func);
+out_free_trees:
+       FREE(trees);
        return ret;
 }
 
        return ret;
 }
 
-/*
- * Extracts an image from a WIM file.
- *
- * @wim:               WIMStruct for the WIM file.
- *
- * @image:             Number of the single image to extract.
- *
- * @target:            Directory or NTFS volume to extract the image to.
- *
- * @extract_flags:     Bitwise or of WIMLIB_EXTRACT_FLAG_*.
- *
- * @progress_func:     If non-NULL, a progress function to be called
- *                     periodically.
- *
- * Returns 0 on success; nonzero on failure.
- */
 static int
 extract_single_image(WIMStruct *wim, int image,
                     const tchar *target, int extract_flags,
                     wimlib_progress_func_t progress_func)
 {
 static int
 extract_single_image(WIMStruct *wim, int image,
                     const tchar *target, int extract_flags,
                     wimlib_progress_func_t progress_func)
 {
-       int ret;
-       tchar *target_copy = canonicalize_fs_path(target);
-       if (target_copy == NULL)
-               return WIMLIB_ERR_NOMEM;
-       struct wimlib_extract_command cmd = {
-               .wim_source_path = T(""),
-               .fs_dest_path = target_copy,
-               .extract_flags = extract_flags,
-       };
-       ret = do_wimlib_extract_files(wim, image, &cmd, 1, progress_func);
-       FREE(target_copy);
-       return ret;
+       const tchar *path = T("");
+       return do_wimlib_extract_paths(wim, image, target, &path, 1,
+                                      extract_flags | WIMLIB_EXTRACT_FLAG_IMAGEMODE,
+                                      progress_func);
 }
 
 static const tchar * const filename_forbidden_chars =
 }
 
 static const tchar * const filename_forbidden_chars =
@@ -2891,6 +2823,15 @@ extract_all_images(WIMStruct *wim,
        return 0;
 }
 
        return 0;
 }
 
+static void
+clear_lte_extracted_file(WIMStruct *wim, int extract_flags)
+{
+       if (unlikely(extract_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK |
+                                     WIMLIB_EXTRACT_FLAG_HARDLINK)))
+               for_lookup_table_entry(wim->lookup_table,
+                                      lte_free_extracted_file, NULL);
+}
+
 static int
 do_wimlib_extract_image(WIMStruct *wim,
                        int image,
 static int
 do_wimlib_extract_image(WIMStruct *wim,
                        int image,
@@ -2900,25 +2841,129 @@ do_wimlib_extract_image(WIMStruct *wim,
 {
        int ret;
 
 {
        int ret;
 
-       if (image == WIMLIB_ALL_IMAGES) {
+       if (image == WIMLIB_ALL_IMAGES)
                ret = extract_all_images(wim, target, extract_flags,
                                         progress_func);
                ret = extract_all_images(wim, target, extract_flags,
                                         progress_func);
-       } else {
+       else
                ret = extract_single_image(wim, image, target, extract_flags,
                                           progress_func);
                ret = extract_single_image(wim, image, target, extract_flags,
                                           progress_func);
+
+       clear_lte_extracted_file(wim, extract_flags);
+       return ret;
+}
+
+
+/****************************************************************************
+ *                          Extraction API                                  *
+ ****************************************************************************/
+
+/* Note: new code should use wimlib_extract_paths() instead of
+ * wimlib_extract_files() if possible.  */
+WIMLIBAPI int
+wimlib_extract_files(WIMStruct *wim,
+                    int image,
+                    const struct wimlib_extract_command *cmds,
+                    size_t num_cmds,
+                    int default_extract_flags,
+                    wimlib_progress_func_t progress_func)
+{
+       int all_flags = 0;
+       int link_flags;
+       int ret;
+
+       if (num_cmds == 0)
+               return 0;
+
+       default_extract_flags |= WIMLIB_EXTRACT_FLAG_NO_PRESERVE_DIR_STRUCTURE;
+
+       for (size_t i = 0; i < num_cmds; i++) {
+               int cmd_flags = ((cmds[i].extract_flags |
+                                 default_extract_flags) &
+                                       WIMLIB_EXTRACT_MASK_PUBLIC);
+               int cmd_link_flags = (cmd_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK |
+                                                  WIMLIB_EXTRACT_FLAG_HARDLINK));
+               if (i == 0) {
+                       link_flags = cmd_link_flags;
+               } else {
+                       if (cmd_link_flags != link_flags) {
+                               ERROR("The same symlink or hardlink extraction mode "
+                                     "must be set on all extraction commands!");
+                               return WIMLIB_ERR_INVALID_PARAM;
+                       }
+               }
+               all_flags |= cmd_flags;
+       }
+       if (all_flags & WIMLIB_EXTRACT_FLAG_GLOB_PATHS) {
+               ERROR("Glob paths not supported for wimlib_extract_files(). "
+                     "Use wimlib_extract_paths() instead.");
+               return WIMLIB_ERR_INVALID_PARAM;
        }
 
        }
 
-       if (extract_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK |
-                            WIMLIB_EXTRACT_FLAG_HARDLINK))
-       {
-               for_lookup_table_entry(wim->lookup_table,
-                                      lte_free_extracted_file,
-                                      NULL);
+       for (size_t i = 0; i < num_cmds; i++) {
+               int extract_flags = ((cmds[i].extract_flags |
+                                     default_extract_flags) &
+                                       WIMLIB_EXTRACT_MASK_PUBLIC);
+               const tchar *target = cmds[i].fs_dest_path;
+               const tchar *wim_source_path = cmds[i].wim_source_path;
+
+               ret = do_wimlib_extract_paths(wim, image, target,
+                                             &wim_source_path, 1,
+                                             extract_flags | WIMLIB_EXTRACT_FLAG_FILEMODE,
+                                             progress_func);
+               if (ret)
+                       break;
        }
        }
+
+       clear_lte_extracted_file(wim, all_flags);
+       return ret;
+}
+
+WIMLIBAPI int
+wimlib_extract_paths(WIMStruct *wim,
+                    int image,
+                    const tchar *target,
+                    const tchar * const *paths,
+                    size_t num_paths,
+                    int extract_flags,
+                    wimlib_progress_func_t progress_func)
+{
+       int ret;
+
+       extract_flags &= WIMLIB_EXTRACT_MASK_PUBLIC;
+
+       ret = do_wimlib_extract_paths(wim, image, target, paths, num_paths,
+                                     extract_flags, progress_func);
+       clear_lte_extracted_file(wim, extract_flags);
+       return ret;
+}
+
+WIMLIBAPI int
+wimlib_extract_pathlist(WIMStruct *wim, int image,
+                       const tchar *target,
+                       const tchar *path_list_file,
+                       int extract_flags,
+                       wimlib_progress_func_t progress_func)
+{
+       int ret;
+       tchar **paths;
+       size_t num_paths;
+       void *mem;
+
+       ret = read_path_list_file(path_list_file, &paths, &num_paths, &mem);
+       if (ret) {
+               ERROR("Failed to read path list file \"%"TS"\"",
+                     path_list_file);
+               return ret;
+       }
+
+       ret = wimlib_extract_paths(wim, image, target,
+                                  (const tchar * const *)paths, num_paths,
+                                  extract_flags, progress_func);
+       FREE(paths);
+       FREE(mem);
        return ret;
 }
 
        return ret;
 }
 
-/* API function documented in wimlib.h  */
 WIMLIBAPI int
 wimlib_extract_image_from_pipe(int pipe_fd, const tchar *image_num_or_name,
                               const tchar *target, int extract_flags,
 WIMLIBAPI int
 wimlib_extract_image_from_pipe(int pipe_fd, const tchar *image_num_or_name,
                               const tchar *target, int extract_flags,
@@ -3080,7 +3125,6 @@ out_wimlib_free:
        return ret;
 }
 
        return ret;
 }
 
-/* API function documented in wimlib.h  */
 WIMLIBAPI int
 wimlib_extract_image(WIMStruct *wim,
                     int image,
 WIMLIBAPI int
 wimlib_extract_image(WIMStruct *wim,
                     int image,
@@ -3092,176 +3136,3 @@ wimlib_extract_image(WIMStruct *wim,
        return do_wimlib_extract_image(wim, image, target, extract_flags,
                                       progress_func);
 }
        return do_wimlib_extract_image(wim, image, target, extract_flags,
                                       progress_func);
 }
-
-/* API function documented in wimlib.h  */
-WIMLIBAPI int
-wimlib_extract_pathlist(WIMStruct *wim, int image,
-                       const tchar *target,
-                       const tchar *path_list_file,
-                       int extract_flags,
-                       wimlib_progress_func_t progress_func)
-{
-       int ret;
-       tchar **paths;
-       size_t num_paths;
-       void *mem;
-
-       ret = read_path_list_file(path_list_file, &paths, &num_paths, &mem);
-       if (ret) {
-               ERROR("Failed to read path list file \"%"TS"\"",
-                     path_list_file);
-               return ret;
-       }
-
-       ret = wimlib_extract_paths(wim, image, target,
-                                  (const tchar * const *)paths, num_paths,
-                                  extract_flags, progress_func);
-       FREE(paths);
-       FREE(mem);
-       return ret;
-}
-
-struct append_dentry_ctx {
-       struct wim_dentry **dentries;
-       size_t num_dentries;
-       size_t num_alloc_dentries;
-};
-
-static int
-append_dentry_cb(struct wim_dentry *dentry, void *_ctx)
-{
-       struct append_dentry_ctx *ctx = _ctx;
-
-       if (ctx->num_dentries == ctx->num_alloc_dentries) {
-               struct wim_dentry **new_dentries;
-               size_t new_length;
-
-               new_length = max(ctx->num_alloc_dentries + 8,
-                                ctx->num_alloc_dentries * 3 / 2);
-               new_dentries = REALLOC(ctx->dentries,
-                                      new_length * sizeof(ctx->dentries[0]));
-               if (new_dentries == NULL)
-                       return WIMLIB_ERR_NOMEM;
-               ctx->dentries = new_dentries;
-               ctx->num_alloc_dentries = new_length;
-       }
-       ctx->dentries[ctx->num_dentries++] = dentry;
-       return 0;
-}
-
-/* API function documented in wimlib.h  */
-WIMLIBAPI int
-wimlib_extract_paths(WIMStruct *wim,
-                    int image,
-                    const tchar *target,
-                    const tchar * const *paths,
-                    size_t num_paths,
-                    int extract_flags,
-                    wimlib_progress_func_t progress_func)
-{
-       int ret;
-       struct wim_dentry **trees;
-       size_t num_trees;
-
-       extract_flags &= WIMLIB_EXTRACT_MASK_PUBLIC;
-
-       if (wim == NULL || target == NULL || target[0] == T('\0') ||
-           (num_paths != 0 && paths == NULL))
-               return WIMLIB_ERR_INVALID_PARAM;
-
-       ret = check_extract_flags(extract_flags, wim->hdr.flags,
-                                 &extract_flags);
-       if (ret)
-               return ret;
-
-       ret = select_wim_image(wim, image);
-       if (ret)
-               return ret;
-
-       if (extract_flags & WIMLIB_EXTRACT_FLAG_GLOB_PATHS) {
-
-               struct append_dentry_ctx append_dentry_ctx = {
-                       .dentries = NULL,
-                       .num_dentries = 0,
-                       .num_alloc_dentries = 0,
-               };
-
-               u32 wildcard_flags = 0;
-
-               if (extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_GLOB)
-                       wildcard_flags |= WILDCARD_FLAG_ERROR_IF_NO_MATCH;
-               else
-                       wildcard_flags |= WILDCARD_FLAG_WARN_IF_NO_MATCH;
-
-               if (default_ignore_case)
-                       wildcard_flags |= WILDCARD_FLAG_CASE_INSENSITIVE;
-
-               for (size_t i = 0; i < num_paths; i++) {
-                       tchar *path = canonicalize_wim_path(paths[i]);
-                       if (path == NULL) {
-                               ret = WIMLIB_ERR_NOMEM;
-                               trees = append_dentry_ctx.dentries;
-                               goto out_free_trees;
-                       }
-                       ret = expand_wildcard(wim, path,
-                                             append_dentry_cb,
-                                             &append_dentry_ctx,
-                                             wildcard_flags);
-                       FREE(path);
-                       if (ret) {
-                               trees = append_dentry_ctx.dentries;
-                               goto out_free_trees;
-                       }
-               }
-               trees = append_dentry_ctx.dentries;
-               num_trees = append_dentry_ctx.num_dentries;
-       } else {
-               trees = MALLOC(num_paths * sizeof(trees[0]));
-               if (trees == NULL)
-                       return WIMLIB_ERR_NOMEM;
-
-               for (size_t i = 0; i < num_paths; i++) {
-
-                       tchar *path = canonicalize_wim_path(paths[i]);
-                       if (path == NULL) {
-                               ret = WIMLIB_ERR_NOMEM;
-                               goto out_free_trees;
-                       }
-
-                       trees[i] = get_dentry(wim, path,
-                                             WIMLIB_CASE_PLATFORM_DEFAULT);
-                       FREE(path);
-                       if (trees[i] == NULL) {
-                                 ERROR("Path \"%"TS"\" does not exist "
-                                       "in WIM image %d",
-                                       paths[i], wim->current_image);
-                                 ret = WIMLIB_ERR_PATH_DOES_NOT_EXIST;
-                                 goto out_free_trees;
-                       }
-               }
-               num_trees = num_paths;
-       }
-
-       if (num_trees == 0) {
-               ret = 0;
-               goto out_free_trees;
-       }
-
-       ret = extract_trees(wim, trees, num_trees,
-                           T(""), target,
-                           ((extract_flags &
-                               ~WIMLIB_EXTRACT_FLAG_GLOB_PATHS)
-                               | WIMLIB_EXTRACT_FLAG_PATHMODE),
-                           progress_func);
-
-       if (extract_flags & (WIMLIB_EXTRACT_FLAG_SYMLINK |
-                            WIMLIB_EXTRACT_FLAG_HARDLINK))
-       {
-               for_lookup_table_entry(wim->lookup_table,
-                                      lte_free_extracted_file,
-                                      NULL);
-       }
-out_free_trees:
-       FREE(trees);
-       return ret;
-}
index c5e25c7cb968d2675e0c5cde821114c910cd8a65..b3c76bd7fdf1958744b978b02baeb11b1772c849 100755 (executable)
@@ -223,12 +223,21 @@ imagex extract test.wim 1 @pathlist --dest-dir=out.dir
 ../tree-cmp hello2 out.dir/hello2
 [ ! -e out.dir/otherfile ]
 
 ../tree-cmp hello2 out.dir/hello2
 [ ! -e out.dir/otherfile ]
 
-msg "Testing path list extract (no match w/ strict; error expected)"
 cat > pathlist << EOF
 hello*
 EOF
 rm -rf out.dir
 cat > pathlist << EOF
 hello*
 EOF
 rm -rf out.dir
-! imagex extract test.wim 1 @pathlist --dest-dir=out.dir --no-wildcards --strict-wildcards
+msg "Testing path list extract (no wildcard, no match; error expected)"
+! imagex extract test.wim 1 @pathlist --dest-dir=out.dir --no-wildcards
+
+cat > pathlist << EOF
+foobar*
+EOF
+rm -rf out.dir
+msg "Testing path list extract (wildcard, no match; error expected)"
+! imagex extract test.wim 1 @pathlist --dest-dir=out.dir
+msg "Testing path list extract (wildcard, no match, nullglob; no error expected)"
+imagex extract test.wim 1 @pathlist --dest-dir=out.dir --nullglob
 
 msg "Testing path list extract (w/ wildcard)"
 cat > pathlist << EOF
 
 msg "Testing path list extract (w/ wildcard)"
 cat > pathlist << EOF
@@ -265,10 +274,10 @@ cat > pathlist << EOF
 hello1
 EOF
 rm -rf out.dir
 hello1
 EOF
 rm -rf out.dir
-! WIMLIB_IMAGEX_IGNORE_CASE=0 imagex extract test.wim 1 @pathlist \
-                               --dest-dir=out.dir --strict-wildcards
-WIMLIB_IMAGEX_IGNORE_CASE=1 imagex extract test.wim 1 @pathlist \
-                               --dest-dir=out.dir --strict-wildcards
+! WIMLIB_IMAGEX_IGNORE_CASE=0 imagex extract test.wim 1 @pathlist --dest-dir=out.dir
+! WIMLIB_IMAGEX_IGNORE_CASE=0 imagex extract test.wim 1 @pathlist --dest-dir=out.dir --no-wildcards
+WIMLIB_IMAGEX_IGNORE_CASE=1 imagex extract test.wim 1 @pathlist --dest-dir=out.dir
+WIMLIB_IMAGEX_IGNORE_CASE=1 imagex extract test.wim 1 @pathlist --dest-dir=out.dir --no-wildcards
 ../tree-cmp hello1 out.dir/HELLO1
 [ ! -e out.dir/topdir/hello1 ]
 
 ../tree-cmp hello1 out.dir/HELLO1
 [ ! -e out.dir/topdir/hello1 ]