]> icculus.org git repositories - icculus/xz.git/blob - src/xz/args.c
Remove the --info option, which was an alias for --list.
[icculus/xz.git] / src / xz / args.c
1 ///////////////////////////////////////////////////////////////////////////////
2 //
3 /// \file       args.c
4 /// \brief      Argument parsing
5 ///
6 /// \note       Filter-specific options parsing is in options.c.
7 //
8 //  Author:     Lasse Collin
9 //
10 //  This file has been put into the public domain.
11 //  You can do whatever you want with this file.
12 //
13 ///////////////////////////////////////////////////////////////////////////////
14
15 #include "private.h"
16
17 #include "getopt.h"
18 #include <ctype.h>
19
20
21 bool opt_stdout = false;
22 bool opt_force = false;
23 bool opt_keep_original = false;
24
25 // We don't modify or free() this, but we need to assign it in some
26 // non-const pointers.
27 const char *stdin_filename = "(stdin)";
28
29
30 static void
31 parse_real(args_info *args, int argc, char **argv)
32 {
33         enum {
34                 OPT_SUBBLOCK = INT_MIN,
35                 OPT_X86,
36                 OPT_POWERPC,
37                 OPT_IA64,
38                 OPT_ARM,
39                 OPT_ARMTHUMB,
40                 OPT_SPARC,
41                 OPT_DELTA,
42                 OPT_LZMA1,
43                 OPT_LZMA2,
44
45                 OPT_FILES,
46                 OPT_FILES0,
47         };
48
49         static const char short_opts[] = "cC:defF:hHlkM:qrS:tT:vVz0123456789";
50
51         static const struct option long_opts[] = {
52                 // Operation mode
53                 { "compress",       no_argument,       NULL,  'z' },
54                 { "decompress",     no_argument,       NULL,  'd' },
55                 { "uncompress",     no_argument,       NULL,  'd' },
56                 { "test",           no_argument,       NULL,  't' },
57                 { "list",           no_argument,       NULL,  'l' },
58
59                 // Operation modifiers
60                 { "keep",           no_argument,       NULL,  'k' },
61                 { "force",          no_argument,       NULL,  'f' },
62                 { "stdout",         no_argument,       NULL,  'c' },
63                 { "to-stdout",      no_argument,       NULL,  'c' },
64                 { "suffix",         required_argument, NULL,  'S' },
65                 // { "recursive",      no_argument,       NULL,  'r' }, // TODO
66                 { "files",          optional_argument, NULL,  OPT_FILES },
67                 { "files0",         optional_argument, NULL,  OPT_FILES0 },
68
69                 // Basic compression settings
70                 { "format",         required_argument, NULL,  'F' },
71                 { "check",          required_argument, NULL,  'C' },
72                 { "memory",         required_argument, NULL,  'M' },
73                 { "threads",        required_argument, NULL,  'T' },
74
75                 { "extreme",        no_argument,       NULL,  'e' },
76                 { "fast",           no_argument,       NULL,  '0' },
77                 { "best",           no_argument,       NULL,  '9' },
78
79                 // Filters
80                 { "lzma1",          optional_argument, NULL,  OPT_LZMA1 },
81                 { "lzma2",          optional_argument, NULL,  OPT_LZMA2 },
82                 { "x86",            no_argument,       NULL,  OPT_X86 },
83                 { "bcj",            no_argument,       NULL,  OPT_X86 },
84                 { "powerpc",        no_argument,       NULL,  OPT_POWERPC },
85                 { "ppc",            no_argument,       NULL,  OPT_POWERPC },
86                 { "ia64",           no_argument,       NULL,  OPT_IA64 },
87                 { "itanium",        no_argument,       NULL,  OPT_IA64 },
88                 { "arm",            no_argument,       NULL,  OPT_ARM },
89                 { "armthumb",       no_argument,       NULL,  OPT_ARMTHUMB },
90                 { "sparc",          no_argument,       NULL,  OPT_SPARC },
91                 { "delta",          optional_argument, NULL,  OPT_DELTA },
92                 { "subblock",       optional_argument, NULL,  OPT_SUBBLOCK },
93
94                 // Other options
95                 { "quiet",          no_argument,       NULL,  'q' },
96                 { "verbose",        no_argument,       NULL,  'v' },
97                 { "help",           no_argument,       NULL,  'h' },
98                 { "long-help",      no_argument,       NULL,  'H' },
99                 { "version",        no_argument,       NULL,  'V' },
100
101                 { NULL,                 0,                 NULL,   0 }
102         };
103
104         int c;
105
106         while ((c = getopt_long(argc, argv, short_opts, long_opts, NULL))
107                         != -1) {
108                 switch (c) {
109                 // Compression preset (also for decompression if --format=raw)
110                 case '0': case '1': case '2': case '3': case '4':
111                 case '5': case '6': case '7': case '8': case '9':
112                         coder_set_preset(c - '0');
113                         break;
114
115                 // --memory
116                 case 'M': {
117                         // Support specifying the limit as a percentage of
118                         // installed physical RAM.
119                         size_t len = strlen(optarg);
120                         if (len > 0 && optarg[len - 1] == '%') {
121                                 optarg[len - 1] = '\0';
122                                 hardware_memlimit_set_percentage(
123                                                 str_to_uint64(
124                                                 "memory%", optarg, 1, 100));
125                         } else {
126                                 // On 32-bit systems, SIZE_MAX would make more
127                                 // sense than UINT64_MAX. But use UINT64_MAX
128                                 // still so that scripts that assume > 4 GiB
129                                 // values don't break.
130                                 hardware_memlimit_set(str_to_uint64(
131                                                 "memory", optarg,
132                                                 0, UINT64_MAX));
133                         }
134
135                         break;
136                 }
137
138                 // --suffix
139                 case 'S':
140                         suffix_set(optarg);
141                         break;
142
143                 case 'T':
144                         hardware_threadlimit_set(str_to_uint64(
145                                         "threads", optarg, 0, UINT32_MAX));
146                         break;
147
148                 // --version
149                 case 'V':
150                         // This doesn't return.
151                         message_version();
152
153                 // --stdout
154                 case 'c':
155                         opt_stdout = true;
156                         break;
157
158                 // --decompress
159                 case 'd':
160                         opt_mode = MODE_DECOMPRESS;
161                         break;
162
163                 // --extreme
164                 case 'e':
165                         coder_set_extreme();
166                         break;
167
168                 // --force
169                 case 'f':
170                         opt_force = true;
171                         break;
172
173                 // --help
174                 case 'h':
175                         // This doesn't return.
176                         message_help(false);
177
178                 // --long-help
179                 case 'H':
180                         // This doesn't return.
181                         message_help(true);
182
183                 // --list
184                 case 'l':
185                         opt_mode = MODE_LIST;
186                         break;
187
188                 // --keep
189                 case 'k':
190                         opt_keep_original = true;
191                         break;
192
193                 // --quiet
194                 case 'q':
195                         message_verbosity_decrease();
196                         break;
197
198                 case 't':
199                         opt_mode = MODE_TEST;
200                         break;
201
202                 // --verbose
203                 case 'v':
204                         message_verbosity_increase();
205                         break;
206
207                 case 'z':
208                         opt_mode = MODE_COMPRESS;
209                         break;
210
211                 // Filter setup
212
213                 case OPT_SUBBLOCK:
214                         coder_add_filter(LZMA_FILTER_SUBBLOCK,
215                                         options_subblock(optarg));
216                         break;
217
218                 case OPT_X86:
219                         coder_add_filter(LZMA_FILTER_X86, NULL);
220                         break;
221
222                 case OPT_POWERPC:
223                         coder_add_filter(LZMA_FILTER_POWERPC, NULL);
224                         break;
225
226                 case OPT_IA64:
227                         coder_add_filter(LZMA_FILTER_IA64, NULL);
228                         break;
229
230                 case OPT_ARM:
231                         coder_add_filter(LZMA_FILTER_ARM, NULL);
232                         break;
233
234                 case OPT_ARMTHUMB:
235                         coder_add_filter(LZMA_FILTER_ARMTHUMB, NULL);
236                         break;
237
238                 case OPT_SPARC:
239                         coder_add_filter(LZMA_FILTER_SPARC, NULL);
240                         break;
241
242                 case OPT_DELTA:
243                         coder_add_filter(LZMA_FILTER_DELTA,
244                                         options_delta(optarg));
245                         break;
246
247                 case OPT_LZMA1:
248                         coder_add_filter(LZMA_FILTER_LZMA1,
249                                         options_lzma(optarg));
250                         break;
251
252                 case OPT_LZMA2:
253                         coder_add_filter(LZMA_FILTER_LZMA2,
254                                         options_lzma(optarg));
255                         break;
256
257                 // Other
258
259                 // --format
260                 case 'F': {
261                         // Just in case, support both "lzma" and "alone" since
262                         // the latter was used for forward compatibility in
263                         // LZMA Utils 4.32.x.
264                         static const struct {
265                                 char str[8];
266                                 enum format_type format;
267                         } types[] = {
268                                 { "auto",   FORMAT_AUTO },
269                                 { "xz",     FORMAT_XZ },
270                                 { "lzma",   FORMAT_LZMA },
271                                 { "alone",  FORMAT_LZMA },
272                                 // { "gzip",   FORMAT_GZIP },
273                                 // { "gz",     FORMAT_GZIP },
274                                 { "raw",    FORMAT_RAW },
275                         };
276
277                         size_t i = 0;
278                         while (strcmp(types[i].str, optarg) != 0)
279                                 if (++i == ARRAY_SIZE(types))
280                                         message_fatal(_("%s: Unknown file "
281                                                         "format type"),
282                                                         optarg);
283
284                         opt_format = types[i].format;
285                         break;
286                 }
287
288                 // --check
289                 case 'C': {
290                         static const struct {
291                                 char str[8];
292                                 lzma_check check;
293                         } types[] = {
294                                 { "none",   LZMA_CHECK_NONE },
295                                 { "crc32",  LZMA_CHECK_CRC32 },
296                                 { "crc64",  LZMA_CHECK_CRC64 },
297                                 { "sha256", LZMA_CHECK_SHA256 },
298                         };
299
300                         size_t i = 0;
301                         while (strcmp(types[i].str, optarg) != 0) {
302                                 if (++i == ARRAY_SIZE(types))
303                                         message_fatal(_("%s: Unsupported "
304                                                         "integrity "
305                                                         "check type"), optarg);
306                         }
307
308                         // Use a separate check in case we are using different
309                         // liblzma than what was used to compile us.
310                         if (!lzma_check_is_supported(types[i].check))
311                                 message_fatal(_("%s: Unsupported integrity "
312                                                 "check type"), optarg);
313
314                         coder_set_check(types[i].check);
315                         break;
316                 }
317
318                 case OPT_FILES:
319                         args->files_delim = '\n';
320
321                 // Fall through
322
323                 case OPT_FILES0:
324                         if (args->files_name != NULL)
325                                 message_fatal(_("Only one file can be "
326                                                 "specified with `--files'"
327                                                 "or `--files0'."));
328
329                         if (optarg == NULL) {
330                                 args->files_name = (char *)stdin_filename;
331                                 args->files_file = stdin;
332                         } else {
333                                 args->files_name = optarg;
334                                 args->files_file = fopen(optarg,
335                                                 c == OPT_FILES ? "r" : "rb");
336                                 if (args->files_file == NULL)
337                                         message_fatal("%s: %s", optarg,
338                                                         strerror(errno));
339                         }
340
341                         break;
342
343                 default:
344                         message_try_help();
345                         my_exit(E_ERROR);
346                 }
347         }
348
349         return;
350 }
351
352
353 static void
354 parse_environment(args_info *args, char *argv0)
355 {
356         char *env = getenv("XZ_OPT");
357         if (env == NULL)
358                 return;
359
360         // We modify the string, so make a copy of it.
361         env = xstrdup(env);
362
363         // Calculate the number of arguments in env. argc stats at one
364         // to include space for the program name.
365         int argc = 1;
366         bool prev_was_space = true;
367         for (size_t i = 0; env[i] != '\0'; ++i) {
368                 if (isspace(env[i])) {
369                         prev_was_space = true;
370                 } else if (prev_was_space) {
371                         prev_was_space = false;
372
373                         // Keep argc small enough to fit into a singed int
374                         // and to keep it usable for memory allocation.
375                         if (++argc == MIN(INT_MAX, SIZE_MAX / sizeof(char *)))
376                                 message_fatal(_("The environment variable "
377                                                 "XZ_OPT contains too many "
378                                                 "arguments"));
379                 }
380         }
381
382         // Allocate memory to hold pointers to the arguments. Add one to get
383         // space for the terminating NULL (if some systems happen to need it).
384         char **argv = xmalloc(((size_t)(argc) + 1) * sizeof(char *));
385         argv[0] = argv0;
386         argv[argc] = NULL;
387
388         // Go through the string again. Split the arguments using '\0'
389         // characters and add pointers to the resulting strings to argv.
390         argc = 1;
391         prev_was_space = true;
392         for (size_t i = 0; env[i] != '\0'; ++i) {
393                 if (isspace(env[i])) {
394                         prev_was_space = true;
395                         env[i] = '\0';
396                 } else if (prev_was_space) {
397                         prev_was_space = false;
398                         argv[argc++] = env + i;
399                 }
400         }
401
402         // Parse the argument list we got from the environment. All non-option
403         // arguments i.e. filenames are ignored.
404         parse_real(args, argc, argv);
405
406         // Reset the state of the getopt_long() so that we can parse the
407         // command line options too. There are two incompatible ways to
408         // do it.
409 #ifdef HAVE_OPTRESET
410         // BSD
411         optind = 1;
412         optreset = 1;
413 #else
414         // GNU, Solaris
415         optind = 0;
416 #endif
417
418         // We don't need the argument list from environment anymore.
419         free(argv);
420         free(env);
421
422         return;
423 }
424
425
426 extern void
427 args_parse(args_info *args, int argc, char **argv)
428 {
429         // Initialize those parts of *args that we need later.
430         args->files_name = NULL;
431         args->files_file = NULL;
432         args->files_delim = '\0';
433
434         // Check how we were called.
435         {
436 #ifdef DOSLIKE
437                 // We adjusted argv[0] in the beginning of main() so we don't
438                 // need to do anything here.
439                 const char *name = argv[0];
440 #else
441                 // Remove the leading path name, if any.
442                 const char *name = strrchr(argv[0], '/');
443                 if (name == NULL)
444                         name = argv[0];
445                 else
446                         ++name;
447 #endif
448
449                 // NOTE: It's possible that name[0] is now '\0' if argv[0]
450                 // is weird, but it doesn't matter here.
451
452                 // If the command name contains "lz",
453                 // it implies --format=lzma.
454                 if (strstr(name, "lz") != NULL)
455                         opt_format = FORMAT_LZMA;
456
457                 // Operation mode
458                 if (strstr(name, "cat") != NULL) {
459                         // Imply --decompress --stdout
460                         opt_mode = MODE_DECOMPRESS;
461                         opt_stdout = true;
462                 } else if (strstr(name, "un") != NULL) {
463                         // Imply --decompress
464                         opt_mode = MODE_DECOMPRESS;
465                 }
466         }
467
468         // First the flags from environment
469         parse_environment(args, argv[0]);
470
471         // Then from the command line
472         optind = 1;
473         parse_real(args, argc, argv);
474
475         // Never remove the source file when the destination is not on disk.
476         // In test mode the data is written nowhere, but setting opt_stdout
477         // will make the rest of the code behave well.
478         if (opt_stdout || opt_mode == MODE_TEST) {
479                 opt_keep_original = true;
480                 opt_stdout = true;
481         }
482
483         // When compressing, if no --format flag was used, or it
484         // was --format=auto, we compress to the .xz format.
485         if (opt_mode == MODE_COMPRESS && opt_format == FORMAT_AUTO)
486                 opt_format = FORMAT_XZ;
487
488         // Compression settings need to be validated (options themselves and
489         // their memory usage) when compressing to any file format. It has to
490         // be done also when uncompressing raw data, since for raw decoding
491         // the options given on the command line are used to know what kind
492         // of raw data we are supposed to decode.
493         if (opt_mode == MODE_COMPRESS || opt_format == FORMAT_RAW)
494                 coder_set_compression_settings();
495
496         // If no filenames are given, use stdin.
497         if (argv[optind] == NULL && args->files_name == NULL) {
498                 // We don't modify or free() the "-" constant. The caller
499                 // modifies this so don't make the struct itself const.
500                 static char *names_stdin[2] = { (char *)"-", NULL };
501                 args->arg_names = names_stdin;
502                 args->arg_count = 1;
503         } else {
504                 // We got at least one filename from the command line, or
505                 // --files or --files0 was specified.
506                 args->arg_names = argv + optind;
507                 args->arg_count = argc - optind;
508         }
509
510         return;
511 }