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