wimextract: allow specifying listfile on stdin
authorEric Biggers <ebiggers3@gmail.com>
Sat, 21 Jul 2018 18:38:08 +0000 (11:38 -0700)
committerEric Biggers <ebiggers3@gmail.com>
Sat, 21 Jul 2018 19:02:52 +0000 (12:02 -0700)
NEWS
doc/man1/wimextract.1
include/wimlib.h
include/wimlib/textfile.h
programs/imagex.c
src/extract.c
src/pathlist.c
src/scan.c
src/textfile.c
src/win32_apply.c
tests/test-imagex-update_and_extract

diff --git a/NEWS b/NEWS
index 7a27202..5d7b94a 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -17,6 +17,9 @@ Version 1.13.0-BETA:
        existing integrity tables.  This can be useful to avoid unwanted
        verification of large WIM files, e.g. WIMs given by '--delta-from'.
 
+       'wimextract' now reads a pathlist file from standard input when "@-" is
+       given as an argument.
+
        wimsplit (API: wimlib_split()) now correctly handles a dot in the path
        to the first split WIM part, prior to the filename extension.
 
index 99c73b4..86275a2 100644 (file)
@@ -22,7 +22,9 @@ 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.
+paths to extract.  If a \fILISTFILE\fR is "-" (i.e. the whole argument is "@-"),
+then the listfile is read from standard input.  See \fBPATHS AND LISTFILES\fR
+for more details.
 .PP
 By default, files and directories are extracted to the current directory.  Use
 \fB--dest-dir\fR to select a different destination directory.  Alternatively,
index 047716c..c2b5b81 100644 (file)
@@ -3055,6 +3055,9 @@ wimlib_extract_image_from_pipe_with_progress(int pipe_fd,
  * are otherwise delimited by the newline character.  However, quotes will be
  * stripped if present.
  *
+ * If @p path_list_file is @c NULL, then the pathlist file is read from standard
+ * input.
+ *
  * The error codes are the same as those returned by wimlib_extract_paths(),
  * except that wimlib_extract_pathlist() returns an appropriate error code if it
  * cannot read the path list file (e.g. ::WIMLIB_ERR_OPEN, ::WIMLIB_ERR_STAT,
index 5854abb..d79f8b1 100644 (file)
@@ -25,22 +25,13 @@ struct text_file_section {
 
 #define LOAD_TEXT_FILE_REMOVE_QUOTES 0x00000001
 #define LOAD_TEXT_FILE_NO_WARNINGS   0x00000002
+#define LOAD_TEXT_FILE_ALLOW_STDIN   0x00000004
 
 extern int
-do_load_text_file(const tchar *path,
-                 const void *buf, size_t bufsize, void **mem_ret,
-                 const struct text_file_section *pos_sections,
-                 int num_pos_sections, int flags,
-                 line_mangle_t mangle_line);
-
-static inline int
-load_text_file(const tchar *path, void **mem_ret,
+load_text_file(const tchar *path, const void *buf, size_t bufsize,
+              void **mem_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, mem_ret,
-                                pos_sections, num_pos_sections,
-                                LOAD_TEXT_FILE_REMOVE_QUOTES, mangle_line);
-}
+              int num_pos_sections,
+              int flags, line_mangle_t mangle_line);
 
 #endif /* _WIMLIB_TEXTFILE_H_ */
index e69d74f..ea0815a 100644 (file)
@@ -3308,9 +3308,15 @@ imagex_extract(int argc, tchar **argv, int cmd)
                        argc -= num_paths;
                        argv += num_paths;
                } else {
+                       const tchar *listfile = argv[0] + 1;
+
+                       if (!tstrcmp(listfile, T("-"))) {
+                               tputs(T("Reading pathlist file from standard input..."));
+                               listfile = NULL;
+                       }
+
                        ret = wimlib_extract_pathlist(wim, image, dest_dir,
-                                                     argv[0] + 1,
-                                                     extract_flags);
+                                                     listfile, extract_flags);
                        argc--;
                        argv++;
                }
index cd1dd2c..49564f4 100644 (file)
@@ -1930,7 +1930,7 @@ wimlib_extract_pathlist(WIMStruct *wim, int image, const tchar *target,
        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);
+                     path_list_file ? path_list_file : T("<stdin>"));
                return ret;
        }
 
