]> wimlib.net Git - wimlib/blob - programs/imagex.c
ed0a977625ed136859fb3e38c52123c9d78b2cdd
[wimlib] / programs / imagex.c
1 /*
2  * imagex.c
3  *
4  * Use wimlib to create, modify, extract, mount, unmount, or display information
5  * about a WIM file
6  */
7
8 /*
9  * Copyright (C) 2012, 2013 Eric Biggers
10  *
11  * This program is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation, either version 3 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
23  */
24
25 #include "config.h"
26 #include "wimlib.h"
27 #include "wimlib_tchar.h"
28
29 #include <ctype.h>
30 #include <errno.h>
31
32 #include <inttypes.h>
33 #include <libgen.h>
34 #include <limits.h>
35 #include <stdarg.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <sys/stat.h>
39 #include <unistd.h>
40 #include <locale.h>
41
42 #ifdef HAVE_ALLOCA_H
43 #include <alloca.h>
44 #endif
45
46 #ifdef __WIN32__
47 #  include "imagex-win32.h"
48 #  define tbasename     win32_wbasename
49 #  define tglob         win32_wglob
50 #else /* __WIN32__ */
51 #  include <glob.h>
52 #  include <getopt.h>
53 #  include <langinfo.h>
54 #  define tbasename     basename
55 #  define tglob         glob
56 #endif /* !__WIN32 */
57
58
59 #define ARRAY_LEN(array) (sizeof(array) / sizeof(array[0]))
60
61 #define for_opt(c, opts) while ((c = getopt_long_only(argc, (tchar**)argv, T(""), \
62                                 opts, NULL)) != -1)
63
64 enum imagex_op_type {
65         APPEND = 0,
66         APPLY,
67         CAPTURE,
68         DELETE,
69         DIR,
70         EXPORT,
71         EXTRACT,
72         INFO,
73         JOIN,
74         MOUNT,
75         MOUNTRW,
76         OPTIMIZE,
77         SPLIT,
78         UNMOUNT,
79         UPDATE,
80 };
81
82 static void usage(int cmd_type);
83 static void usage_all();
84
85 static bool imagex_be_quiet = false;
86
87
88 static const tchar *usage_strings[] = {
89 [APPEND] =
90 T(
91 IMAGEX_PROGNAME" append (DIRECTORY | NTFS_VOLUME) WIMFILE [IMAGE_NAME]\n"
92 "                     [DESCRIPTION] [--boot] [--check] [--flags EDITION_ID]\n"
93 "                     [--verbose] [--dereference] [--config=FILE]\n"
94 "                     [--threads=NUM_THREADS] [--rebuild] [--unix-data]\n"
95 "                     [--source-list] [--no-acls] [--strict-acls]\n"
96 "                     [--rpfix] [--norpfix]\n"
97 ),
98 [APPLY] =
99 T(
100 IMAGEX_PROGNAME" apply WIMFILE [IMAGE_NUM | IMAGE_NAME | all]\n"
101 "                    (DIRECTORY | NTFS_VOLUME) [--check] [--hardlink]\n"
102 "                    [--symlink] [--verbose] [--ref=\"GLOB\"] [--unix-data]\n"
103 "                    [--no-acls] [--strict-acls] [--rpfix] [--norpfix]\n"
104 ),
105 [CAPTURE] =
106 T(
107 IMAGEX_PROGNAME" capture (DIRECTORY | NTFS_VOLUME) WIMFILE [IMAGE_NAME]\n"
108 "                      [DESCRIPTION] [--boot] [--check] [--compress=TYPE]\n"
109 "                      [--flags EDITION_ID] [--verbose] [--dereference]\n"
110 "                      [--config=FILE] [--threads=NUM_THREADS] [--unix-data]\n"
111 "                      [--source-list] [--no-acls] [--strict-acls]\n"
112 "                      [--rpfix] [--norpfix]\n"
113 ),
114 [DELETE] =
115 T(
116 IMAGEX_PROGNAME" delete WIMFILE (IMAGE_NUM | IMAGE_NAME | all) [--check] [--soft]\n"
117 ),
118 [DIR] =
119 T(
120 IMAGEX_PROGNAME" dir WIMFILE (IMAGE_NUM | IMAGE_NAME | all)\n"
121 ),
122 [EXPORT] =
123 T(
124 IMAGEX_PROGNAME" export SRC_WIMFILE (SRC_IMAGE_NUM | SRC_IMAGE_NAME | all ) \n"
125 "              DEST_WIMFILE [DEST_IMAGE_NAME] [DEST_IMAGE_DESCRIPTION]\n"
126 "              [--boot] [--check] [--compress=TYPE] [--ref=\"GLOB\"]\n"
127 "              [--threads=NUM_THREADS] [--rebuild]\n"
128 ),
129 [EXTRACT] =
130 T(
131 IMAGEX_PROGNAME" extract WIMFILE (IMAGE_NUM | IMAGE_NAME) [PATH...]\n"
132 "              [--check] [--ref=\"GLOB\"] [--verbose] [--unix-data]\n"
133 "              [--no-acls] [--strict-acls] [--to-stdout] [--dest-dir=DIR]\n"
134 ),
135 [INFO] =
136 T(
137 IMAGEX_PROGNAME" info WIMFILE [IMAGE_NUM | IMAGE_NAME] [NEW_NAME]\n"
138 "                   [NEW_DESC] [--boot] [--check] [--header] [--lookup-table]\n"
139 "                   [--xml] [--extract-xml FILE] [--metadata]\n"
140 ),
141 [JOIN] =
142 T(
143 IMAGEX_PROGNAME" join [--check] WIMFILE SPLIT_WIM...\n"
144 ),
145 [MOUNT] =
146 T(
147 IMAGEX_PROGNAME" mount WIMFILE (IMAGE_NUM | IMAGE_NAME) DIRECTORY\n"
148 "                    [--check] [--debug] [--streams-interface=INTERFACE]\n"
149 "                    [--ref=\"GLOB\"] [--unix-data] [--allow-other]\n"
150 ),
151 [MOUNTRW] =
152 T(
153 IMAGEX_PROGNAME" mountrw WIMFILE [IMAGE_NUM | IMAGE_NAME] DIRECTORY\n"
154 "                      [--check] [--debug] [--streams-interface=INTERFACE]\n"
155 "                      [--staging-dir=DIR] [--unix-data] [--allow-other]\n"
156 ),
157 [OPTIMIZE] =
158 T(
159 IMAGEX_PROGNAME" optimize WIMFILE [--check] [--recompress]\n"
160 "                      [--threads=NUM_THREADS]\n"
161 ),
162 [SPLIT] =
163 T(
164 IMAGEX_PROGNAME" split WIMFILE SPLIT_WIMFILE PART_SIZE_MB [--check]\n"
165 ),
166 [UNMOUNT] =
167 T(
168 IMAGEX_PROGNAME" unmount DIRECTORY [--commit] [--check] [--rebuild]\n"
169 ),
170 [UPDATE] =
171 T(
172 IMAGEX_PROGNAME" update WIMFILE IMAGE [--check] [--rebuild]\n"
173 "                       [--threads=NUM_THREADS] [DEFAULT_ADD_OPTIONS]\n"
174 "                       [DEFAULT_DELETE_OPTIONS] < CMDFILE\n"
175 ),
176 };
177
178
179 static void
180 recommend_man_page(const tchar *cmd_name)
181 {
182 #ifdef __WIN32__
183         tprintf(T("See "IMAGEX_PROGNAME"-%"TS".pdf in the "
184                   "doc directory for more details.\n"),
185                 cmd_name);
186 #else
187         tprintf(T("Try `man "IMAGEX_PROGNAME"-%"TS"' "
188                   "for more details.\n"), cmd_name);
189 #endif
190 }
191
192 enum {
193         IMAGEX_ALLOW_OTHER_OPTION,
194         IMAGEX_BOOT_OPTION,
195         IMAGEX_CHECK_OPTION,
196         IMAGEX_COMMIT_OPTION,
197         IMAGEX_COMPRESS_OPTION,
198         IMAGEX_CONFIG_OPTION,
199         IMAGEX_DEBUG_OPTION,
200         IMAGEX_DEREFERENCE_OPTION,
201         IMAGEX_DEST_DIR_OPTION,
202         IMAGEX_EXTRACT_XML_OPTION,
203         IMAGEX_FLAGS_OPTION,
204         IMAGEX_FORCE_OPTION,
205         IMAGEX_HARDLINK_OPTION,
206         IMAGEX_HEADER_OPTION,
207         IMAGEX_LOOKUP_TABLE_OPTION,
208         IMAGEX_METADATA_OPTION,
209         IMAGEX_NORPFIX_OPTION,
210         IMAGEX_NO_ACLS_OPTION,
211         IMAGEX_REBUILD_OPTION,
212         IMAGEX_RECOMPRESS_OPTION,
213         IMAGEX_RECURSIVE_OPTION,
214         IMAGEX_REF_OPTION,
215         IMAGEX_RPFIX_OPTION,
216         IMAGEX_SOFT_OPTION,
217         IMAGEX_SOURCE_LIST_OPTION,
218         IMAGEX_STAGING_DIR_OPTION,
219         IMAGEX_STREAMS_INTERFACE_OPTION,
220         IMAGEX_STRICT_ACLS_OPTION,
221         IMAGEX_SYMLINK_OPTION,
222         IMAGEX_THREADS_OPTION,
223         IMAGEX_TO_STDOUT_OPTION,
224         IMAGEX_UNIX_DATA_OPTION,
225         IMAGEX_VERBOSE_OPTION,
226         IMAGEX_XML_OPTION,
227 };
228
229 static const struct option apply_options[] = {
230         {T("check"),       no_argument,       NULL, IMAGEX_CHECK_OPTION},
231         {T("hardlink"),    no_argument,       NULL, IMAGEX_HARDLINK_OPTION},
232         {T("symlink"),     no_argument,       NULL, IMAGEX_SYMLINK_OPTION},
233         {T("verbose"),     no_argument,       NULL, IMAGEX_VERBOSE_OPTION},
234         {T("ref"),         required_argument, NULL, IMAGEX_REF_OPTION},
235         {T("unix-data"),   no_argument,       NULL, IMAGEX_UNIX_DATA_OPTION},
236         {T("noacls"),      no_argument,       NULL, IMAGEX_NO_ACLS_OPTION},
237         {T("no-acls"),     no_argument,       NULL, IMAGEX_NO_ACLS_OPTION},
238         {T("strict-acls"), no_argument,       NULL, IMAGEX_STRICT_ACLS_OPTION},
239         {T("rpfix"),       no_argument,       NULL, IMAGEX_RPFIX_OPTION},
240         {T("norpfix"),     no_argument,       NULL, IMAGEX_NORPFIX_OPTION},
241         {NULL, 0, NULL, 0},
242 };
243 static const struct option capture_or_append_options[] = {
244         {T("boot"),        no_argument,       NULL, IMAGEX_BOOT_OPTION},
245         {T("check"),       no_argument,       NULL, IMAGEX_CHECK_OPTION},
246         {T("compress"),    required_argument, NULL, IMAGEX_COMPRESS_OPTION},
247         {T("config"),      required_argument, NULL, IMAGEX_CONFIG_OPTION},
248         {T("dereference"), no_argument,       NULL, IMAGEX_DEREFERENCE_OPTION},
249         {T("flags"),       required_argument, NULL, IMAGEX_FLAGS_OPTION},
250         {T("verbose"),     no_argument,       NULL, IMAGEX_VERBOSE_OPTION},
251         {T("threads"),     required_argument, NULL, IMAGEX_THREADS_OPTION},
252         {T("rebuild"),     no_argument,       NULL, IMAGEX_REBUILD_OPTION},
253         {T("unix-data"),   no_argument,       NULL, IMAGEX_UNIX_DATA_OPTION},
254         {T("source-list"), no_argument,       NULL, IMAGEX_SOURCE_LIST_OPTION},
255         {T("noacls"),      no_argument,       NULL, IMAGEX_NO_ACLS_OPTION},
256         {T("no-acls"),     no_argument,       NULL, IMAGEX_NO_ACLS_OPTION},
257         {T("strict-acls"), no_argument,       NULL, IMAGEX_STRICT_ACLS_OPTION},
258         {T("rpfix"),       no_argument,       NULL, IMAGEX_RPFIX_OPTION},
259         {T("norpfix"),     no_argument,       NULL, IMAGEX_NORPFIX_OPTION},
260         {NULL, 0, NULL, 0},
261 };
262 static const struct option delete_options[] = {
263         {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
264         {T("soft"),  no_argument, NULL, IMAGEX_SOFT_OPTION},
265         {NULL, 0, NULL, 0},
266 };
267
268 static const struct option export_options[] = {
269         {T("boot"),       no_argument,       NULL, IMAGEX_BOOT_OPTION},
270         {T("check"),      no_argument,       NULL, IMAGEX_CHECK_OPTION},
271         {T("compress"),   required_argument, NULL, IMAGEX_COMPRESS_OPTION},
272         {T("ref"),        required_argument, NULL, IMAGEX_REF_OPTION},
273         {T("threads"),    required_argument, NULL, IMAGEX_THREADS_OPTION},
274         {T("rebuild"),    no_argument,       NULL, IMAGEX_REBUILD_OPTION},
275         {NULL, 0, NULL, 0},
276 };
277
278 static const struct option extract_options[] = {
279         {T("check"),       no_argument,       NULL, IMAGEX_CHECK_OPTION},
280         {T("verbose"),     no_argument,       NULL, IMAGEX_VERBOSE_OPTION},
281         {T("ref"),         required_argument, NULL, IMAGEX_REF_OPTION},
282         {T("unix-data"),   no_argument,       NULL, IMAGEX_UNIX_DATA_OPTION},
283         {T("noacls"),      no_argument,       NULL, IMAGEX_NO_ACLS_OPTION},
284         {T("no-acls"),     no_argument,       NULL, IMAGEX_NO_ACLS_OPTION},
285         {T("strict-acls"), no_argument,       NULL, IMAGEX_STRICT_ACLS_OPTION},
286         {T("dest-dir"),    required_argument, NULL, IMAGEX_DEST_DIR_OPTION},
287         {T("to-stdout"),   no_argument,       NULL, IMAGEX_TO_STDOUT_OPTION},
288         {NULL, 0, NULL, 0},
289 };
290
291 static const struct option info_options[] = {
292         {T("boot"),         no_argument,       NULL, IMAGEX_BOOT_OPTION},
293         {T("check"),        no_argument,       NULL, IMAGEX_CHECK_OPTION},
294         {T("extract-xml"),  required_argument, NULL, IMAGEX_EXTRACT_XML_OPTION},
295         {T("header"),       no_argument,       NULL, IMAGEX_HEADER_OPTION},
296         {T("lookup-table"), no_argument,       NULL, IMAGEX_LOOKUP_TABLE_OPTION},
297         {T("metadata"),     no_argument,       NULL, IMAGEX_METADATA_OPTION},
298         {T("xml"),          no_argument,       NULL, IMAGEX_XML_OPTION},
299         {NULL, 0, NULL, 0},
300 };
301
302 static const struct option join_options[] = {
303         {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
304         {NULL, 0, NULL, 0},
305 };
306
307 static const struct option mount_options[] = {
308         {T("check"),             no_argument,       NULL, IMAGEX_CHECK_OPTION},
309         {T("debug"),             no_argument,       NULL, IMAGEX_DEBUG_OPTION},
310         {T("streams-interface"), required_argument, NULL, IMAGEX_STREAMS_INTERFACE_OPTION},
311         {T("ref"),               required_argument, NULL, IMAGEX_REF_OPTION},
312         {T("staging-dir"),       required_argument, NULL, IMAGEX_STAGING_DIR_OPTION},
313         {T("unix-data"),         no_argument,       NULL, IMAGEX_UNIX_DATA_OPTION},
314         {T("allow-other"),       no_argument,       NULL, IMAGEX_ALLOW_OTHER_OPTION},
315         {NULL, 0, NULL, 0},
316 };
317
318 static const struct option optimize_options[] = {
319         {T("check"),      no_argument, NULL, IMAGEX_CHECK_OPTION},
320         {T("recompress"), no_argument, NULL, IMAGEX_RECOMPRESS_OPTION},
321         {T("threads"),    required_argument, NULL, IMAGEX_THREADS_OPTION},
322         {NULL, 0, NULL, 0},
323 };
324
325 static const struct option split_options[] = {
326         {T("check"), no_argument, NULL, IMAGEX_CHECK_OPTION},
327         {NULL, 0, NULL, 0},
328 };
329
330 static const struct option unmount_options[] = {
331         {T("commit"),  no_argument, NULL, IMAGEX_COMMIT_OPTION},
332         {T("check"),   no_argument, NULL, IMAGEX_CHECK_OPTION},
333         {T("rebuild"), no_argument, NULL, IMAGEX_REBUILD_OPTION},
334         {NULL, 0, NULL, 0},
335 };
336
337 static const struct option update_options[] = {
338         /* Careful: some of the options here set the defaults for update
339          * commands, but the flags given to an actual update command (and not to
340          * `imagex update' itself are also handled in
341          * update_command_add_option().  */
342         {T("threads"),     required_argument, NULL, IMAGEX_THREADS_OPTION},
343         {T("check"),       no_argument,       NULL, IMAGEX_CHECK_OPTION},
344         {T("rebuild"),     no_argument,       NULL, IMAGEX_REBUILD_OPTION},
345         {T("force"),       no_argument,       NULL, IMAGEX_FORCE_OPTION},
346         {T("recursive"),   no_argument,       NULL, IMAGEX_RECURSIVE_OPTION},
347         {T("config"),      required_argument, NULL, IMAGEX_CONFIG_OPTION},
348         {T("dereference"), no_argument,       NULL, IMAGEX_DEREFERENCE_OPTION},
349         {T("unix-data"),   no_argument,       NULL, IMAGEX_UNIX_DATA_OPTION},
350         {T("noacls"),      no_argument,       NULL, IMAGEX_NO_ACLS_OPTION},
351         {T("no-acls"),     no_argument,       NULL, IMAGEX_NO_ACLS_OPTION},
352         {T("strict-acls"), no_argument,       NULL, IMAGEX_STRICT_ACLS_OPTION},
353         {NULL, 0, NULL, 0},
354 };
355
356 /* Print formatted error message to stderr. */
357 static void
358 imagex_error(const tchar *format, ...)
359 {
360         va_list va;
361         va_start(va, format);
362         tfputs(T("ERROR: "), stderr);
363         tvfprintf(stderr, format, va);
364         tputc(T('\n'), stderr);
365         va_end(va);
366 }
367
368 /* Print formatted error message to stderr. */
369 static void
370 imagex_error_with_errno(const tchar *format, ...)
371 {
372         int errno_save = errno;
373         va_list va;
374         va_start(va, format);
375         tfputs(T("ERROR: "), stderr);
376         tvfprintf(stderr, format, va);
377         tfprintf(stderr, T(": %"TS"\n"), tstrerror(errno_save));
378         va_end(va);
379 }
380
381 static int
382 verify_image_exists(int image, const tchar *image_name, const tchar *wim_name)
383 {
384         if (image == WIMLIB_NO_IMAGE) {
385                 imagex_error(T("\"%"TS"\" is not a valid image in \"%"TS"\"!\n"
386                              "       Please specify a 1-based image index or "
387                              "image name.\n"
388                              "       You may use `"IMAGEX_PROGNAME" info' to list the images "
389                              "contained in a WIM."),
390                              image_name, wim_name);
391                 return -1;
392         }
393         return 0;
394 }
395
396 static int
397 verify_image_is_single(int image)
398 {
399         if (image == WIMLIB_ALL_IMAGES) {
400                 imagex_error(T("Cannot specify all images for this action!"));
401                 return -1;
402         }
403         return 0;
404 }
405
406 static int
407 verify_image_exists_and_is_single(int image, const tchar *image_name,
408                                   const tchar *wim_name)
409 {
410         int ret;
411         ret = verify_image_exists(image, image_name, wim_name);
412         if (ret == 0)
413                 ret = verify_image_is_single(image);
414         return ret;
415 }
416
417 /* Parse the argument to --compress */
418 static int
419 get_compression_type(const tchar *optarg)
420 {
421         if (!tstrcasecmp(optarg, T("maximum")) || !tstrcasecmp(optarg, T("lzx")))
422                 return WIMLIB_COMPRESSION_TYPE_LZX;
423         else if (!tstrcasecmp(optarg, T("fast")) || !tstrcasecmp(optarg, T("xpress")))
424                 return WIMLIB_COMPRESSION_TYPE_XPRESS;
425         else if (!tstrcasecmp(optarg, T("none")))
426                 return WIMLIB_COMPRESSION_TYPE_NONE;
427         else {
428                 imagex_error(T("Invalid compression type \"%"TS"\"! Must be "
429                              "\"maximum\", \"fast\", or \"none\"."), optarg);
430                 return WIMLIB_COMPRESSION_TYPE_INVALID;
431         }
432 }
433
434 /* Returns the size of a file given its name, or -1 if the file does not exist
435  * or its size cannot be determined.  */
436 static off_t
437 file_get_size(const tchar *filename)
438 {
439         struct stat st;
440         if (tstat(filename, &st) == 0)
441                 return st.st_size;
442         else
443                 return (off_t)-1;
444 }
445
446 tchar pat_ntfs_log[]                  = T("/$ntfs.log");
447 tchar pat_hiberfil_sys[]              = T("/hiberfil.sys");
448 tchar pat_pagefile_sys[]              = T("/pagefile.sys");
449 tchar pat_system_volume_information[] = T("/System Volume Information");
450 tchar pat_recycler[]                  = T("/RECYCLER");
451 tchar pat_windows_csc[]               = T("/Windows/CSC");
452
453 tchar *default_pats[] = {
454         pat_ntfs_log,
455         pat_hiberfil_sys,
456         pat_pagefile_sys,
457         pat_system_volume_information,
458         pat_recycler,
459         pat_windows_csc,
460 };
461
462 static struct wimlib_capture_config default_capture_config = {
463         .exclusion_pats = {
464                 .num_pats = sizeof(default_pats) / sizeof(*default_pats),
465                 .pats = default_pats,
466         },
467 };
468
469 enum {
470         PARSE_STRING_SUCCESS = 0,
471         PARSE_STRING_FAILURE = 1,
472         PARSE_STRING_NONE = 2,
473 };
474
475 /*
476  * Parses a string token from an array of characters.
477  *
478  * Tokens are either whitespace-delimited, or double or single-quoted.
479  *
480  * @line_p:  Pointer to the pointer to the line of data.  Will be updated
481  *           to point past the string token iff the return value is
482  *           PARSE_STRING_SUCCESS.  If *len_p > 0, (*line_p)[*len_p - 1] must
483  *           be '\0'.
484  *
485  * @len_p:   @len_p initially stores the length of the line of data, which may
486  *           be 0, and it will be updated to the number of bytes remaining in
487  *           the line iff the return value is PARSE_STRING_SUCCESS.
488  *
489  * @fn_ret:  Iff the return value is PARSE_STRING_SUCCESS, a pointer to the
490  *           parsed string token will be returned here.
491  *
492  * Returns: PARSE_STRING_SUCCESS if a string token was successfully parsed; or
493  *          PARSE_STRING_FAILURE if the data was invalid due to a missing
494  *          closing quote; or PARSE_STRING_NONE if the line ended before the
495  *          beginning of a string token was found.
496  */
497 static int
498 parse_string(tchar **line_p, size_t *len_p, tchar **fn_ret)
499 {
500         size_t len = *len_p;
501         tchar *line = *line_p;
502         tchar *fn;
503         tchar quote_char;
504
505         /* Skip leading whitespace */
506         for (;;) {
507                 if (len == 0)
508                         return PARSE_STRING_NONE;
509                 if (!istspace(*line) && *line != T('\0'))
510                         break;
511                 line++;
512                 len--;
513         }
514         quote_char = *line;
515         if (quote_char == T('"') || quote_char == T('\'')) {
516                 /* Quoted string */
517                 line++;
518                 len--;
519                 fn = line;
520                 line = tmemchr(line, quote_char, len);
521                 if (!line) {
522                         imagex_error(T("Missing closing quote: %"TS), fn - 1);
523                         return PARSE_STRING_FAILURE;
524                 }
525         } else {
526                 /* Unquoted string.  Go until whitespace.  Line is terminated
527                  * by '\0', so no need to check 'len'. */
528                 fn = line;
529                 do {
530                         line++;
531                 } while (!istspace(*line) && *line != T('\0'));
532         }
533         *line = T('\0');
534         len -= line - fn;
535         *len_p = len;
536         *line_p = line;
537         *fn_ret = fn;
538         return PARSE_STRING_SUCCESS;
539 }
540
541 /* Parses a line of data (not an empty line or comment) in the source list file
542  * format.  (See the man page for 'wimlib-imagex capture' for details on this
543  * format and the meaning.)
544  *
545  * @line:  Line of data to be parsed.  line[len - 1] must be '\0', unless
546  *         len == 0.  The data in @line will be modified by this function call.
547  *
548  * @len:   Length of the line of data.
549  *
550  * @source:  On success, the capture source and target described by the line is
551  *           written into this destination.  Note that it will contain pointers
552  *           to data in the @line array.
553  *
554  * Returns true if the line was valid; false otherwise.  */
555 static bool
556 parse_source_list_line(tchar *line, size_t len,
557                        struct wimlib_capture_source *source)
558 {
559         /* SOURCE [DEST] */
560         int ret;
561         ret = parse_string(&line, &len, &source->fs_source_path);
562         if (ret != PARSE_STRING_SUCCESS)
563                 return false;
564         ret = parse_string(&line, &len, &source->wim_target_path);
565         if (ret == PARSE_STRING_NONE)
566                 source->wim_target_path = source->fs_source_path;
567         return ret != PARSE_STRING_FAILURE;
568 }
569
570 /* Returns %true if the given line of length @len > 0 is a comment or empty line
571  * in the source list file format. */
572 static bool
573 is_comment_line(const tchar *line, size_t len)
574 {
575         for (;;) {
576                 if (*line == T('#'))
577                         return true;
578                 if (!istspace(*line) && *line != T('\0'))
579                         return false;
580                 ++line;
581                 --len;
582                 if (len == 0)
583                         return true;
584         }
585 }
586
587 static ssize_t
588 text_file_count_lines(tchar **contents_p, size_t *nchars_p)
589 {
590         ssize_t nlines = 0;
591         tchar *contents = *contents_p;
592         size_t nchars = *nchars_p;
593         size_t i;
594
595         for (i = 0; i < nchars; i++)
596                 if (contents[i] == T('\n'))
597                         nlines++;
598
599         /* Handle last line not terminated by a newline */
600         if (nchars != 0 && contents[nchars - 1] != T('\n')) {
601                 contents = realloc(contents, (nchars + 1) * sizeof(tchar));
602                 if (!contents) {
603                         imagex_error(T("Out of memory!"));
604                         return -1;
605                 }
606                 contents[nchars] = T('\n');
607                 *contents_p = contents;
608                 nchars++;
609                 nlines++;
610         }
611         *nchars_p = nchars;
612         return nlines;
613 }
614
615 /* Parses a file in the source list format.  (See the man page for
616  * 'wimlib-imagex capture' for details on this format and the meaning.)
617  *
618  * @source_list_contents:  Contents of the source list file.  Note that this
619  *                         buffer will be modified to save memory allocations,
620  *                         and cannot be freed until the returned array of
621  *                         wimlib_capture_source's has also been freed.
622  *
623  * @source_list_nbytes:    Number of bytes of data in the @source_list_contents
624  *                         buffer.
625  *
626  * @nsources_ret:          On success, the length of the returned array is
627  *                         returned here.
628  *
629  * Returns:   An array of `struct wimlib_capture_source's that can be passed to
630  * the wimlib_add_image_multisource() function to specify how a WIM image is to
631  * be created.  */
632 static struct wimlib_capture_source *
633 parse_source_list(tchar **source_list_contents_p, size_t source_list_nchars,
634                   size_t *nsources_ret)
635 {
636         ssize_t nlines;
637         tchar *p;
638         struct wimlib_capture_source *sources;
639         size_t i, j;
640
641         nlines = text_file_count_lines(source_list_contents_p,
642                                        &source_list_nchars);
643         if (nlines < 0)
644                 return NULL;
645
646         /* Always allocate at least 1 slot, just in case the implementation of
647          * calloc() returns NULL if 0 bytes are requested. */
648         sources = calloc(nlines ?: 1, sizeof(*sources));
649         if (!sources) {
650                 imagex_error(T("out of memory"));
651                 return NULL;
652         }
653         p = *source_list_contents_p;
654         j = 0;
655         for (i = 0; i < nlines; i++) {
656                 /* XXX: Could use rawmemchr() here instead, but it may not be
657                  * available on all platforms. */
658                 tchar *endp = tmemchr(p, T('\n'), source_list_nchars);
659                 size_t len = endp - p + 1;
660                 *endp = T('\0');
661                 if (!is_comment_line(p, len)) {
662                         if (!parse_source_list_line(p, len, &sources[j++])) {
663                                 free(sources);
664                                 return NULL;
665                         }
666                 }
667                 p = endp + 1;
668
669         }
670         *nsources_ret = j;
671         return sources;
672 }
673
674
675 enum capture_config_section {
676         CAPTURE_CONFIG_NO_SECTION,
677         CAPTURE_CONFIG_EXCLUSION_SECTION,
678         CAPTURE_CONFIG_EXCLUSION_EXCEPTION_SECTION,
679         CAPTURE_CONFIG_IGNORE_SECTION,
680 };
681
682 enum {
683         CAPTURE_CONFIG_INVALID_SECTION,
684         CAPTURE_CONFIG_CHANGED_SECTION,
685         CAPTURE_CONFIG_SAME_SECTION,
686 };
687
688 static int
689 check_config_section(tchar *line, size_t len,
690                      enum capture_config_section *cur_section)
691 {
692         while (istspace(*line))
693                 line++;
694
695         if (*line != T('['))
696                 return CAPTURE_CONFIG_SAME_SECTION;
697
698         line++;
699         tchar *endbrace = tstrrchr(line, T(']'));
700         if (!endbrace)
701                 return CAPTURE_CONFIG_SAME_SECTION;
702
703         if (!tmemcmp(line, T("ExclusionList"), endbrace - line)) {
704                 *cur_section = CAPTURE_CONFIG_EXCLUSION_SECTION;
705         } else if (!tmemcmp(line, T("ExclusionException"), endbrace - line)) {
706                 *cur_section = CAPTURE_CONFIG_EXCLUSION_EXCEPTION_SECTION;
707         } else if (!tmemcmp(line, T("CompressionExclusionList"), endbrace - line)) {
708                 *cur_section = CAPTURE_CONFIG_IGNORE_SECTION;
709                 tfputs(T("WARNING: Ignoring [CompressionExclusionList] section "
710                          "of capture config file\n"),
711                        stderr);
712         } else if (!tmemcmp(line, T("AlignmentList"), endbrace - line)) {
713                 *cur_section = CAPTURE_CONFIG_IGNORE_SECTION;
714                 tfputs(T("WARNING: Ignoring [AlignmentList] section "
715                          "of capture config file\n"),
716                        stderr);
717         } else {
718                 imagex_error(T("Invalid capture config file section \"%"TS"\""),
719                              line - 1);
720                 return CAPTURE_CONFIG_INVALID_SECTION;
721         }
722         return CAPTURE_CONFIG_CHANGED_SECTION;
723 }
724
725
726 static bool
727 pattern_list_add_pattern(struct wimlib_pattern_list *pat_list,
728                          tchar *pat)
729 {
730         if (pat_list->num_pats == pat_list->num_allocated_pats) {
731                 tchar **pats;
732                 size_t num_allocated_pats = pat_list->num_pats + 8;
733
734                 pats = realloc(pat_list->pats,
735                                num_allocated_pats * sizeof(pat_list->pats[0]));
736                 if (!pats) {
737                         imagex_error(T("Out of memory!"));
738                         return false;
739                 }
740                 pat_list->pats = pats;
741                 pat_list->num_allocated_pats = num_allocated_pats;
742         }
743         pat_list->pats[pat_list->num_pats++] = pat;
744         return true;
745 }
746
747 static bool
748 parse_capture_config_line(tchar *line, size_t len,
749                           enum capture_config_section *cur_section,
750                           struct wimlib_capture_config *config)
751 {
752         tchar *filename;
753         int ret;
754
755         ret = check_config_section(line, len, cur_section);
756         if (ret == CAPTURE_CONFIG_INVALID_SECTION)
757                 return false;
758         if (ret == CAPTURE_CONFIG_CHANGED_SECTION)
759                 return true;
760
761         switch (*cur_section) {
762         case CAPTURE_CONFIG_NO_SECTION:
763                 imagex_error(T("Line \"%"TS"\" is not in a section "
764                                "(such as [ExclusionList]"), line);
765                 return false;
766         case CAPTURE_CONFIG_EXCLUSION_SECTION:
767                 if (parse_string(&line, &len, &filename) != PARSE_STRING_SUCCESS)
768                         return false;
769                 return pattern_list_add_pattern(&config->exclusion_pats,
770                                                 filename);
771         case CAPTURE_CONFIG_EXCLUSION_EXCEPTION_SECTION:
772                 if (parse_string(&line, &len, &filename) != PARSE_STRING_SUCCESS)
773                         return false;
774                 return pattern_list_add_pattern(&config->exclusion_exception_pats,
775                                                 filename);
776         case CAPTURE_CONFIG_IGNORE_SECTION:
777                 return true;
778         }
779         return false;
780 }
781
782 static int
783 parse_capture_config(tchar **contents_p, size_t nchars,
784                      struct wimlib_capture_config *config)
785 {
786         ssize_t nlines;
787         tchar *p;
788         size_t i;
789         enum capture_config_section cur_section;
790
791         memset(config, 0, sizeof(*config));
792
793         nlines = text_file_count_lines(contents_p, &nchars);
794         if (nlines < 0)
795                 return -1;
796
797         cur_section = CAPTURE_CONFIG_NO_SECTION;
798         p = *contents_p;
799         for (i = 0; i < nlines; i++) {
800                 tchar *endp = tmemchr(p, T('\n'), nchars);
801                 size_t len = endp - p + 1;
802                 *endp = T('\0');
803                 if (!is_comment_line(p, len))
804                         if (!parse_capture_config_line(p, len, &cur_section, config))
805                                 return -1;
806                 p = endp + 1;
807
808         }
809         return 0;
810 }
811
812 /* Reads the contents of a file into memory. */
813 static char *
814 file_get_contents(const tchar *filename, size_t *len_ret)
815 {
816         struct stat stbuf;
817         void *buf = NULL;
818         size_t len;
819         FILE *fp;
820
821         if (tstat(filename, &stbuf) != 0) {
822                 imagex_error_with_errno(T("Failed to stat the file \"%"TS"\""), filename);
823                 goto out;
824         }
825         len = stbuf.st_size;
826
827         fp = tfopen(filename, T("rb"));
828         if (!fp) {
829                 imagex_error_with_errno(T("Failed to open the file \"%"TS"\""), filename);
830                 goto out;
831         }
832
833         buf = malloc(len);
834         if (!buf) {
835                 imagex_error(T("Failed to allocate buffer of %zu bytes to hold "
836                                "contents of file \"%"TS"\""), len, filename);
837                 goto out_fclose;
838         }
839         if (fread(buf, 1, len, fp) != len) {
840                 imagex_error_with_errno(T("Failed to read %zu bytes from the "
841                                           "file \"%"TS"\""), len, filename);
842                 goto out_free_buf;
843         }
844         *len_ret = len;
845         goto out_fclose;
846 out_free_buf:
847         free(buf);
848         buf = NULL;
849 out_fclose:
850         fclose(fp);
851 out:
852         return buf;
853 }
854
855 /* Read standard input until EOF and return the full contents in a malloc()ed
856  * buffer and the number of bytes of data in @len_ret.  Returns NULL on read
857  * error. */
858 static char *
859 stdin_get_contents(size_t *len_ret)
860 {
861         /* stdin can, of course, be a pipe or other non-seekable file, so the
862          * total length of the data cannot be pre-determined */
863         char *buf = NULL;
864         size_t newlen = 1024;
865         size_t pos = 0;
866         size_t inc = 1024;
867         for (;;) {
868                 char *p = realloc(buf, newlen);
869                 size_t bytes_read, bytes_to_read;
870                 if (!p) {
871                         imagex_error(T("out of memory while reading stdin"));
872                         break;
873                 }
874                 buf = p;
875                 bytes_to_read = newlen - pos;
876                 bytes_read = fread(&buf[pos], 1, bytes_to_read, stdin);
877                 pos += bytes_read;
878                 if (bytes_read != bytes_to_read) {
879                         if (feof(stdin)) {
880                                 *len_ret = pos;
881                                 return buf;
882                         } else {
883                                 imagex_error_with_errno(T("error reading stdin"));
884                                 break;
885                         }
886                 }
887                 newlen += inc;
888                 inc *= 3;
889                 inc /= 2;
890         }
891         free(buf);
892         return NULL;
893 }
894
895
896 static tchar *
897 translate_text_to_tstr(char *text, size_t num_bytes, size_t *num_tchars_ret)
898 {
899 #ifndef __WIN32__
900         /* On non-Windows, assume an ASCII-compatible encoding, such as UTF-8.
901          * */
902         *num_tchars_ret = num_bytes;
903         return text;
904 #else /* !__WIN32__ */
905         /* On Windows, translate the text to UTF-16LE */
906         wchar_t *text_wstr;
907         size_t num_wchars;
908
909         if (num_bytes >= 2 &&
910             ((text[0] == 0xff && text[1] == 0xfe) ||
911              (text[0] <= 0x7f && text[1] == 0x00)))
912         {
913                 /* File begins with 0xfeff, the BOM for UTF-16LE, or it begins
914                  * with something that looks like an ASCII character encoded as
915                  * a UTF-16LE code unit.  Assume the file is encoded as
916                  * UTF-16LE.  This is not a 100% reliable check. */
917                 num_wchars = num_bytes / 2;
918                 text_wstr = (wchar_t*)text;
919         } else {
920                 /* File does not look like UTF-16LE.  Assume it is encoded in
921                  * the current Windows code page.  I think these are always
922                  * ASCII-compatible, so any so-called "plain-text" (ASCII) files
923                  * should work as expected. */
924                 text_wstr = win32_mbs_to_wcs(text,
925                                              num_bytes,
926                                              &num_wchars);
927                 free(text);
928         }
929         *num_tchars_ret = num_wchars;
930         return text_wstr;
931 #endif /* __WIN32__ */
932 }
933
934 static tchar *
935 file_get_text_contents(const tchar *filename, size_t *num_tchars_ret)
936 {
937         char *contents;
938         size_t num_bytes;
939
940         contents = file_get_contents(filename, &num_bytes);
941         if (!contents)
942                 return NULL;
943         return translate_text_to_tstr(contents, num_bytes, num_tchars_ret);
944 }
945
946 static tchar *
947 stdin_get_text_contents(size_t *num_tchars_ret)
948 {
949         char *contents;
950         size_t num_bytes;
951
952         contents = stdin_get_contents(&num_bytes);
953         if (!contents)
954                 return NULL;
955         return translate_text_to_tstr(contents, num_bytes, num_tchars_ret);
956 }
957
958 /* Return 0 if a path names a file to which the current user has write access;
959  * -1 otherwise (and print an error message). */
960 static int
961 file_writable(const tchar *path)
962 {
963         int ret;
964         ret = taccess(path, W_OK);
965         if (ret != 0)
966                 imagex_error_with_errno(T("Can't modify \"%"TS"\""), path);
967         return ret;
968 }
969
970 #define TO_PERCENT(numerator, denominator) \
971         (((denominator) == 0) ? 0 : ((numerator) * 100 / (denominator)))
972
973 /* Given an enumerated value for WIM compression type, return a descriptive
974  * string. */
975 static const tchar *
976 get_data_type(int ctype)
977 {
978         switch (ctype) {
979         case WIMLIB_COMPRESSION_TYPE_NONE:
980                 return T("uncompressed");
981         case WIMLIB_COMPRESSION_TYPE_LZX:
982                 return T("LZX-compressed");
983         case WIMLIB_COMPRESSION_TYPE_XPRESS:
984                 return T("XPRESS-compressed");
985         }
986         return NULL;
987 }
988
989 /* Progress callback function passed to various wimlib functions. */
990 static int
991 imagex_progress_func(enum wimlib_progress_msg msg,
992                      const union wimlib_progress_info *info)
993 {
994         unsigned percent_done;
995         if (imagex_be_quiet)
996                 return 0;
997         switch (msg) {
998         case WIMLIB_PROGRESS_MSG_WRITE_STREAMS:
999                 percent_done = TO_PERCENT(info->write_streams.completed_bytes,
1000                                           info->write_streams.total_bytes);
1001                 if (info->write_streams.completed_streams == 0) {
1002                         const tchar *data_type;
1003
1004                         data_type = get_data_type(info->write_streams.compression_type);
1005                         tprintf(T("Writing %"TS" data using %u thread%"TS"\n"),
1006                                 data_type, info->write_streams.num_threads,
1007                                 (info->write_streams.num_threads == 1) ? T("") : T("s"));
1008                 }
1009                 tprintf(T("\r%"PRIu64" MiB of %"PRIu64" MiB (uncompressed) "
1010                         "written (%u%% done)"),
1011                         info->write_streams.completed_bytes >> 20,
1012                         info->write_streams.total_bytes >> 20,
1013                         percent_done);
1014                 if (info->write_streams.completed_bytes >= info->write_streams.total_bytes)
1015                         tputchar(T('\n'));
1016                 break;
1017         case WIMLIB_PROGRESS_MSG_SCAN_BEGIN:
1018                 tprintf(T("Scanning \"%"TS"\""), info->scan.source);
1019                 if (*info->scan.wim_target_path) {
1020                         tprintf(T(" (loading as WIM path: \"/%"TS"\")...\n"),
1021                                info->scan.wim_target_path);
1022                 } else {
1023                         tprintf(T(" (loading as root of WIM image)...\n"));
1024                 }
1025                 break;
1026         case WIMLIB_PROGRESS_MSG_SCAN_DENTRY:
1027                 if (info->scan.excluded)
1028                         tprintf(T("Excluding \"%"TS"\" from capture\n"), info->scan.cur_path);
1029                 else
1030                         tprintf(T("Scanning \"%"TS"\"\n"), info->scan.cur_path);
1031                 break;
1032         /*case WIMLIB_PROGRESS_MSG_SCAN_END:*/
1033                 /*break;*/
1034         case WIMLIB_PROGRESS_MSG_VERIFY_INTEGRITY:
1035                 percent_done = TO_PERCENT(info->integrity.completed_bytes,
1036                                           info->integrity.total_bytes);
1037                 tprintf(T("\rVerifying integrity of \"%"TS"\": %"PRIu64" MiB "
1038                         "of %"PRIu64" MiB (%u%%) done"),
1039                         info->integrity.filename,
1040                         info->integrity.completed_bytes >> 20,
1041                         info->integrity.total_bytes >> 20,
1042                         percent_done);
1043                 if (info->integrity.completed_bytes == info->integrity.total_bytes)
1044                         tputchar(T('\n'));
1045                 break;
1046         case WIMLIB_PROGRESS_MSG_CALC_INTEGRITY:
1047                 percent_done = TO_PERCENT(info->integrity.completed_bytes,
1048                                           info->integrity.total_bytes);
1049                 tprintf(T("\rCalculating integrity table for WIM: %"PRIu64" MiB "
1050                           "of %"PRIu64" MiB (%u%%) done"),
1051                         info->integrity.completed_bytes >> 20,
1052                         info->integrity.total_bytes >> 20,
1053                         percent_done);
1054                 if (info->integrity.completed_bytes == info->integrity.total_bytes)
1055                         tputchar(T('\n'));
1056                 break;
1057         case WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_BEGIN:
1058                 tprintf(T("Applying image %d (\"%"TS"\") from \"%"TS"\" "
1059                           "to %"TS" \"%"TS"\"\n"),
1060                         info->extract.image,
1061                         info->extract.image_name,
1062                         info->extract.wimfile_name,
1063                         ((info->extract.extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) ?
1064                          T("NTFS volume") : T("directory")),
1065                         info->extract.target);
1066                 break;
1067         case WIMLIB_PROGRESS_MSG_EXTRACT_TREE_BEGIN:
1068                 tprintf(T("Extracting \"%"TS"\" from image %d (\"%"TS"\") "
1069                           "in \"%"TS"\" to \"%"TS"\"\n"),
1070                         info->extract.extract_root_wim_source_path,
1071                         info->extract.image,
1072                         info->extract.image_name,
1073                         info->extract.wimfile_name,
1074                         info->extract.target);
1075                 break;
1076         /*case WIMLIB_PROGRESS_MSG_EXTRACT_DIR_STRUCTURE_BEGIN:*/
1077                 /*tprintf(T("Applying directory structure to %"TS"\n"),*/
1078                         /*info->extract.target);*/
1079                 /*break;*/
1080         case WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS:
1081                 percent_done = TO_PERCENT(info->extract.completed_bytes,
1082                                           info->extract.total_bytes);
1083                 tprintf(T("\rExtracting files: "
1084                           "%"PRIu64" MiB of %"PRIu64" MiB (%u%%) done"),
1085                         info->extract.completed_bytes >> 20,
1086                         info->extract.total_bytes >> 20,
1087                         percent_done);
1088                 if (info->extract.completed_bytes >= info->extract.total_bytes)
1089                         tputchar(T('\n'));
1090                 break;
1091         case WIMLIB_PROGRESS_MSG_EXTRACT_DENTRY:
1092                 tprintf(T("%"TS"\n"), info->extract.cur_path);
1093                 break;
1094         case WIMLIB_PROGRESS_MSG_APPLY_TIMESTAMPS:
1095                 if (info->extract.extract_root_wim_source_path[0] == T('\0'))
1096                         tprintf(T("Setting timestamps on all extracted files...\n"));
1097                 break;
1098         case WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_END:
1099                 if (info->extract.extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) {
1100                         tprintf(T("Unmounting NTFS volume \"%"TS"\"...\n"),
1101                                 info->extract.target);
1102                 }
1103                 break;
1104         case WIMLIB_PROGRESS_MSG_JOIN_STREAMS:
1105                 percent_done = TO_PERCENT(info->join.completed_bytes,
1106                                           info->join.total_bytes);
1107                 tprintf(T("Writing resources from part %u of %u: "
1108                           "%"PRIu64 " MiB of %"PRIu64" MiB (%u%%) written\n"),
1109                         (info->join.completed_parts == info->join.total_parts) ?
1110                         info->join.completed_parts : info->join.completed_parts + 1,
1111                         info->join.total_parts,
1112                         info->join.completed_bytes >> 20,
1113                         info->join.total_bytes >> 20,
1114                         percent_done);
1115                 break;
1116         case WIMLIB_PROGRESS_MSG_SPLIT_BEGIN_PART:
1117                 percent_done = TO_PERCENT(info->split.completed_bytes,
1118                                           info->split.total_bytes);
1119                 tprintf(T("Writing \"%"TS"\": %"PRIu64" MiB of "
1120                           "%"PRIu64" MiB (%u%%) written\n"),
1121                         info->split.part_name,
1122                         info->split.completed_bytes >> 20,
1123                         info->split.total_bytes >> 20,
1124                         percent_done);
1125                 break;
1126         case WIMLIB_PROGRESS_MSG_SPLIT_END_PART:
1127                 if (info->split.completed_bytes == info->split.total_bytes) {
1128                         tprintf(T("Finished writing %u split WIM parts\n"),
1129                                 info->split.cur_part_number);
1130                 }
1131                 break;
1132         default:
1133                 break;
1134         }
1135         fflush(stdout);
1136         return 0;
1137 }
1138
1139 /* Open all the split WIM parts that correspond to a file glob.
1140  *
1141  * @first_part specifies the first part of the split WIM and it may be either
1142  * included or omitted from the glob. */
1143 static int
1144 open_swms_from_glob(const tchar *swm_glob,
1145                     const tchar *first_part,
1146                     int open_flags,
1147                     WIMStruct ***additional_swms_ret,
1148                     unsigned *num_additional_swms_ret)
1149 {
1150         unsigned num_additional_swms = 0;
1151         WIMStruct **additional_swms = NULL;
1152         glob_t globbuf;
1153         int ret;
1154
1155         /* Warning: glob() is replaced in Windows native builds */
1156         ret = tglob(swm_glob, GLOB_ERR | GLOB_NOSORT, NULL, &globbuf);
1157         if (ret != 0) {
1158                 if (ret == GLOB_NOMATCH) {
1159                         imagex_error(T("Found no files for glob \"%"TS"\""),
1160                                      swm_glob);
1161                 } else {
1162                         imagex_error_with_errno(T("Failed to process glob \"%"TS"\""),
1163                                                 swm_glob);
1164                 }
1165                 ret = -1;
1166                 goto out;
1167         }
1168         num_additional_swms = globbuf.gl_pathc;
1169         additional_swms = calloc(num_additional_swms, sizeof(additional_swms[0]));
1170         if (!additional_swms) {
1171                 imagex_error(T("Out of memory"));
1172                 ret = -1;
1173                 goto out_globfree;
1174         }
1175         unsigned offset = 0;
1176         for (unsigned i = 0; i < num_additional_swms; i++) {
1177                 if (tstrcmp(globbuf.gl_pathv[i], first_part) == 0) {
1178                         offset++;
1179                         continue;
1180                 }
1181                 ret = wimlib_open_wim(globbuf.gl_pathv[i],
1182                                       open_flags | WIMLIB_OPEN_FLAG_SPLIT_OK,
1183                                       &additional_swms[i - offset],
1184                                       imagex_progress_func);
1185                 if (ret != 0)
1186                         goto out_close_swms;
1187         }
1188         *additional_swms_ret = additional_swms;
1189         *num_additional_swms_ret = num_additional_swms - offset;
1190         ret = 0;
1191         goto out_globfree;
1192 out_close_swms:
1193         for (unsigned i = 0; i < num_additional_swms; i++)
1194                 wimlib_free(additional_swms[i]);
1195         free(additional_swms);
1196 out_globfree:
1197         globfree(&globbuf);
1198 out:
1199         return ret;
1200 }
1201
1202
1203 static unsigned
1204 parse_num_threads(const tchar *optarg)
1205 {
1206         tchar *tmp;
1207         unsigned long ul_nthreads = tstrtoul(optarg, &tmp, 10);
1208         if (ul_nthreads >= UINT_MAX || *tmp || tmp == optarg) {
1209                 imagex_error(T("Number of threads must be a non-negative integer!"));
1210                 return UINT_MAX;
1211         } else {
1212                 return ul_nthreads;
1213         }
1214 }
1215
1216 /*
1217  * Parse an option passed to an update command.
1218  *
1219  * @op:         One of WIMLIB_UPDATE_OP_* that indicates the command being
1220  *              parsed.
1221  *
1222  * @option:     Text string for the option (beginning with --)
1223  *
1224  * @cmd:        `struct wimlib_update_command' that is being constructed for
1225  *              this command.
1226  *
1227  * Returns true if the option was recognized; false if not.
1228  */
1229 static bool
1230 update_command_add_option(int op, const tchar *option,
1231                           struct wimlib_update_command *cmd)
1232 {
1233         bool recognized = true;
1234         switch (op) {
1235         case WIMLIB_UPDATE_OP_ADD:
1236                 if (!tstrcmp(option, T("--unix-data")))
1237                         cmd->add.add_flags |= WIMLIB_ADD_IMAGE_FLAG_UNIX_DATA;
1238                 else if (!tstrcmp(option, T("--no-acls")) || !tstrcmp(option, T("--noacls")))
1239                         cmd->add.add_flags |= WIMLIB_ADD_IMAGE_FLAG_NO_ACLS;
1240                 else if (!tstrcmp(option, T("--strict-acls")))
1241                         cmd->add.add_flags |= WIMLIB_ADD_IMAGE_FLAG_STRICT_ACLS;
1242                 else if (!tstrcmp(option, T("--dereference")))
1243                         cmd->add.add_flags |= WIMLIB_ADD_IMAGE_FLAG_DEREFERENCE;
1244                 else
1245                         recognized = false;
1246                 break;
1247         case WIMLIB_UPDATE_OP_DELETE:
1248                 if (!tstrcmp(option, T("--force")))
1249                         cmd->delete.delete_flags |= WIMLIB_DELETE_FLAG_FORCE;
1250                 else if (!tstrcmp(option, T("--recursive")))
1251                         cmd->delete.delete_flags |= WIMLIB_DELETE_FLAG_RECURSIVE;
1252                 else
1253                         recognized = false;
1254                 break;
1255         case WIMLIB_UPDATE_OP_RENAME:
1256                 recognized = false;
1257                 break;
1258         }
1259         return recognized;
1260 }
1261
1262 /* How many nonoption arguments each `imagex update' command expects */
1263 static const unsigned update_command_num_nonoptions[] = {
1264         [WIMLIB_UPDATE_OP_ADD] = 2,
1265         [WIMLIB_UPDATE_OP_DELETE] = 1,
1266         [WIMLIB_UPDATE_OP_RENAME] = 2,
1267 };
1268
1269 static void
1270 update_command_add_nonoption(int op, const tchar *nonoption,
1271                              struct wimlib_update_command *cmd,
1272                              unsigned num_nonoptions)
1273 {
1274         switch (op) {
1275         case WIMLIB_UPDATE_OP_ADD:
1276                 if (num_nonoptions == 0)
1277                         cmd->add.fs_source_path = (tchar*)nonoption;
1278                 else
1279                         cmd->add.wim_target_path = (tchar*)nonoption;
1280                 break;
1281         case WIMLIB_UPDATE_OP_DELETE:
1282                 cmd->delete.wim_path = (tchar*)nonoption;
1283                 break;
1284         case WIMLIB_UPDATE_OP_RENAME:
1285                 if (num_nonoptions == 0)
1286                         cmd->rename.wim_source_path = (tchar*)nonoption;
1287                 else
1288                         cmd->rename.wim_target_path = (tchar*)nonoption;
1289                 break;
1290         }
1291 }
1292
1293 /*
1294  * Parse a command passed on stdin to `imagex update'.
1295  *
1296  * @line:       Text of the command.
1297  * @len:        Length of the line, including a null terminator
1298  *              at line[len - 1].
1299  *
1300  * @command:    A `struct wimlib_update_command' to fill in from the parsed
1301  *              line.
1302  *
1303  * @line_number: Line number of the command, for diagnostics.
1304  *
1305  * Returns true on success; returns false on parse error.
1306  */
1307 static bool
1308 parse_update_command(tchar *line, size_t len,
1309                      struct wimlib_update_command *command,
1310                      size_t line_number)
1311 {
1312         int ret;
1313         tchar *command_name;
1314         int op;
1315         size_t num_nonoptions;
1316
1317         /* Get the command name ("add", "delete", "rename") */
1318         ret = parse_string(&line, &len, &command_name);
1319         if (ret != PARSE_STRING_SUCCESS)
1320                 return false;
1321
1322         if (!tstrcasecmp(command_name, T("add"))) {
1323                 op = WIMLIB_UPDATE_OP_ADD;
1324         } else if (!tstrcasecmp(command_name, T("delete"))) {
1325                 op = WIMLIB_UPDATE_OP_DELETE;
1326         } else if (!tstrcasecmp(command_name, T("rename"))) {
1327                 op = WIMLIB_UPDATE_OP_RENAME;
1328         } else {
1329                 imagex_error(T("Unknown update command \"%"TS"\" on line %zu"),
1330                              command_name, line_number);
1331                 return false;
1332         }
1333         command->op = op;
1334
1335         /* Parse additional options and non-options as needed */
1336         num_nonoptions = 0;
1337         for (;;) {
1338                 tchar *next_string;
1339
1340                 ret = parse_string(&line, &len, &next_string);
1341                 if (ret == PARSE_STRING_NONE) /* End of line */
1342                         break;
1343                 else if (ret != PARSE_STRING_SUCCESS) /* Parse failure */
1344                         return false;
1345                 if (next_string[0] == T('-') && next_string[1] == T('-')) {
1346                         /* Option */
1347                         if (!update_command_add_option(op, next_string, command))
1348                         {
1349                                 imagex_error(T("Unrecognized option \"%"TS"\" to "
1350                                                "update command on line %zu"),
1351                                              next_string, line_number);
1352
1353                                 return false;
1354                         }
1355                 } else {
1356                         /* Nonoption */
1357                         if (num_nonoptions == update_command_num_nonoptions[op])
1358                         {
1359                                 imagex_error(T("Unexpected argument \"%"TS"\" in "
1360                                                "update command on line %zu\n"
1361                                                "       (The \"%"TS"\" command only "
1362                                                "takes %u nonoption arguments!)\n"),
1363                                              next_string, line_number,
1364                                              command_name, num_nonoptions);
1365                                 return false;
1366                         }
1367                         update_command_add_nonoption(op, next_string,
1368                                                      command, num_nonoptions);
1369                         num_nonoptions++;
1370                 }
1371         }
1372
1373         if (num_nonoptions != update_command_num_nonoptions[op]) {
1374                 imagex_error(T("Not enough arguments to update command "
1375                                "\"%"TS"\" on line %zu"), command_name, line_number);
1376                 return false;
1377         }
1378         return true;
1379 }
1380
1381 static struct wimlib_update_command *
1382 parse_update_command_file(tchar **cmd_file_contents_p, size_t cmd_file_nchars,
1383                           size_t *num_cmds_ret)
1384 {
1385         ssize_t nlines;
1386         tchar *p;
1387         struct wimlib_update_command *cmds;
1388         size_t i, j;
1389
1390         nlines = text_file_count_lines(cmd_file_contents_p,
1391                                        &cmd_file_nchars);
1392         if (nlines < 0)
1393                 return NULL;
1394
1395         /* Always allocate at least 1 slot, just in case the implementation of
1396          * calloc() returns NULL if 0 bytes are requested. */
1397         cmds = calloc(nlines ?: 1, sizeof(struct wimlib_update_command));
1398         if (!cmds) {
1399                 imagex_error(T("out of memory"));
1400                 return NULL;
1401         }
1402         p = *cmd_file_contents_p;
1403         j = 0;
1404         for (i = 0; i < nlines; i++) {
1405                 /* XXX: Could use rawmemchr() here instead, but it may not be
1406                  * available on all platforms. */
1407                 tchar *endp = tmemchr(p, T('\n'), cmd_file_nchars);
1408                 size_t len = endp - p + 1;
1409                 *endp = T('\0');
1410                 if (!is_comment_line(p, len)) {
1411                         if (!parse_update_command(p, len, &cmds[j++], i + 1)) {
1412                                 free(cmds);
1413                                 return NULL;
1414                         }
1415                 }
1416                 p = endp + 1;
1417         }
1418         *num_cmds_ret = j;
1419         return cmds;
1420 }
1421
1422 /* Apply one image, or all images, from a WIM file into a directory, OR apply
1423  * one image from a WIM file to a NTFS volume. */
1424 static int
1425 imagex_apply(int argc, tchar **argv)
1426 {
1427         int c;
1428         int open_flags = WIMLIB_OPEN_FLAG_SPLIT_OK;
1429         int image;
1430         int num_images;
1431         WIMStruct *w;
1432         int ret;
1433         const tchar *wimfile;
1434         const tchar *target;
1435         const tchar *image_num_or_name;
1436         int extract_flags = WIMLIB_EXTRACT_FLAG_SEQUENTIAL;
1437
1438         const tchar *swm_glob = NULL;
1439         WIMStruct **additional_swms = NULL;
1440         unsigned num_additional_swms = 0;
1441
1442         for_opt(c, apply_options) {
1443                 switch (c) {
1444                 case IMAGEX_CHECK_OPTION:
1445                         open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
1446                         break;
1447                 case IMAGEX_HARDLINK_OPTION:
1448                         extract_flags |= WIMLIB_EXTRACT_FLAG_HARDLINK;
1449                         break;
1450                 case IMAGEX_SYMLINK_OPTION:
1451                         extract_flags |= WIMLIB_EXTRACT_FLAG_SYMLINK;
1452                         break;
1453                 case IMAGEX_VERBOSE_OPTION:
1454                         extract_flags |= WIMLIB_EXTRACT_FLAG_VERBOSE;
1455                         break;
1456                 case IMAGEX_REF_OPTION:
1457                         swm_glob = optarg;
1458                         break;
1459                 case IMAGEX_UNIX_DATA_OPTION:
1460                         extract_flags |= WIMLIB_EXTRACT_FLAG_UNIX_DATA;
1461                         break;
1462                 case IMAGEX_NO_ACLS_OPTION:
1463                         extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ACLS;
1464                         break;
1465                 case IMAGEX_STRICT_ACLS_OPTION:
1466                         extract_flags |= WIMLIB_EXTRACT_FLAG_STRICT_ACLS;
1467                         break;
1468                 case IMAGEX_NORPFIX_OPTION:
1469                         extract_flags |= WIMLIB_EXTRACT_FLAG_NORPFIX;
1470                         break;
1471                 case IMAGEX_RPFIX_OPTION:
1472                         extract_flags |= WIMLIB_EXTRACT_FLAG_RPFIX;
1473                         break;
1474                 default:
1475                         usage(APPLY);
1476                         return -1;
1477                 }
1478         }
1479         argc -= optind;
1480         argv += optind;
1481         if (argc != 2 && argc != 3) {
1482                 usage(APPLY);
1483                 return -1;
1484         }
1485
1486         wimfile = argv[0];
1487         if (argc == 2) {
1488                 image_num_or_name = T("1");
1489                 target = argv[1];
1490         } else {
1491                 image_num_or_name = argv[1];
1492                 target = argv[2];
1493         }
1494
1495         ret = wimlib_open_wim(wimfile, open_flags, &w, imagex_progress_func);
1496         if (ret != 0)
1497                 return ret;
1498
1499         image = wimlib_resolve_image(w, image_num_or_name);
1500         ret = verify_image_exists(image, image_num_or_name, wimfile);
1501         if (ret != 0)
1502                 goto out;
1503
1504         num_images = wimlib_get_num_images(w);
1505         if (argc == 2 && num_images != 1) {
1506                 imagex_error(T("\"%"TS"\" contains %d images; Please select one "
1507                                "(or all)"), wimfile, num_images);
1508                 usage(APPLY);
1509                 ret = -1;
1510                 goto out;
1511         }
1512
1513         if (swm_glob) {
1514                 ret = open_swms_from_glob(swm_glob, wimfile, open_flags,
1515                                           &additional_swms,
1516                                           &num_additional_swms);
1517                 if (ret != 0)
1518                         goto out;
1519         }
1520
1521         struct stat stbuf;
1522
1523         ret = tstat(target, &stbuf);
1524         if (ret == 0) {
1525                 if (S_ISBLK(stbuf.st_mode) || S_ISREG(stbuf.st_mode))
1526                         extract_flags |= WIMLIB_EXTRACT_FLAG_NTFS;
1527         } else {
1528                 if (errno != ENOENT) {
1529                         imagex_error_with_errno(T("Failed to stat \"%"TS"\""),
1530                                                 target);
1531                         ret = -1;
1532                         goto out;
1533                 }
1534         }
1535
1536 #ifdef __WIN32__
1537         win32_acquire_restore_privileges();
1538 #endif
1539         ret = wimlib_extract_image(w, image, target, extract_flags,
1540                                    additional_swms, num_additional_swms,
1541                                    imagex_progress_func);
1542         if (ret == 0)
1543                 tprintf(T("Done applying WIM image.\n"));
1544 #ifdef __WIN32__
1545         win32_release_restore_privileges();
1546 #endif
1547 out:
1548         wimlib_free(w);
1549         if (additional_swms) {
1550                 for (unsigned i = 0; i < num_additional_swms; i++)
1551                         wimlib_free(additional_swms[i]);
1552                 free(additional_swms);
1553         }
1554         return ret;
1555 }
1556
1557 /* Create a WIM image from a directory tree, NTFS volume, or multiple files or
1558  * directory trees.  'wimlib-imagex capture': create a new WIM file containing
1559  * the desired image.  'wimlib-imagex append': add a new image to an existing
1560  * WIM file. */
1561 static int
1562 imagex_capture_or_append(int argc, tchar **argv)
1563 {
1564         int c;
1565         int open_flags = 0;
1566         int add_image_flags = WIMLIB_ADD_IMAGE_FLAG_EXCLUDE_VERBOSE;
1567         int write_flags = 0;
1568         int compression_type = WIMLIB_COMPRESSION_TYPE_XPRESS;
1569         const tchar *wimfile;
1570         const tchar *name;
1571         const tchar *desc;
1572         const tchar *flags_element = NULL;
1573         WIMStruct *w;
1574         int ret;
1575         int cur_image;
1576         int cmd = tstrcmp(argv[0], T("append")) ? CAPTURE : APPEND;
1577         unsigned num_threads = 0;
1578
1579         tchar *source;
1580         size_t source_name_len;
1581         tchar *source_copy;
1582
1583         const tchar *config_file = NULL;
1584         tchar *config_str;
1585         struct wimlib_capture_config *config = NULL;
1586
1587         bool source_list = false;
1588         size_t source_list_nchars;
1589         tchar *source_list_contents;
1590         bool capture_sources_malloced;
1591         struct wimlib_capture_source *capture_sources;
1592         size_t num_sources;
1593
1594         for_opt(c, capture_or_append_options) {
1595                 switch (c) {
1596                 case IMAGEX_BOOT_OPTION:
1597                         add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_BOOT;
1598                         break;
1599                 case IMAGEX_CHECK_OPTION:
1600                         open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
1601                         write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
1602                         break;
1603                 case IMAGEX_CONFIG_OPTION:
1604                         config_file = optarg;
1605                         break;
1606                 case IMAGEX_COMPRESS_OPTION:
1607                         compression_type = get_compression_type(optarg);
1608                         if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID)
1609                                 return -1;
1610                         break;
1611                 case IMAGEX_FLAGS_OPTION:
1612                         flags_element = optarg;
1613                         break;
1614                 case IMAGEX_DEREFERENCE_OPTION:
1615                         add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_DEREFERENCE;
1616                         break;
1617                 case IMAGEX_VERBOSE_OPTION:
1618                         add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_VERBOSE;
1619                         break;
1620                 case IMAGEX_THREADS_OPTION:
1621                         num_threads = parse_num_threads(optarg);
1622                         if (num_threads == UINT_MAX)
1623                                 return -1;
1624                         break;
1625                 case IMAGEX_REBUILD_OPTION:
1626                         write_flags |= WIMLIB_WRITE_FLAG_REBUILD;
1627                         break;
1628                 case IMAGEX_UNIX_DATA_OPTION:
1629                         add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_UNIX_DATA;
1630                         break;
1631                 case IMAGEX_SOURCE_LIST_OPTION:
1632                         source_list = true;
1633                         break;
1634                 case IMAGEX_NO_ACLS_OPTION:
1635                         add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_NO_ACLS;
1636                         break;
1637                 case IMAGEX_STRICT_ACLS_OPTION:
1638                         add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_STRICT_ACLS;
1639                         break;
1640                 case IMAGEX_RPFIX_OPTION:
1641                         add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_RPFIX;
1642                         break;
1643                 case IMAGEX_NORPFIX_OPTION:
1644                         add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_NORPFIX;
1645                         break;
1646                 default:
1647                         usage(cmd);
1648                         return -1;
1649                 }
1650         }
1651         argc -= optind;
1652         argv += optind;
1653
1654         if (argc < 2 || argc > 4) {
1655                 usage(cmd);
1656                 return -1;
1657         }
1658
1659         source = argv[0];
1660         wimfile = argv[1];
1661
1662         if (argc >= 3) {
1663                 name = argv[2];
1664         } else {
1665                 /* Set default name to SOURCE argument, omitting any directory
1666                  * prefixes and trailing slashes.  This requires making a copy
1667                  * of @source. */
1668                 source_name_len = tstrlen(source);
1669                 source_copy = alloca((source_name_len + 1) * sizeof(tchar));
1670                 name = tbasename(tstrcpy(source_copy, source));
1671         }
1672         /* Image description defaults to NULL if not given. */
1673         desc = (argc >= 4) ? argv[3] : NULL;
1674
1675         if (source_list) {
1676                 /* Set up capture sources in source list mode */
1677                 if (source[0] == T('-') && source[1] == T('\0')) {
1678                         source_list_contents = stdin_get_text_contents(&source_list_nchars);
1679                 } else {
1680                         source_list_contents = file_get_text_contents(source,
1681                                                                       &source_list_nchars);
1682                 }
1683                 if (!source_list_contents)
1684                         return -1;
1685
1686                 capture_sources = parse_source_list(&source_list_contents,
1687                                                     source_list_nchars,
1688                                                     &num_sources);
1689                 if (!capture_sources) {
1690                         ret = -1;
1691                         goto out_free_source_list_contents;
1692                 }
1693                 capture_sources_malloced = true;
1694         } else {
1695                 /* Set up capture source in non-source-list mode (could be
1696                  * either "normal" mode or "NTFS mode"--- see the man page). */
1697                 capture_sources = alloca(sizeof(struct wimlib_capture_source));
1698                 capture_sources[0].fs_source_path = source;
1699                 capture_sources[0].wim_target_path = NULL;
1700                 capture_sources[0].reserved = 0;
1701                 num_sources = 1;
1702                 capture_sources_malloced = false;
1703                 source_list_contents = NULL;
1704         }
1705
1706         if (config_file) {
1707                 size_t config_len;
1708
1709                 config_str = file_get_text_contents(config_file, &config_len);
1710                 if (!config_str) {
1711                         ret = -1;
1712                         goto out_free_capture_sources;
1713                 }
1714
1715                 config = alloca(sizeof(*config));
1716                 ret = parse_capture_config(&config_str, config_len, config);
1717                 if (ret)
1718                         goto out_free_config;
1719         } else {
1720                 config = &default_capture_config;
1721         }
1722
1723         if (cmd == APPEND)
1724                 ret = wimlib_open_wim(wimfile, open_flags, &w,
1725                                       imagex_progress_func);
1726         else
1727                 ret = wimlib_create_new_wim(compression_type, &w);
1728         if (ret)
1729                 goto out_free_config;
1730
1731         if (!source_list) {
1732                 struct stat stbuf;
1733                 ret = tstat(source, &stbuf);
1734                 if (ret == 0) {
1735                         if (S_ISBLK(stbuf.st_mode) || S_ISREG(stbuf.st_mode)) {
1736                                 tprintf(T("Capturing WIM image from NTFS "
1737                                           "filesystem on \"%"TS"\"\n"), source);
1738                                 add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_NTFS;
1739                         }
1740                 } else {
1741                         if (errno != ENOENT) {
1742                                 imagex_error_with_errno(T("Failed to stat "
1743                                                           "\"%"TS"\""), source);
1744                                 ret = -1;
1745                                 goto out_wimlib_free;
1746                         }
1747                 }
1748         }
1749 #ifdef __WIN32__
1750         win32_acquire_capture_privileges();
1751 #endif
1752
1753         ret = wimlib_add_image_multisource(w,
1754                                            capture_sources,
1755                                            num_sources,
1756                                            name,
1757                                            config,
1758                                            add_image_flags,
1759                                            imagex_progress_func);
1760         if (ret != 0)
1761                 goto out_release_privs;
1762         cur_image = wimlib_get_num_images(w);
1763         if (desc) {
1764                 ret = wimlib_set_image_descripton(w, cur_image, desc);
1765                 if (ret != 0)
1766                         goto out_release_privs;
1767         }
1768         if (flags_element) {
1769                 ret = wimlib_set_image_flags(w, cur_image, flags_element);
1770                 if (ret != 0)
1771                         goto out_release_privs;
1772         }
1773         if (cmd == APPEND) {
1774                 ret = wimlib_overwrite(w, write_flags, num_threads,
1775                                        imagex_progress_func);
1776         } else {
1777                 ret = wimlib_write(w, wimfile, WIMLIB_ALL_IMAGES, write_flags,
1778                                    num_threads, imagex_progress_func);
1779         }
1780         if (ret == WIMLIB_ERR_REOPEN)
1781                 ret = 0;
1782         if (ret != 0)
1783                 imagex_error(T("Failed to write the WIM file \"%"TS"\""),
1784                              wimfile);
1785 out_release_privs:
1786 #ifdef __WIN32__
1787         win32_release_capture_privileges();
1788 #endif
1789 out_wimlib_free:
1790         wimlib_free(w);
1791 out_free_config:
1792         if (config != NULL && config != &default_capture_config) {
1793                 free(config->exclusion_pats.pats);
1794                 free(config->exclusion_exception_pats.pats);
1795                 free(config_str);
1796         }
1797 out_free_capture_sources:
1798         if (capture_sources_malloced)
1799                 free(capture_sources);
1800 out_free_source_list_contents:
1801         free(source_list_contents);
1802         return ret;
1803 }
1804
1805 /* Remove image(s) from a WIM. */
1806 static int
1807 imagex_delete(int argc, tchar **argv)
1808 {
1809         int c;
1810         int open_flags = 0;
1811         int write_flags = 0;
1812         const tchar *wimfile;
1813         const tchar *image_num_or_name;
1814         WIMStruct *w;
1815         int image;
1816         int ret;
1817
1818         for_opt(c, delete_options) {
1819                 switch (c) {
1820                 case IMAGEX_CHECK_OPTION:
1821                         open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
1822                         write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
1823                         break;
1824                 case IMAGEX_SOFT_OPTION:
1825                         write_flags |= WIMLIB_WRITE_FLAG_SOFT_DELETE;
1826                         break;
1827                 default:
1828                         usage(DELETE);
1829                         return -1;
1830                 }
1831         }
1832         argc -= optind;
1833         argv += optind;
1834
1835         if (argc != 2) {
1836                 if (argc < 1)
1837                         imagex_error(T("Must specify a WIM file"));
1838                 if (argc < 2)
1839                         imagex_error(T("Must specify an image"));
1840                 usage(DELETE);
1841                 return -1;
1842         }
1843         wimfile = argv[0];
1844         image_num_or_name = argv[1];
1845
1846         ret = file_writable(wimfile);
1847         if (ret != 0)
1848                 return ret;
1849
1850         ret = wimlib_open_wim(wimfile, open_flags, &w,
1851                               imagex_progress_func);
1852         if (ret != 0)
1853                 return ret;
1854
1855         image = wimlib_resolve_image(w, image_num_or_name);
1856
1857         ret = verify_image_exists(image, image_num_or_name, wimfile);
1858         if (ret != 0)
1859                 goto out;
1860
1861         ret = wimlib_delete_image(w, image);
1862         if (ret != 0) {
1863                 imagex_error(T("Failed to delete image from \"%"TS"\""), wimfile);
1864                 goto out;
1865         }
1866
1867         ret = wimlib_overwrite(w, write_flags, 0, imagex_progress_func);
1868         if (ret == WIMLIB_ERR_REOPEN)
1869                 ret = 0;
1870         if (ret != 0) {
1871                 imagex_error(T("Failed to write the file \"%"TS"\" with image "
1872                                "deleted"), wimfile);
1873         }
1874 out:
1875         wimlib_free(w);
1876         return ret;
1877 }
1878
1879 /* Print the files contained in an image(s) in a WIM file. */
1880 static int
1881 imagex_dir(int argc, tchar **argv)
1882 {
1883         const tchar *wimfile;
1884         WIMStruct *w;
1885         int image;
1886         int ret;
1887         int num_images;
1888
1889         if (argc < 2) {
1890                 imagex_error(T("Must specify a WIM file"));
1891                 usage(DIR);
1892                 return -1;
1893         }
1894         if (argc > 3) {
1895                 imagex_error(T("Too many arguments"));
1896                 usage(DIR);
1897                 return -1;
1898         }
1899
1900         wimfile = argv[1];
1901         ret = wimlib_open_wim(wimfile, WIMLIB_OPEN_FLAG_SPLIT_OK, &w,
1902                               imagex_progress_func);
1903         if (ret != 0)
1904                 return ret;
1905
1906         if (argc == 3) {
1907                 image = wimlib_resolve_image(w, argv[2]);
1908                 ret = verify_image_exists(image, argv[2], wimfile);
1909                 if (ret != 0)
1910                         goto out;
1911         } else {
1912                 /* Image was not specified.  If the WIM only contains one image,
1913                  * choose that one; otherwise, print an error. */
1914                 num_images = wimlib_get_num_images(w);
1915                 if (num_images != 1) {
1916                         imagex_error(T("The file \"%"TS"\" contains %d images; Please "
1917                                        "select one."), wimfile, num_images);
1918                         usage(DIR);
1919                         ret = -1;
1920                         goto out;
1921                 }
1922                 image = 1;
1923         }
1924
1925         ret = wimlib_print_files(w, image);
1926 out:
1927         wimlib_free(w);
1928         return ret;
1929 }
1930
1931 /* Exports one, or all, images from a WIM file to a new WIM file or an existing
1932  * WIM file. */
1933 static int
1934 imagex_export(int argc, tchar **argv)
1935 {
1936         int c;
1937         int open_flags = 0;
1938         int export_flags = 0;
1939         int write_flags = 0;
1940         int compression_type = WIMLIB_COMPRESSION_TYPE_NONE;
1941         bool compression_type_specified = false;
1942         const tchar *src_wimfile;
1943         const tchar *src_image_num_or_name;
1944         const tchar *dest_wimfile;
1945         const tchar *dest_name;
1946         const tchar *dest_desc;
1947         WIMStruct *src_w = NULL;
1948         WIMStruct *dest_w = NULL;
1949         int ret;
1950         int image;
1951         struct stat stbuf;
1952         bool wim_is_new;
1953         const tchar *swm_glob = NULL;
1954         WIMStruct **additional_swms = NULL;
1955         unsigned num_additional_swms = 0;
1956         unsigned num_threads = 0;
1957
1958         for_opt(c, export_options) {
1959                 switch (c) {
1960                 case IMAGEX_BOOT_OPTION:
1961                         export_flags |= WIMLIB_EXPORT_FLAG_BOOT;
1962                         break;
1963                 case IMAGEX_CHECK_OPTION:
1964                         open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
1965                         write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
1966                         break;
1967                 case IMAGEX_COMPRESS_OPTION:
1968                         compression_type = get_compression_type(optarg);
1969                         if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID)
1970                                 return -1;
1971                         compression_type_specified = true;
1972                         break;
1973                 case IMAGEX_REF_OPTION:
1974                         swm_glob = optarg;
1975                         break;
1976                 case IMAGEX_THREADS_OPTION:
1977                         num_threads = parse_num_threads(optarg);
1978                         if (num_threads == UINT_MAX)
1979                                 return -1;
1980                         break;
1981                 case IMAGEX_REBUILD_OPTION:
1982                         write_flags |= WIMLIB_WRITE_FLAG_REBUILD;
1983                         break;
1984                 default:
1985                         usage(EXPORT);
1986                         return -1;
1987                 }
1988         }
1989         argc -= optind;
1990         argv += optind;
1991         if (argc < 3 || argc > 5) {
1992                 usage(EXPORT);
1993                 return -1;
1994         }
1995         src_wimfile           = argv[0];
1996         src_image_num_or_name = argv[1];
1997         dest_wimfile          = argv[2];
1998         dest_name             = (argc >= 4) ? argv[3] : NULL;
1999         dest_desc             = (argc >= 5) ? argv[4] : NULL;
2000         ret = wimlib_open_wim(src_wimfile,
2001                               open_flags | WIMLIB_OPEN_FLAG_SPLIT_OK, &src_w,
2002                               imagex_progress_func);
2003         if (ret != 0)
2004                 return ret;
2005
2006         /* Determine if the destination is an existing file or not.
2007          * If so, we try to append the exported image(s) to it; otherwise, we
2008          * create a new WIM containing the exported image(s). */
2009         if (tstat(dest_wimfile, &stbuf) == 0) {
2010                 int dest_ctype;
2011
2012                 wim_is_new = false;
2013                 /* Destination file exists. */
2014
2015                 if (!S_ISREG(stbuf.st_mode)) {
2016                         imagex_error(T("\"%"TS"\" is not a regular file"),
2017                                      dest_wimfile);
2018                         ret = -1;
2019                         goto out;
2020                 }
2021                 ret = wimlib_open_wim(dest_wimfile, open_flags, &dest_w,
2022                                       imagex_progress_func);
2023                 if (ret != 0)
2024                         goto out;
2025
2026                 ret = file_writable(dest_wimfile);
2027                 if (ret != 0)
2028                         goto out;
2029
2030                 dest_ctype = wimlib_get_compression_type(dest_w);
2031                 if (compression_type_specified
2032                     && compression_type != dest_ctype)
2033                 {
2034                         imagex_error(T("Cannot specify a compression type that is "
2035                                        "not the same as that used in the "
2036                                        "destination WIM"));
2037                         ret = -1;
2038                         goto out;
2039                 }
2040         } else {
2041                 wim_is_new = true;
2042                 /* dest_wimfile is not an existing file, so create a new WIM. */
2043                 if (!compression_type_specified)
2044                         compression_type = wimlib_get_compression_type(src_w);
2045                 if (errno == ENOENT) {
2046                         ret = wimlib_create_new_wim(compression_type, &dest_w);
2047                         if (ret != 0)
2048                                 goto out;
2049                 } else {
2050                         imagex_error_with_errno(T("Cannot stat file \"%"TS"\""),
2051                                                 dest_wimfile);
2052                         ret = -1;
2053                         goto out;
2054                 }
2055         }
2056
2057         image = wimlib_resolve_image(src_w, src_image_num_or_name);
2058         ret = verify_image_exists(image, src_image_num_or_name, src_wimfile);
2059         if (ret != 0)
2060                 goto out;
2061
2062         if (swm_glob) {
2063                 ret = open_swms_from_glob(swm_glob, src_wimfile, open_flags,
2064                                           &additional_swms,
2065                                           &num_additional_swms);
2066                 if (ret != 0)
2067                         goto out;
2068         }
2069
2070         ret = wimlib_export_image(src_w, image, dest_w, dest_name, dest_desc,
2071                                   export_flags, additional_swms,
2072                                   num_additional_swms, imagex_progress_func);
2073         if (ret != 0)
2074                 goto out;
2075
2076
2077         if (wim_is_new)
2078                 ret = wimlib_write(dest_w, dest_wimfile, WIMLIB_ALL_IMAGES,
2079                                    write_flags, num_threads,
2080                                    imagex_progress_func);
2081         else
2082                 ret = wimlib_overwrite(dest_w, write_flags, num_threads,
2083                                        imagex_progress_func);
2084 out:
2085         if (ret == WIMLIB_ERR_REOPEN)
2086                 ret = 0;
2087         wimlib_free(src_w);
2088         wimlib_free(dest_w);
2089         if (additional_swms) {
2090                 for (unsigned i = 0; i < num_additional_swms; i++)
2091                         wimlib_free(additional_swms[i]);
2092                 free(additional_swms);
2093         }
2094         return ret;
2095 }
2096
2097 static bool
2098 is_root_wim_path(const tchar *path)
2099 {
2100         const tchar *p;
2101         for (p = path; *p; p++)
2102                 if (*p != T('\\') && *p != T('/'))
2103                         return false;
2104         return true;
2105 }
2106
2107 static void
2108 free_extract_commands(struct wimlib_extract_command *cmds, size_t num_cmds,
2109                       const tchar *dest_dir)
2110 {
2111         for (size_t i = 0; i < num_cmds; i++)
2112                 if (cmds[i].fs_dest_path != dest_dir)
2113                         free(cmds[i].fs_dest_path);
2114         free(cmds);
2115 }
2116
2117 static struct wimlib_extract_command *
2118 prepare_extract_commands(tchar **paths, unsigned num_paths,
2119                          int extract_flags, tchar *dest_dir,
2120                          size_t *num_cmds_ret)
2121 {
2122         struct wimlib_extract_command *cmds;
2123         size_t num_cmds;
2124         tchar *emptystr = T("");
2125
2126         if (num_paths == 0) {
2127                 num_paths = 1;
2128                 paths = &emptystr;
2129         }
2130         num_cmds = num_paths;
2131         cmds = calloc(num_cmds, sizeof(cmds[0]));
2132         if (!cmds) {
2133                 imagex_error(T("Out of memory!"));
2134                 return NULL;
2135         }
2136
2137         for (size_t i = 0; i < num_cmds; i++) {
2138                 cmds[i].extract_flags = extract_flags;
2139                 cmds[i].wim_source_path = paths[i];
2140                 if (is_root_wim_path(paths[i])) {
2141                         cmds[i].fs_dest_path = dest_dir;
2142                 } else {
2143                         size_t len = tstrlen(dest_dir) + 1 + tstrlen(paths[i]);
2144                         cmds[i].fs_dest_path = malloc((len + 1) * sizeof(tchar));
2145                         if (!cmds[i].fs_dest_path) {
2146                                 free_extract_commands(cmds, num_cmds, dest_dir);
2147                                 return NULL;
2148                         }
2149                         tsprintf(cmds[i].fs_dest_path, T("%"TS"/%"TS), dest_dir,
2150                                  tbasename(paths[i]));
2151                 }
2152         }
2153         *num_cmds_ret = num_cmds;
2154         return cmds;
2155 }
2156
2157 /* Extract files or directories from a WIM image */
2158 static int
2159 imagex_extract(int argc, tchar **argv)
2160 {
2161         int c;
2162         int open_flags = WIMLIB_OPEN_FLAG_SPLIT_OK;
2163         int image;
2164         WIMStruct *wim;
2165         int ret;
2166         const tchar *wimfile;
2167         const tchar *image_num_or_name;
2168         tchar *dest_dir = T(".");
2169         int extract_flags = WIMLIB_EXTRACT_FLAG_SEQUENTIAL | WIMLIB_EXTRACT_FLAG_NORPFIX;
2170
2171         const tchar *swm_glob = NULL;
2172         WIMStruct **additional_swms = NULL;
2173         unsigned num_additional_swms = 0;
2174
2175         struct wimlib_extract_command *cmds;
2176         size_t num_cmds;
2177
2178         for_opt(c, extract_options) {
2179                 switch (c) {
2180                 case IMAGEX_CHECK_OPTION:
2181                         open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
2182                         break;
2183                 case IMAGEX_VERBOSE_OPTION:
2184                         extract_flags |= WIMLIB_EXTRACT_FLAG_VERBOSE;
2185                         break;
2186                 case IMAGEX_REF_OPTION:
2187                         swm_glob = optarg;
2188                         break;
2189                 case IMAGEX_UNIX_DATA_OPTION:
2190                         extract_flags |= WIMLIB_EXTRACT_FLAG_UNIX_DATA;
2191                         break;
2192                 case IMAGEX_NO_ACLS_OPTION:
2193                         extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ACLS;
2194                         break;
2195                 case IMAGEX_STRICT_ACLS_OPTION:
2196                         extract_flags |= WIMLIB_EXTRACT_FLAG_STRICT_ACLS;
2197                         break;
2198                 case IMAGEX_DEST_DIR_OPTION:
2199                         dest_dir = optarg;
2200                         break;
2201                 case IMAGEX_TO_STDOUT_OPTION:
2202                         extract_flags |= WIMLIB_EXTRACT_FLAG_TO_STDOUT;
2203                         imagex_be_quiet = true;
2204                         break;
2205                 default:
2206                         goto out_usage;
2207                 }
2208         }
2209         argc -= optind;
2210         argv += optind;
2211
2212         if (argc < 2)
2213                 goto out_usage;
2214
2215         wimfile = argv[0];
2216         image_num_or_name = argv[1];
2217
2218         argc -= 2;
2219         argv += 2;
2220
2221         cmds = prepare_extract_commands(argv, argc, extract_flags, dest_dir,
2222                                         &num_cmds);
2223         if (!cmds) {
2224                 ret = -1;
2225                 goto out;
2226         }
2227
2228         ret = wimlib_open_wim(wimfile, open_flags, &wim, imagex_progress_func);
2229         if (ret)
2230                 goto out_free_cmds;
2231
2232         image = wimlib_resolve_image(wim, image_num_or_name);
2233         ret = verify_image_exists_and_is_single(image,
2234                                                 image_num_or_name,
2235                                                 wimfile);
2236         if (ret)
2237                 goto out_wimlib_free;
2238
2239         if (swm_glob) {
2240                 ret = open_swms_from_glob(swm_glob, wimfile, open_flags,
2241                                           &additional_swms,
2242                                           &num_additional_swms);
2243                 if (ret)
2244                         goto out_wimlib_free;
2245         }
2246
2247 #ifdef __WIN32__
2248         win32_acquire_restore_privileges();
2249 #endif
2250
2251         ret = wimlib_extract_files(wim, image, 0, cmds, num_cmds,
2252                                    additional_swms, num_additional_swms,
2253                                    imagex_progress_func);
2254         if (ret == 0) {
2255                 if (!imagex_be_quiet)
2256                         tprintf(T("Done extracting files.\n"));
2257         } else if (ret == WIMLIB_ERR_PATH_DOES_NOT_EXIST) {
2258                 tfprintf(stderr, T("Note: You can use `"IMAGEX_PROGNAME" dir' to see what "
2259                                    "files and directories\n"
2260                                    "      are in the WIM image.\n"));
2261         }
2262 #ifdef __WIN32__
2263         win32_release_restore_privileges();
2264 #endif
2265         if (additional_swms) {
2266                 for (unsigned i = 0; i < num_additional_swms; i++)
2267                         wimlib_free(additional_swms[i]);
2268                 free(additional_swms);
2269         }
2270 out_wimlib_free:
2271         wimlib_free(wim);
2272 out_free_cmds:
2273         free_extract_commands(cmds, num_cmds, dest_dir);
2274 out:
2275         return ret;
2276 out_usage:
2277         usage(EXTRACT);
2278         ret = -1;
2279         goto out;
2280 }
2281
2282 /* Prints information about a WIM file; also can mark an image as bootable,
2283  * change the name of an image, or change the description of an image. */
2284 static int
2285 imagex_info(int argc, tchar **argv)
2286 {
2287         int c;
2288         bool boot         = false;
2289         bool check        = false;
2290         bool header       = false;
2291         bool lookup_table = false;
2292         bool xml          = false;
2293         bool metadata     = false;
2294         bool short_header = true;
2295         const tchar *xml_out_file = NULL;
2296         const tchar *wimfile;
2297         const tchar *image_num_or_name = T("all");
2298         const tchar *new_name = NULL;
2299         const tchar *new_desc = NULL;
2300         WIMStruct *w;
2301         FILE *fp;
2302         int image;
2303         int ret;
2304         int open_flags = WIMLIB_OPEN_FLAG_SPLIT_OK;
2305         int part_number;
2306         int total_parts;
2307         int num_images;
2308
2309         for_opt(c, info_options) {
2310                 switch (c) {
2311                 case IMAGEX_BOOT_OPTION:
2312                         boot = true;
2313                         break;
2314                 case IMAGEX_CHECK_OPTION:
2315                         check = true;
2316                         break;
2317                 case IMAGEX_HEADER_OPTION:
2318                         header = true;
2319                         short_header = false;
2320                         break;
2321                 case IMAGEX_LOOKUP_TABLE_OPTION:
2322                         lookup_table = true;
2323                         short_header = false;
2324                         break;
2325                 case IMAGEX_XML_OPTION:
2326                         xml = true;
2327                         short_header = false;
2328                         break;
2329                 case IMAGEX_EXTRACT_XML_OPTION:
2330                         xml_out_file = optarg;
2331                         short_header = false;
2332                         break;
2333                 case IMAGEX_METADATA_OPTION:
2334                         metadata = true;
2335                         short_header = false;
2336                         break;
2337                 default:
2338                         usage(INFO);
2339                         return -1;
2340                 }
2341         }
2342
2343         argc -= optind;
2344         argv += optind;
2345         if (argc == 0 || argc > 4) {
2346                 usage(INFO);
2347                 return -1;
2348         }
2349         wimfile = argv[0];
2350         if (argc > 1) {
2351                 image_num_or_name = argv[1];
2352                 if (argc > 2) {
2353                         new_name = argv[2];
2354                         if (argc > 3) {
2355                                 new_desc = argv[3];
2356                         }
2357                 }
2358         }
2359
2360         if (check)
2361                 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
2362
2363         ret = wimlib_open_wim(wimfile, open_flags, &w,
2364                               imagex_progress_func);
2365         if (ret != 0)
2366                 return ret;
2367
2368         part_number = wimlib_get_part_number(w, &total_parts);
2369
2370         image = wimlib_resolve_image(w, image_num_or_name);
2371         if (image == WIMLIB_NO_IMAGE && tstrcmp(image_num_or_name, T("0"))) {
2372                 imagex_error(T("The image \"%"TS"\" does not exist"),
2373                              image_num_or_name);
2374                 if (boot) {
2375                         imagex_error(T("If you would like to set the boot "
2376                                        "index to 0, specify image \"0\" with "
2377                                        "the --boot flag."));
2378                 }
2379                 ret = WIMLIB_ERR_INVALID_IMAGE;
2380                 goto out;
2381         }
2382
2383         num_images = wimlib_get_num_images(w);
2384
2385         if (num_images == 0) {
2386                 if (boot) {
2387                         imagex_error(T("--boot is meaningless on a WIM with no "
2388                                        "images"));
2389                         ret = WIMLIB_ERR_INVALID_IMAGE;
2390                         goto out;
2391                 }
2392         }
2393
2394         if (image == WIMLIB_ALL_IMAGES && num_images > 1) {
2395                 if (boot) {
2396                         imagex_error(T("Cannot specify the --boot flag "
2397                                        "without specifying a specific "
2398                                        "image in a multi-image WIM"));
2399                         ret = WIMLIB_ERR_INVALID_IMAGE;
2400                         goto out;
2401                 }
2402                 if (new_name) {
2403                         imagex_error(T("Cannot specify the NEW_NAME "
2404                                        "without specifying a specific "
2405                                        "image in a multi-image WIM"));
2406                         ret = WIMLIB_ERR_INVALID_IMAGE;
2407                         goto out;
2408                 }
2409         }
2410
2411         /* Operations that print information are separated from operations that
2412          * recreate the WIM file. */
2413         if (!new_name && !boot) {
2414
2415                 /* Read-only operations */
2416
2417                 if (image == WIMLIB_NO_IMAGE) {
2418                         imagex_error(T("\"%"TS"\" is not a valid image"),
2419                                      image_num_or_name);
2420                         ret = WIMLIB_ERR_INVALID_IMAGE;
2421                         goto out;
2422                 }
2423
2424                 if (image == WIMLIB_ALL_IMAGES && short_header)
2425                         wimlib_print_wim_information(w);
2426
2427                 if (header)
2428                         wimlib_print_header(w);
2429
2430                 if (lookup_table) {
2431                         if (total_parts != 1) {
2432                                 tprintf(T("Warning: Only showing the lookup table "
2433                                           "for part %d of a %d-part WIM.\n"),
2434                                         part_number, total_parts);
2435                         }
2436                         wimlib_print_lookup_table(w);
2437                 }
2438
2439                 if (xml) {
2440                         ret = wimlib_extract_xml_data(w, stdout);
2441                         if (ret != 0)
2442                                 goto out;
2443                 }
2444
2445                 if (xml_out_file) {
2446                         fp = tfopen(xml_out_file, T("wb"));
2447                         if (!fp) {
2448                                 imagex_error_with_errno(T("Failed to open the "
2449                                                           "file \"%"TS"\" for "
2450                                                           "writing "),
2451                                                         xml_out_file);
2452                                 ret = -1;
2453                                 goto out;
2454                         }
2455                         ret = wimlib_extract_xml_data(w, fp);
2456                         if (fclose(fp) != 0) {
2457                                 imagex_error(T("Failed to close the file "
2458                                                "\"%"TS"\""),
2459                                              xml_out_file);
2460                                 ret = -1;
2461                         }
2462
2463                         if (ret != 0)
2464                                 goto out;
2465                 }
2466
2467                 if (short_header)
2468                         wimlib_print_available_images(w, image);
2469
2470                 if (metadata) {
2471                         ret = wimlib_print_metadata(w, image);
2472                         if (ret != 0)
2473                                 goto out;
2474                 }
2475         } else {
2476
2477                 /* Modification operations */
2478                 if (total_parts != 1) {
2479                         imagex_error(T("Modifying a split WIM is not supported."));
2480                         ret = -1;
2481                         goto out;
2482                 }
2483                 if (image == WIMLIB_ALL_IMAGES)
2484                         image = 1;
2485
2486                 if (image == WIMLIB_NO_IMAGE && new_name) {
2487                         imagex_error(T("Cannot specify new_name (\"%"TS"\") "
2488                                        "when using image 0"), new_name);
2489                         ret = -1;
2490                         goto out;
2491                 }
2492
2493                 if (boot) {
2494                         if (image == wimlib_get_boot_idx(w)) {
2495                                 tprintf(T("Image %d is already marked as "
2496                                           "bootable.\n"), image);
2497                                 boot = false;
2498                         } else {
2499                                 tprintf(T("Marking image %d as bootable.\n"),
2500                                         image);
2501                                 wimlib_set_boot_idx(w, image);
2502                         }
2503                 }
2504                 if (new_name) {
2505                         if (!tstrcmp(wimlib_get_image_name(w, image), new_name))
2506                         {
2507                                 tprintf(T("Image %d is already named \"%"TS"\".\n"),
2508                                         image, new_name);
2509                                 new_name = NULL;
2510                         } else {
2511                                 tprintf(T("Changing the name of image %d to "
2512                                           "\"%"TS"\".\n"), image, new_name);
2513                                 ret = wimlib_set_image_name(w, image, new_name);
2514                                 if (ret != 0)
2515                                         goto out;
2516                         }
2517                 }
2518                 if (new_desc) {
2519                         const tchar *old_desc;
2520                         old_desc = wimlib_get_image_description(w, image);
2521                         if (old_desc && !tstrcmp(old_desc, new_desc)) {
2522                                 tprintf(T("The description of image %d is already "
2523                                           "\"%"TS"\".\n"), image, new_desc);
2524                                 new_desc = NULL;
2525                         } else {
2526                                 tprintf(T("Changing the description of image %d "
2527                                           "to \"%"TS"\".\n"), image, new_desc);
2528                                 ret = wimlib_set_image_descripton(w, image,
2529                                                                   new_desc);
2530                                 if (ret != 0)
2531                                         goto out;
2532                         }
2533                 }
2534
2535                 /* Only call wimlib_overwrite() if something actually needs to
2536                  * be changed. */
2537                 if (boot || new_name || new_desc ||
2538                     (check && !wimlib_has_integrity_table(w)))
2539                 {
2540                         int write_flags;
2541
2542                         ret = file_writable(wimfile);
2543                         if (ret != 0)
2544                                 goto out;
2545
2546                         if (check)
2547                                 write_flags = WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
2548                         else
2549                                 write_flags = 0;
2550
2551                         ret = wimlib_overwrite(w, write_flags, 1,
2552                                                imagex_progress_func);
2553                         if (ret == WIMLIB_ERR_REOPEN)
2554                                 ret = 0;
2555                 } else {
2556                         tprintf(T("The file \"%"TS"\" was not modified because nothing "
2557                                   "needed to be done.\n"), wimfile);
2558                         ret = 0;
2559                 }
2560         }
2561 out:
2562         wimlib_free(w);
2563         return ret;
2564 }
2565
2566 /* Join split WIMs into one part WIM */
2567 static int
2568 imagex_join(int argc, tchar **argv)
2569 {
2570         int c;
2571         int swm_open_flags = WIMLIB_OPEN_FLAG_SPLIT_OK;
2572         int wim_write_flags = 0;
2573         const tchar *output_path;
2574
2575         for_opt(c, join_options) {
2576                 switch (c) {
2577                 case IMAGEX_CHECK_OPTION:
2578                         swm_open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
2579                         wim_write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
2580                         break;
2581                 default:
2582                         goto err;
2583                 }
2584         }
2585         argc -= optind;
2586         argv += optind;
2587
2588         if (argc < 2) {
2589                 imagex_error(T("Must specify one or more split WIM (.swm) "
2590                                "parts to join"));
2591                 goto err;
2592         }
2593         output_path = argv[0];
2594         return wimlib_join((const tchar * const *)++argv,
2595                            --argc,
2596                            output_path,
2597                            swm_open_flags,
2598                            wim_write_flags,
2599                            imagex_progress_func);
2600 err:
2601         usage(JOIN);
2602         return -1;
2603 }
2604
2605 /* Mounts an image using a FUSE mount. */
2606 static int
2607 imagex_mount_rw_or_ro(int argc, tchar **argv)
2608 {
2609         int c;
2610         int mount_flags = 0;
2611         int open_flags = WIMLIB_OPEN_FLAG_SPLIT_OK;
2612         const tchar *wimfile;
2613         const tchar *dir;
2614         WIMStruct *w;
2615         int image;
2616         int num_images;
2617         int ret;
2618         const tchar *swm_glob = NULL;
2619         WIMStruct **additional_swms = NULL;
2620         unsigned num_additional_swms = 0;
2621         const tchar *staging_dir = NULL;
2622
2623         if (!tstrcmp(argv[0], T("mountrw")))
2624                 mount_flags |= WIMLIB_MOUNT_FLAG_READWRITE;
2625
2626         for_opt(c, mount_options) {
2627                 switch (c) {
2628                 case IMAGEX_ALLOW_OTHER_OPTION:
2629                         mount_flags |= WIMLIB_MOUNT_FLAG_ALLOW_OTHER;
2630                         break;
2631                 case IMAGEX_CHECK_OPTION:
2632                         open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
2633                         break;
2634                 case IMAGEX_DEBUG_OPTION:
2635                         mount_flags |= WIMLIB_MOUNT_FLAG_DEBUG;
2636                         break;
2637                 case IMAGEX_STREAMS_INTERFACE_OPTION:
2638                         if (!tstrcasecmp(optarg, T("none")))
2639                                 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_NONE;
2640                         else if (!tstrcasecmp(optarg, T("xattr")))
2641                                 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR;
2642                         else if (!tstrcasecmp(optarg, T("windows")))
2643                                 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_WINDOWS;
2644                         else {
2645                                 imagex_error(T("Unknown stream interface \"%"TS"\""),
2646                                              optarg);
2647                                 goto mount_usage;
2648                         }
2649                         break;
2650                 case IMAGEX_REF_OPTION:
2651                         swm_glob = optarg;
2652                         break;
2653                 case IMAGEX_STAGING_DIR_OPTION:
2654                         staging_dir = optarg;
2655                         break;
2656                 case IMAGEX_UNIX_DATA_OPTION:
2657                         mount_flags |= WIMLIB_MOUNT_FLAG_UNIX_DATA;
2658                         break;
2659                 default:
2660                         goto mount_usage;
2661                 }
2662         }
2663         argc -= optind;
2664         argv += optind;
2665         if (argc != 2 && argc != 3)
2666                 goto mount_usage;
2667
2668         wimfile = argv[0];
2669
2670         ret = wimlib_open_wim(wimfile, open_flags, &w,
2671                               imagex_progress_func);
2672         if (ret != 0)
2673                 return ret;
2674
2675         if (swm_glob) {
2676                 ret = open_swms_from_glob(swm_glob, wimfile, open_flags,
2677                                           &additional_swms,
2678                                           &num_additional_swms);
2679                 if (ret != 0)
2680                         goto out;
2681         }
2682
2683         if (argc == 2) {
2684                 image = 1;
2685                 num_images = wimlib_get_num_images(w);
2686                 if (num_images != 1) {
2687                         imagex_error(T("The file \"%"TS"\" contains %d images; Please "
2688                                        "select one."), wimfile, num_images);
2689                         usage((mount_flags & WIMLIB_MOUNT_FLAG_READWRITE)
2690                                         ? MOUNTRW : MOUNT);
2691                         ret = -1;
2692                         goto out;
2693                 }
2694                 dir = argv[1];
2695         } else {
2696                 image = wimlib_resolve_image(w, argv[1]);
2697                 dir = argv[2];
2698                 ret = verify_image_exists_and_is_single(image, argv[1], wimfile);
2699                 if (ret != 0)
2700                         goto out;
2701         }
2702
2703         if (mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) {
2704                 ret = file_writable(wimfile);
2705                 if (ret != 0)
2706                         goto out;
2707         }
2708
2709         ret = wimlib_mount_image(w, image, dir, mount_flags, additional_swms,
2710                                  num_additional_swms, staging_dir);
2711         if (ret != 0) {
2712                 imagex_error(T("Failed to mount image %d from \"%"TS"\" "
2713                                "on \"%"TS"\""),
2714                              image, wimfile, dir);
2715
2716         }
2717 out:
2718         wimlib_free(w);
2719         if (additional_swms) {
2720                 for (unsigned i = 0; i < num_additional_swms; i++)
2721                         wimlib_free(additional_swms[i]);
2722                 free(additional_swms);
2723         }
2724         return ret;
2725 mount_usage:
2726         usage((mount_flags & WIMLIB_MOUNT_FLAG_READWRITE)
2727                         ? MOUNTRW : MOUNT);
2728         return -1;
2729 }
2730
2731 /* Rebuild a WIM file */
2732 static int
2733 imagex_optimize(int argc, tchar **argv)
2734 {
2735         int c;
2736         int open_flags = 0;
2737         int write_flags = WIMLIB_WRITE_FLAG_REBUILD;
2738         int ret;
2739         WIMStruct *w;
2740         const tchar *wimfile;
2741         off_t old_size;
2742         off_t new_size;
2743         unsigned num_threads = 0;
2744
2745         for_opt(c, optimize_options) {
2746                 switch (c) {
2747                 case IMAGEX_CHECK_OPTION:
2748                         open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
2749                         write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
2750                         break;
2751                 case IMAGEX_RECOMPRESS_OPTION:
2752                         write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
2753                         break;
2754                 case IMAGEX_THREADS_OPTION:
2755                         num_threads = parse_num_threads(optarg);
2756                         if (num_threads == UINT_MAX)
2757                                 return -1;
2758                         break;
2759                 default:
2760                         usage(OPTIMIZE);
2761                         return -1;
2762                 }
2763         }
2764         argc -= optind;
2765         argv += optind;
2766
2767         if (argc != 1) {
2768                 usage(OPTIMIZE);
2769                 return -1;
2770         }
2771
2772         wimfile = argv[0];
2773
2774         ret = wimlib_open_wim(wimfile, open_flags, &w,
2775                               imagex_progress_func);
2776         if (ret != 0)
2777                 return ret;
2778
2779         old_size = file_get_size(argv[0]);
2780         tprintf(T("\"%"TS"\" original size: "), wimfile);
2781         if (old_size == -1)
2782                 tfputs(T("Unknown\n"), stdout);
2783         else
2784                 tprintf(T("%"PRIu64" KiB\n"), old_size >> 10);
2785
2786         ret = wimlib_overwrite(w, write_flags, num_threads,
2787                                imagex_progress_func);
2788
2789         if (ret == 0) {
2790                 new_size = file_get_size(argv[0]);
2791                 tprintf(T("\"%"TS"\" optimized size: "), wimfile);
2792                 if (new_size == -1)
2793                         tfputs(T("Unknown\n"), stdout);
2794                 else
2795                         tprintf(T("%"PRIu64" KiB\n"), new_size >> 10);
2796
2797                 tfputs(T("Space saved: "), stdout);
2798                 if (new_size != -1 && old_size != -1) {
2799                         tprintf(T("%lld KiB\n"),
2800                                ((long long)old_size - (long long)new_size) >> 10);
2801                 } else {
2802                         tfputs(T("Unknown\n"), stdout);
2803                 }
2804         }
2805
2806         wimlib_free(w);
2807         return ret;
2808 }
2809
2810 /* Split a WIM into a spanned set */
2811 static int
2812 imagex_split(int argc, tchar **argv)
2813 {
2814         int c;
2815         int open_flags = WIMLIB_OPEN_FLAG_SPLIT_OK;
2816         int write_flags = 0;
2817         unsigned long part_size;
2818         tchar *tmp;
2819         int ret;
2820         WIMStruct *w;
2821
2822         for_opt(c, split_options) {
2823                 switch (c) {
2824                 case IMAGEX_CHECK_OPTION:
2825                         open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
2826                         write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
2827                         break;
2828                 default:
2829                         usage(SPLIT);
2830                         return -1;
2831                 }
2832         }
2833         argc -= optind;
2834         argv += optind;
2835
2836         if (argc != 3) {
2837                 usage(SPLIT);
2838                 return -1;
2839         }
2840         part_size = tstrtod(argv[2], &tmp) * (1 << 20);
2841         if (tmp == argv[2] || *tmp) {
2842                 imagex_error(T("Invalid part size \"%"TS"\""), argv[2]);
2843                 imagex_error(T("The part size must be an integer or "
2844                                "floating-point number of megabytes."));
2845                 return -1;
2846         }
2847         ret = wimlib_open_wim(argv[0], open_flags, &w, imagex_progress_func);
2848         if (ret != 0)
2849                 return ret;
2850         ret = wimlib_split(w, argv[1], part_size, write_flags, imagex_progress_func);
2851         wimlib_free(w);
2852         return ret;
2853 }
2854
2855 /* Unmounts a mounted WIM image. */
2856 static int
2857 imagex_unmount(int argc, tchar **argv)
2858 {
2859         int c;
2860         int unmount_flags = 0;
2861         int ret;
2862
2863         for_opt(c, unmount_options) {
2864                 switch (c) {
2865                 case IMAGEX_COMMIT_OPTION:
2866                         unmount_flags |= WIMLIB_UNMOUNT_FLAG_COMMIT;
2867                         break;
2868                 case IMAGEX_CHECK_OPTION:
2869                         unmount_flags |= WIMLIB_UNMOUNT_FLAG_CHECK_INTEGRITY;
2870                         break;
2871                 case IMAGEX_REBUILD_OPTION:
2872                         unmount_flags |= WIMLIB_UNMOUNT_FLAG_REBUILD;
2873                         break;
2874                 default:
2875                         usage(UNMOUNT);
2876                         return -1;
2877                 }
2878         }
2879         argc -= optind;
2880         argv += optind;
2881         if (argc != 1) {
2882                 usage(UNMOUNT);
2883                 return -1;
2884         }
2885
2886         ret = wimlib_unmount_image(argv[0], unmount_flags,
2887                                    imagex_progress_func);
2888         if (ret != 0)
2889                 imagex_error(T("Failed to unmount \"%"TS"\""), argv[0]);
2890         return ret;
2891 }
2892
2893 /*
2894  * Add, delete, or rename files in a WIM image.
2895  */
2896 static int
2897 imagex_update(int argc, tchar **argv)
2898 {
2899         const tchar *wimfile;
2900         const tchar *image_num_or_name;
2901         int image;
2902         WIMStruct *wim;
2903         int ret;
2904         int open_flags = 0;
2905         int write_flags = WIMLIB_WRITE_FLAG_SOFT_DELETE;
2906         int update_flags = 0;
2907         int default_add_flags = WIMLIB_ADD_IMAGE_FLAG_EXCLUDE_VERBOSE;
2908         int default_delete_flags = 0;
2909         unsigned num_threads = 0;
2910         int c;
2911         tchar *cmd_file_contents;
2912         size_t cmd_file_nchars;
2913         struct wimlib_update_command *cmds;
2914         size_t num_cmds;
2915
2916         const tchar *config_file = NULL;
2917         tchar *config_str;
2918         struct wimlib_capture_config *config = NULL;
2919
2920         for_opt(c, update_options) {
2921                 switch (c) {
2922                 case IMAGEX_THREADS_OPTION:
2923                         num_threads = parse_num_threads(optarg);
2924                         if (num_threads == UINT_MAX) {
2925                                 ret = -1;
2926                                 goto out;
2927                         }
2928                         break;
2929                 case IMAGEX_CHECK_OPTION:
2930                         open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
2931                         write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
2932                         break;
2933                 case IMAGEX_REBUILD_OPTION:
2934                         write_flags |= WIMLIB_WRITE_FLAG_REBUILD;
2935                         break;
2936                 case IMAGEX_FORCE_OPTION:
2937                         default_delete_flags |= WIMLIB_DELETE_FLAG_FORCE;
2938                         break;
2939                 case IMAGEX_RECURSIVE_OPTION:
2940                         default_delete_flags |= WIMLIB_DELETE_FLAG_RECURSIVE;
2941                         break;
2942                 case IMAGEX_CONFIG_OPTION:
2943                         config_file = optarg;
2944                         break;
2945                 case IMAGEX_DEREFERENCE_OPTION:
2946                         default_add_flags |= WIMLIB_ADD_IMAGE_FLAG_DEREFERENCE;
2947                         break;
2948                 case IMAGEX_UNIX_DATA_OPTION:
2949                         default_add_flags |= WIMLIB_ADD_IMAGE_FLAG_UNIX_DATA;
2950                         break;
2951                 case IMAGEX_NO_ACLS_OPTION:
2952                         default_add_flags |= WIMLIB_ADD_IMAGE_FLAG_NO_ACLS;
2953                         break;
2954                 case IMAGEX_STRICT_ACLS_OPTION:
2955                         default_add_flags |= WIMLIB_ADD_IMAGE_FLAG_STRICT_ACLS;
2956                         break;
2957                 default:
2958                         goto out_usage;
2959                 }
2960         }
2961         argv += optind;
2962         argc -= optind;
2963
2964         if (argc < 1 || argc > 2)
2965                 goto out_usage;
2966         wimfile = argv[0];
2967         ret = wimlib_open_wim(wimfile, open_flags, &wim, imagex_progress_func);
2968         if (ret)
2969                 goto out;
2970
2971         if (argc == 2)
2972                 image_num_or_name = argv[1];
2973         else
2974                 image_num_or_name = T("1");
2975
2976         image = wimlib_resolve_image(wim, image_num_or_name);
2977
2978         ret = verify_image_exists_and_is_single(image, image_num_or_name,
2979                                                 wimfile);
2980         if (ret)
2981                 goto out_wimlib_free;
2982
2983         /* Parse capture configuration file if specified */
2984         if (config_file) {
2985                 size_t config_len;
2986
2987                 config_str = file_get_text_contents(config_file, &config_len);
2988                 if (!config_str) {
2989                         ret = -1;
2990                         goto out_wimlib_free;
2991                 }
2992
2993                 config = alloca(sizeof(*config));
2994                 ret = parse_capture_config(&config_str, config_len, config);
2995                 if (ret)
2996                         goto out_free_config;
2997         } else {
2998                 config = &default_capture_config;
2999         }
3000
3001         /* Read update commands from standard input */
3002         if (isatty(STDIN_FILENO)) {
3003                 tputs(T("Reading update commands from standard input..."));
3004                 recommend_man_page(T("update"));
3005         }
3006         cmd_file_contents = stdin_get_text_contents(&cmd_file_nchars);
3007         if (!cmd_file_contents) {
3008                 ret = -1;
3009                 goto out_free_config;
3010         }
3011
3012         /* Parse the update commands */
3013         cmds = parse_update_command_file(&cmd_file_contents, cmd_file_nchars,
3014                                          &num_cmds);
3015         if (!cmds) {
3016                 ret = -1;
3017                 goto out_free_cmd_file_contents;
3018         }
3019
3020         /* Set default flags and capture config on the update commands */
3021         for (size_t i = 0; i < num_cmds; i++) {
3022                 switch (cmds[i].op) {
3023                 case WIMLIB_UPDATE_OP_ADD:
3024                         cmds[i].add.add_flags |= default_add_flags;
3025                         cmds[i].add.config = config;
3026                         break;
3027                 case WIMLIB_UPDATE_OP_DELETE:
3028                         cmds[i].delete.delete_flags |= default_delete_flags;
3029                         break;
3030                 default:
3031                         break;
3032                 }
3033         }
3034
3035         /* Execute the update commands */
3036         ret = wimlib_update_image(wim, image, cmds, num_cmds, update_flags,
3037                                   imagex_progress_func);
3038         if (ret)
3039                 goto out_free_cmds;
3040
3041         /* Overwrite the updated WIM */
3042         ret = wimlib_overwrite(wim, write_flags, num_threads,
3043                                imagex_progress_func);
3044 out_free_cmds:
3045         free(cmds);
3046 out_free_cmd_file_contents:
3047         free(cmd_file_contents);
3048 out_free_config:
3049         if (config != NULL && config != &default_capture_config) {
3050                 free(config->exclusion_pats.pats);
3051                 free(config->exclusion_exception_pats.pats);
3052                 free(config_str);
3053         }
3054 out_wimlib_free:
3055         wimlib_free(wim);
3056 out:
3057         return ret;
3058 out_usage:
3059         usage(UPDATE);
3060         ret = -1;
3061         goto out;
3062 }
3063
3064 struct imagex_command {
3065         const tchar *name;
3066         int (*func)(int , tchar **);
3067         int cmd;
3068 };
3069
3070
3071 #define for_imagex_command(p) for (p = &imagex_commands[0]; \
3072                 p != &imagex_commands[ARRAY_LEN(imagex_commands)]; p++)
3073
3074 static const struct imagex_command imagex_commands[] = {
3075         {T("append"),  imagex_capture_or_append, APPEND},
3076         {T("apply"),   imagex_apply,             APPLY},
3077         {T("capture"), imagex_capture_or_append, CAPTURE},
3078         {T("delete"),  imagex_delete,            DELETE},
3079         {T("dir"),     imagex_dir,               DIR},
3080         {T("export"),  imagex_export,            EXPORT},
3081         {T("extract"), imagex_extract,           EXTRACT},
3082         {T("info"),    imagex_info,              INFO},
3083         {T("join"),    imagex_join,              JOIN},
3084         {T("mount"),   imagex_mount_rw_or_ro,    MOUNT},
3085         {T("mountrw"), imagex_mount_rw_or_ro,    MOUNTRW},
3086         {T("optimize"),imagex_optimize,          OPTIMIZE},
3087         {T("split"),   imagex_split,             SPLIT},
3088         {T("unmount"), imagex_unmount,           UNMOUNT},
3089         {T("update"),  imagex_update,            UPDATE},
3090 };
3091
3092 static void
3093 version()
3094 {
3095         static const tchar *s =
3096         T(
3097 IMAGEX_PROGNAME " (" PACKAGE ") " PACKAGE_VERSION "\n"
3098 "Copyright (C) 2012, 2013 Eric Biggers\n"
3099 "License GPLv3+; GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.\n"
3100 "This is free software: you are free to change and redistribute it.\n"
3101 "There is NO WARRANTY, to the extent permitted by law.\n"
3102 "\n"
3103 "Report bugs to "PACKAGE_BUGREPORT".\n"
3104         );
3105         tfputs(s, stdout);
3106 }
3107
3108
3109 static void
3110 help_or_version(int argc, tchar **argv)
3111 {
3112         int i;
3113         const tchar *p;
3114         const struct imagex_command *cmd;
3115
3116         for (i = 1; i < argc; i++) {
3117                 p = argv[i];
3118                 if (*p == T('-'))
3119                         p++;
3120                 else
3121                         continue;
3122                 if (*p == T('-'))
3123                         p++;
3124                 if (!tstrcmp(p, T("help"))) {
3125                         for_imagex_command(cmd) {
3126                                 if (!tstrcmp(cmd->name, argv[1])) {
3127                                         usage(cmd->cmd);
3128                                         exit(0);
3129                                 }
3130                         }
3131                         usage_all();
3132                         exit(0);
3133                 }
3134                 if (!tstrcmp(p, T("version"))) {
3135                         version();
3136                         exit(0);
3137                 }
3138         }
3139 }
3140
3141
3142 static void
3143 usage(int cmd_type)
3144 {
3145         const struct imagex_command *cmd;
3146         tprintf(T("Usage:\n%"TS), usage_strings[cmd_type]);
3147         for_imagex_command(cmd) {
3148                 if (cmd->cmd == cmd_type) {
3149                         tputc(T('\n'), stdout);
3150                         recommend_man_page(cmd->name);
3151                 }
3152         }
3153 }
3154
3155 static void
3156 usage_all()
3157 {
3158         tfputs(T("Usage:\n"), stdout);
3159         for (int i = 0; i < ARRAY_LEN(usage_strings); i++)
3160                 tprintf(T("    %"TS), usage_strings[i]);
3161         static const tchar *extra =
3162         T(
3163 "    "IMAGEX_PROGNAME" --help\n"
3164 "    "IMAGEX_PROGNAME" --version\n"
3165 "\n"
3166 "    The compression TYPE may be \"maximum\", \"fast\", or \"none\".\n"
3167 "\n"
3168 "    Try `man "IMAGEX_PROGNAME"' for more information.\n"
3169         );
3170         tfputs(extra, stdout);
3171 }
3172
3173 /* Entry point for wimlib's ImageX implementation.  On UNIX the command
3174  * arguments will just be 'char' strings (ideally UTF-8 encoded, but could be
3175  * something else), while an Windows the command arguments will be UTF-16LE
3176  * encoded 'wchar_t' strings. */
3177 int
3178 #ifdef __WIN32__
3179 wmain(int argc, wchar_t **argv, wchar_t **envp)
3180 #else
3181 main(int argc, char **argv)
3182 #endif
3183 {
3184         const struct imagex_command *cmd;
3185         int ret;
3186         int init_flags = 0;
3187
3188 #ifndef __WIN32__
3189         if (getenv("WIMLIB_IMAGEX_USE_UTF8")) {
3190                 init_flags |= WIMLIB_INIT_FLAG_ASSUME_UTF8;
3191         } else {
3192                 char *codeset;
3193
3194                 setlocale(LC_ALL, "");
3195                 codeset = nl_langinfo(CODESET);
3196                 if (!strstr(codeset, "UTF-8") &&
3197                     !strstr(codeset, "UTF8") &&
3198                     !strstr(codeset, "utf-8") &&
3199                     !strstr(codeset, "utf8"))
3200                 {
3201                         fputs(
3202 "WARNING: Running "IMAGEX_PROGNAME" in a UTF-8 locale is recommended!\n"
3203 "         Maybe try: `export LANG=en_US.UTF-8'?\n"
3204 "         Alternatively, set the environmental variable WIMLIB_IMAGEX_USE_UTF8\n"
3205 "         to any value to force wimlib to use UTF-8.\n",
3206                         stderr);
3207
3208                 }
3209         }
3210 #endif /* !__WIN32__ */
3211
3212         if (argc < 2) {
3213                 imagex_error(T("No command specified"));
3214                 usage_all();
3215                 ret = 2;
3216                 goto out;
3217         }
3218
3219         /* Handle --help and --version for all commands.  Note that this will
3220          * not return if either of these arguments are present. */
3221         help_or_version(argc, argv);
3222         argc--;
3223         argv++;
3224
3225         /* The user may like to see more informative error messages. */
3226         wimlib_set_print_errors(true);
3227
3228         /* Do any initializations that the library needs */
3229         ret = wimlib_global_init(init_flags);
3230         if (ret)
3231                 goto out_check_status;
3232
3233         /* Search for the function to handle the ImageX subcommand. */
3234         for_imagex_command(cmd) {
3235                 if (!tstrcmp(cmd->name, *argv)) {
3236                         ret = cmd->func(argc, argv);
3237                         goto out_check_write_error;
3238                 }
3239         }
3240
3241         imagex_error(T("Unrecognized command: `%"TS"'"), argv[0]);
3242         usage_all();
3243         ret = 2;
3244         goto out_cleanup;
3245 out_check_write_error:
3246         /* For 'wimlib-imagex info' and 'wimlib-imagex dir', data printed to
3247          * standard output is part of the program's actual behavior and not just
3248          * for informational purposes, so we should set a failure exit status if
3249          * there was a write error. */
3250         if (cmd == &imagex_commands[INFO] || cmd == &imagex_commands[DIR]) {
3251                 if (ferror(stdout) || fclose(stdout)) {
3252                         imagex_error_with_errno(T("error writing to standard output"));
3253                         if (ret == 0)
3254                                 ret = -1;
3255                 }
3256         }
3257 out_check_status:
3258         /* Exit status (ret):  -1 indicates an error found by 'wimlib-imagex'
3259          * outside of the wimlib library code.  0 indicates success.  > 0
3260          * indicates a wimlib error code from which an error message can be
3261          * printed. */
3262         if (ret > 0) {
3263                 imagex_error(T("Exiting with error code %d:\n"
3264                                "       %"TS"."), ret,
3265                              wimlib_get_error_string(ret));
3266                 if (ret == WIMLIB_ERR_NTFS_3G && errno != 0)
3267                         imagex_error_with_errno(T("errno"));
3268         }
3269 out_cleanup:
3270         /* Make the library free any resources it's holding (not strictly
3271          * necessary because the process is ending anyway). */
3272         wimlib_global_cleanup();
3273 out:
3274         return ret;
3275 }