]> wimlib.net Git - wimlib/blob - programs/imagex.c
image default names
[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 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 "wimlib.h"
26 #include "config.h"
27 #include <getopt.h>
28 #include <stdlib.h>
29 #include <stdarg.h>
30 #include <glob.h>
31 #include <string.h>
32 #include <errno.h>
33 #include <libgen.h>
34 #include <sys/stat.h>
35
36 #define ARRAY_LEN(array) (sizeof(array) / sizeof(array[0]))
37
38 #define swap(a, b) ({ typeof(a) __a = (a); typeof(b) __b = (b); \
39                                 a = __b; b = __a; })
40
41 #define for_opt(c, opts) while ((c = getopt_long_only(argc, (char**)argv, "", \
42                                 opts, NULL)) != -1)
43
44
45 enum imagex_op_type {
46         APPEND,
47         APPLY,
48         CAPTURE,
49         DELETE,
50         DIR,
51         EXPORT,
52         INFO,
53         JOIN,
54         MOUNT,
55         MOUNTRW,
56         SPLIT,
57         UNMOUNT,
58 };
59
60 static void usage(int cmd_type);
61 static void usage_all();
62
63 static const char *usage_strings[] = {
64 [APPEND] = 
65 "    imagex append (DIRECTORY | NTFS_VOLUME) WIMFILE [IMAGE_NAME]\n"
66 "                  [DESCRIPTION] [--boot] [--check] [--flags EDITION_ID]\n"
67 "                  [--verbose] [--dereference] [--config=FILE]\n",
68 [APPLY] = 
69 "    imagex apply WIMFILE [IMAGE_NUM | IMAGE_NAME | all]\n"
70 "                 (DIRECTORY | NTFS_VOLUME) [--check] [--hardlink]\n"
71 "                 [--symlink] [--verbose] [--ref=\"GLOB\"]\n",
72 [CAPTURE] = 
73 "    imagex capture (DIRECTORY | NTFS_VOLUME) WIMFILE [IMAGE_NAME]\n"
74 "                   [DESCRIPTION] [--boot] [--check] [--compress=TYPE]\n"
75 "                   [--flags EDITION_ID] [--verbose] [--dereference]\n"
76 "                   [--config=FILE]\n",
77 [DELETE] = 
78 "    imagex delete WIMFILE (IMAGE_NUM | IMAGE_NAME | all) [--check]\n",
79 [DIR] = 
80 "    imagex dir WIMFILE (IMAGE_NUM | IMAGE_NAME | all)\n",
81 [EXPORT] = 
82 "    imagex export SRC_WIMFILE (SRC_IMAGE_NUM | SRC_IMAGE_NAME | all ) \n"
83 "                  DEST_WIMFILE [DEST_IMAGE_NAME]\n"
84 "                  [DEST_IMAGE_DESCRIPTION] [--boot] [--check]\n"
85 "                  [--compress=TYPE] [--ref=\"GLOB\"]\n",
86 [INFO] = 
87 "    imagex info WIMFILE [IMAGE_NUM | IMAGE_NAME] [NEW_NAME]\n"
88 "                [NEW_DESC] [--boot] [--check] [--header] [--lookup-table]\n"
89 "                [--xml] [--extract-xml FILE] [--metadata]\n",
90 [JOIN] = 
91 "    imagex join [--check] WIMFILE SPLIT_WIM...\n",
92 [MOUNT] = 
93 "    imagex mount WIMFILE (IMAGE_NUM | IMAGE_NAME) DIRECTORY\n"
94 "                 [--check] [--debug] [--streams-interface=INTERFACE]\n"
95 "                 [--ref=\"GLOB\"]\n",
96 [MOUNTRW] = 
97 "    imagex mountrw WIMFILE [IMAGE_NUM | IMAGE_NAME] DIRECTORY\n"
98 "                   [--check] [--debug] [--streams-interface=INTERFACE]\n",
99 [SPLIT] = 
100 "    imagex split WIMFILE SPLIT_WIMFILE PART_SIZE_MB [--check]\n",
101 [UNMOUNT] = 
102 "    imagex unmount DIRECTORY [--commit] [--check]\n",
103 };
104
105 static const struct option common_options[] = {
106         {"help", 0, NULL, 'h'},
107         {"version", 0, NULL, 'v'},
108         {NULL, 0, NULL, 0},
109 };
110
111 static const struct option append_options[] = {
112         {"boot",        no_argument,       NULL, 'b'},
113         {"check",       no_argument,       NULL, 'c'},
114         {"config",      required_argument, NULL, 'C'},
115         {"dereference", no_argument,       NULL, 'L'},
116         {"flags",       required_argument, NULL, 'f'},
117         {"verbose",     no_argument,       NULL, 'v'},
118         {NULL, 0, NULL, 0},
119 };
120 static const struct option apply_options[] = {
121         {"check",    no_argument,       NULL, 'c'},
122         {"hardlink", no_argument,       NULL, 'h'},
123         {"symlink",  no_argument,       NULL, 's'},
124         {"verbose",  no_argument,       NULL, 'v'},
125         {"ref",      required_argument, NULL, 'r'},
126         {NULL, 0, NULL, 0},
127 };
128 static const struct option capture_options[] = {
129         {"boot",        no_argument,       NULL, 'b'},
130         {"check",       no_argument,       NULL, 'c'},
131         {"compress",    required_argument, NULL, 'x'},
132         {"config",      required_argument, NULL, 'C'},
133         {"dereference", no_argument,       NULL, 'L'},
134         {"flags",       required_argument, NULL, 'f'},
135         {"verbose",     no_argument,       NULL, 'v'},
136         {NULL, 0, NULL, 0},
137 };
138 static const struct option delete_options[] = {
139         {"check", no_argument, NULL, 'c'},
140         {NULL, 0, NULL, 0},
141 };
142
143 static const struct option export_options[] = {
144         {"boot",       no_argument,       NULL, 'b'},
145         {"check",      no_argument,       NULL, 'c'},
146         {"compress",   required_argument, NULL, 'x'},
147         {"ref",        required_argument, NULL, 'r'},
148         {NULL, 0, NULL, 0},
149 };
150
151 static const struct option info_options[] = {
152         {"boot",         no_argument, NULL, 'b'},
153         {"check",        no_argument, NULL, 'c'},
154         {"extract-xml",  required_argument, NULL, 'X'},
155         {"header",       no_argument, NULL, 'h'},
156         {"lookup-table", no_argument, NULL, 'l'},
157         {"metadata",     no_argument, NULL, 'm'},
158         {"xml",          no_argument, NULL, 'x'},
159         {NULL, 0, NULL, 0},
160 };
161
162 static const struct option join_options[] = {
163         {"check", no_argument, NULL, 'c'},
164         {NULL, 0, NULL, 0},
165 };
166
167 static const struct option mount_options[] = {
168         {"check", no_argument, NULL, 'c'},
169         {"debug", no_argument, NULL, 'd'},
170         {"streams-interface", required_argument, NULL, 's'},
171         {"ref",      required_argument, NULL, 'r'},
172         {NULL, 0, NULL, 0},
173 };
174
175 static const struct option split_options[] = {
176         {"check", no_argument, NULL, 'c'},
177         {NULL, 0, NULL, 0},
178 };
179
180 static const struct option unmount_options[] = {
181         {"commit", no_argument, NULL, 'c'},
182         {"check", no_argument, NULL, 'C'},
183         {NULL, 0, NULL, 0},
184 };
185
186
187
188 /* Print formatted error message to stderr. */
189 static void imagex_error(const char *format, ...)
190 {
191         va_list va;
192         va_start(va, format);
193         fputs("ERROR: ", stderr);
194         vfprintf(stderr, format, va);
195         putc('\n', stderr);
196         va_end(va);
197 }
198
199 /* Print formatted error message to stderr. */
200 static void imagex_error_with_errno(const char *format, ...)
201 {
202         int errno_save = errno;
203         va_list va;
204         va_start(va, format);
205         fputs("ERROR: ", stderr);
206         vfprintf(stderr, format, va);
207         fprintf(stderr, ": %s\n", strerror(errno_save));
208         va_end(va);
209 }
210
211 static const char *path_basename(const char *path)
212 {
213         const char *p = path;
214         while (*p)
215                 p++;
216         p--;
217
218         /* Trailing slashes. */
219         while ((p != path - 1) && *p == '/')
220                 p--;
221
222         while ((p != path - 1) && *p != '/')
223                 p--;
224
225         return p + 1;
226 }
227
228
229 static int verify_image_exists(int image)
230 {
231         if (image == WIM_NO_IMAGE) {
232                 imagex_error("Not a valid image");
233                 return WIMLIB_ERR_INVALID_IMAGE;
234         }
235         return 0;
236 }
237
238 static int verify_image_is_single(int image)
239 {
240         if (image == WIM_ALL_IMAGES) {
241                 imagex_error("Cannot specify all images for this action");
242                 return WIMLIB_ERR_INVALID_IMAGE;
243         }
244         return 0;
245 }
246
247 static int verify_image_exists_and_is_single(int image)
248 {
249         int ret;
250         ret = verify_image_exists(image);
251         if (ret == 0)
252                 ret = verify_image_is_single(image);
253         return ret;
254 }
255
256 static int get_compression_type(const char *optarg)
257 {
258         if (strcasecmp(optarg, "maximum") == 0 || strcasecmp(optarg, "lzx") == 0)
259                 return WIM_COMPRESSION_TYPE_LZX;
260         else if (strcasecmp(optarg, "fast") == 0 || strcasecmp(optarg, "xpress") == 0)
261                 return WIM_COMPRESSION_TYPE_XPRESS;
262         else if (strcasecmp(optarg, "none") == 0)
263                 return WIM_COMPRESSION_TYPE_NONE;
264         else {
265                 imagex_error("Invalid compression type `%s'! Must be "
266                              "\"maximum\", \"fast\", or \"none\".", optarg);
267                 return WIM_COMPRESSION_TYPE_INVALID;
268         }
269 }
270
271 static const char *file_get_contents(const char *filename, size_t *len_ret)
272 {
273         struct stat stbuf;
274         char *buf;
275         size_t len;
276         FILE *fp;
277
278         if (stat(filename, &stbuf) != 0) {
279                 imagex_error_with_errno("Failed to stat the file `%s'", filename);
280                 return NULL;
281         }
282         len = stbuf.st_size;
283
284         fp = fopen(filename, "rb");
285         if (!fp) {
286                 imagex_error_with_errno("Failed to open the file `%s'", filename);
287                 return NULL;
288         }
289
290         buf = malloc(len);
291         if (!buf) {
292                 imagex_error("Failed to allocate buffer of %zu bytes to hold "
293                              "contents of file `%s'", len, filename);
294                 goto out_fclose;
295         }
296         if (fread(buf, 1, len, fp) != len) {
297                 imagex_error_with_errno("Failed to read %lu bytes from the "
298                                         "file `%s'", len, filename);
299                 goto out_free_buf;
300         }
301         *len_ret = len;
302         return buf;
303 out_free_buf:
304         free(buf);
305 out_fclose:
306         fclose(fp);
307         return NULL;
308 }
309
310 static int open_swms_from_glob(const char *swm_glob,
311                                const char *first_part,
312                                int open_flags,
313                                WIMStruct ***additional_swms_ret,
314                                unsigned *num_additional_swms_ret)
315 {
316         unsigned num_additional_swms = 0;
317         WIMStruct **additional_swms = NULL;
318         glob_t globbuf;
319         int ret;
320
321         ret = glob(swm_glob, GLOB_ERR | GLOB_NOSORT, NULL, &globbuf);
322         if (ret != 0) {
323                 if (ret == GLOB_NOMATCH) {
324                         imagex_error("Found no files for glob \"%s\"",
325                                      swm_glob);
326                 } else {
327                         imagex_error_with_errno("Failed to process glob "
328                                                 "\"%s\"", swm_glob);
329                 }
330                 ret = -1;
331                 goto out;
332         }
333         num_additional_swms = globbuf.gl_pathc;
334         additional_swms = calloc(num_additional_swms, sizeof(additional_swms[0]));
335         if (!additional_swms) {
336                 imagex_error("Out of memory");
337                 ret = -1;
338                 goto out_globfree;
339         }
340         size_t offset = 0;
341         for (size_t i = 0; i < num_additional_swms; i++) {
342                 if (strcmp(globbuf.gl_pathv[i], first_part) == 0) {
343                         offset++;
344                         continue;
345                 }
346                 ret = wimlib_open_wim(globbuf.gl_pathv[i],
347                                       open_flags | WIMLIB_OPEN_FLAG_SPLIT_OK,
348                                       &additional_swms[i - offset]);
349                 if (ret != 0)
350                         goto out_close_swms;
351         }
352         *additional_swms_ret = additional_swms;
353         *num_additional_swms_ret = num_additional_swms - offset;
354         ret = 0;
355         goto out_globfree;
356 out_close_swms:
357         for (unsigned i = 0; i < num_additional_swms; i++)
358                 wimlib_free(additional_swms[i]);
359         free(additional_swms);
360 out_globfree:
361         globfree(&globbuf);
362 out:
363         return ret;
364 }
365
366 static int imagex_append(int argc, const char **argv)
367 {
368         int c;
369         const char *flags_element = NULL;
370         int open_flags = WIMLIB_OPEN_FLAG_SHOW_PROGRESS;
371         int add_image_flags = 0;
372         int write_flags = WIMLIB_WRITE_FLAG_SHOW_PROGRESS;
373         const char *dir;
374         const char *wimfile;
375         const char *name;
376         const char *desc;
377         const char *config_file = NULL;
378         const char *config_str = NULL;
379         size_t config_len = 0;
380         WIMStruct *w;
381         int ret;
382         int cur_image;
383         char *default_name;
384
385         for_opt(c, append_options) {
386                 switch (c) {
387                 case 'b':
388                         add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_BOOT;
389                         break;
390                 case 'c':
391                         open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
392                         write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
393                         break;
394                 case 'C':
395                         config_file = optarg;
396                         break;
397                 case 'f':
398                         flags_element = optarg;
399                         break;
400                 case 'L':
401                         add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_DEREFERENCE;
402                         break;
403                 case 'v':
404                         add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_VERBOSE;
405                         break;
406                 default:
407                         usage(APPEND);
408                         return -1;
409                 }
410         }
411         argc -= optind;
412         argv += optind;
413         if (argc < 2 || argc > 4) {
414                 usage(APPEND);
415                 return -1;
416         }
417         dir     = argv[0];
418         wimfile = argv[1];
419
420         char dir_copy[strlen(dir) + 1];
421         memcpy(dir_copy, dir, strlen(dir) + 1);
422         default_name = basename(dir_copy);
423
424         name    = (argc >= 3) ? argv[2] : default_name;
425         desc    = (argc >= 4) ? argv[3] : NULL;
426
427         if (config_file) {
428                 config_str = file_get_contents(config_file, &config_len);
429                 if (!config_str)
430                         return -1;
431         }
432
433         ret = wimlib_open_wim(wimfile, open_flags, &w);
434         if (ret != 0)
435                 return ret;
436
437 #ifdef WITH_NTFS_3G
438         struct stat stbuf;
439
440         ret = stat(dir, &stbuf);
441         if (ret == 0) {
442                 if (S_ISBLK(stbuf.st_mode) || S_ISREG(stbuf.st_mode)) {
443                         const char *ntfs_device = dir;
444                         printf("Capturing WIM image NTFS filesystem on `%s'\n",
445                                ntfs_device);
446                         ret = wimlib_add_image_from_ntfs_volume(w, ntfs_device,
447                                                                 name,
448                                                                 config_str,
449                                                                 config_len,
450                                                                 add_image_flags);
451                         goto out_write;
452                 }
453         } else {
454                 if (errno != ENOENT)
455                         imagex_error_with_errno("Failed to stat `%s'", dir);
456         }
457 #endif
458         ret = wimlib_add_image(w, dir, name, config_str, config_len,
459                                add_image_flags);
460
461 out_write:
462         if (ret != 0)
463                 goto out;
464         cur_image = wimlib_get_num_images(w);
465         if (desc) {
466                 ret = wimlib_set_image_descripton(w, cur_image, desc);
467                 if (ret != 0)
468                         goto out;
469         }
470         if (flags_element) {
471                 ret = wimlib_set_image_flags(w, cur_image, flags_element);
472                 if (ret != 0)
473                         goto out;
474         }
475         ret = wimlib_overwrite(w, write_flags);
476 out:
477         wimlib_free(w);
478         return ret;
479 }
480
481 /* Extract one image, or all images, from a WIM file into a directory. */
482 static int imagex_apply(int argc, const char **argv)
483 {
484         int c;
485         int open_flags = WIMLIB_OPEN_FLAG_SHOW_PROGRESS |
486                          WIMLIB_OPEN_FLAG_SPLIT_OK;
487         int image;
488         int num_images;
489         WIMStruct *w;
490         int ret;
491         const char *wimfile;
492         const char *dir;
493         const char *image_num_or_name;
494         int extract_flags = 0;
495
496         const char *swm_glob = NULL;
497         WIMStruct **additional_swms = NULL;
498         unsigned num_additional_swms = 0;
499
500         for_opt(c, apply_options) {
501                 switch (c) {
502                 case 'c':
503                         open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
504                         break;
505                 case 'h':
506                         extract_flags |= WIMLIB_EXTRACT_FLAG_HARDLINK;
507                         break;
508                 case 's':
509                         extract_flags |= WIMLIB_EXTRACT_FLAG_SYMLINK;
510                         break;
511                 case 'v':
512                         extract_flags |= WIMLIB_EXTRACT_FLAG_VERBOSE;
513                         break;
514                 case 'r':
515                         swm_glob = optarg;
516                         break;
517                 default:
518                         usage(APPLY);
519                         return -1;
520                 }
521         }
522         argc -= optind;
523         argv += optind;
524         if (argc != 2 && argc != 3) {
525                 usage(APPLY);
526                 return -1;
527         }
528
529         wimfile = argv[0];
530         if (argc == 2) {
531                 image_num_or_name =  "1";
532                 dir = argv[1];
533         } else {
534                 image_num_or_name = argv[1];
535                 dir = argv[2];
536         }
537
538         ret = wimlib_open_wim(wimfile, open_flags, &w);
539         if (ret != 0)
540                 return ret;
541
542         image = wimlib_resolve_image(w, image_num_or_name);
543         ret = verify_image_exists(image);
544         if (ret != 0)
545                 goto out;
546
547         num_images = wimlib_get_num_images(w);
548         if (argc == 2 && num_images != 1) {
549                 imagex_error("`%s' contains %d images; Please select one "
550                              "(or all)", wimfile, num_images);
551                 usage(APPLY);
552                 ret = -1;
553                 goto out;
554         }
555
556         if (swm_glob) {
557                 ret = open_swms_from_glob(swm_glob, wimfile, open_flags,
558                                           &additional_swms,
559                                           &num_additional_swms);
560                 if (ret != 0)
561                         goto out;
562         }
563
564 #ifdef WITH_NTFS_3G
565         struct stat stbuf;
566
567         ret = stat(dir, &stbuf);
568         if (ret == 0) {
569                 if (S_ISBLK(stbuf.st_mode) || S_ISREG(stbuf.st_mode)) {
570                         const char *ntfs_device = dir;
571                         printf("Applying image %d of `%s' to NTFS filesystem on `%s'\n",
572                                image, wimfile, ntfs_device);
573                         ret = wimlib_apply_image_to_ntfs_volume(w, image,
574                                                                 ntfs_device,
575                                                                 extract_flags,
576                                                                 additional_swms,
577                                                                 num_additional_swms);
578                         goto out;
579                 }
580         } else {
581                 if (errno != ENOENT)
582                         imagex_error_with_errno("Failed to stat `%s'", dir);
583         }
584 #endif
585
586         ret = wimlib_extract_image(w, image, dir, extract_flags,
587                                    additional_swms, num_additional_swms);
588 out:
589         wimlib_free(w);
590         if (additional_swms)
591                 for (size_t i = 0; i < num_additional_swms; i++)
592                         wimlib_free(additional_swms[i]);
593         return ret;
594 }
595
596
597 /* Create a WIM file from a directory. */
598 static int imagex_capture(int argc, const char **argv)
599 {
600         int c;
601         int add_image_flags = 0;
602         int write_flags = WIMLIB_WRITE_FLAG_SHOW_PROGRESS;
603         int compression_type = WIM_COMPRESSION_TYPE_XPRESS;
604         const char *flags_element = NULL;
605         const char *dir;
606         const char *wimfile;
607         const char *name;
608         const char *desc;
609         const char *config_file = NULL;
610         const char *config_str = NULL;
611         size_t config_len = 0;
612         WIMStruct *w;
613         int cur_image;
614         char *default_name;
615         int ret;
616
617         for_opt(c, capture_options) {
618                 switch (c) {
619                 case 'b':
620                         add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_BOOT;
621                         break;
622                 case 'c':
623                         write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
624                         break;
625                 case 'C':
626                         config_file = optarg;
627                         break;
628                 case 'x':
629                         compression_type = get_compression_type(optarg);
630                         if (compression_type == WIM_COMPRESSION_TYPE_INVALID)
631                                 return -1;
632                         break;
633                 case 'f':
634                         flags_element = optarg;
635                         break;
636                 case 'v':
637                         add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_VERBOSE;
638                         write_flags |= WIMLIB_WRITE_FLAG_VERBOSE;
639                         break;
640                 case 'L':
641                         add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_DEREFERENCE;
642                         break;
643                 default:
644                         usage(CAPTURE);
645                         return -1;
646                 }
647         }
648
649         argc -= optind;
650         argv += optind;
651         if (argc < 2 || argc > 4) {
652                 usage(CAPTURE);
653                 return -1;
654         }
655         dir     = argv[0];
656         wimfile = argv[1];
657
658         char dir_copy[strlen(dir) + 1];
659         memcpy(dir_copy, dir, strlen(dir) + 1);
660         default_name = basename(dir_copy);
661
662         name    = (argc >= 3) ? argv[2] : default_name;
663         desc    = (argc >= 4) ? argv[3] : NULL;
664
665         if (config_file) {
666                 config_str = file_get_contents(config_file, &config_len);
667                 if (!config_str)
668                         return -1;
669         }
670
671         ret = wimlib_create_new_wim(compression_type, &w);
672         if (ret != 0)
673                 return ret;
674
675 #ifdef WITH_NTFS_3G
676         struct stat stbuf;
677
678         ret = stat(dir, &stbuf);
679         if (ret == 0) {
680                 if (S_ISBLK(stbuf.st_mode) || S_ISREG(stbuf.st_mode)) {
681                         const char *ntfs_device = dir;
682                         printf("Capturing WIM image NTFS filesystem on `%s'\n",
683                                ntfs_device);
684                         ret = wimlib_add_image_from_ntfs_volume(w, ntfs_device,
685                                                                 name,
686                                                                 config_str,
687                                                                 config_len,
688                                                                 add_image_flags);
689                         goto out_write;
690                 }
691         } else {
692                 if (errno != ENOENT)
693                         imagex_error_with_errno("Failed to stat `%s'", dir);
694         }
695 #endif
696         ret = wimlib_add_image(w, dir, name, config_str,
697                                config_len, add_image_flags);
698
699 out_write:
700         if (ret != 0) {
701                 imagex_error("Failed to add the image `%s'", dir);
702                 goto out;
703         }
704         cur_image = wimlib_get_num_images(w);
705         if (desc) {
706                 ret = wimlib_set_image_descripton(w, cur_image, desc);
707                 if (ret != 0)
708                         goto out;
709         }
710         if (flags_element) {
711                 ret = wimlib_set_image_flags(w, cur_image, flags_element);
712                 if (ret != 0)
713                         goto out;
714         }
715
716         ret = wimlib_write(w, wimfile, WIM_ALL_IMAGES, write_flags);
717         if (ret != 0)
718                 imagex_error("Failed to write the WIM file `%s'", wimfile);
719 out:
720         wimlib_free(w);
721         return ret;
722 }
723
724 /* Remove image(s) from a WIM. */
725 static int imagex_delete(int argc, const char **argv)
726 {
727         int c;
728         int open_flags = WIMLIB_OPEN_FLAG_SHOW_PROGRESS;
729         int write_flags = WIMLIB_WRITE_FLAG_SHOW_PROGRESS;
730         const char *wimfile;
731         const char *image_num_or_name;
732         WIMStruct *w;
733         int image;
734         int ret;
735
736         for_opt(c, delete_options) {
737                 switch (c) {
738                 case 'c':
739                         open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
740                         write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
741                         break;
742                 default:
743                         usage(DELETE);
744                         return -1;
745                 }
746         }
747         argc -= optind;
748         argv += optind;
749
750         if (argc != 2) {
751                 if (argc < 1)
752                         imagex_error("Must specify a WIM file");
753                 if (argc < 2)
754                         imagex_error("Must specify an image");
755                 usage(DELETE);
756                 return -1;
757         }
758         wimfile = argv[0];
759         image_num_or_name = argv[1];
760
761         ret = wimlib_open_wim(wimfile, open_flags, &w);
762         if (ret != 0)
763                 return ret;
764
765         image = wimlib_resolve_image(w, image_num_or_name);
766
767         ret = verify_image_exists(image);
768         if (ret != 0)
769                 goto done;
770
771         ret = wimlib_delete_image(w, image);
772         if (ret != 0) {
773                 imagex_error("Failed to delete image from `%s'", wimfile);
774                 goto done;
775         }
776
777         ret = wimlib_overwrite(w, write_flags);
778         if (ret != 0) {
779                 imagex_error("Failed to write the file `%s' with image "
780                              "deleted", wimfile);
781         }
782 done:
783         wimlib_free(w);
784         return ret;
785 }
786
787 /* Print the files contained in an image(s) in a WIM file. */
788 static int imagex_dir(int argc, const char **argv)
789 {
790         const char *wimfile;
791         WIMStruct *w;
792         int image;
793         int ret;
794         int num_images;
795         int part_number;
796
797         if (argc < 2) {
798                 imagex_error("Must specify a WIM file");
799                 usage(DIR);
800                 return -1;
801         }
802         if (argc > 3) {
803                 imagex_error("Too many arguments");
804                 usage(DIR);
805                 return -1;
806         }
807
808         wimfile = argv[1];
809         ret = wimlib_open_wim(wimfile, WIMLIB_OPEN_FLAG_SPLIT_OK, &w);
810         if (ret != 0)
811                 return ret;
812
813         part_number = wimlib_get_part_number(w, NULL);
814         if (part_number != 1) {
815                 imagex_error("`%s' is part %d of a split WIM!  Specify the "
816                              "first part to see the files",
817                              wimfile, part_number);
818                 ret = WIMLIB_ERR_SPLIT_UNSUPPORTED;
819                 goto done;
820         }
821
822         if (argc == 3) {
823                 image = wimlib_resolve_image(w, argv[2]);
824                 ret = verify_image_exists(image);
825                 if (ret != 0)
826                         goto done;
827         } else {
828                 /* Image was not specified.  If the WIM only contains one image,
829                  * choose that one; otherwise, print an error. */
830                 num_images = wimlib_get_num_images(w);
831                 if (num_images != 1) {
832                         imagex_error("The file `%s' contains %d images; Please "
833                                      "select one.", wimfile, num_images);
834                         usage(DIR);
835                         ret = -1;
836                         goto done;
837                 }
838                 image = 1;
839         }
840
841         ret = wimlib_print_files(w, image);
842 done:
843         wimlib_free(w);
844         return ret;
845 }
846
847 /* Exports one, or all, images from a WIM file to a new WIM file or an existing
848  * WIM file. */
849 static int imagex_export(int argc, const char **argv)
850 {
851         int c;
852         int open_flags = WIMLIB_OPEN_FLAG_SHOW_PROGRESS;
853         int export_flags = 0;
854         int write_flags = WIMLIB_WRITE_FLAG_SHOW_PROGRESS;
855         int compression_type = WIM_COMPRESSION_TYPE_XPRESS;
856         bool compression_type_specified = false;
857         const char *src_wimfile;
858         const char *src_image_num_or_name;
859         const char *dest_wimfile;
860         const char *dest_name;
861         const char *dest_desc;
862         WIMStruct *src_w = NULL;
863         WIMStruct *dest_w = NULL;
864         int ret;
865         int image;
866         struct stat stbuf;
867         bool wim_is_new;
868         const char *swm_glob = NULL;
869         WIMStruct **additional_swms = NULL;
870         unsigned num_additional_swms = 0;
871
872         for_opt(c, export_options) {
873                 switch (c) {
874                 case 'b':
875                         export_flags |= WIMLIB_EXPORT_FLAG_BOOT;
876                         break;
877                 case 'c':
878                         open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
879                         write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
880                         break;
881                 case 'x':
882                         compression_type = get_compression_type(optarg);
883                         if (compression_type == WIM_COMPRESSION_TYPE_INVALID)
884                                 return -1;
885                         compression_type_specified = true;
886                         break;
887                 case 'r':
888                         swm_glob = optarg;
889                         break;
890                 default:
891                         usage(EXPORT);
892                         return -1;
893                 }
894         }
895         argc -= optind;
896         argv += optind;
897         if (argc < 3 || argc > 5) {
898                 usage(EXPORT);
899                 return -1;
900         }
901         src_wimfile           = argv[0];
902         src_image_num_or_name = argv[1];
903         dest_wimfile          = argv[2];
904         dest_name             = (argc >= 4) ? argv[3] : NULL;
905         dest_desc             = (argc >= 5) ? argv[4] : NULL;
906         ret = wimlib_open_wim(src_wimfile,
907                               open_flags | WIMLIB_OPEN_FLAG_SPLIT_OK, &src_w);
908         if (ret != 0)
909                 return ret;
910
911         /* Determine if the destination is an existing file or not.  
912          * If so, we try to append the exported image(s) to it; otherwise, we
913          * create a new WIM containing the exported image(s). */
914         if (stat(dest_wimfile, &stbuf) == 0) {
915                 wim_is_new = false;
916                 /* Destination file exists. */
917                 if (!S_ISREG(stbuf.st_mode) && !S_ISLNK(stbuf.st_mode)) {
918                         imagex_error("`%s' is not a regular file",
919                                         dest_wimfile);
920                         goto out;
921                 }
922                 ret = wimlib_open_wim(dest_wimfile, open_flags, &dest_w);
923                 if (ret != 0)
924                         goto out;
925
926                 if (compression_type_specified && compression_type != 
927                                 wimlib_get_compression_type(dest_w)) {
928                         imagex_error("Cannot specify a compression type that is "
929                                      "not the same as that used in the "
930                                      "destination WIM");
931                         ret = -1;
932                         goto out;
933                 }
934                 compression_type = wimlib_get_compression_type(dest_w);
935         } else {
936                 wim_is_new = true;
937                 /* dest_wimfile is not an existing file, so create a new WIM. */
938                 if (errno == ENOENT) {
939                         ret = wimlib_create_new_wim(compression_type, &dest_w);
940                         if (ret != 0)
941                                 goto out;
942                 } else {
943                         imagex_error_with_errno("Cannot stat file `%s'",
944                                                 dest_wimfile);
945                         goto out;
946                 }
947         }
948
949         image = wimlib_resolve_image(src_w, src_image_num_or_name);
950         ret = verify_image_exists(image);
951         if (ret != 0)
952                 goto out;
953
954         if (swm_glob) {
955                 ret = open_swms_from_glob(swm_glob, src_wimfile, open_flags,
956                                           &additional_swms,
957                                           &num_additional_swms);
958                 if (ret != 0)
959                         goto out;
960         }
961
962         ret = wimlib_export_image(src_w, image, dest_w, dest_name, dest_desc, 
963                                   export_flags, additional_swms,
964                                   num_additional_swms);
965         if (ret != 0)
966                 goto out;
967
968
969         if (wim_is_new)
970                 ret = wimlib_write(dest_w, dest_wimfile, WIM_ALL_IMAGES, 
971                                    write_flags);
972         else
973                 ret = wimlib_overwrite(dest_w, write_flags);
974 out:
975         wimlib_free(src_w);
976         wimlib_free(dest_w);
977         if (additional_swms)
978                 for (unsigned i = 0; i < num_additional_swms; i++)
979                         wimlib_free(additional_swms[i]);
980         return ret;
981 }
982
983 /* Prints information about a WIM file; also can mark an image as bootable,
984  * change the name of an image, or change the description of an image. */
985 static int imagex_info(int argc, const char **argv)
986 {
987         int c;
988         bool boot         = false;
989         bool check        = false;
990         bool header       = false;
991         bool lookup_table = false;
992         bool xml          = false;
993         bool metadata     = false;
994         bool short_header = true;
995         const char *xml_out_file = NULL;
996         const char *wimfile;
997         const char *image_num_or_name = "all";
998         const char *new_name = NULL;
999         const char *new_desc = NULL;
1000         WIMStruct *w;
1001         FILE *fp;
1002         int image;
1003         int ret;
1004         int open_flags = WIMLIB_OPEN_FLAG_SHOW_PROGRESS | 
1005                          WIMLIB_OPEN_FLAG_SPLIT_OK;
1006         int part_number;
1007         int total_parts;
1008
1009         for_opt(c, info_options) {
1010                 switch (c) {
1011                 case 'b':
1012                         boot = true;
1013                         break;
1014                 case 'c':
1015                         check = true;
1016                         break;
1017                 case 'h':
1018                         header = true;
1019                         short_header = false;
1020                         break;
1021                 case 'l':
1022                         lookup_table = true;
1023                         short_header = false;
1024                         break;
1025                 case 'x':
1026                         xml = true;
1027                         short_header = false;
1028                         break;
1029                 case 'X':
1030                         xml_out_file = optarg;
1031                         short_header = false;
1032                         break;
1033                 case 'm':
1034                         metadata = true;
1035                         short_header = false;
1036                         break;
1037                 default:
1038                         usage(INFO);
1039                         return -1;
1040                 }
1041         }
1042
1043         argc -= optind;
1044         argv += optind;
1045         if (argc == 0 || argc > 4) {
1046                 usage(INFO);
1047                 return -1;
1048         }
1049         wimfile = argv[0];
1050         if (argc > 1) {
1051                 image_num_or_name = argv[1];
1052                 if (argc > 2) {
1053                         new_name = argv[2];
1054                         if (argc > 3) {
1055                                 new_desc = argv[3];
1056                         } 
1057                 }
1058         }
1059
1060         if (check)
1061                 open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
1062
1063         ret = wimlib_open_wim(wimfile, open_flags, &w);
1064         if (ret != 0)
1065                 return ret;
1066
1067         part_number = wimlib_get_part_number(w, &total_parts);
1068
1069         /*if (total_parts > 1 && part_number > 1) {*/
1070                 /*printf("Warning: this is part %d of a %d-part split WIM.\n"*/
1071                        /*"         Select the first part if you want to see information\n"*/
1072                        /*"         about images in the WIM.\n", */
1073                        /*part_number, total_parts);*/
1074         /*}*/
1075
1076         image = wimlib_resolve_image(w, image_num_or_name);
1077         if (image == WIM_NO_IMAGE && strcmp(image_num_or_name, "0") != 0) {
1078                 imagex_error("The image `%s' does not exist", 
1079                                                 image_num_or_name);
1080                 if (boot)
1081                         imagex_error("If you would like to set the boot "
1082                                      "index to 0, specify image \"0\" with "
1083                                      "the --boot flag.");
1084                 ret = WIMLIB_ERR_INVALID_IMAGE;
1085                 goto done;
1086         }
1087
1088         if (image == WIM_ALL_IMAGES && wimlib_get_num_images(w) > 1) {
1089                 if (boot) {
1090                         imagex_error("Cannot specify the --boot flag "
1091                                      "without specifying a specific "
1092                                      "image in a multi-image WIM");
1093                         ret = WIMLIB_ERR_INVALID_IMAGE;
1094                         goto done;
1095                 }
1096                 if (new_name) {
1097                         imagex_error("Cannot specify the NEW_NAME "
1098                                      "without specifying a specific "
1099                                      "image in a multi-image WIM");
1100                         ret = WIMLIB_ERR_INVALID_IMAGE;
1101                         goto done;
1102                 }
1103         }
1104
1105         /* Operations that print information are separated from operations that
1106          * recreate the WIM file. */
1107         if (!new_name && !boot) {
1108
1109                 if (image == WIM_NO_IMAGE) {
1110                         imagex_error("`%s' is not a valid image",
1111                                      image_num_or_name);
1112                         ret = WIMLIB_ERR_INVALID_IMAGE;
1113                         goto done;
1114                 }
1115
1116                 if (image == WIM_ALL_IMAGES && short_header)
1117                         wimlib_print_wim_information(w);
1118
1119                 if (header)
1120                         wimlib_print_header(w);
1121
1122                 if (lookup_table) {
1123                         if (total_parts != 1) {
1124                                 printf("Warning: Only showing the lookup table "
1125                                        "for part %d of a %d-part WIM.\n",
1126                                        part_number, total_parts);
1127                         }
1128                         wimlib_print_lookup_table(w);
1129                 }
1130
1131                 if (xml) {
1132                         ret = wimlib_extract_xml_data(w, stdout);
1133                         if (ret != 0)
1134                                 goto done;
1135                 }
1136
1137                 if (xml_out_file) {
1138                         fp = fopen(xml_out_file, "wb");
1139                         if (!fp) {
1140                                 imagex_error_with_errno("Failed to open the "
1141                                                         "file `%s' for "
1142                                                         "writing ",
1143                                                         xml_out_file);
1144                                 goto done;
1145                         }
1146                         ret = wimlib_extract_xml_data(w, fp);
1147                         if (fclose(fp) != 0) {
1148                                 imagex_error("Failed to close the file `%s'",
1149                                              xml_out_file);
1150                                 goto done;
1151                         }
1152
1153                         if (ret != 0)
1154                                 goto done;
1155                 }
1156
1157                 if (short_header)
1158                         wimlib_print_available_images(w, image);
1159
1160                 if (metadata) {
1161                         if (total_parts != 1 && part_number != 1) {
1162                                 imagex_error("Select part 1 of this %d-part WIM "
1163                                              "to see the image metadata",
1164                                              total_parts);
1165                                 return WIMLIB_ERR_SPLIT_UNSUPPORTED;
1166                         }
1167                         ret = wimlib_print_metadata(w, image);
1168                         if (ret != 0)
1169                                 goto done;
1170                 }
1171         } else {
1172                 if (total_parts != 1) {
1173                         imagex_error("Modifying a split WIM is not supported.");
1174                         return -1;
1175                 }
1176                 if (image == WIM_ALL_IMAGES)
1177                         image = 1;
1178
1179                 if (image == WIM_NO_IMAGE && new_name) {
1180                         imagex_error("Cannot specify new_name (`%s') when "
1181                                      "using image 0", new_name);
1182                         return -1;
1183                 }
1184
1185                 if (boot) {
1186                         if (image == wimlib_get_boot_idx(w)) {
1187                                 printf("Image %d is already marked as "
1188                                        "bootable.\n", image);
1189                                 boot = false;
1190                         } else {
1191                                 printf("Marking image %d as bootable.\n",
1192                                        image);
1193                                 wimlib_set_boot_idx(w, image);
1194                         }
1195                 }
1196                 if (new_name) {
1197                         if (strcmp(wimlib_get_image_name(w, image), 
1198                                                 new_name) == 0) {
1199                                 printf("Image %d is already named \"%s\".\n",
1200                                        image, new_name);
1201                                 new_name = NULL;
1202                         } else {
1203                                 printf("Changing the name of image %d to "
1204                                        "\"%s\".\n", image, new_name);
1205                                 ret = wimlib_set_image_name(w, image, new_name);
1206                                 if (ret != 0)
1207                                         goto done;
1208                         }
1209                 }
1210                 if (new_desc) {
1211                         const char *old_desc;
1212                         old_desc = wimlib_get_image_description(w, image);
1213                         if (old_desc && strcmp(old_desc, new_desc) == 0) {
1214                                 printf("The description of image %d is already "
1215                                        "\"%s\".\n", image, new_desc);
1216                                 new_desc = NULL;
1217                         } else {
1218                                 printf("Changing the description of image %d "
1219                                        "to \"%s\".\n", image, new_desc);
1220                                 ret = wimlib_set_image_descripton(w, image, 
1221                                                                   new_desc);
1222                                 if (ret != 0)
1223                                         goto done;
1224                         }
1225                 }
1226
1227                 /* Only call wimlib_overwrite_xml_and_header() if something
1228                  * actually needs to be changed. */
1229                 if (boot || new_name || new_desc || 
1230                                 check != wimlib_has_integrity_table(w)) {
1231
1232                         ret = wimlib_overwrite_xml_and_header(w, check ? 
1233                                         WIMLIB_WRITE_FLAG_CHECK_INTEGRITY | 
1234                                         WIMLIB_WRITE_FLAG_SHOW_PROGRESS : 0);
1235                 } else {
1236                         printf("The file `%s' was not modified because nothing "
1237                                         "needed to be done.\n", wimfile);
1238                         ret = 0;
1239                 }
1240         }
1241
1242 done:
1243         wimlib_free(w);
1244         return ret;
1245 }
1246
1247 /* Join split WIMs into one part WIM */
1248 static int imagex_join(int argc, const char **argv)
1249 {
1250         int c;
1251         int flags = WIMLIB_OPEN_FLAG_SPLIT_OK | WIMLIB_OPEN_FLAG_SHOW_PROGRESS;
1252         const char *output_path;
1253
1254         for_opt(c, join_options) {
1255                 switch (c) {
1256                 case 'c':
1257                         flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
1258                         break;
1259                 default:
1260                         goto err;
1261                 }
1262         }
1263         argc -= optind;
1264         argv += optind;
1265
1266         if (argc < 2) {
1267                 imagex_error("Must specify at least one split WIM (.swm) parts "
1268                              "to join");
1269                 goto err;
1270         }
1271         output_path = argv[0];
1272         return wimlib_join(++argv, --argc, output_path, flags);
1273 err:
1274         usage(JOIN);
1275         return -1;
1276 }
1277
1278 /* Mounts an image using a FUSE mount. */
1279 static int imagex_mount_rw_or_ro(int argc, const char **argv)
1280 {
1281         int c;
1282         int mount_flags = 0;
1283         int open_flags = WIMLIB_OPEN_FLAG_SHOW_PROGRESS |
1284                          WIMLIB_OPEN_FLAG_SPLIT_OK;
1285         const char *wimfile;
1286         const char *dir;
1287         WIMStruct *w;
1288         int image;
1289         int num_images;
1290         int ret;
1291         const char *swm_glob = NULL;
1292         WIMStruct **additional_swms = NULL;
1293         unsigned num_additional_swms = 0;
1294
1295         if (strcmp(argv[0], "mountrw") == 0)
1296                 mount_flags |= WIMLIB_MOUNT_FLAG_READWRITE;
1297         for_opt(c, mount_options) {
1298                 switch (c) {
1299                 case 'c':
1300                         open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
1301                         break;
1302                 case 'd':
1303                         mount_flags |= WIMLIB_MOUNT_FLAG_DEBUG;
1304                         break;
1305                 case 's':
1306                         if (strcasecmp(optarg, "none") == 0)
1307                                 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_NONE;
1308                         else if (strcasecmp(optarg, "xattr") == 0)
1309                                 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR;
1310                         else if (strcasecmp(optarg, "windows") == 0)
1311                                 mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_WINDOWS;
1312                         else {
1313                                 imagex_error("Unknown stream interface \"%s\"", optarg);
1314                                 goto mount_usage;
1315                         }
1316                         break;
1317                 case 'r':
1318                         swm_glob = optarg;
1319                         break;
1320                 default:
1321                         goto mount_usage;
1322                 }
1323         }
1324         argc -= optind;
1325         argv += optind;
1326         if (argc != 2 && argc != 3)
1327                 goto mount_usage;
1328
1329         wimfile = argv[0];
1330
1331         ret = wimlib_open_wim(wimfile, open_flags, &w);
1332         if (ret != 0)
1333                 return ret;
1334
1335         if (swm_glob) {
1336                 ret = open_swms_from_glob(swm_glob, wimfile, open_flags,
1337                                           &additional_swms,
1338                                           &num_additional_swms);
1339                 if (ret != 0)
1340                         goto out;
1341         }
1342
1343         if (argc == 2) {
1344                 image = 1;
1345                 num_images = wimlib_get_num_images(w);
1346                 if (num_images != 1) {
1347                         imagex_error("The file `%s' contains %d images; Please "
1348                                      "select one", wimfile, num_images);
1349                         usage((mount_flags & WIMLIB_MOUNT_FLAG_READWRITE)  
1350                                         ? MOUNTRW : MOUNT);
1351                         ret = WIMLIB_ERR_INVALID_IMAGE;
1352                         goto out;
1353                 }
1354                 dir = argv[1];
1355         } else {
1356                 image = wimlib_resolve_image(w, argv[1]);
1357                 dir = argv[2];
1358         }
1359
1360         ret = verify_image_exists_and_is_single(image);
1361         if (ret != 0)
1362                 goto out;
1363
1364         ret = wimlib_mount(w, image, dir, mount_flags, additional_swms,
1365                            num_additional_swms);
1366         if (ret != 0) {
1367                 imagex_error("Failed to mount image %d from `%s' on `%s'",
1368                              image, wimfile, dir);
1369
1370         }
1371 out:
1372         wimlib_free(w);
1373         if (additional_swms)
1374                 for (unsigned i = 0; i < num_additional_swms; i++)
1375                         wimlib_free(additional_swms[i]);
1376         return ret;
1377 mount_usage:
1378         usage((mount_flags & WIMLIB_MOUNT_FLAG_READWRITE)  
1379                         ? MOUNTRW : MOUNT);
1380         return -1;
1381 }
1382
1383 /* Split a WIM into a spanned set */
1384 static int imagex_split(int argc, const char **argv)
1385 {
1386         int c;
1387         int flags = WIMLIB_OPEN_FLAG_SHOW_PROGRESS;
1388         unsigned long part_size;
1389
1390         for_opt(c, split_options) {
1391                 switch (c) {
1392                 case 'c':
1393                         flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
1394                         break;
1395                 default:
1396                         usage(SPLIT);
1397                         return -1;
1398                 }
1399         }
1400         argc -= optind;
1401         argv += optind;
1402
1403         if (argc != 3) {
1404                 usage(SPLIT);
1405                 return -1;
1406         }
1407         part_size = strtoul(argv[2], NULL, 10) * (1 << 20);
1408         return wimlib_split(argv[0], argv[1], part_size, flags);
1409 }
1410
1411 /* Unmounts an image. */
1412 static int imagex_unmount(int argc, const char **argv)
1413 {
1414         int c;
1415         int unmount_flags = 0;
1416         int ret;
1417
1418         for_opt(c, unmount_options) {
1419                 switch (c) {
1420                 case 'c':
1421                         unmount_flags |= WIMLIB_UNMOUNT_FLAG_COMMIT;
1422                         break;
1423                 case 'C':
1424                         unmount_flags |= WIMLIB_UNMOUNT_FLAG_CHECK_INTEGRITY;
1425                         break;
1426                 default:
1427                         usage(UNMOUNT);
1428                         return -1;
1429                 }
1430         }
1431         argc -= optind;
1432         argv += optind;
1433         if (argc != 1) {
1434                 usage(UNMOUNT);
1435                 return -1;
1436         }
1437
1438         ret = wimlib_unmount(argv[0], unmount_flags);
1439         if (ret != 0)
1440                 imagex_error("Failed to unmount `%s'", argv[0]);
1441         return ret;
1442 }
1443
1444 struct imagex_command {
1445         const char *name;
1446         int (*func)(int , const char **);
1447         int cmd;
1448 };
1449
1450
1451 #define for_imagex_command(p) for (p = &imagex_commands[0]; \
1452                 p != &imagex_commands[ARRAY_LEN(imagex_commands)]; p++)
1453
1454 static struct imagex_command imagex_commands[] = {
1455         {"append",  imagex_append,         APPEND},
1456         {"apply",   imagex_apply,          APPLY},
1457         {"capture", imagex_capture,        CAPTURE},
1458         {"delete",  imagex_delete,         DELETE},
1459         {"dir",     imagex_dir,            DIR},
1460         {"export",  imagex_export,         EXPORT},
1461         {"info",    imagex_info,           INFO},
1462         {"join",    imagex_join,           JOIN},
1463         {"mount",   imagex_mount_rw_or_ro, MOUNT},
1464         {"mountrw", imagex_mount_rw_or_ro, MOUNTRW},
1465         {"split",   imagex_split,          SPLIT},
1466         {"unmount", imagex_unmount,        UNMOUNT},
1467 };
1468
1469 static void version()
1470 {
1471         static const char *s = 
1472         "imagex (" PACKAGE ") " PACKAGE_VERSION "\n"
1473         "Copyright (C) 2012 Eric Biggers\n"
1474         "License GPLv3+; GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.\n"
1475         "This is free software: you are free to change and redistribute it.\n"
1476         "There is NO WARRANTY, to the extent permitted by law.\n"
1477         "\n"
1478         "Report bugs to "PACKAGE_BUGREPORT".\n";
1479         fputs(s, stdout);
1480 }
1481
1482
1483 static void help_or_version(int argc, const char **argv)
1484 {
1485         int i;
1486         const char *p;
1487         struct imagex_command *cmd;
1488
1489         for (i = 1; i < argc; i++) {
1490                 p = argv[i];
1491                 if (*p == '-')
1492                         p++;
1493                 else
1494                         continue;
1495                 if (*p == '-')
1496                         p++;
1497                 if (strcmp(p, "help") == 0 || (*p == '?' && *(p + 1) == '\0')) {
1498                         for_imagex_command(cmd) {
1499                                 if (strcmp(cmd->name, argv[1]) == 0) {
1500                                         usage(cmd->cmd);
1501                                         exit(0);
1502                                 }
1503                         }
1504                         usage_all();
1505                         exit(0);
1506                 }
1507                 if (strcmp(p, "version") == 0) {
1508                         version();
1509                         exit(0);
1510                 }
1511         }
1512 }
1513
1514
1515 static void usage(int cmd_type)
1516 {
1517         struct imagex_command *cmd;
1518         puts("IMAGEX: Usage:");
1519         fputs(usage_strings[cmd_type], stdout);
1520         for_imagex_command(cmd)
1521                 if (cmd->cmd == cmd_type)
1522                         printf("\nTry `man imagex-%s' for more details.\n",
1523                                cmd->name);
1524 }
1525
1526 static void usage_all()
1527 {
1528         puts("IMAGEX: Usage:");
1529         for (int i = 0; i < ARRAY_LEN(usage_strings); i++)
1530                 fputs(usage_strings[i], stdout);
1531         static const char *extra = 
1532 "    imagex --help\n"
1533 "    imagex --version\n"
1534 "\n"
1535 "    The compression TYPE may be \"maximum\", \"fast\", or \"none\".\n"
1536 "\n"
1537 "    Try `man imagex' for more information.\n"
1538         ;
1539         fputs(extra, stdout);
1540 }
1541
1542
1543 int main(int argc, const char **argv)
1544 {
1545         struct imagex_command *cmd;
1546         int ret;
1547
1548         if (argc < 2) {
1549                 imagex_error("No command specified");
1550                 usage_all();
1551                 return 1;
1552         }
1553
1554         help_or_version(argc, argv);
1555         argc--;
1556         argv++;
1557
1558         wimlib_set_print_errors(true);
1559
1560         for_imagex_command(cmd) {
1561                 if (strcmp(cmd->name, *argv) == 0) {
1562                         ret = cmd->func(argc, argv);
1563                         if (ret > 0) {
1564                                 imagex_error("Exiting with error code %d:\n"
1565                                              "       %s.", ret,
1566                                              wimlib_get_error_string(ret));
1567                                 if (ret == WIMLIB_ERR_NTFS_3G)
1568                                         imagex_error_with_errno("errno");
1569                         }
1570                         return ret;
1571                 }
1572         }
1573
1574         imagex_error("Unrecognized command: `%s'", argv[0]);
1575         usage_all();
1576         return 1;
1577 }
1578 /*#ifndef WITH_NTFS_3G*/
1579                 /*ERROR("wimlib was not compiled with support for NTFS-3g, so we cannot extract");*/
1580                 /*ERROR("a WIM to a NTFS filesystem while preserving NTFS-specific metadata.");*/
1581                 /*ERROR("Please apply the WIM to a directory rather than a block device, ");*/
1582                 /*ERROR("and without the NTFS flag; or compile in support for NTFS-3g.");*/
1583                 /*return WIMLIB_ERR_UNSUPPORTED;*/
1584 /*#endif*/