Update wimlib-imagex documentation
[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 **argv, int argc, int extract_flags,
2119                          tchar *dest_dir, size_t *num_cmds_ret)
2120 {
2121         struct wimlib_extract_command *cmds;
2122         size_t num_cmds;
2123         tchar *emptystr = T("");
2124
2125         if (argc == 0) {
2126                 argc = 1;
2127                 argv = &emptystr;
2128         }
2129         num_cmds = argc;
2130         cmds = calloc(num_cmds, sizeof(cmds[0]));
2131         if (!cmds) {
2132                 imagex_error(T("Out of memory!"));
2133                 return NULL;
2134         }
2135
2136         for (size_t i = 0; i < num_cmds; i++) {
2137                 cmds[i].extract_flags = extract_flags;
2138                 cmds[i].wim_source_path = argv[i];
2139                 if (is_root_wim_path(argv[i])) {
2140                         cmds[i].fs_dest_path = dest_dir;
2141                 } else {
2142                         size_t len = tstrlen(dest_dir) + 1 + tstrlen(argv[i]);
2143                         cmds[i].fs_dest_path = malloc((len + 1) * sizeof(tchar));
2144                         if (!cmds[i].fs_dest_path) {
2145                                 free_extract_commands(cmds, num_cmds, dest_dir);
2146                                 return NULL;
2147                         }
2148                         tsprintf(cmds[i].fs_dest_path, T("%"TS"/%"TS), dest_dir,
2149                                  tbasename(argv[i]));
2150                 }
2151         }
2152         *num_cmds_ret = num_cmds;
2153         return cmds;
2154 }
2155
2156 /* Extract files or directories from a WIM image */
2157 static int
2158 imagex_extract(int argc, tchar **argv)
2159 {
2160         int c;
2161         int open_flags = WIMLIB_OPEN_FLAG_SPLIT_OK;
2162         int image;
2163         WIMStruct *wim;
2164         int ret;
2165         const tchar *wimfile;
2166         const tchar *image_num_or_name;
2167         tchar *dest_dir = T(".");
2168         int extract_flags = WIMLIB_EXTRACT_FLAG_SEQUENTIAL | WIMLIB_EXTRACT_FLAG_NORPFIX;
2169
2170         const tchar *swm_glob = NULL;
2171         WIMStruct **additional_swms = NULL;
2172         unsigned num_additional_swms = 0;
2173
2174         struct wimlib_extract_command *cmds;
2175         size_t num_cmds;
2176
2177         for_opt(c, extract_options) {
2178                 switch (c) {
2179                 case IMAGEX_CHECK_OPTION:
2180                         open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
2181                         break;
2182                 case IMAGEX_VERBOSE_OPTION:
2183                         extract_flags |= WIMLIB_EXTRACT_FLAG_VERBOSE;
2184                         break;
2185                 case IMAGEX_REF_OPTION:
2186                         swm_glob = optarg;
2187                         break;
2188                 case IMAGEX_UNIX_DATA_OPTION:
2189                         extract_flags |= WIMLIB_EXTRACT_FLAG_UNIX_DATA;
2190                         break;
2191                 case IMAGEX_NO_ACLS_OPTION:
2192                         extract_flags |= WIMLIB_EXTRACT_FLAG_NO_ACLS;
2193                         break;
2194                 case IMAGEX_STRICT_ACLS_OPTION:
2195                         extract_flags |= WIMLIB_EXTRACT_FLAG_STRICT_ACLS;
2196                         break;
2197                 case IMAGEX_DEST_DIR_OPTION:
2198                         dest_dir = optarg;
2199                         break;
2200                 case IMAGEX_TO_STDOUT_OPTION:
2201                         extract_flags |= WIMLIB_EXTRACT_FLAG_TO_STDOUT;
2202                         imagex_be_quiet = true;
2203                         break;
2204                 default:
2205                         usage(EXTRACT);
2206                         ret = -1;
2207                         goto out;
2208                 }
2209         }
2210         argc -= optind;
2211         argv += optind;
2212
2213         if (argc < 2) {
2214                 usage(EXTRACT);
2215                 ret = -1;
2216                 goto out;
2217         }
2218         wimfile = argv[0];
2219         image_num_or_name = argv[1];
2220
2221         argc -= 2;
2222         argv += 2;
2223
2224         cmds = prepare_extract_commands(argv, argc, extract_flags, dest_dir,
2225                                         &num_cmds);
2226         if (!cmds) {
2227                 ret = -1;
2228                 goto out;
2229         }
2230
2231         ret = wimlib_open_wim(wimfile, open_flags, &wim, imagex_progress_func);
2232         if (ret)
2233                 goto out_free_cmds;
2234
2235         image = wimlib_resolve_image(wim, image_num_or_name);
2236         ret = verify_image_exists_and_is_single(image,
2237                                                 image_num_or_name,
2238                                                 wimfile);
2239         if (ret)
2240                 goto out_wimlib_free;
2241
2242         if (swm_glob) {
2243                 ret = open_swms_from_glob(swm_glob, wimfile, open_flags,
2244                                           &additional_swms,
2245                                           &num_additional_swms);
2246                 if (ret)
2247                         goto out_wimlib_free;
2248         }
2249
2250 #ifdef __WIN32__
2251         win32_acquire_restore_privileges();
2252 #endif
2253
2254         ret = wimlib_extract_files(wim, image, 0, cmds, num_cmds,
2255                                    additional_swms, num_additional_swms,
2256                                    imagex_progress_func);
2257         if (ret == 0) {
2258                 if (!imagex_be_quiet)
2259                         tprintf(T("Done extracting files.\n"));
2260         } else if (ret == WIMLIB_ERR_PATH_DOES_NOT_EXIST) {
2261                 tfprintf(stderr, T("Note: You can use `"IMAGEX_PROGNAME" dir' to see what "
2262                                    "files and directories\n"
2263                                    "      are in the WIM image.\n"));
2264         }
2265 #ifdef __WIN32__
2266         win32_release_restore_privileges();
2267 #endif
2268         if (additional_swms) {
2269                 for (unsigned i = 0; i < num_additional_swms; i++)
2270                         wimlib_free(additional_swms[i]);
2271                 free(additional_swms);
2272         }
2273 out_wimlib_free:
2274         wimlib_free(wim);
2275 out_free_cmds:
2276         free_extract_commands(cmds, num_cmds, dest_dir);
2277 out:
2278         return ret;
2279 }
2280
2281 /* Prints information about a WIM file; also can mark an image as bootable,
2282  * change the name of an image, or change the description of an image. */
2283 static int
2284 imagex_info(int argc, tchar **argv)
2285 {
2286         int c;
2287         bool boot         = false;
2288         bool check        = false;
2289         bool header       = false;
2290         bool lookup_table = false;
2291         bool xml          = false;
2292         bool metadata     = false;
2293         bool short_header = true;
2294         const tchar *xml_out_file = NULL;
2295         const tchar *wimfile;
2296         const tchar *image_num_or_name = T("all");
2297         const tchar *new_name = NULL;
2298         const tchar *new_desc = NULL;
2299         WIMStruct *w;
2300         FILE *fp;
2301         int image;
2302         int ret;
2303         int open_flags = WIMLIB_OPEN_FLAG_SPLIT_OK;
2304         int part_number;
2305         int total_parts;
2306         int num_images;
2307
2308         for_opt(c, info_options) {
2309                 switch (c) {
2310                 case IMAGEX_BOOT_OPTION:
2311                         boot = true;
2312                         break;
2313                 case IMAGEX_CHECK_OPTION:
2314                         check = true;
2315                         break;
2316                 case IMAGEX_HEADER_OPTION:
2317                         header = true;
2318                         short_header = false;
2319                         break;
2320                 case IMAGEX_LOOKUP_TABLE_OPTION:
2321                         lookup_table = true;
2322                         short_header = false;
2323                         break;
2324                 case IMAGEX_XML_OPTION:
2325                         xml = true;
2326                         short_header = false;
2327                         break;
2328                 case IMAGEX_EXTRACT_XML_OPTION:
2329                         xml_out_file = optarg;
2330                         short_header = false;
2331                         break;
2332                 case IMAGEX_METADATA_OPTION:
2333                         metadata = true;
2334                         short_header = false;
2335                         break;
2336                 default:
2337                         usage(INFO);
2338                         return -1;
2339                 }
2340         }
2341
2342         argc -= optind;
2343         argv += optind;
2344         if (argc == 0 || argc > 4) {
2345                 usage(INFO);
2346                 return -1;
2347         }
2348         wimfile = argv[0];
2349         if (argc > 1) {
2350                 image_num_or_name = argv[1];
2351                 if (argc > 2) {
2352                         new_name = argv[2];
2353                         if (argc > 3) {
2354                                 new_desc = argv[3];
2355                         }
2356                 }
2357         }
2358
2359         if (check)
2360                 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
2361
2362         ret = wimlib_open_wim(wimfile, open_flags, &w,
2363                               imagex_progress_func);
2364         if (ret != 0)
2365                 return ret;
2366
2367         part_number = wimlib_get_part_number(w, &total_parts);
2368
2369         image = wimlib_resolve_image(w, image_num_or_name);
2370         if (image == WIMLIB_NO_IMAGE && tstrcmp(image_num_or_name, T("0"))) {
2371                 imagex_error(T("The image \"%"TS"\" does not exist"),
2372                              image_num_or_name);
2373                 if (boot) {
2374                         imagex_error(T("If you would like to set the boot "
2375                                        "index to 0, specify image \"0\" with "
2376                                        "the --boot flag."));
2377                 }
2378                 ret = WIMLIB_ERR_INVALID_IMAGE;
2379                 goto out;
2380         }
2381
2382         num_images = wimlib_get_num_images(w);
2383
2384         if (num_images == 0) {
2385                 if (boot) {
2386                         imagex_error(T("--boot is meaningless on a WIM with no "
2387                                        "images"));
2388                         ret = WIMLIB_ERR_INVALID_IMAGE;
2389                         goto out;
2390                 }
2391         }
2392
2393         if (image == WIMLIB_ALL_IMAGES && num_images > 1) {
2394                 if (boot) {
2395                         imagex_error(T("Cannot specify the --boot flag "
2396                                        "without specifying a specific "
2397                                        "image in a multi-image WIM"));
2398                         ret = WIMLIB_ERR_INVALID_IMAGE;
2399                         goto out;
2400                 }
2401                 if (new_name) {
2402                         imagex_error(T("Cannot specify the NEW_NAME "
2403                                        "without specifying a specific "
2404                                        "image in a multi-image WIM"));
2405                         ret = WIMLIB_ERR_INVALID_IMAGE;
2406                         goto out;
2407                 }
2408         }
2409
2410         /* Operations that print information are separated from operations that
2411          * recreate the WIM file. */
2412         if (!new_name && !boot) {
2413
2414                 /* Read-only operations */
2415
2416                 if (image == WIMLIB_NO_IMAGE) {
2417                         imagex_error(T("\"%"TS"\" is not a valid image"),
2418                                      image_num_or_name);
2419                         ret = WIMLIB_ERR_INVALID_IMAGE;
2420                         goto out;
2421                 }
2422
2423                 if (image == WIMLIB_ALL_IMAGES && short_header)
2424                         wimlib_print_wim_information(w);
2425
2426                 if (header)
2427                         wimlib_print_header(w);
2428
2429                 if (lookup_table) {
2430                         if (total_parts != 1) {
2431                                 tprintf(T("Warning: Only showing the lookup table "
2432                                           "for part %d of a %d-part WIM.\n"),
2433                                         part_number, total_parts);
2434                         }
2435                         wimlib_print_lookup_table(w);
2436                 }
2437
2438                 if (xml) {
2439                         ret = wimlib_extract_xml_data(w, stdout);
2440                         if (ret != 0)
2441                                 goto out;
2442                 }
2443
2444                 if (xml_out_file) {
2445                         fp = tfopen(xml_out_file, T("wb"));
2446                         if (!fp) {
2447                                 imagex_error_with_errno(T("Failed to open the "
2448                                                           "file \"%"TS"\" for "
2449                                                           "writing "),
2450                                                         xml_out_file);
2451                                 ret = -1;
2452                                 goto out;
2453                         }
2454                         ret = wimlib_extract_xml_data(w, fp);
2455                         if (fclose(fp) != 0) {
2456                                 imagex_error(T("Failed to close the file "
2457                                                "\"%"TS"\""),
2458                                              xml_out_file);
2459                                 ret = -1;
2460                         }
2461
2462                         if (ret != 0)
2463                                 goto out;
2464                 }
2465
2466                 if (short_header)
2467                         wimlib_print_available_images(w, image);
2468
2469                 if (metadata) {
2470                         ret = wimlib_print_metadata(w, image);
2471                         if (ret != 0)
2472                                 goto out;
2473                 }
2474         } else {
2475
2476                 /* Modification operations */
2477                 if (total_parts != 1) {
2478                         imagex_error(T("Modifying a split WIM is not supported."));
2479                         ret = -1;
2480                         goto out;
2481                 }
2482                 if (image == WIMLIB_ALL_IMAGES)
2483                         image = 1;
2484
2485                 if (image == WIMLIB_NO_IMAGE && new_name) {
2486                         imagex_error(T("Cannot specify new_name (\"%"TS"\") "
2487                                        "when using image 0"), new_name);
2488                         ret = -1;
2489                         goto out;
2490                 }
2491
2492                 if (boot) {
2493                         if (image == wimlib_get_boot_idx(w)) {
2494                                 tprintf(T("Image %d is already marked as "
2495                                           "bootable.\n"), image);
2496                                 boot = false;
2497                         } else {
2498                                 tprintf(T("Marking image %d as bootable.\n"),
2499                                         image);
2500                                 wimlib_set_boot_idx(w, image);
2501                         }
2502                 }
2503                 if (new_name) {
2504                         if (!tstrcmp(wimlib_get_image_name(w, image), new_name))
2505                         {
2506                                 tprintf(T("Image %d is already named \"%"TS"\".\n"),
2507                                         image, new_name);
2508                                 new_name = NULL;
2509                         } else {
2510                                 tprintf(T("Changing the name of image %d to "
2511                                           "\"%"TS"\".\n"), image, new_name);
2512                                 ret = wimlib_set_image_name(w, image, new_name);
2513                                 if (ret != 0)
2514                                         goto out;
2515                         }
2516                 }
2517                 if (new_desc) {
2518                         const tchar *old_desc;
2519                         old_desc = wimlib_get_image_description(w, image);
2520                         if (old_desc && !tstrcmp(old_desc, new_desc)) {
2521                                 tprintf(T("The description of image %d is already "
2522                                           "\"%"TS"\".\n"), image, new_desc);
2523                                 new_desc = NULL;
2524                         } else {
2525                                 tprintf(T("Changing the description of image %d "
2526                                           "to \"%"TS"\".\n"), image, new_desc);
2527                                 ret = wimlib_set_image_descripton(w, image,
2528                                                                   new_desc);
2529                                 if (ret != 0)
2530                                         goto out;
2531                         }
2532                 }
2533
2534                 /* Only call wimlib_overwrite() if something actually needs to
2535                  * be changed. */
2536                 if (boot || new_name || new_desc ||
2537                     (check && !wimlib_has_integrity_table(w)))
2538                 {
2539                         int write_flags;
2540
2541                         ret = file_writable(wimfile);
2542                         if (ret != 0)
2543                                 goto out;
2544
2545                         if (check)
2546                                 write_flags = WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
2547                         else
2548                                 write_flags = 0;
2549
2550                         ret = wimlib_overwrite(w, write_flags, 1,
2551                                                imagex_progress_func);
2552                         if (ret == WIMLIB_ERR_REOPEN)
2553                                 ret = 0;
2554                 } else {
2555                         tprintf(T("The file \"%"TS"\" was not modified because nothing "
2556                                   "needed to be done.\n"), wimfile);
2557                         ret = 0;
2558                 }
2559         }
2560 out:
2561         wimlib_free(w);
2562         return ret;
2563 }
2564
2565 /* Join split WIMs into one part WIM */
2566 static int
2567 imagex_join(int argc, tchar **argv)
2568 {
2569         int c;
2570         int swm_open_flags = WIMLIB_OPEN_FLAG_SPLIT_OK;
2571         int wim_write_flags = 0;
2572         const tchar *output_path;
2573
2574         for_opt(c, join_options) {
2575                 switch (c) {
2576                 case IMAGEX_CHECK_OPTION:
2577                         swm_open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
2578                         wim_write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
2579                         break;
2580                 default:
2581                         goto err;
2582                 }
2583         }
2584         argc -= optind;
2585         argv += optind;
2586
2587         if (argc < 2) {
2588                 imagex_error(T("Must specify one or more split WIM (.swm) "
2589                                "parts to join"));
2590                 goto err;
2591         }
2592         output_path = argv[0];
2593         return wimlib_join((const tchar * const *)++argv,
2594                            --argc,
2595                            output_path,
2596                            swm_open_flags,
2597                            wim_write_flags,
2598                            imagex_progress_func);
2599 err:
2600         usage(JOIN);
2601         return -1;
2602 }
2603
2604 /* Mounts an image using a FUSE mount. */
2605 static int
2606 imagex_mount_rw_or_ro(int argc, tchar **argv)
2607 {
2608         int c;
2609         int mount_flags = 0;
2610         int open_flags = WIMLIB_OPEN_FLAG_SPLIT_OK;
2611         const tchar *wimfile;
2612         const tchar *dir;
2613         WIMStruct *w;
2614         int image;
2615         int num_images;
2616         int ret;
2617         const tchar *swm_glob = NULL;
2618         WIMStruct **additional_swms = NULL;
2619         unsigned num_additional_swms = 0;
2620         const tchar *staging_dir = NULL;
2621
2622         if (!tstrcmp(argv[0], T("mountrw")))
2623                 mount_flags |= WIMLIB_MOUNT_FLAG_READWRITE;
2624
2625         for_opt(c, mount_options) {
2626                 switch (c) {
2627                 case IMAGEX_ALLOW_OTHER_OPTION:
2628                         mount_flags |= WIMLIB_MOUNT_FLAG_ALLOW_OTHER;
2629                         break;
2630                 case IMAGEX_CHECK_OPTION:
2631                         open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
2632                         break;
2633                 case IMAGEX_DEBUG_OPTION:
2634                         mount_flags |= WIMLIB_MOUNT_FLAG_DEBUG;
2635                         break;
2636                 case IMAGEX_STREAMS_INTERFACE_OPTION:
2637                         if (!tstrcasecmp(optarg, T("none")))
2638                                 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_NONE;
2639                         else if (!tstrcasecmp(optarg, T("xattr")))
2640                                 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR;
2641                         else if (!tstrcasecmp(optarg, T("windows")))
2642                                 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_WINDOWS;
2643                         else {
2644                                 imagex_error(T("Unknown stream interface \"%"TS"\""),
2645                                              optarg);
2646                                 goto mount_usage;
2647                         }
2648                         break;
2649                 case IMAGEX_REF_OPTION:
2650                         swm_glob = optarg;
2651                         break;
2652                 case IMAGEX_STAGING_DIR_OPTION:
2653                         staging_dir = optarg;
2654                         break;
2655                 case IMAGEX_UNIX_DATA_OPTION:
2656                         mount_flags |= WIMLIB_MOUNT_FLAG_UNIX_DATA;
2657                         break;
2658                 default:
2659                         goto mount_usage;
2660                 }
2661         }
2662         argc -= optind;
2663         argv += optind;
2664         if (argc != 2 && argc != 3)
2665                 goto mount_usage;
2666
2667         wimfile = argv[0];
2668
2669         ret = wimlib_open_wim(wimfile, open_flags, &w,
2670                               imagex_progress_func);
2671         if (ret != 0)
2672                 return ret;
2673
2674         if (swm_glob) {
2675                 ret = open_swms_from_glob(swm_glob, wimfile, open_flags,
2676                                           &additional_swms,
2677                                           &num_additional_swms);
2678                 if (ret != 0)
2679                         goto out;
2680         }
2681
2682         if (argc == 2) {
2683                 image = 1;
2684                 num_images = wimlib_get_num_images(w);
2685                 if (num_images != 1) {
2686                         imagex_error(T("The file \"%"TS"\" contains %d images; Please "
2687                                        "select one."), wimfile, num_images);
2688                         usage((mount_flags & WIMLIB_MOUNT_FLAG_READWRITE)
2689                                         ? MOUNTRW : MOUNT);
2690                         ret = -1;
2691                         goto out;
2692                 }
2693                 dir = argv[1];
2694         } else {
2695                 image = wimlib_resolve_image(w, argv[1]);
2696                 dir = argv[2];
2697                 ret = verify_image_exists_and_is_single(image, argv[1], wimfile);
2698                 if (ret != 0)
2699                         goto out;
2700         }
2701
2702         if (mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) {
2703                 ret = file_writable(wimfile);
2704                 if (ret != 0)
2705                         goto out;
2706         }
2707
2708         ret = wimlib_mount_image(w, image, dir, mount_flags, additional_swms,
2709                                  num_additional_swms, staging_dir);
2710         if (ret != 0) {
2711                 imagex_error(T("Failed to mount image %d from \"%"TS"\" "
2712                                "on \"%"TS"\""),
2713                              image, wimfile, dir);
2714
2715         }
2716 out:
2717         wimlib_free(w);
2718         if (additional_swms) {
2719                 for (unsigned i = 0; i < num_additional_swms; i++)
2720                         wimlib_free(additional_swms[i]);
2721                 free(additional_swms);
2722         }
2723         return ret;
2724 mount_usage:
2725         usage((mount_flags & WIMLIB_MOUNT_FLAG_READWRITE)
2726                         ? MOUNTRW : MOUNT);
2727         return -1;
2728 }
2729
2730 /* Rebuild a WIM file */
2731 static int
2732 imagex_optimize(int argc, tchar **argv)
2733 {
2734         int c;
2735         int open_flags = 0;
2736         int write_flags = WIMLIB_WRITE_FLAG_REBUILD;
2737         int ret;
2738         WIMStruct *w;
2739         const tchar *wimfile;
2740         off_t old_size;
2741         off_t new_size;
2742         unsigned num_threads = 0;
2743
2744         for_opt(c, optimize_options) {
2745                 switch (c) {
2746                 case IMAGEX_CHECK_OPTION:
2747                         open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
2748                         write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
2749                         break;
2750                 case IMAGEX_RECOMPRESS_OPTION:
2751                         write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
2752                         break;
2753                 case IMAGEX_THREADS_OPTION:
2754                         num_threads = parse_num_threads(optarg);
2755                         if (num_threads == UINT_MAX)
2756                                 return -1;
2757                         break;
2758                 default:
2759                         usage(OPTIMIZE);
2760                         return -1;
2761                 }
2762         }
2763         argc -= optind;
2764         argv += optind;
2765
2766         if (argc != 1) {
2767                 usage(OPTIMIZE);
2768                 return -1;
2769         }
2770
2771         wimfile = argv[0];
2772
2773         ret = wimlib_open_wim(wimfile, open_flags, &w,
2774                               imagex_progress_func);
2775         if (ret != 0)
2776                 return ret;
2777
2778         old_size = file_get_size(argv[0]);
2779         tprintf(T("\"%"TS"\" original size: "), wimfile);
2780         if (old_size == -1)
2781                 tfputs(T("Unknown\n"), stdout);
2782         else
2783                 tprintf(T("%"PRIu64" KiB\n"), old_size >> 10);
2784
2785         ret = wimlib_overwrite(w, write_flags, num_threads,
2786                                imagex_progress_func);
2787
2788         if (ret == 0) {
2789                 new_size = file_get_size(argv[0]);
2790                 tprintf(T("\"%"TS"\" optimized size: "), wimfile);
2791                 if (new_size == -1)
2792                         tfputs(T("Unknown\n"), stdout);
2793                 else
2794                         tprintf(T("%"PRIu64" KiB\n"), new_size >> 10);
2795
2796                 tfputs(T("Space saved: "), stdout);
2797                 if (new_size != -1 && old_size != -1) {
2798                         tprintf(T("%lld KiB\n"),
2799                                ((long long)old_size - (long long)new_size) >> 10);
2800                 } else {
2801                         tfputs(T("Unknown\n"), stdout);
2802                 }
2803         }
2804
2805         wimlib_free(w);
2806         return ret;
2807 }
2808
2809 /* Split a WIM into a spanned set */
2810 static int
2811 imagex_split(int argc, tchar **argv)
2812 {
2813         int c;
2814         int open_flags = WIMLIB_OPEN_FLAG_SPLIT_OK;
2815         int write_flags = 0;
2816         unsigned long part_size;
2817         tchar *tmp;
2818         int ret;
2819         WIMStruct *w;
2820
2821         for_opt(c, split_options) {
2822                 switch (c) {
2823                 case IMAGEX_CHECK_OPTION:
2824                         open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
2825                         write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
2826                         break;
2827                 default:
2828                         usage(SPLIT);
2829                         return -1;
2830                 }
2831         }
2832         argc -= optind;
2833         argv += optind;
2834
2835         if (argc != 3) {
2836                 usage(SPLIT);
2837                 return -1;
2838         }
2839         part_size = tstrtod(argv[2], &tmp) * (1 << 20);
2840         if (tmp == argv[2] || *tmp) {
2841                 imagex_error(T("Invalid part size \"%"TS"\""), argv[2]);
2842                 imagex_error(T("The part size must be an integer or "
2843                                "floating-point number of megabytes."));
2844                 return -1;
2845         }
2846         ret = wimlib_open_wim(argv[0], open_flags, &w, imagex_progress_func);
2847         if (ret != 0)
2848                 return ret;
2849         ret = wimlib_split(w, argv[1], part_size, write_flags, imagex_progress_func);
2850         wimlib_free(w);
2851         return ret;
2852 }
2853
2854 /* Unmounts a mounted WIM image. */
2855 static int
2856 imagex_unmount(int argc, tchar **argv)
2857 {
2858         int c;
2859         int unmount_flags = 0;
2860         int ret;
2861
2862         for_opt(c, unmount_options) {
2863                 switch (c) {
2864                 case IMAGEX_COMMIT_OPTION:
2865                         unmount_flags |= WIMLIB_UNMOUNT_FLAG_COMMIT;
2866                         break;
2867                 case IMAGEX_CHECK_OPTION:
2868                         unmount_flags |= WIMLIB_UNMOUNT_FLAG_CHECK_INTEGRITY;
2869                         break;
2870                 case IMAGEX_REBUILD_OPTION:
2871                         unmount_flags |= WIMLIB_UNMOUNT_FLAG_REBUILD;
2872                         break;
2873                 default:
2874                         usage(UNMOUNT);
2875                         return -1;
2876                 }
2877         }
2878         argc -= optind;
2879         argv += optind;
2880         if (argc != 1) {
2881                 usage(UNMOUNT);
2882                 return -1;
2883         }
2884
2885         ret = wimlib_unmount_image(argv[0], unmount_flags,
2886                                    imagex_progress_func);
2887         if (ret != 0)
2888                 imagex_error(T("Failed to unmount \"%"TS"\""), argv[0]);
2889         return ret;
2890 }
2891
2892 /*
2893  * Add, delete, or rename files in a WIM image.
2894  */
2895 static int
2896 imagex_update(int argc, tchar **argv)
2897 {
2898         const tchar *wimfile;
2899         const tchar *image_num_or_name;
2900         int image;
2901         WIMStruct *wim;
2902         int ret;
2903         int open_flags = 0;
2904         int write_flags = WIMLIB_WRITE_FLAG_SOFT_DELETE;
2905         int update_flags = 0;
2906         int default_add_flags = WIMLIB_ADD_IMAGE_FLAG_EXCLUDE_VERBOSE;
2907         int default_delete_flags = 0;
2908         unsigned num_threads = 0;
2909         int c;
2910         tchar *cmd_file_contents;
2911         size_t cmd_file_nchars;
2912         struct wimlib_update_command *cmds;
2913         size_t num_cmds;
2914
2915         const tchar *config_file = NULL;
2916         tchar *config_str;
2917         struct wimlib_capture_config *config = NULL;
2918
2919         for_opt(c, update_options) {
2920                 switch (c) {
2921                 case IMAGEX_THREADS_OPTION:
2922                         num_threads = parse_num_threads(optarg);
2923                         if (num_threads == UINT_MAX) {
2924                                 ret = -1;
2925                                 goto out;
2926                         }
2927                         break;
2928                 case IMAGEX_CHECK_OPTION:
2929                         open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
2930                         write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
2931                         break;
2932                 case IMAGEX_REBUILD_OPTION:
2933                         write_flags |= WIMLIB_WRITE_FLAG_REBUILD;
2934                         break;
2935                 case IMAGEX_FORCE_OPTION:
2936                         default_delete_flags |= WIMLIB_DELETE_FLAG_FORCE;
2937                         break;
2938                 case IMAGEX_RECURSIVE_OPTION:
2939                         default_delete_flags |= WIMLIB_DELETE_FLAG_RECURSIVE;
2940                         break;
2941                 case IMAGEX_CONFIG_OPTION:
2942                         config_file = optarg;
2943                         break;
2944                 case IMAGEX_DEREFERENCE_OPTION:
2945                         default_add_flags |= WIMLIB_ADD_IMAGE_FLAG_DEREFERENCE;
2946                         break;
2947                 case IMAGEX_UNIX_DATA_OPTION:
2948                         default_add_flags |= WIMLIB_ADD_IMAGE_FLAG_UNIX_DATA;
2949                         break;
2950                 case IMAGEX_NO_ACLS_OPTION:
2951                         default_add_flags |= WIMLIB_ADD_IMAGE_FLAG_NO_ACLS;
2952                         break;
2953                 case IMAGEX_STRICT_ACLS_OPTION:
2954                         default_add_flags |= WIMLIB_ADD_IMAGE_FLAG_STRICT_ACLS;
2955                         break;
2956                 default:
2957                         goto out_usage;
2958                 }
2959         }
2960         argv += optind;
2961         argc -= optind;
2962
2963         if (argc < 1 || argc > 2)
2964                 goto out_usage;
2965         wimfile = argv[0];
2966         ret = wimlib_open_wim(wimfile, open_flags, &wim, imagex_progress_func);
2967         if (ret)
2968                 goto out;
2969
2970         if (argc == 2)
2971                 image_num_or_name = argv[1];
2972         else
2973                 image_num_or_name = T("1");
2974
2975         image = wimlib_resolve_image(wim, image_num_or_name);
2976
2977         ret = verify_image_exists_and_is_single(image, image_num_or_name,
2978                                                 wimfile);
2979         if (ret)
2980                 goto out_wimlib_free;
2981
2982         /* Parse capture configuration file if specified */
2983         if (config_file) {
2984                 size_t config_len;
2985
2986                 config_str = file_get_text_contents(config_file, &config_len);
2987                 if (!config_str) {
2988                         ret = -1;
2989                         goto out_wimlib_free;
2990                 }
2991
2992                 config = alloca(sizeof(*config));
2993                 ret = parse_capture_config(&config_str, config_len, config);
2994                 if (ret)
2995                         goto out_free_config;
2996         } else {
2997                 config = &default_capture_config;
2998         }
2999
3000         /* Read update commands from standard input */
3001         if (isatty(STDIN_FILENO)) {
3002                 tputs(T("Reading update commands from standard input..."));
3003                 recommend_man_page(T("update"));
3004         }
3005         cmd_file_contents = stdin_get_text_contents(&cmd_file_nchars);
3006         if (!cmd_file_contents) {
3007                 ret = -1;
3008                 goto out_free_config;
3009         }
3010
3011         /* Parse the update commands */
3012         cmds = parse_update_command_file(&cmd_file_contents, cmd_file_nchars,
3013                                          &num_cmds);
3014         if (!cmds) {
3015                 ret = -1;
3016                 goto out_free_cmd_file_contents;
3017         }
3018
3019         /* Set default flags and capture config on the update commands */
3020         for (size_t i = 0; i < num_cmds; i++) {
3021                 switch (cmds[i].op) {
3022                 case WIMLIB_UPDATE_OP_ADD:
3023                         cmds[i].add.add_flags |= default_add_flags;
3024                         cmds[i].add.config = config;
3025                         break;
3026                 case WIMLIB_UPDATE_OP_DELETE:
3027                         cmds[i].delete.delete_flags |= default_delete_flags;
3028                         break;
3029                 default:
3030                         break;
3031                 }
3032         }
3033
3034         /* Execute the update commands */
3035         ret = wimlib_update_image(wim, image, cmds, num_cmds, update_flags,
3036                                   imagex_progress_func);
3037         if (ret)
3038                 goto out_free_cmds;
3039
3040         /* Overwrite the updated WIM */
3041         ret = wimlib_overwrite(wim, write_flags, num_threads,
3042                                imagex_progress_func);
3043 out_free_cmds:
3044         free(cmds);
3045 out_free_cmd_file_contents:
3046         free(cmd_file_contents);
3047 out_free_config:
3048         if (config != NULL && config != &default_capture_config) {
3049                 free(config->exclusion_pats.pats);
3050                 free(config->exclusion_exception_pats.pats);
3051                 free(config_str);
3052         }
3053 out_wimlib_free:
3054         wimlib_free(wim);
3055 out:
3056         return ret;
3057 out_usage:
3058         usage(UPDATE);
3059         ret = -1;
3060         goto out;
3061 }
3062
3063 struct imagex_command {
3064         const tchar *name;
3065         int (*func)(int , tchar **);
3066         int cmd;
3067 };
3068
3069
3070 #define for_imagex_command(p) for (p = &imagex_commands[0]; \
3071                 p != &imagex_commands[ARRAY_LEN(imagex_commands)]; p++)
3072
3073 static const struct imagex_command imagex_commands[] = {
3074         {T("append"),  imagex_capture_or_append, APPEND},
3075         {T("apply"),   imagex_apply,             APPLY},
3076         {T("capture"), imagex_capture_or_append, CAPTURE},
3077         {T("delete"),  imagex_delete,            DELETE},
3078         {T("dir"),     imagex_dir,               DIR},
3079         {T("export"),  imagex_export,            EXPORT},
3080         {T("extract"), imagex_extract,           EXTRACT},
3081         {T("info"),    imagex_info,              INFO},
3082         {T("join"),    imagex_join,              JOIN},
3083         {T("mount"),   imagex_mount_rw_or_ro,    MOUNT},
3084         {T("mountrw"), imagex_mount_rw_or_ro,    MOUNTRW},
3085         {T("optimize"),imagex_optimize,          OPTIMIZE},
3086         {T("split"),   imagex_split,             SPLIT},
3087         {T("unmount"), imagex_unmount,           UNMOUNT},
3088         {T("update"),  imagex_update,            UPDATE},
3089 };
3090
3091 static void
3092 version()
3093 {
3094         static const tchar *s =
3095         T(
3096 IMAGEX_PROGNAME " (" PACKAGE ") " PACKAGE_VERSION "\n"
3097 "Copyright (C) 2012, 2013 Eric Biggers\n"
3098 "License GPLv3+; GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.\n"
3099 "This is free software: you are free to change and redistribute it.\n"
3100 "There is NO WARRANTY, to the extent permitted by law.\n"
3101 "\n"
3102 "Report bugs to "PACKAGE_BUGREPORT".\n"
3103         );
3104         tfputs(s, stdout);
3105 }
3106
3107
3108 static void
3109 help_or_version(int argc, tchar **argv)
3110 {
3111         int i;
3112         const tchar *p;
3113         const struct imagex_command *cmd;
3114
3115         for (i = 1; i < argc; i++) {
3116                 p = argv[i];
3117                 if (*p == T('-'))
3118                         p++;
3119                 else
3120                         continue;
3121                 if (*p == T('-'))
3122                         p++;
3123                 if (!tstrcmp(p, T("help"))) {
3124                         for_imagex_command(cmd) {
3125                                 if (!tstrcmp(cmd->name, argv[1])) {
3126                                         usage(cmd->cmd);
3127                                         exit(0);
3128                                 }
3129                         }
3130                         usage_all();
3131                         exit(0);
3132                 }
3133                 if (!tstrcmp(p, T("version"))) {
3134                         version();
3135                         exit(0);
3136                 }
3137         }
3138 }
3139
3140
3141 static void
3142 usage(int cmd_type)
3143 {
3144         const struct imagex_command *cmd;
3145         tprintf(T("Usage:\n%"TS), usage_strings[cmd_type]);
3146         for_imagex_command(cmd) {
3147                 if (cmd->cmd == cmd_type) {
3148                         tputc(T('\n'), stdout);
3149                         recommend_man_page(cmd->name);
3150                 }
3151         }
3152 }
3153
3154 static void
3155 usage_all()
3156 {
3157         tfputs(T("Usage:\n"), stdout);
3158         for (int i = 0; i < ARRAY_LEN(usage_strings); i++)
3159                 tprintf(T("    %"TS), usage_strings[i]);
3160         static const tchar *extra =
3161         T(
3162 "    "IMAGEX_PROGNAME" --help\n"
3163 "    "IMAGEX_PROGNAME" --version\n"
3164 "\n"
3165 "    The compression TYPE may be \"maximum\", \"fast\", or \"none\".\n"
3166 "\n"
3167 "    Try `man "IMAGEX_PROGNAME"' for more information.\n"
3168         );
3169         tfputs(extra, stdout);
3170 }
3171
3172 /* Entry point for wimlib's ImageX implementation.  On UNIX the command
3173  * arguments will just be 'char' strings (ideally UTF-8 encoded, but could be
3174  * something else), while an Windows the command arguments will be UTF-16LE
3175  * encoded 'wchar_t' strings. */
3176 int
3177 #ifdef __WIN32__
3178 wmain(int argc, wchar_t **argv, wchar_t **envp)
3179 #else
3180 main(int argc, char **argv)
3181 #endif
3182 {
3183         const struct imagex_command *cmd;
3184         int ret;
3185         int init_flags = 0;
3186
3187 #ifndef __WIN32__
3188         if (getenv("WIMLIB_IMAGEX_USE_UTF8")) {
3189                 init_flags |= WIMLIB_INIT_FLAG_ASSUME_UTF8;
3190         } else {
3191                 char *codeset;
3192
3193                 setlocale(LC_ALL, "");
3194                 codeset = nl_langinfo(CODESET);
3195                 if (!strstr(codeset, "UTF-8") &&
3196                     !strstr(codeset, "UTF8") &&
3197                     !strstr(codeset, "utf-8") &&
3198                     !strstr(codeset, "utf8"))
3199                 {
3200                         fputs(
3201 "WARNING: Running "IMAGEX_PROGNAME" in a UTF-8 locale is recommended!\n"
3202 "         Maybe try: `export LANG=en_US.UTF-8'?\n"
3203 "         Alternatively, set the environmental variable WIMLIB_IMAGEX_USE_UTF8\n"
3204 "         to any value to force wimlib to use UTF-8.\n",
3205                         stderr);
3206
3207                 }
3208         }
3209 #endif /* !__WIN32__ */
3210
3211         if (argc < 2) {
3212                 imagex_error(T("No command specified"));
3213                 usage_all();
3214                 ret = 2;
3215                 goto out;
3216         }
3217
3218         /* Handle --help and --version for all commands.  Note that this will
3219          * not return if either of these arguments are present. */
3220         help_or_version(argc, argv);
3221         argc--;
3222         argv++;
3223
3224         /* The user may like to see more informative error messages. */
3225         wimlib_set_print_errors(true);
3226
3227         /* Do any initializations that the library needs */
3228         ret = wimlib_global_init(init_flags);
3229         if (ret)
3230                 goto out_check_status;
3231
3232         /* Search for the function to handle the ImageX subcommand. */
3233         for_imagex_command(cmd) {
3234                 if (!tstrcmp(cmd->name, *argv)) {
3235                         ret = cmd->func(argc, argv);
3236                         goto out_check_write_error;
3237                 }
3238         }
3239
3240         imagex_error(T("Unrecognized command: `%"TS"'"), argv[0]);
3241         usage_all();
3242         ret = 2;
3243         goto out_cleanup;
3244 out_check_write_error:
3245         /* For 'wimlib-imagex info' and 'wimlib-imagex dir', data printed to
3246          * standard output is part of the program's actual behavior and not just
3247          * for informational purposes, so we should set a failure exit status if
3248          * there was a write error. */
3249         if (cmd == &imagex_commands[INFO] || cmd == &imagex_commands[DIR]) {
3250                 if (ferror(stdout) || fclose(stdout)) {
3251                         imagex_error_with_errno(T("error writing to standard output"));
3252                         if (ret == 0)
3253                                 ret = -1;
3254                 }
3255         }
3256 out_check_status:
3257         /* Exit status (ret):  -1 indicates an error found by 'wimlib-imagex'
3258          * outside of the wimlib library code.  0 indicates success.  > 0
3259          * indicates a wimlib error code from which an error message can be
3260          * printed. */
3261         if (ret > 0) {
3262                 imagex_error(T("Exiting with error code %d:\n"
3263                                "       %"TS"."), ret,
3264                              wimlib_get_error_string(ret));
3265                 if (ret == WIMLIB_ERR_NTFS_3G && errno != 0)
3266                         imagex_error_with_errno(T("errno"));
3267         }
3268 out_cleanup:
3269         /* Make the library free any resources it's holding (not strictly
3270          * necessary because the process is ending anyway). */
3271         wimlib_global_cleanup();
3272 out:
3273         return ret;
3274 }