index 9ce3929..11bd846 100644 (file)
@@ -41,7 +41,9 @@ read_path_list_file(const tchar *listfile,
        void *buf;
        int ret;
 
-       ret = load_text_file(listfile, &buf, &tmp, 1, NULL);
+       ret = load_text_file(listfile, NULL, 0, &buf, &tmp, 1,
+                            LOAD_TEXT_FILE_REMOVE_QUOTES |
+                            LOAD_TEXT_FILE_ALLOW_STDIN, NULL);
        if (ret)
                return ret;
 
index ab1f527..da6fc09 100644 (file)
@@ -214,9 +214,9 @@ read_capture_config(const tchar *config_file, const void *buf,
        };
        void *mem;
 
-       ret = do_load_text_file(config_file, buf, bufsize, &mem,
-                               sections, ARRAY_LEN(sections),
-                               LOAD_TEXT_FILE_REMOVE_QUOTES, mangle_pat);
+       ret = load_text_file(config_file, buf, bufsize, &mem,
+                            sections, ARRAY_LEN(sections),
+                            LOAD_TEXT_FILE_REMOVE_QUOTES, mangle_pat);
        if (ret) {
                ERROR("Failed to load capture configuration file \"%"TS"\"",
                      config_file);
index 114772c..2e57e2f 100644 (file)
 #include "wimlib/util.h"
 
 static int
+stdin_get_contents(void **buf_ret, size_t *bufsize_ret)
+{
+       char *buf = NULL;
+       size_t filled = 0;
+       size_t capacity = 0;
+
+       do {
+               size_t new_capacity = (capacity * 2) + 256;
+               char *new_buf;
+
+               if (new_capacity <= capacity ||
+                   !(new_buf = REALLOC(buf, new_capacity))) {
+                       ERROR("Too much data sent on stdin!");
+                       FREE(buf);
+                       return WIMLIB_ERR_INVALID_PARAM;
+               }
+               buf = new_buf;
+               capacity = new_capacity;
+               filled += fread(&buf[filled], 1, capacity - filled, stdin);
+       } while (filled == capacity);
+
+       if (!feof(stdin)) {
+               ERROR_WITH_ERRNO("Error reading stdin");
+               FREE(buf);
+               return WIMLIB_ERR_READ;
+       }
+       *buf_ret = buf;
+       *bufsize_ret = filled;
+       return 0;
+}
+
+static int
 read_file_contents(const tchar *path, void **buf_ret, size_t *bufsize_ret)
 {
        int raw_fd;
@@ -46,9 +78,6 @@ read_file_contents(const tchar *path, void **buf_ret, size_t *bufsize_ret)
        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);
@@ -273,13 +302,14 @@ parse_text_file(const tchar *path, tchar *buf, size_t buflen,
 }
 
 /**
- * do_load_text_file -
+ * 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.
+ * Read and parse lines from a text file given as an on-disk file, standard
+ * input, 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.
+ *     If @buf is NULL, then either the path to the file on-disk to read, or
+ *     NULL to read from standard input.  Else, 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.
@@ -297,7 +327,7 @@ parse_text_file(const tchar *path, tchar *buf, size_t buflen,
  * @num_pos_sections
  *     Number of entries in the @pos_sections array.
  * @flags
- *     Flags: LOAD_TEXT_FILE_REMOVE_QUOTES, LOAD_TEXT_FILE_NO_WARNINGS.
+ *     Flags: LOAD_TEXT_FILE_* flags.
  * @mangle_line
  *     Optional callback to validate and/or modify each line being read.
  *
@@ -307,34 +337,40 @@ parse_text_file(const tchar *path, tchar *buf, size_t buflen,
  * LOAD_TEXT_FILE_NO_WARNINGS is specified.
  */
 int
-do_load_text_file(const tchar *path,
-                 const void *buf, size_t bufsize,
-                 void **mem_ret,
-                 const struct text_file_section *pos_sections,
-                 int num_pos_sections,
-                 int flags,
-                 line_mangle_t mangle_line)
+load_text_file(const tchar *path, const void *buf, size_t bufsize,
+              void **mem_ret,
+              const struct text_file_section *pos_sections,
+              int num_pos_sections,
+              int flags, line_mangle_t mangle_line)
 {
        int ret;
-       bool pathmode = (buf == NULL);
+       bool is_filemode = (buf == NULL);
+       bool is_stdin = (is_filemode && path == NULL);
        tchar *tstr;
        size_t tstr_nchars;
 
-       if (pathmode) {
-               ret = read_file_contents(path, (void **)&buf, &bufsize);
+       if (is_stdin && !(flags & LOAD_TEXT_FILE_ALLOW_STDIN))
+               return WIMLIB_ERR_INVALID_PARAM;
+
+       if (is_filemode) {
+               if (is_stdin)
+                       ret = stdin_get_contents((void **)&buf, &bufsize);
+               else
+                       ret = read_file_contents(path, (void **)&buf, &bufsize);
                if (ret)
                        return ret;
        }
 
        ret = translate_text_buffer(buf, bufsize, &tstr, &tstr_nchars);
-       if (pathmode)
+       if (is_filemode)
                FREE((void *)buf);
        if (ret)
                return ret;
 
        tstr[tstr_nchars++] = T('\n');
 
-       ret = parse_text_file(path, tstr, tstr_nchars, pos_sections,
+       ret = parse_text_file(is_stdin ? T("<stdin>") : path,
+                             tstr, tstr_nchars, pos_sections,
                              num_pos_sections, flags, mangle_line);
        if (ret) {
                for (int i = 0; i < num_pos_sections; i++)
index f677918..c55847d 100644 (file)
@@ -415,10 +415,10 @@ load_prepopulate_pats(struct win32_apply_ctx *ctx)
        sec.name = T("PrepopulateList");
        sec.strings = strings;
 
-       ret = do_load_text_file(path, buf, blob->size, &mem, &sec, 1,
-                               LOAD_TEXT_FILE_REMOVE_QUOTES |
-                                       LOAD_TEXT_FILE_NO_WARNINGS,
-                               mangle_pat);
+       ret = load_text_file(path, buf, blob->size, &mem, &sec, 1,
+                            LOAD_TEXT_FILE_REMOVE_QUOTES |
+                            LOAD_TEXT_FILE_NO_WARNINGS,
+                            mangle_pat);
        STATIC_ASSERT(OS_PREFERRED_PATH_SEPARATOR == WIM_PATH_SEPARATOR);
        FREE(buf);
        if (ret) {
index 202371b..6c87716 100755 (executable)
@@ -225,6 +225,16 @@ wimextract test.wim 1 @pathlist --dest-dir=out.dir
 ../tree-cmp hello2 out.dir/hello2
 [ ! -e out.dir/otherfile ]
 
+msg "Testing path list extract (stdin)"
+rm -rf out.dir
+wimextract test.wim 1 @- --dest-dir=out.dir << EOF
+hello1
+hello2
+EOF
+../tree-cmp hello1 out.dir/hello1
+../tree-cmp hello2 out.dir/hello2
+[ ! -e out.dir/otherfile ]
+
 msg "Testing path list extract (w/ wildcard)"
 cat > pathlist << EOF
 hello*