From: Eric Biggers Date: Sat, 21 Jul 2018 18:38:08 +0000 (-0700) Subject: wimextract: allow specifying listfile on stdin X-Git-Tag: v1.13.0~7 X-Git-Url: https://wimlib.net/git/?p=wimlib;a=commitdiff_plain;h=075e14ac8b227141bc68fec5486b0644dcab8013 wimextract: allow specifying listfile on stdin --- diff --git a/NEWS b/NEWS index 7a27202f..5d7b94af 100644 --- 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. diff --git a/doc/man1/wimextract.1 b/doc/man1/wimextract.1 index 99c73b4f..86275a2b 100644 --- a/doc/man1/wimextract.1 +++ b/doc/man1/wimextract.1 @@ -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, diff --git a/include/wimlib.h b/include/wimlib.h index 047716c0..c2b5b81f 100644 --- a/include/wimlib.h +++ b/include/wimlib.h @@ -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, diff --git a/include/wimlib/textfile.h b/include/wimlib/textfile.h index 5854abb5..d79f8b11 100644 --- a/include/wimlib/textfile.h +++ b/include/wimlib/textfile.h @@ -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_ */ diff --git a/programs/imagex.c b/programs/imagex.c index e69d74fd..ea0815a6 100644 --- a/programs/imagex.c +++ b/programs/imagex.c @@ -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++; } diff --git a/src/extract.c b/src/extract.c index cd1dd2c1..49564f4f 100644 --- a/src/extract.c +++ b/src/extract.c @@ -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("")); return ret; } diff --git a/src/pathlist.c b/src/pathlist.c index 9ce3929e..11bd8461 100644 --- a/src/pathlist.c +++ b/src/pathlist.c @@ -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; diff --git a/src/scan.c b/src/scan.c index ab1f5273..da6fc09e 100644 --- a/src/scan.c +++ b/src/scan.c @@ -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); diff --git a/src/textfile.c b/src/textfile.c index 114772cf..2e57e2f6 100644 --- a/src/textfile.c +++ b/src/textfile.c @@ -36,6 +36,38 @@ #include "wimlib/textfile.h" #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) { @@ -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("") : path, + tstr, tstr_nchars, pos_sections, num_pos_sections, flags, mangle_line); if (ret) { for (int i = 0; i < num_pos_sections; i++) diff --git a/src/win32_apply.c b/src/win32_apply.c index f677918b..c55847d2 100644 --- a/src/win32_apply.c +++ b/src/win32_apply.c @@ -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) { diff --git a/tests/test-imagex-update_and_extract b/tests/test-imagex-update_and_extract index 202371b1..6c877166 100755 --- a/tests/test-imagex-update_and_extract +++ b/tests/test-imagex-update_and_extract @@ -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*