]> wimlib.net Git - wimlib/blob - programs/imagex.c
Win32 fixes
[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
27 #include "wimlib.h"
28
29 #include <ctype.h>
30 #include <errno.h>
31 #include <getopt.h>
32 #include <glob.h>
33 #include <inttypes.h>
34 #include <libgen.h>
35 #include <limits.h>
36 #include <stdarg.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <sys/stat.h>
40 #include <unistd.h>
41
42 #ifdef HAVE_ALLOCA_H
43 #include <alloca.h>
44 #endif
45
46 #define ARRAY_LEN(array) (sizeof(array) / sizeof(array[0]))
47
48 #define for_opt(c, opts) while ((c = getopt_long_only(argc, (char**)argv, "", \
49                                 opts, NULL)) != -1)
50
51 enum imagex_op_type {
52         APPEND = 0,
53         APPLY,
54         CAPTURE,
55         DELETE,
56         DIR,
57         EXPORT,
58         INFO,
59         JOIN,
60         MOUNT,
61         MOUNTRW,
62         OPTIMIZE,
63         SPLIT,
64         UNMOUNT,
65 };
66
67 static void usage(int cmd_type);
68 static void usage_all();
69
70 static const char *usage_strings[] = {
71 [APPEND] =
72 "imagex append (DIRECTORY | NTFS_VOLUME) WIMFILE [IMAGE_NAME]\n"
73 "                     [DESCRIPTION] [--boot] [--check] [--flags EDITION_ID]\n"
74 "                     [--verbose] [--dereference] [--config=FILE]\n"
75 "                     [--threads=NUM_THREADS] [--rebuild] [--unix-data]\n"
76 "                     [--source-list]\n",
77 [APPLY] =
78 "imagex apply WIMFILE [IMAGE_NUM | IMAGE_NAME | all]\n"
79 "                    (DIRECTORY | NTFS_VOLUME) [--check] [--hardlink]\n"
80 "                    [--symlink] [--verbose] [--ref=\"GLOB\"] [--unix-data]\n",
81 [CAPTURE] =
82 "imagex capture (DIRECTORY | NTFS_VOLUME) WIMFILE [IMAGE_NAME]\n"
83 "                      [DESCRIPTION] [--boot] [--check] [--compress=TYPE]\n"
84 "                      [--flags EDITION_ID] [--verbose] [--dereference]\n"
85 "                      [--config=FILE] [--threads=NUM_THREADS] [--unix-data]\n"
86 "                      [--source-list]\n",
87 [DELETE] =
88 "imagex delete WIMFILE (IMAGE_NUM | IMAGE_NAME | all) [--check] [--soft]\n",
89 [DIR] =
90 "imagex dir WIMFILE (IMAGE_NUM | IMAGE_NAME | all)\n",
91 [EXPORT] =
92 "imagex export SRC_WIMFILE (SRC_IMAGE_NUM | SRC_IMAGE_NAME | all ) \n"
93 "              DEST_WIMFILE [DEST_IMAGE_NAME] [DEST_IMAGE_DESCRIPTION]\n"
94 "              [--boot] [--check] [--compress=TYPE] [--ref=\"GLOB\"]\n"
95 "              [--threads=NUM_THREADS] [--rebuild]\n",
96 [INFO] =
97 "imagex info WIMFILE [IMAGE_NUM | IMAGE_NAME] [NEW_NAME]\n"
98 "                   [NEW_DESC] [--boot] [--check] [--header] [--lookup-table]\n"
99 "                   [--xml] [--extract-xml FILE] [--metadata]\n",
100 [JOIN] =
101 "imagex join [--check] WIMFILE SPLIT_WIM...\n",
102 [MOUNT] =
103 "imagex mount WIMFILE (IMAGE_NUM | IMAGE_NAME) DIRECTORY\n"
104 "                    [--check] [--debug] [--streams-interface=INTERFACE]\n"
105 "                    [--ref=\"GLOB\"] [--unix-data] [--allow-other]\n",
106 [MOUNTRW] =
107 "imagex mountrw WIMFILE [IMAGE_NUM | IMAGE_NAME] DIRECTORY\n"
108 "                      [--check] [--debug] [--streams-interface=INTERFACE]\n"
109 "                      [--staging-dir=DIR] [--unix-data] [--allow-other]\n",
110 [OPTIMIZE] =
111 "imagex optimize WIMFILE [--check] [--recompress] [--compress=TYPE]\n",
112 [SPLIT] =
113 "imagex split WIMFILE SPLIT_WIMFILE PART_SIZE_MB [--check]\n",
114 [UNMOUNT] =
115 "imagex unmount DIRECTORY [--commit] [--check] [--rebuild]\n",
116 };
117
118 static const struct option apply_options[] = {
119         {"check",     no_argument,       NULL, 'c'},
120         {"hardlink",  no_argument,       NULL, 'h'},
121         {"symlink",   no_argument,       NULL, 's'},
122         {"verbose",   no_argument,       NULL, 'v'},
123         {"ref",       required_argument, NULL, 'r'},
124         {"unix-data", no_argument,       NULL, 'U'},
125         {NULL, 0, NULL, 0},
126 };
127 static const struct option capture_or_append_options[] = {
128         {"boot",        no_argument,       NULL, 'b'},
129         {"check",       no_argument,       NULL, 'c'},
130         {"compress",    required_argument, NULL, 'x'},
131         {"config",      required_argument, NULL, 'C'},
132         {"dereference", no_argument,       NULL, 'L'},
133         {"flags",       required_argument, NULL, 'f'},
134         {"verbose",     no_argument,       NULL, 'v'},
135         {"threads",     required_argument, NULL, 't'},
136         {"rebuild",     no_argument,       NULL, 'R'},
137         {"unix-data",   no_argument,       NULL, 'U'},
138         {"source-list", no_argument,       NULL, 'S'},
139         {NULL, 0, NULL, 0},
140 };
141 static const struct option delete_options[] = {
142         {"check", no_argument, NULL, 'c'},
143         {"soft",  no_argument, NULL, 's'},
144         {NULL, 0, NULL, 0},
145 };
146
147 static const struct option export_options[] = {
148         {"boot",       no_argument,       NULL, 'b'},
149         {"check",      no_argument,       NULL, 'c'},
150         {"compress",   required_argument, NULL, 'x'},
151         {"ref",        required_argument, NULL, 'r'},
152         {"threads",    required_argument, NULL, 't'},
153         {"rebuild",    no_argument,       NULL, 'R'},
154         {NULL, 0, NULL, 0},
155 };
156
157 static const struct option info_options[] = {
158         {"boot",         no_argument, NULL, 'b'},
159         {"check",        no_argument, NULL, 'c'},
160         {"extract-xml",  required_argument, NULL, 'X'},
161         {"header",       no_argument, NULL, 'h'},
162         {"lookup-table", no_argument, NULL, 'l'},
163         {"metadata",     no_argument, NULL, 'm'},
164         {"xml",          no_argument, NULL, 'x'},
165         {NULL, 0, NULL, 0},
166 };
167
168 static const struct option join_options[] = {
169         {"check", no_argument, NULL, 'c'},
170         {NULL, 0, NULL, 0},
171 };
172
173 static const struct option mount_options[] = {
174         {"check",             no_argument,       NULL, 'c'},
175         {"debug",             no_argument,       NULL, 'd'},
176         {"streams-interface", required_argument, NULL, 's'},
177         {"ref",               required_argument, NULL, 'r'},
178         {"staging-dir",       required_argument, NULL, 'D'},
179         {"unix-data",         no_argument,       NULL, 'U'},
180         {"allow-other",       no_argument,       NULL, 'A'},
181         {NULL, 0, NULL, 0},
182 };
183
184 static const struct option optimize_options[] = {
185         {"check",      no_argument, NULL, 'c'},
186         {"recompress", no_argument, NULL, 'r'},
187         {NULL, 0, NULL, 0},
188 };
189
190 static const struct option split_options[] = {
191         {"check", no_argument, NULL, 'c'},
192         {NULL, 0, NULL, 0},
193 };
194
195 static const struct option unmount_options[] = {
196         {"commit",  no_argument, NULL, 'c'},
197         {"check",   no_argument, NULL, 'C'},
198         {"rebuild", no_argument, NULL, 'R'},
199         {NULL, 0, NULL, 0},
200 };
201
202
203
204 /* Print formatted error message to stderr. */
205 static void imagex_error(const char *format, ...)
206 {
207         va_list va;
208         va_start(va, format);
209         fputs("ERROR: ", stderr);
210         vfprintf(stderr, format, va);
211         putc('\n', stderr);
212         va_end(va);
213 }
214
215 /* Print formatted error message to stderr. */
216 static void imagex_error_with_errno(const char *format, ...)
217 {
218         int errno_save = errno;
219         va_list va;
220         va_start(va, format);
221         fputs("ERROR: ", stderr);
222         vfprintf(stderr, format, va);
223         fprintf(stderr, ": %s\n", strerror(errno_save));
224         va_end(va);
225 }
226
227 static int verify_image_exists(int image, const char *image_name,
228                                const char *wim_name)
229 {
230         if (image == WIMLIB_NO_IMAGE) {
231                 imagex_error("\"%s\" is not a valid image in `%s'!\n"
232                              "       Please specify a 1-based imagex index or "
233                              "image name.\n"
234                              "       You may use `imagex info' to list the images "
235                              "contained in a WIM.",
236                              image_name, wim_name);
237                 return -1;
238         }
239         return 0;
240 }
241
242 static int verify_image_is_single(int image)
243 {
244         if (image == WIMLIB_ALL_IMAGES) {
245                 imagex_error("Cannot specify all images for this action!");
246                 return -1;
247         }
248         return 0;
249 }
250
251 static int verify_image_exists_and_is_single(int image, const char *image_name,
252                                              const char *wim_name)
253 {
254         int ret;
255         ret = verify_image_exists(image, image_name, wim_name);
256         if (ret == 0)
257                 ret = verify_image_is_single(image);
258         return ret;
259 }
260
261 /* Parse the argument to --compress */
262 static int get_compression_type(const char *optarg)
263 {
264         if (strcasecmp(optarg, "maximum") == 0 || strcasecmp(optarg, "lzx") == 0)
265                 return WIMLIB_COMPRESSION_TYPE_LZX;
266         else if (strcasecmp(optarg, "fast") == 0 || strcasecmp(optarg, "xpress") == 0)
267                 return WIMLIB_COMPRESSION_TYPE_XPRESS;
268         else if (strcasecmp(optarg, "none") == 0)
269                 return WIMLIB_COMPRESSION_TYPE_NONE;
270         else {
271                 imagex_error("Invalid compression type `%s'! Must be "
272                              "\"maximum\", \"fast\", or \"none\".", optarg);
273                 return WIMLIB_COMPRESSION_TYPE_INVALID;
274         }
275 }
276
277 /* Returns the size of a file given its name, or -1 if the file does not exist
278  * or its size cannot be determined.  */
279 static off_t file_get_size(const char *filename)
280 {
281         struct stat st;
282         if (stat(filename, &st) == 0)
283                 return st.st_size;
284         else
285                 return (off_t)-1;
286 }
287
288 static const char *default_capture_config =
289 "[ExclusionList]\n"
290 "\\$ntfs.log\n"
291 "\\hiberfil.sys\n"
292 "\\pagefile.sys\n"
293 "\\System Volume Information\n"
294 "\\RECYCLER\n"
295 "\\Windows\\CSC\n"
296 "\n"
297 "[CompressionExclusionList]\n"
298 "*.mp3\n"
299 "*.zip\n"
300 "*.cab\n"
301 "\\WINDOWS\\inf\\*.pnf\n";
302
303 /* Read standard input until EOF and return the full contents in a malloc()ed
304  * buffer and the number of bytes of data in @len_ret.  Returns NULL on read
305  * error. */
306 static char *stdin_get_contents(size_t *len_ret)
307 {
308         /* stdin can, of course, be a pipe or other non-seekable file, so the
309          * total length of the data cannot be pre-determined */
310         char *buf = NULL;
311         size_t newlen = 1024;
312         size_t pos = 0;
313         size_t inc = 1024;
314         for (;;) {
315                 char *p = realloc(buf, newlen);
316                 size_t bytes_read, bytes_to_read;
317                 if (!p) {
318                         imagex_error("out of memory while reading stdin");
319                         break;
320                 }
321                 buf = p;
322                 bytes_to_read = newlen - pos;
323                 bytes_read = fread(&buf[pos], 1, bytes_to_read, stdin);
324                 pos += bytes_read;
325                 if (bytes_read != bytes_to_read) {
326                         if (feof(stdin)) {
327                                 *len_ret = pos;
328                                 return buf;
329                         } else {
330                                 imagex_error_with_errno("error reading stdin");
331                                 break;
332                         }
333                 }
334                 newlen += inc;
335                 inc *= 3;
336                 inc /= 2;
337         }
338         free(buf);
339         return NULL;
340 }
341
342 enum {
343         PARSE_FILENAME_SUCCESS = 0,
344         PARSE_FILENAME_FAILURE = 1,
345         PARSE_FILENAME_NONE = 2,
346 };
347
348 /*
349  * Parses a filename in the source list file format.  (See the man page for
350  * 'imagex capture' for details on this format and the meaning.)  Accepted
351  * formats for filenames are an unquoted string, whitespace-delimited, or a
352  * double or single-quoted string.
353  *
354  * @line_p:  Pointer to the pointer to the line of data.  Will be updated
355  *           to point past the filename iff the return value is
356  *           PARSE_FILENAME_SUCCESS.  If *len_p > 0, (*line_p)[*len_p - 1] must
357  *           be '\0'.
358  *
359  * @len_p:   @len_p initially stores the length of the line of data, which may
360  *           be 0, and it will be updated to the number of bytes remaining in
361  *           the line iff the return value is PARSE_FILENAME_SUCCESS.
362  *
363  * @fn_ret:  Iff the return value is PARSE_FILENAME_SUCCESS, a pointer to the
364  *           parsed filename will be returned here.
365  *
366  * Returns: PARSE_FILENAME_SUCCESS if a filename was successfully parsed; or
367  *          PARSE_FILENAME_FAILURE if the data was invalid due to a missing
368  *          closing quote; or PARSE_FILENAME_NONE if the line ended before the
369  *          beginning of a filename was found.
370  */
371 static int parse_filename(char **line_p, size_t *len_p, char **fn_ret)
372 {
373         size_t len = *len_p;
374         char *line = *line_p;
375         char *fn;
376         char quote_char;
377
378         /* Skip leading whitespace */
379         for (;;) {
380                 if (len == 0)
381                         return PARSE_FILENAME_NONE;
382                 if (!isspace(*line) && *line != '\0')
383                         break;
384                 line++;
385                 len--;
386         }
387         quote_char = *line;
388         if (quote_char == '"' || quote_char == '\'') {
389                 /* Quoted filename */
390                 line++;
391                 len--;
392                 fn = line;
393                 line = memchr(line, quote_char, len);
394                 if (!line) {
395                         imagex_error("Missing closing quote: %s", fn - 1);
396                         return PARSE_FILENAME_FAILURE;
397                 }
398         } else {
399                 /* Unquoted filename.  Go until whitespace.  Line is terminated
400                  * by '\0', so no need to check 'len'. */
401                 fn = line;
402                 do {
403                         line++;
404                 } while (!isspace(*line) && *line != '\0');
405         }
406         *line = '\0';
407         len -= line - fn;
408         *len_p = len;
409         *line_p = line;
410         *fn_ret = fn;
411         return PARSE_FILENAME_SUCCESS;
412 }
413
414 /* Parses a line of data (not an empty line or comment) in the source list file
415  * format.  (See the man page for 'imagex capture' for details on this format
416  * and the meaning.)
417  *
418  * @line:  Line of data to be parsed.  line[len - 1] must be '\0', unless
419  *         len == 0.  The data in @line will be modified by this function call.
420  *
421  * @len:   Length of the line of data.
422  *
423  * @source:  On success, the capture source and target described by the line is
424  *           written into this destination.  Note that it will contain pointers
425  *           to data in the @line array.
426  *
427  * Returns true if the line was valid; false otherwise.  */
428 static bool
429 parse_source_list_line(char *line, size_t len,
430                        struct wimlib_capture_source *source)
431 {
432         /* SOURCE [DEST] */
433         int ret;
434         ret = parse_filename(&line, &len, &source->fs_source_path);
435         if (ret != PARSE_FILENAME_SUCCESS)
436                 return false;
437         ret = parse_filename(&line, &len, &source->wim_target_path);
438         if (ret == PARSE_FILENAME_NONE)
439                 source->wim_target_path = source->fs_source_path;
440         return ret != PARSE_FILENAME_FAILURE;
441 }
442
443 /* Returns %true if the given line of length @len > 0 is a comment or empty line
444  * in the source list file format. */
445 static bool is_comment_line(const char *line, size_t len)
446 {
447         for (;;) {
448                 if (*line == '#')
449                         return true;
450                 if (!isspace(*line) && *line != '\0')
451                         return false;
452                 ++line;
453                 --len;
454                 if (len == 0)
455                         return true;
456         }
457 }
458
459 /* Parses a file in the source list format.  (See the man page for 'imagex
460  * capture' for details on this format and the meaning.)
461  *
462  * @source_list_contents:  Contents of the source list file.  Note that this
463  *                         buffer will be modified to save memory allocations,
464  *                         and cannot be freed until the returned array of
465  *                         wimlib_capture_source's has also been freed.
466  *
467  * @source_list_nbytes:    Number of bytes of data in the @source_list_contents
468  *                         buffer.
469  *
470  * @nsources_ret:          On success, the length of the returned array is
471  *                         returned here.
472  *
473  * Returns:   An array of `struct wimlib_capture_source's that can be passed to
474  * the wimlib_add_image_multisource() function to specify how a WIM image is to
475  * be created.  */
476 static struct wimlib_capture_source *
477 parse_source_list(char *source_list_contents, size_t source_list_nbytes,
478                   size_t *nsources_ret)
479 {
480         size_t nlines;
481         char *p;
482         struct wimlib_capture_source *sources;
483         size_t i, j;
484
485         nlines = 0;
486         for (i = 0; i < source_list_nbytes; i++)
487                 if (source_list_contents[i] == '\n')
488                         nlines++;
489         sources = calloc(nlines, sizeof(*sources));
490         if (!sources) {
491                 imagex_error("out of memory");
492                 return NULL;
493         }
494         p = source_list_contents;
495         j = 0;
496         for (i = 0; i < nlines; i++) {
497                 char *endp = strchr(p, '\n');
498                 size_t len = endp - p + 1;
499                 *endp = '\0';
500                 if (!is_comment_line(p, len)) {
501                         if (!parse_source_list_line(p, len, &sources[j++])) {
502                                 free(sources);
503                                 return NULL;
504                         }
505                 }
506                 p = endp + 1;
507
508         }
509         *nsources_ret = j;
510         return sources;
511 }
512
513 /* Reads the contents of a file into memory. */
514 static char *file_get_contents(const char *filename, size_t *len_ret)
515 {
516         struct stat stbuf;
517         char *buf = NULL;
518         size_t len;
519         FILE *fp;
520
521         if (stat(filename, &stbuf) != 0) {
522                 imagex_error_with_errno("Failed to stat the file `%s'", filename);
523                 goto out;
524         }
525         len = stbuf.st_size;
526
527         fp = fopen(filename, "rb");
528         if (!fp) {
529                 imagex_error_with_errno("Failed to open the file `%s'", filename);
530                 goto out;
531         }
532
533         buf = malloc(len);
534         if (!buf) {
535                 imagex_error("Failed to allocate buffer of %zu bytes to hold "
536                              "contents of file `%s'", len, filename);
537                 goto out_fclose;
538         }
539         if (fread(buf, 1, len, fp) != len) {
540                 imagex_error_with_errno("Failed to read %lu bytes from the "
541                                         "file `%s'", len, filename);
542                 goto out_free_buf;
543         }
544         *len_ret = len;
545         goto out_fclose;
546 out_free_buf:
547         free(buf);
548         buf = NULL;
549 out_fclose:
550         fclose(fp);
551 out:
552         return buf;
553 }
554
555 /* Return 0 if a path names a file to which the current user has write access;
556  * -1 otherwise (and print an error message). */
557 static int file_writable(const char *path)
558 {
559         int ret;
560         ret = access(path, W_OK);
561         if (ret != 0)
562                 imagex_error_with_errno("Can't modify `%s'", path);
563         return ret;
564 }
565
566 #define TO_PERCENT(numerator, denominator) \
567         (((denominator) == 0) ? 0 : ((numerator) * 100 / (denominator)))
568
569 /* Given an enumerated value for WIM compression type, return a descriptive
570  * string. */
571 static const char *get_data_type(int ctype)
572 {
573         switch (ctype) {
574         case WIMLIB_COMPRESSION_TYPE_NONE:
575                 return "uncompressed";
576         case WIMLIB_COMPRESSION_TYPE_LZX:
577                 return "LZX-compressed";
578         case WIMLIB_COMPRESSION_TYPE_XPRESS:
579                 return "XPRESS-compressed";
580         }
581         return NULL;
582 }
583
584 /* Progress callback function passed to various wimlib functions. */
585 static int imagex_progress_func(enum wimlib_progress_msg msg,
586                                 const union wimlib_progress_info *info)
587 {
588         unsigned percent_done;
589         switch (msg) {
590         case WIMLIB_PROGRESS_MSG_WRITE_STREAMS:
591                 percent_done = TO_PERCENT(info->write_streams.completed_bytes,
592                                           info->write_streams.total_bytes);
593                 if (info->write_streams.completed_streams == 0) {
594                         const char *data_type;
595
596                         data_type = get_data_type(info->write_streams.compression_type);
597                         printf("Writing %s data using %u thread%s\n",
598                                data_type, info->write_streams.num_threads,
599                                (info->write_streams.num_threads == 1) ? "" : "s");
600                 }
601                 printf("\r%"PRIu64" MiB of %"PRIu64" MiB (uncompressed) "
602                        "written (%u%% done)",
603                        info->write_streams.completed_bytes >> 20,
604                        info->write_streams.total_bytes >> 20,
605                        percent_done);
606                 if (info->write_streams.completed_bytes >= info->write_streams.total_bytes)
607                         putchar('\n');
608                 break;
609         case WIMLIB_PROGRESS_MSG_SCAN_BEGIN:
610                 printf("Scanning `%s'", info->scan.source);
611                 if (*info->scan.wim_target_path) {
612                         printf(" (loading as WIM path: `/%s')...\n",
613                                info->scan.wim_target_path);
614                 } else {
615                         printf(" (loading as root of WIM image)...\n");
616                 }
617                 break;
618         case WIMLIB_PROGRESS_MSG_SCAN_DENTRY:
619                 if (info->scan.excluded)
620                         printf("Excluding `%s' from capture\n", info->scan.cur_path);
621                 else
622                         printf("Scanning `%s'\n", info->scan.cur_path);
623                 break;
624         /*case WIMLIB_PROGRESS_MSG_SCAN_END:*/
625                 /*break;*/
626         case WIMLIB_PROGRESS_MSG_VERIFY_INTEGRITY:
627                 percent_done = TO_PERCENT(info->integrity.completed_bytes,
628                                           info->integrity.total_bytes);
629                 printf("\rVerifying integrity of `%s': %"PRIu64" MiB "
630                        "of %"PRIu64" MiB (%u%%) done",
631                        info->integrity.filename,
632                        info->integrity.completed_bytes >> 20,
633                        info->integrity.total_bytes >> 20,
634                        percent_done);
635                 if (info->integrity.completed_bytes == info->integrity.total_bytes)
636                         putchar('\n');
637                 break;
638         case WIMLIB_PROGRESS_MSG_CALC_INTEGRITY:
639                 percent_done = TO_PERCENT(info->integrity.completed_bytes,
640                                           info->integrity.total_bytes);
641                 printf("\rCalculating integrity table for WIM: %"PRIu64" MiB "
642                        "of %"PRIu64" MiB (%u%%) done",
643                        info->integrity.completed_bytes >> 20,
644                        info->integrity.total_bytes >> 20,
645                        percent_done);
646                 if (info->integrity.completed_bytes == info->integrity.total_bytes)
647                         putchar('\n');
648                 break;
649         case WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_BEGIN:
650                 printf("Applying image %d (%s) from `%s' to %s `%s'\n",
651                        info->extract.image,
652                        info->extract.image_name,
653                        info->extract.wimfile_name,
654                        ((info->extract.extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) ?
655                                 "NTFS volume" : "directory"),
656                        info->extract.target);
657                 break;
658         /*case WIMLIB_PROGRESS_MSG_EXTRACT_DIR_STRUCTURE_BEGIN:*/
659                 /*printf("Applying directory structure to %s\n",*/
660                        /*info->extract.target);*/
661                 /*break;*/
662         case WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS:
663                 percent_done = TO_PERCENT(info->extract.completed_bytes,
664                                           info->extract.total_bytes);
665                 printf("\rExtracting files: "
666                        "%"PRIu64" MiB of %"PRIu64" MiB (%u%%) done",
667                        info->extract.completed_bytes >> 20,
668                        info->extract.total_bytes >> 20,
669                        percent_done);
670                 if (info->extract.completed_bytes >= info->extract.total_bytes)
671                         putchar('\n');
672                 break;
673         case WIMLIB_PROGRESS_MSG_EXTRACT_DENTRY:
674                 puts(info->extract.cur_path);
675                 break;
676         case WIMLIB_PROGRESS_MSG_APPLY_TIMESTAMPS:
677                 printf("Setting timestamps on all extracted files...\n");
678                 break;
679         case WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_END:
680                 if (info->extract.extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) {
681                         printf("Unmounting NTFS volume `%s'...\n",
682                                info->extract.target);
683                 }
684                 break;
685         case WIMLIB_PROGRESS_MSG_JOIN_STREAMS:
686                 percent_done = TO_PERCENT(info->join.completed_bytes,
687                                           info->join.total_bytes);
688                 printf("Writing resources from part %u of %u: "
689                        "%"PRIu64 " MiB of %"PRIu64" MiB (%u%%) written\n",
690                        (info->join.completed_parts == info->join.total_parts) ?
691                                 info->join.completed_parts : info->join.completed_parts + 1,
692                        info->join.total_parts,
693                        info->join.completed_bytes >> 20,
694                        info->join.total_bytes >> 20,
695                        percent_done);
696                 break;
697         case WIMLIB_PROGRESS_MSG_SPLIT_BEGIN_PART:
698                 percent_done = TO_PERCENT(info->split.completed_bytes,
699                                           info->split.total_bytes);
700                 printf("Writing `%s': %"PRIu64" MiB of %"PRIu64" MiB (%u%%) written\n",
701                        info->split.part_name,
702                        info->split.completed_bytes >> 20,
703                        info->split.total_bytes >> 20,
704                        percent_done);
705                 break;
706         case WIMLIB_PROGRESS_MSG_SPLIT_END_PART:
707                 if (info->split.completed_bytes == info->split.total_bytes) {
708                         printf("Finished writing %u split WIM parts\n",
709                                info->split.cur_part_number);
710                 }
711                 break;
712         default:
713                 break;
714         }
715         fflush(stdout);
716         return 0;
717 }
718
719 /* Open all the split WIM parts that correspond to a file glob.
720  *
721  * @first_part specifies the first part of the split WIM and it may be either
722  * included or omitted from the glob. */
723 static int open_swms_from_glob(const char *swm_glob,
724                                const char *first_part,
725                                int open_flags,
726                                WIMStruct ***additional_swms_ret,
727                                unsigned *num_additional_swms_ret)
728 {
729         unsigned num_additional_swms = 0;
730         WIMStruct **additional_swms = NULL;
731         glob_t globbuf;
732         int ret;
733
734         ret = glob(swm_glob, GLOB_ERR | GLOB_NOSORT, NULL, &globbuf);
735         if (ret != 0) {
736                 if (ret == GLOB_NOMATCH) {
737                         imagex_error("Found no files for glob \"%s\"",
738                                      swm_glob);
739                 } else {
740                         imagex_error_with_errno("Failed to process glob "
741                                                 "\"%s\"", swm_glob);
742                 }
743                 ret = -1;
744                 goto out;
745         }
746         num_additional_swms = globbuf.gl_pathc;
747         additional_swms = calloc(num_additional_swms, sizeof(additional_swms[0]));
748         if (!additional_swms) {
749                 imagex_error("Out of memory");
750                 ret = -1;
751                 goto out_globfree;
752         }
753         unsigned offset = 0;
754         for (unsigned i = 0; i < num_additional_swms; i++) {
755                 if (strcmp(globbuf.gl_pathv[i], first_part) == 0) {
756                         offset++;
757                         continue;
758                 }
759                 ret = wimlib_open_wim(globbuf.gl_pathv[i],
760                                       open_flags | WIMLIB_OPEN_FLAG_SPLIT_OK,
761                                       &additional_swms[i - offset],
762                                       imagex_progress_func);
763                 if (ret != 0)
764                         goto out_close_swms;
765         }
766         *additional_swms_ret = additional_swms;
767         *num_additional_swms_ret = num_additional_swms - offset;
768         ret = 0;
769         goto out_globfree;
770 out_close_swms:
771         for (unsigned i = 0; i < num_additional_swms; i++)
772                 wimlib_free(additional_swms[i]);
773         free(additional_swms);
774 out_globfree:
775         globfree(&globbuf);
776 out:
777         return ret;
778 }
779
780
781 static unsigned parse_num_threads(const char *optarg)
782 {
783         char *tmp;
784         unsigned nthreads = strtoul(optarg, &tmp, 10);
785         if (nthreads == UINT_MAX || *tmp || tmp == optarg) {
786                 imagex_error("Number of threads must be a non-negative integer!");
787                 return UINT_MAX;
788         } else {
789                 return nthreads;
790         }
791 }
792
793
794 /* Apply one image, or all images, from a WIM file into a directory, OR apply
795  * one image from a WIM file to a NTFS volume. */
796 static int imagex_apply(int argc, char **argv)
797 {
798         int c;
799         int open_flags = WIMLIB_OPEN_FLAG_SPLIT_OK;
800         int image;
801         int num_images;
802         WIMStruct *w;
803         int ret;
804         const char *wimfile;
805         const char *target;
806         const char *image_num_or_name;
807         int extract_flags = WIMLIB_EXTRACT_FLAG_SEQUENTIAL;
808
809         const char *swm_glob = NULL;
810         WIMStruct **additional_swms = NULL;
811         unsigned num_additional_swms = 0;
812
813         for_opt(c, apply_options) {
814                 switch (c) {
815                 case 'c':
816                         open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
817                         break;
818                 case 'h':
819                         extract_flags |= WIMLIB_EXTRACT_FLAG_HARDLINK;
820                         break;
821                 case 's':
822                         extract_flags |= WIMLIB_EXTRACT_FLAG_SYMLINK;
823                         break;
824                 case 'v':
825                         extract_flags |= WIMLIB_EXTRACT_FLAG_VERBOSE;
826                         break;
827                 case 'r':
828                         swm_glob = optarg;
829                         break;
830                 case 'U':
831                         extract_flags |= WIMLIB_EXTRACT_FLAG_UNIX_DATA;
832                         break;
833                 default:
834                         usage(APPLY);
835                         return -1;
836                 }
837         }
838         argc -= optind;
839         argv += optind;
840         if (argc != 2 && argc != 3) {
841                 usage(APPLY);
842                 return -1;
843         }
844
845         wimfile = argv[0];
846         if (argc == 2) {
847                 image_num_or_name = "1";
848                 target = argv[1];
849         } else {
850                 image_num_or_name = argv[1];
851                 target = argv[2];
852         }
853
854         ret = wimlib_open_wim(wimfile, open_flags, &w, imagex_progress_func);
855         if (ret != 0)
856                 return ret;
857
858         image = wimlib_resolve_image(w, image_num_or_name);
859         ret = verify_image_exists(image, image_num_or_name, wimfile);
860         if (ret != 0)
861                 goto out;
862
863         num_images = wimlib_get_num_images(w);
864         if (argc == 2 && num_images != 1) {
865                 imagex_error("`%s' contains %d images; Please select one "
866                              "(or all)", wimfile, num_images);
867                 usage(APPLY);
868                 ret = -1;
869                 goto out;
870         }
871
872         if (swm_glob) {
873                 ret = open_swms_from_glob(swm_glob, wimfile, open_flags,
874                                           &additional_swms,
875                                           &num_additional_swms);
876                 if (ret != 0)
877                         goto out;
878         }
879
880         struct stat stbuf;
881
882         ret = stat(target, &stbuf);
883         if (ret == 0) {
884                 if (S_ISBLK(stbuf.st_mode) || S_ISREG(stbuf.st_mode))
885                         extract_flags |= WIMLIB_EXTRACT_FLAG_NTFS;
886         } else {
887                 if (errno != ENOENT) {
888                         imagex_error_with_errno("Failed to stat `%s'", target);
889                         ret = -1;
890                         goto out;
891                 }
892         }
893
894         ret = wimlib_extract_image(w, image, target, extract_flags,
895                                    additional_swms, num_additional_swms,
896                                    imagex_progress_func);
897         if (ret == 0)
898                 printf("Done applying WIM image.\n");
899 out:
900         wimlib_free(w);
901         if (additional_swms) {
902                 for (unsigned i = 0; i < num_additional_swms; i++)
903                         wimlib_free(additional_swms[i]);
904                 free(additional_swms);
905         }
906         return ret;
907 }
908
909 /* Create a WIM image from a directory tree, NTFS volume, or multiple files or
910  * directory trees.  'imagex capture': create a new WIM file containing the
911  * desired image.  'imagex append': add a new image to an existing WIM file. */
912 static int imagex_capture_or_append(int argc, char **argv)
913 {
914         int c;
915         int open_flags = 0;
916         int add_image_flags = 0;
917         int write_flags = 0;
918         int compression_type = WIMLIB_COMPRESSION_TYPE_XPRESS;
919         const char *wimfile;
920         const char *name;
921         const char *desc;
922         const char *flags_element = NULL;
923         WIMStruct *w = NULL;
924         int ret;
925         int cur_image;
926         int cmd = strcmp(argv[0], "append") ? CAPTURE : APPEND;
927         unsigned num_threads = 0;
928
929         char *source;
930         size_t source_name_len;
931         char *source_copy;
932
933         const char *config_file = NULL;
934         char *config_str = NULL;
935         size_t config_len;
936
937         bool source_list = false;
938         size_t source_list_nbytes;
939         char *source_list_contents = NULL;
940         bool capture_sources_malloced = false;
941         struct wimlib_capture_source *capture_sources;
942         size_t num_sources;
943
944         for_opt(c, capture_or_append_options) {
945                 switch (c) {
946                 case 'b':
947                         add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_BOOT;
948                         break;
949                 case 'c':
950                         open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
951                         write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
952                         break;
953                 case 'C':
954                         config_file = optarg;
955                         break;
956                 case 'x':
957                         compression_type = get_compression_type(optarg);
958                         if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID)
959                                 return -1;
960                         break;
961                 case 'f':
962                         flags_element = optarg;
963                         break;
964                 case 'L':
965                         add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_DEREFERENCE;
966                         break;
967                 case 'v':
968                         add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_VERBOSE;
969                         break;
970                 case 't':
971                         num_threads = parse_num_threads(optarg);
972                         if (num_threads == UINT_MAX)
973                                 return -1;
974                         break;
975                 case 'R':
976                         write_flags |= WIMLIB_WRITE_FLAG_REBUILD;
977                         break;
978                 case 'U':
979                         add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_UNIX_DATA;
980                         break;
981                 case 'S':
982                         source_list = true;
983                         break;
984                 default:
985                         usage(cmd);
986                         return -1;
987                 }
988         }
989         argc -= optind;
990         argv += optind;
991
992         if (argc < 2 || argc > 4) {
993                 usage(cmd);
994                 return -1;
995         }
996
997         source = argv[0];
998         wimfile = argv[1];
999
1000         if (argc >= 3) {
1001                 name = argv[2];
1002         } else {
1003                 /* Set default name to SOURCE argument, omitting any directory
1004                  * prefixes and trailing slashes.  This requires making a copy
1005                  * of @source. */
1006                 source_name_len = strlen(source);
1007                 source_copy = alloca(source_name_len + 1);
1008                 name = basename(strcpy(source_copy, source));
1009         }
1010         /* Image description defaults to NULL if not given. */
1011         desc = (argc >= 4) ? argv[3] : NULL;
1012
1013         if (source_list) {
1014                 /* Set up capture sources in source list mode */
1015                 if (source[0] == '-' && source[1] == '\0') {
1016                         source_list_contents = stdin_get_contents(&source_list_nbytes);
1017                 } else {
1018                         source_list_contents = file_get_contents(source,
1019                                                                  &source_list_nbytes);
1020                 }
1021                 if (!source_list_contents)
1022                         return -1;
1023
1024                 capture_sources = parse_source_list(source_list_contents,
1025                                                     source_list_nbytes,
1026                                                     &num_sources);
1027                 if (!capture_sources) {
1028                         ret = -1;
1029                         goto out;
1030                 }
1031                 capture_sources_malloced = true;
1032         } else {
1033                 /* Set up capture source in non-source-list mode (could be
1034                  * either "normal" mode or "NTFS mode"--- see the man page). */
1035                 capture_sources = alloca(sizeof(struct wimlib_capture_source));
1036                 capture_sources[0].fs_source_path = source;
1037                 capture_sources[0].wim_target_path = NULL;
1038                 capture_sources[0].reserved = 0;
1039                 num_sources = 1;
1040         }
1041
1042         if (config_file) {
1043                 config_str = file_get_contents(config_file, &config_len);
1044                 if (!config_str) {
1045                         ret = -1;
1046                         goto out;
1047                 }
1048         }
1049
1050         if (cmd == APPEND)
1051                 ret = wimlib_open_wim(wimfile, open_flags, &w,
1052                                       imagex_progress_func);
1053         else
1054                 ret = wimlib_create_new_wim(compression_type, &w);
1055         if (ret != 0)
1056                 goto out;
1057
1058         if (!source_list) {
1059                 struct stat stbuf;
1060                 ret = stat(source, &stbuf);
1061                 if (ret == 0) {
1062                         if (S_ISBLK(stbuf.st_mode) || S_ISREG(stbuf.st_mode)) {
1063                                 printf("Capturing WIM image from NTFS filesystem on `%s'\n",
1064                                        source);
1065                                 add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_NTFS;
1066                         }
1067                 } else {
1068                         if (errno != ENOENT) {
1069                                 imagex_error_with_errno("Failed to stat `%s'", source);
1070                                 ret = -1;
1071                                 goto out;
1072                         }
1073                 }
1074         }
1075
1076         ret = wimlib_add_image_multisource(w, capture_sources,
1077                                            num_sources, name,
1078                                            (config_str ? config_str :
1079                                                 default_capture_config),
1080                                            (config_str ? config_len :
1081                                                 strlen(default_capture_config)),
1082                                            add_image_flags,
1083                                            imagex_progress_func);
1084         if (ret != 0)
1085                 goto out;
1086         cur_image = wimlib_get_num_images(w);
1087         if (desc) {
1088                 ret = wimlib_set_image_descripton(w, cur_image, desc);
1089                 if (ret != 0)
1090                         goto out;
1091         }
1092         if (flags_element) {
1093                 ret = wimlib_set_image_flags(w, cur_image, flags_element);
1094                 if (ret != 0)
1095                         goto out;
1096         }
1097         if (cmd == APPEND) {
1098                 ret = wimlib_overwrite(w, write_flags, num_threads,
1099                                        imagex_progress_func);
1100         } else {
1101                 ret = wimlib_write(w, wimfile, WIMLIB_ALL_IMAGES, write_flags,
1102                                    num_threads, imagex_progress_func);
1103         }
1104         if (ret == WIMLIB_ERR_REOPEN)
1105                 ret = 0;
1106         if (ret != 0)
1107                 imagex_error("Failed to write the WIM file `%s'", wimfile);
1108 out:
1109         wimlib_free(w);
1110         free(config_str);
1111         free(source_list_contents);
1112         if (capture_sources_malloced)
1113                 free(capture_sources);
1114         return ret;
1115 }
1116
1117 /* Remove image(s) from a WIM. */
1118 static int imagex_delete(int argc, char **argv)
1119 {
1120         int c;
1121         int open_flags = 0;
1122         int write_flags = 0;
1123         const char *wimfile;
1124         const char *image_num_or_name;
1125         WIMStruct *w;
1126         int image;
1127         int ret;
1128
1129         for_opt(c, delete_options) {
1130                 switch (c) {
1131                 case 'c':
1132                         open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
1133                         write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
1134                         break;
1135                 case 's':
1136                         write_flags |= WIMLIB_WRITE_FLAG_SOFT_DELETE;
1137                         break;
1138                 default:
1139                         usage(DELETE);
1140                         return -1;
1141                 }
1142         }
1143         argc -= optind;
1144         argv += optind;
1145
1146         if (argc != 2) {
1147                 if (argc < 1)
1148                         imagex_error("Must specify a WIM file");
1149                 if (argc < 2)
1150                         imagex_error("Must specify an image");
1151                 usage(DELETE);
1152                 return -1;
1153         }
1154         wimfile = argv[0];
1155         image_num_or_name = argv[1];
1156
1157         ret = file_writable(wimfile);
1158         if (ret != 0)
1159                 return ret;
1160
1161         ret = wimlib_open_wim(wimfile, open_flags, &w,
1162                               imagex_progress_func);
1163         if (ret != 0)
1164                 return ret;
1165
1166         image = wimlib_resolve_image(w, image_num_or_name);
1167
1168         ret = verify_image_exists(image, image_num_or_name, wimfile);
1169         if (ret != 0)
1170                 goto out;
1171
1172         ret = wimlib_delete_image(w, image);
1173         if (ret != 0) {
1174                 imagex_error("Failed to delete image from `%s'", wimfile);
1175                 goto out;
1176         }
1177
1178         ret = wimlib_overwrite(w, write_flags, 0, imagex_progress_func);
1179         if (ret == WIMLIB_ERR_REOPEN)
1180                 ret = 0;
1181         if (ret != 0) {
1182                 imagex_error("Failed to write the file `%s' with image "
1183                              "deleted", wimfile);
1184         }
1185 out:
1186         wimlib_free(w);
1187         return ret;
1188 }
1189
1190 /* Print the files contained in an image(s) in a WIM file. */
1191 static int imagex_dir(int argc, char **argv)
1192 {
1193         const char *wimfile;
1194         WIMStruct *w;
1195         int image;
1196         int ret;
1197         int num_images;
1198
1199         if (argc < 2) {
1200                 imagex_error("Must specify a WIM file");
1201                 usage(DIR);
1202                 return -1;
1203         }
1204         if (argc > 3) {
1205                 imagex_error("Too many arguments");
1206                 usage(DIR);
1207                 return -1;
1208         }
1209
1210         wimfile = argv[1];
1211         ret = wimlib_open_wim(wimfile, WIMLIB_OPEN_FLAG_SPLIT_OK, &w,
1212                               imagex_progress_func);
1213         if (ret != 0)
1214                 return ret;
1215
1216         if (argc == 3) {
1217                 image = wimlib_resolve_image(w, argv[2]);
1218                 ret = verify_image_exists(image, argv[2], wimfile);
1219                 if (ret != 0)
1220                         goto out;
1221         } else {
1222                 /* Image was not specified.  If the WIM only contains one image,
1223                  * choose that one; otherwise, print an error. */
1224                 num_images = wimlib_get_num_images(w);
1225                 if (num_images != 1) {
1226                         imagex_error("The file `%s' contains %d images; Please "
1227                                      "select one.", wimfile, num_images);
1228                         usage(DIR);
1229                         ret = -1;
1230                         goto out;
1231                 }
1232                 image = 1;
1233         }
1234
1235         ret = wimlib_print_files(w, image);
1236 out:
1237         wimlib_free(w);
1238         return ret;
1239 }
1240
1241 /* Exports one, or all, images from a WIM file to a new WIM file or an existing
1242  * WIM file. */
1243 static int imagex_export(int argc, char **argv)
1244 {
1245         int c;
1246         int open_flags = 0;
1247         int export_flags = 0;
1248         int write_flags = 0;
1249         int compression_type = WIMLIB_COMPRESSION_TYPE_NONE;
1250         bool compression_type_specified = false;
1251         const char *src_wimfile;
1252         const char *src_image_num_or_name;
1253         const char *dest_wimfile;
1254         const char *dest_name;
1255         const char *dest_desc;
1256         WIMStruct *src_w = NULL;
1257         WIMStruct *dest_w = NULL;
1258         int ret;
1259         int image;
1260         struct stat stbuf;
1261         bool wim_is_new;
1262         const char *swm_glob = NULL;
1263         WIMStruct **additional_swms = NULL;
1264         unsigned num_additional_swms = 0;
1265         unsigned num_threads = 0;
1266
1267         for_opt(c, export_options) {
1268                 switch (c) {
1269                 case 'b':
1270                         export_flags |= WIMLIB_EXPORT_FLAG_BOOT;
1271                         break;
1272                 case 'c':
1273                         open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
1274                         write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
1275                         break;
1276                 case 'x':
1277                         compression_type = get_compression_type(optarg);
1278                         if (compression_type == WIMLIB_COMPRESSION_TYPE_INVALID)
1279                                 return -1;
1280                         compression_type_specified = true;
1281                         break;
1282                 case 'r':
1283                         swm_glob = optarg;
1284                         break;
1285                 case 't':
1286                         num_threads = parse_num_threads(optarg);
1287                         if (num_threads == UINT_MAX)
1288                                 return -1;
1289                         break;
1290                 case 'R':
1291                         write_flags |= WIMLIB_WRITE_FLAG_REBUILD;
1292                         break;
1293                 default:
1294                         usage(EXPORT);
1295                         return -1;
1296                 }
1297         }
1298         argc -= optind;
1299         argv += optind;
1300         if (argc < 3 || argc > 5) {
1301                 usage(EXPORT);
1302                 return -1;
1303         }
1304         src_wimfile           = argv[0];
1305         src_image_num_or_name = argv[1];
1306         dest_wimfile          = argv[2];
1307         dest_name             = (argc >= 4) ? argv[3] : NULL;
1308         dest_desc             = (argc >= 5) ? argv[4] : NULL;
1309         ret = wimlib_open_wim(src_wimfile,
1310                               open_flags | WIMLIB_OPEN_FLAG_SPLIT_OK, &src_w,
1311                               imagex_progress_func);
1312         if (ret != 0)
1313                 return ret;
1314
1315         /* Determine if the destination is an existing file or not.
1316          * If so, we try to append the exported image(s) to it; otherwise, we
1317          * create a new WIM containing the exported image(s). */
1318         if (stat(dest_wimfile, &stbuf) == 0) {
1319                 int dest_ctype;
1320
1321                 wim_is_new = false;
1322                 /* Destination file exists. */
1323                 if (!S_ISREG(stbuf.st_mode) && !S_ISLNK(stbuf.st_mode)) {
1324                         imagex_error("`%s' is not a regular file",
1325                                         dest_wimfile);
1326                         ret = -1;
1327                         goto out;
1328                 }
1329                 ret = wimlib_open_wim(dest_wimfile, open_flags, &dest_w,
1330                                       imagex_progress_func);
1331                 if (ret != 0)
1332                         goto out;
1333
1334                 ret = file_writable(dest_wimfile);
1335                 if (ret != 0)
1336                         goto out;
1337
1338                 dest_ctype = wimlib_get_compression_type(dest_w);
1339                 if (compression_type_specified
1340                     && compression_type != dest_ctype)
1341                 {
1342                         imagex_error("Cannot specify a compression type that is "
1343                                      "not the same as that used in the "
1344                                      "destination WIM");
1345                         ret = -1;
1346                         goto out;
1347                 }
1348         } else {
1349                 wim_is_new = true;
1350                 /* dest_wimfile is not an existing file, so create a new WIM. */
1351                 if (!compression_type_specified)
1352                         compression_type = wimlib_get_compression_type(src_w);
1353                 if (errno == ENOENT) {
1354                         ret = wimlib_create_new_wim(compression_type, &dest_w);
1355                         if (ret != 0)
1356                                 goto out;
1357                 } else {
1358                         imagex_error_with_errno("Cannot stat file `%s'",
1359                                                 dest_wimfile);
1360                         ret = -1;
1361                         goto out;
1362                 }
1363         }
1364
1365         image = wimlib_resolve_image(src_w, src_image_num_or_name);
1366         ret = verify_image_exists(image, src_image_num_or_name, src_wimfile);
1367         if (ret != 0)
1368                 goto out;
1369
1370         if (swm_glob) {
1371                 ret = open_swms_from_glob(swm_glob, src_wimfile, open_flags,
1372                                           &additional_swms,
1373                                           &num_additional_swms);
1374                 if (ret != 0)
1375                         goto out;
1376         }
1377
1378         ret = wimlib_export_image(src_w, image, dest_w, dest_name, dest_desc,
1379                                   export_flags, additional_swms,
1380                                   num_additional_swms, imagex_progress_func);
1381         if (ret != 0)
1382                 goto out;
1383
1384
1385         if (wim_is_new)
1386                 ret = wimlib_write(dest_w, dest_wimfile, WIMLIB_ALL_IMAGES,
1387                                    write_flags, num_threads,
1388                                    imagex_progress_func);
1389         else
1390                 ret = wimlib_overwrite(dest_w, write_flags, num_threads,
1391                                        imagex_progress_func);
1392 out:
1393         if (ret == WIMLIB_ERR_REOPEN)
1394                 ret = 0;
1395         wimlib_free(src_w);
1396         wimlib_free(dest_w);
1397         if (additional_swms) {
1398                 for (unsigned i = 0; i < num_additional_swms; i++)
1399                         wimlib_free(additional_swms[i]);
1400                 free(additional_swms);
1401         }
1402         return ret;
1403 }
1404
1405 /* Prints information about a WIM file; also can mark an image as bootable,
1406  * change the name of an image, or change the description of an image. */
1407 static int imagex_info(int argc, char **argv)
1408 {
1409         int c;
1410         bool boot         = false;
1411         bool check        = false;
1412         bool header       = false;
1413         bool lookup_table = false;
1414         bool xml          = false;
1415         bool metadata     = false;
1416         bool short_header = true;
1417         const char *xml_out_file = NULL;
1418         const char *wimfile;
1419         const char *image_num_or_name = "all";
1420         const char *new_name = NULL;
1421         const char *new_desc = NULL;
1422         WIMStruct *w;
1423         FILE *fp;
1424         int image;
1425         int ret;
1426         int open_flags = WIMLIB_OPEN_FLAG_SPLIT_OK;
1427         int part_number;
1428         int total_parts;
1429         int num_images;
1430
1431         for_opt(c, info_options) {
1432                 switch (c) {
1433                 case 'b':
1434                         boot = true;
1435                         break;
1436                 case 'c':
1437                         check = true;
1438                         break;
1439                 case 'h':
1440                         header = true;
1441                         short_header = false;
1442                         break;
1443                 case 'l':
1444                         lookup_table = true;
1445                         short_header = false;
1446                         break;
1447                 case 'x':
1448                         xml = true;
1449                         short_header = false;
1450                         break;
1451                 case 'X':
1452                         xml_out_file = optarg;
1453                         short_header = false;
1454                         break;
1455                 case 'm':
1456                         metadata = true;
1457                         short_header = false;
1458                         break;
1459                 default:
1460                         usage(INFO);
1461                         return -1;
1462                 }
1463         }
1464
1465         argc -= optind;
1466         argv += optind;
1467         if (argc == 0 || argc > 4) {
1468                 usage(INFO);
1469                 return -1;
1470         }
1471         wimfile = argv[0];
1472         if (argc > 1) {
1473                 image_num_or_name = argv[1];
1474                 if (argc > 2) {
1475                         new_name = argv[2];
1476                         if (argc > 3) {
1477                                 new_desc = argv[3];
1478                         }
1479                 }
1480         }
1481
1482         if (check)
1483                 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
1484
1485         ret = wimlib_open_wim(wimfile, open_flags, &w,
1486                               imagex_progress_func);
1487         if (ret != 0)
1488                 return ret;
1489
1490         part_number = wimlib_get_part_number(w, &total_parts);
1491
1492         image = wimlib_resolve_image(w, image_num_or_name);
1493         if (image == WIMLIB_NO_IMAGE && strcmp(image_num_or_name, "0") != 0) {
1494                 imagex_error("The image `%s' does not exist",
1495                              image_num_or_name);
1496                 if (boot)
1497                         imagex_error("If you would like to set the boot "
1498                                      "index to 0, specify image \"0\" with "
1499                                      "the --boot flag.");
1500                 ret = WIMLIB_ERR_INVALID_IMAGE;
1501                 goto out;
1502         }
1503
1504         num_images = wimlib_get_num_images(w);
1505
1506         if (num_images == 0) {
1507                 if (boot) {
1508                         imagex_error("--boot is meaningless on a WIM with no "
1509                                      "images");
1510                         ret = WIMLIB_ERR_INVALID_IMAGE;
1511                         goto out;
1512                 }
1513         }
1514
1515         if (image == WIMLIB_ALL_IMAGES && num_images > 1) {
1516                 if (boot) {
1517                         imagex_error("Cannot specify the --boot flag "
1518                                      "without specifying a specific "
1519                                      "image in a multi-image WIM");
1520                         ret = WIMLIB_ERR_INVALID_IMAGE;
1521                         goto out;
1522                 }
1523                 if (new_name) {
1524                         imagex_error("Cannot specify the NEW_NAME "
1525                                      "without specifying a specific "
1526                                      "image in a multi-image WIM");
1527                         ret = WIMLIB_ERR_INVALID_IMAGE;
1528                         goto out;
1529                 }
1530         }
1531
1532         /* Operations that print information are separated from operations that
1533          * recreate the WIM file. */
1534         if (!new_name && !boot) {
1535
1536                 /* Read-only operations */
1537
1538                 if (image == WIMLIB_NO_IMAGE) {
1539                         imagex_error("`%s' is not a valid image",
1540                                      image_num_or_name);
1541                         ret = WIMLIB_ERR_INVALID_IMAGE;
1542                         goto out;
1543                 }
1544
1545                 if (image == WIMLIB_ALL_IMAGES && short_header)
1546                         wimlib_print_wim_information(w);
1547
1548                 if (header)
1549                         wimlib_print_header(w);
1550
1551                 if (lookup_table) {
1552                         if (total_parts != 1) {
1553                                 printf("Warning: Only showing the lookup table "
1554                                        "for part %d of a %d-part WIM.\n",
1555                                        part_number, total_parts);
1556                         }
1557                         wimlib_print_lookup_table(w);
1558                 }
1559
1560                 if (xml) {
1561                         ret = wimlib_extract_xml_data(w, stdout);
1562                         if (ret != 0)
1563                                 goto out;
1564                 }
1565
1566                 if (xml_out_file) {
1567                         fp = fopen(xml_out_file, "wb");
1568                         if (!fp) {
1569                                 imagex_error_with_errno("Failed to open the "
1570                                                         "file `%s' for "
1571                                                         "writing ",
1572                                                         xml_out_file);
1573                                 ret = -1;
1574                                 goto out;
1575                         }
1576                         ret = wimlib_extract_xml_data(w, fp);
1577                         if (fclose(fp) != 0) {
1578                                 imagex_error("Failed to close the file `%s'",
1579                                              xml_out_file);
1580                                 ret = -1;
1581                         }
1582
1583                         if (ret != 0)
1584                                 goto out;
1585                 }
1586
1587                 if (short_header)
1588                         wimlib_print_available_images(w, image);
1589
1590                 if (metadata) {
1591                         ret = wimlib_print_metadata(w, image);
1592                         if (ret != 0)
1593                                 goto out;
1594                 }
1595         } else {
1596
1597                 /* Modification operations */
1598                 if (total_parts != 1) {
1599                         imagex_error("Modifying a split WIM is not supported.");
1600                         ret = -1;
1601                         goto out;
1602                 }
1603                 if (image == WIMLIB_ALL_IMAGES)
1604                         image = 1;
1605
1606                 if (image == WIMLIB_NO_IMAGE && new_name) {
1607                         imagex_error("Cannot specify new_name (`%s') when "
1608                                      "using image 0", new_name);
1609                         ret = -1;
1610                         goto out;
1611                 }
1612
1613                 if (boot) {
1614                         if (image == wimlib_get_boot_idx(w)) {
1615                                 printf("Image %d is already marked as "
1616                                        "bootable.\n", image);
1617                                 boot = false;
1618                         } else {
1619                                 printf("Marking image %d as bootable.\n",
1620                                        image);
1621                                 wimlib_set_boot_idx(w, image);
1622                         }
1623                 }
1624                 if (new_name) {
1625                         if (strcmp(wimlib_get_image_name(w, image),
1626                                                 new_name) == 0) {
1627                                 printf("Image %d is already named \"%s\".\n",
1628                                        image, new_name);
1629                                 new_name = NULL;
1630                         } else {
1631                                 printf("Changing the name of image %d to "
1632                                        "\"%s\".\n", image, new_name);
1633                                 ret = wimlib_set_image_name(w, image, new_name);
1634                                 if (ret != 0)
1635                                         goto out;
1636                         }
1637                 }
1638                 if (new_desc) {
1639                         const char *old_desc;
1640                         old_desc = wimlib_get_image_description(w, image);
1641                         if (old_desc && strcmp(old_desc, new_desc) == 0) {
1642                                 printf("The description of image %d is already "
1643                                        "\"%s\".\n", image, new_desc);
1644                                 new_desc = NULL;
1645                         } else {
1646                                 printf("Changing the description of image %d "
1647                                        "to \"%s\".\n", image, new_desc);
1648                                 ret = wimlib_set_image_descripton(w, image,
1649                                                                   new_desc);
1650                                 if (ret != 0)
1651                                         goto out;
1652                         }
1653                 }
1654
1655                 /* Only call wimlib_overwrite() if something actually needs to
1656                  * be changed. */
1657                 if (boot || new_name || new_desc ||
1658                     (check && !wimlib_has_integrity_table(w)))
1659                 {
1660                         int write_flags;
1661
1662                         ret = file_writable(wimfile);
1663                         if (ret != 0)
1664                                 return ret;
1665
1666                         if (check)
1667                                 write_flags = WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
1668                         else
1669                                 write_flags = 0;
1670
1671                         ret = wimlib_overwrite(w, write_flags, 1,
1672                                                imagex_progress_func);
1673                         if (ret == WIMLIB_ERR_REOPEN)
1674                                 ret = 0;
1675                 } else {
1676                         printf("The file `%s' was not modified because nothing "
1677                                "needed to be done.\n", wimfile);
1678                         ret = 0;
1679                 }
1680         }
1681 out:
1682         wimlib_free(w);
1683         return ret;
1684 }
1685
1686 /* Join split WIMs into one part WIM */
1687 static int imagex_join(int argc, char **argv)
1688 {
1689         int c;
1690         int swm_open_flags = WIMLIB_OPEN_FLAG_SPLIT_OK;
1691         int wim_write_flags = 0;
1692         const char *output_path;
1693
1694         for_opt(c, join_options) {
1695                 switch (c) {
1696                 case 'c':
1697                         swm_open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
1698                         wim_write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
1699                         break;
1700                 default:
1701                         goto err;
1702                 }
1703         }
1704         argc -= optind;
1705         argv += optind;
1706
1707         if (argc < 2) {
1708                 imagex_error("Must specify one or more split WIM (.swm) parts "
1709                              "to join");
1710                 goto err;
1711         }
1712         output_path = argv[0];
1713         return wimlib_join((const char **)++argv, --argc, output_path,
1714                            swm_open_flags, wim_write_flags,
1715                            imagex_progress_func);
1716 err:
1717         usage(JOIN);
1718         return -1;
1719 }
1720
1721 /* Mounts an image using a FUSE mount. */
1722 static int imagex_mount_rw_or_ro(int argc, char **argv)
1723 {
1724         int c;
1725         int mount_flags = 0;
1726         int open_flags = WIMLIB_OPEN_FLAG_SPLIT_OK;
1727         const char *wimfile;
1728         const char *dir;
1729         WIMStruct *w;
1730         int image;
1731         int num_images;
1732         int ret;
1733         const char *swm_glob = NULL;
1734         WIMStruct **additional_swms = NULL;
1735         unsigned num_additional_swms = 0;
1736         const char *staging_dir = NULL;
1737
1738         if (strcmp(argv[0], "mountrw") == 0)
1739                 mount_flags |= WIMLIB_MOUNT_FLAG_READWRITE;
1740
1741         for_opt(c, mount_options) {
1742                 switch (c) {
1743                 case 'A':
1744                         mount_flags |= WIMLIB_MOUNT_FLAG_ALLOW_OTHER;
1745                         break;
1746                 case 'c':
1747                         open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
1748                         break;
1749                 case 'd':
1750                         mount_flags |= WIMLIB_MOUNT_FLAG_DEBUG;
1751                         break;
1752                 case 's':
1753                         if (strcasecmp(optarg, "none") == 0)
1754                                 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_NONE;
1755                         else if (strcasecmp(optarg, "xattr") == 0)
1756                                 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR;
1757                         else if (strcasecmp(optarg, "windows") == 0)
1758                                 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_WINDOWS;
1759                         else {
1760                                 imagex_error("Unknown stream interface \"%s\"", optarg);
1761                                 goto mount_usage;
1762                         }
1763                         break;
1764                 case 'r':
1765                         swm_glob = optarg;
1766                         break;
1767                 case 'D':
1768                         staging_dir = optarg;
1769                         break;
1770                 case 'U':
1771                         mount_flags |= WIMLIB_MOUNT_FLAG_UNIX_DATA;
1772                         break;
1773                 default:
1774                         goto mount_usage;
1775                 }
1776         }
1777         argc -= optind;
1778         argv += optind;
1779         if (argc != 2 && argc != 3)
1780                 goto mount_usage;
1781
1782         wimfile = argv[0];
1783
1784         ret = wimlib_open_wim(wimfile, open_flags, &w,
1785                               imagex_progress_func);
1786         if (ret != 0)
1787                 return ret;
1788
1789         if (swm_glob) {
1790                 ret = open_swms_from_glob(swm_glob, wimfile, open_flags,
1791                                           &additional_swms,
1792                                           &num_additional_swms);
1793                 if (ret != 0)
1794                         goto out;
1795         }
1796
1797         if (argc == 2) {
1798                 image = 1;
1799                 num_images = wimlib_get_num_images(w);
1800                 if (num_images != 1) {
1801                         imagex_error("The file `%s' contains %d images; Please "
1802                                      "select one.", wimfile, num_images);
1803                         usage((mount_flags & WIMLIB_MOUNT_FLAG_READWRITE)
1804                                         ? MOUNTRW : MOUNT);
1805                         ret = -1;
1806                         goto out;
1807                 }
1808                 dir = argv[1];
1809         } else {
1810                 image = wimlib_resolve_image(w, argv[1]);
1811                 dir = argv[2];
1812                 ret = verify_image_exists_and_is_single(image, argv[1], wimfile);
1813                 if (ret != 0)
1814                         goto out;
1815         }
1816
1817         if (mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) {
1818                 ret = file_writable(wimfile);
1819                 if (ret != 0)
1820                         goto out;
1821         }
1822
1823         ret = wimlib_mount_image(w, image, dir, mount_flags, additional_swms,
1824                                  num_additional_swms, staging_dir);
1825         if (ret != 0) {
1826                 imagex_error("Failed to mount image %d from `%s' on `%s'",
1827                              image, wimfile, dir);
1828
1829         }
1830 out:
1831         wimlib_free(w);
1832         if (additional_swms) {
1833                 for (unsigned i = 0; i < num_additional_swms; i++)
1834                         wimlib_free(additional_swms[i]);
1835                 free(additional_swms);
1836         }
1837         return ret;
1838 mount_usage:
1839         usage((mount_flags & WIMLIB_MOUNT_FLAG_READWRITE)
1840                         ? MOUNTRW : MOUNT);
1841         return -1;
1842 }
1843
1844 /* Rebuild a WIM file */
1845 static int imagex_optimize(int argc, char **argv)
1846 {
1847         int c;
1848         int open_flags = 0;
1849         int write_flags = WIMLIB_WRITE_FLAG_REBUILD;
1850         int ret;
1851         WIMStruct *w;
1852         const char *wimfile;
1853         off_t old_size;
1854         off_t new_size;
1855
1856         for_opt(c, optimize_options) {
1857                 switch (c) {
1858                 case 'c':
1859                         open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
1860                         write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
1861                         break;
1862                 case 'r':
1863                         write_flags |= WIMLIB_WRITE_FLAG_RECOMPRESS;
1864                         break;
1865                 default:
1866                         usage(OPTIMIZE);
1867                         return -1;
1868                 }
1869         }
1870         argc -= optind;
1871         argv += optind;
1872
1873         if (argc != 1) {
1874                 usage(OPTIMIZE);
1875                 return -1;
1876         }
1877
1878         wimfile = argv[0];
1879
1880         ret = wimlib_open_wim(wimfile, open_flags, &w,
1881                               imagex_progress_func);
1882         if (ret != 0)
1883                 return ret;
1884
1885         old_size = file_get_size(argv[0]);
1886         printf("`%s' original size: ", wimfile);
1887         if (old_size == -1)
1888                 puts("Unknown");
1889         else
1890                 printf("%"PRIu64" KiB\n", old_size >> 10);
1891
1892         ret = wimlib_overwrite(w, write_flags, 0, imagex_progress_func);
1893
1894         if (ret == 0) {
1895                 new_size = file_get_size(argv[0]);
1896                 printf("`%s' optimized size: ", wimfile);
1897                 if (new_size == -1)
1898                         puts("Unknown");
1899                 else
1900                         printf("%"PRIu64" KiB\n", new_size >> 10);
1901
1902                 fputs("Space saved: ", stdout);
1903                 if (new_size != -1 && old_size != -1) {
1904                         printf("%lld KiB\n",
1905                                ((long long)old_size - (long long)new_size) >> 10);
1906                 } else {
1907                         puts("Unknown");
1908                 }
1909         }
1910
1911         wimlib_free(w);
1912         return ret;
1913 }
1914
1915 /* Split a WIM into a spanned set */
1916 static int imagex_split(int argc, char **argv)
1917 {
1918         int c;
1919         int open_flags = WIMLIB_OPEN_FLAG_SPLIT_OK;
1920         int write_flags = 0;
1921         unsigned long part_size;
1922         char *tmp;
1923         int ret;
1924         WIMStruct *w;
1925
1926         for_opt(c, split_options) {
1927                 switch (c) {
1928                 case 'c':
1929                         open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
1930                         write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
1931                         break;
1932                 default:
1933                         usage(SPLIT);
1934                         return -1;
1935                 }
1936         }
1937         argc -= optind;
1938         argv += optind;
1939
1940         if (argc != 3) {
1941                 usage(SPLIT);
1942                 return -1;
1943         }
1944         part_size = strtod(argv[2], &tmp) * (1 << 20);
1945         if (tmp == argv[2] || *tmp) {
1946                 imagex_error("Invalid part size \"%s\"", argv[2]);
1947                 imagex_error("The part size must be an integer or floating-point number of megabytes.");
1948                 return -1;
1949         }
1950         ret = wimlib_open_wim(argv[0], open_flags, &w, imagex_progress_func);
1951         if (ret != 0)
1952                 return ret;
1953         ret = wimlib_split(w, argv[1], part_size, write_flags, imagex_progress_func);
1954         wimlib_free(w);
1955         return ret;
1956 }
1957
1958 /* Unmounts a mounted WIM image. */
1959 static int imagex_unmount(int argc, char **argv)
1960 {
1961         int c;
1962         int unmount_flags = 0;
1963         int ret;
1964
1965         for_opt(c, unmount_options) {
1966                 switch (c) {
1967                 case 'c':
1968                         unmount_flags |= WIMLIB_UNMOUNT_FLAG_COMMIT;
1969                         break;
1970                 case 'C':
1971                         unmount_flags |= WIMLIB_UNMOUNT_FLAG_CHECK_INTEGRITY;
1972                         break;
1973                 case 'R':
1974                         unmount_flags |= WIMLIB_UNMOUNT_FLAG_REBUILD;
1975                         break;
1976                 default:
1977                         usage(UNMOUNT);
1978                         return -1;
1979                 }
1980         }
1981         argc -= optind;
1982         argv += optind;
1983         if (argc != 1) {
1984                 usage(UNMOUNT);
1985                 return -1;
1986         }
1987
1988         ret = wimlib_unmount_image(argv[0], unmount_flags,
1989                                    imagex_progress_func);
1990         if (ret != 0)
1991                 imagex_error("Failed to unmount `%s'", argv[0]);
1992         return ret;
1993 }
1994
1995 struct imagex_command {
1996         const char *name;
1997         int (*func)(int , char **);
1998         int cmd;
1999 };
2000
2001
2002 #define for_imagex_command(p) for (p = &imagex_commands[0]; \
2003                 p != &imagex_commands[ARRAY_LEN(imagex_commands)]; p++)
2004
2005 static const struct imagex_command imagex_commands[] = {
2006         {"append",  imagex_capture_or_append, APPEND},
2007         {"apply",   imagex_apply,             APPLY},
2008         {"capture", imagex_capture_or_append, CAPTURE},
2009         {"delete",  imagex_delete,            DELETE},
2010         {"dir",     imagex_dir,               DIR},
2011         {"export",  imagex_export,            EXPORT},
2012         {"info",    imagex_info,              INFO},
2013         {"join",    imagex_join,              JOIN},
2014         {"mount",   imagex_mount_rw_or_ro,    MOUNT},
2015         {"mountrw", imagex_mount_rw_or_ro,    MOUNTRW},
2016         {"optimize",imagex_optimize,          OPTIMIZE},
2017         {"split",   imagex_split,             SPLIT},
2018         {"unmount", imagex_unmount,           UNMOUNT},
2019 };
2020
2021 static void version()
2022 {
2023         static const char *s =
2024         "imagex (" PACKAGE ") " PACKAGE_VERSION "\n"
2025         "Copyright (C) 2012, 2013 Eric Biggers\n"
2026         "License GPLv3+; GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.\n"
2027         "This is free software: you are free to change and redistribute it.\n"
2028         "There is NO WARRANTY, to the extent permitted by law.\n"
2029         "\n"
2030         "Report bugs to "PACKAGE_BUGREPORT".\n";
2031         fputs(s, stdout);
2032 }
2033
2034
2035 static void help_or_version(int argc, char **argv)
2036 {
2037         int i;
2038         const char *p;
2039         const struct imagex_command *cmd;
2040
2041         for (i = 1; i < argc; i++) {
2042                 p = argv[i];
2043                 if (*p == '-')
2044                         p++;
2045                 else
2046                         continue;
2047                 if (*p == '-')
2048                         p++;
2049                 if (strcmp(p, "help") == 0) {
2050                         for_imagex_command(cmd) {
2051                                 if (strcmp(cmd->name, argv[1]) == 0) {
2052                                         usage(cmd->cmd);
2053                                         exit(0);
2054                                 }
2055                         }
2056                         usage_all();
2057                         exit(0);
2058                 }
2059                 if (strcmp(p, "version") == 0) {
2060                         version();
2061                         exit(0);
2062                 }
2063         }
2064 }
2065
2066
2067 static void usage(int cmd_type)
2068 {
2069         const struct imagex_command *cmd;
2070         printf("Usage: %s", usage_strings[cmd_type]);
2071         for_imagex_command(cmd) {
2072                 if (cmd->cmd == cmd_type)
2073                         printf("\nTry `man imagex-%s' for more details.\n",
2074                                cmd->name);
2075         }
2076 }
2077
2078 static void usage_all()
2079 {
2080         puts("IMAGEX: Usage:");
2081         for (int i = 0; i < ARRAY_LEN(usage_strings); i++)
2082                 printf("    %s", usage_strings[i]);
2083         static const char *extra =
2084 "    imagex --help\n"
2085 "    imagex --version\n"
2086 "\n"
2087 "    The compression TYPE may be \"maximum\", \"fast\", or \"none\".\n"
2088 "\n"
2089 "    Try `man imagex' for more information.\n"
2090         ;
2091         fputs(extra, stdout);
2092 }
2093
2094 /* Entry point for the 'imagex' program. */
2095 int main(int argc, char **argv)
2096 {
2097         const struct imagex_command *cmd;
2098         int ret;
2099
2100         if (argc < 2) {
2101                 imagex_error("No command specified");
2102                 usage_all();
2103                 return 1;
2104         }
2105
2106         /* Handle --help and --version for all commands.  Note that this will
2107          * not return if either of these arguments are present. */
2108         help_or_version(argc, argv);
2109         argc--;
2110         argv++;
2111
2112         /* The user may like to see more informative error messages. */
2113         wimlib_set_print_errors(true);
2114
2115         /* Calling wimlib_global_init() is not strictly necessary because
2116          * 'imagex' is single-threaded. */
2117         ret = wimlib_global_init();
2118         if (ret)
2119                 goto out;
2120
2121         /* Search for the function to handle the 'imagex' subcommand. */
2122         for_imagex_command(cmd) {
2123                 if (strcmp(cmd->name, *argv) == 0) {
2124                         ret = cmd->func(argc, argv);
2125                         goto out;
2126                 }
2127         }
2128
2129         imagex_error("Unrecognized command: `%s'", argv[0]);
2130         usage_all();
2131         return 1;
2132 out:
2133         /* For 'imagex info' and 'imagex dir', data printed to standard output
2134          * is part of the program's actual behavior and not just for
2135          * informational purposes, so we should set a failure exit status if
2136          * there was a write error. */
2137         if (cmd == &imagex_commands[INFO] || cmd == &imagex_commands[DIR]) {
2138                 if (ferror(stdout) || fclose(stdout)) {
2139                         imagex_error_with_errno("output error");
2140                         if (ret == 0)
2141                                 ret = -1;
2142                 }
2143         }
2144
2145         /* Exit status (ret):  -1 indicates an error found by 'imagex' outside
2146          * of the wimlib library code.  0 indicates success.  > 0 indicates a
2147          * wimlib error code from which an error message can be printed. */
2148         if (ret > 0) {
2149                 imagex_error("Exiting with error code %d:\n"
2150                              "       %s.", ret,
2151                              wimlib_get_error_string(ret));
2152                 if (ret == WIMLIB_ERR_NTFS_3G && errno != 0)
2153                         imagex_error_with_errno("errno");
2154         }
2155         /* Calling wimlib_global_cleanup() is not strictly necessary because the
2156          * process is exiting anyway. */
2157         wimlib_global_cleanup();
2158         return ret;
2159 }