]> icculus.org git repositories - icculus/xz.git/blob - src/lzma/args.c
Renamed constants:
[icculus/xz.git] / src / lzma / 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 //  Copyright (C) 2007 Lasse Collin
9 //
10 //  This program is free software; you can redistribute it and/or
11 //  modify it under the terms of the GNU Lesser General Public
12 //  License as published by the Free Software Foundation; either
13 //  version 2.1 of the License, or (at your option) any later version.
14 //
15 //  This program is distributed in the hope that it will be useful,
16 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
17 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18 //  Lesser General Public License for more details.
19 //
20 ///////////////////////////////////////////////////////////////////////////////
21
22 #include "private.h"
23
24 #include "getopt.h"
25 #include <ctype.h>
26
27
28 enum tool_mode opt_mode = MODE_COMPRESS;
29 enum header_type opt_header = HEADER_AUTO;
30
31 char *opt_suffix = NULL;
32
33 char *opt_files_name = NULL;
34 char opt_files_split = '\0';
35 FILE *opt_files_file = NULL;
36
37 bool opt_stdout = false;
38 bool opt_force = false;
39 bool opt_keep_original = false;
40 bool opt_preserve_name = false;
41
42 lzma_check opt_check = LZMA_CHECK_CRC64;
43 lzma_filter opt_filters[8];
44
45 // We don't modify or free() this, but we need to assign it in some
46 // non-const pointers.
47 const char *stdin_filename = "(stdin)";
48
49 static size_t preset_number = 7 - 1;
50 static bool preset_default = true;
51 static size_t filter_count = 0;
52
53
54 enum {
55         OPT_SUBBLOCK = INT_MIN,
56         OPT_X86,
57         OPT_POWERPC,
58         OPT_IA64,
59         OPT_ARM,
60         OPT_ARMTHUMB,
61         OPT_SPARC,
62         OPT_DELTA,
63         OPT_LZMA1,
64         OPT_LZMA2,
65
66         OPT_FILES,
67         OPT_FILES0,
68 };
69
70
71 static const char short_opts[] = "cC:dfF:hlLkM:qrS:tT:vVz123456789";
72
73
74 static const struct option long_opts[] = {
75         // gzip-like options
76         { "fast",               no_argument,       NULL,  '1' },
77         { "best",               no_argument,       NULL,  '9' },
78         { "memory",             required_argument, NULL,  'M' },
79         { "name",               no_argument,       NULL,  'N' },
80         { "suffix",             required_argument, NULL,  'S' },
81         { "threads",            required_argument, NULL,  'T' },
82         { "version",            no_argument,       NULL,  'V' },
83         { "stdout",             no_argument,       NULL,  'c' },
84         { "to-stdout",          no_argument,       NULL,  'c' },
85         { "decompress",         no_argument,       NULL,  'd' },
86         { "uncompress",         no_argument,       NULL,  'd' },
87         { "force",              no_argument,       NULL,  'f' },
88         { "help",               no_argument,       NULL,  'h' },
89         { "list",               no_argument,       NULL,  'l' },
90         { "info",               no_argument,       NULL,  'l' },
91         { "keep",               no_argument,       NULL,  'k' },
92         { "no-name",            no_argument,       NULL,  'n' },
93         { "quiet",              no_argument,       NULL,  'q' },
94 //      { "recursive",          no_argument,       NULL,  'r' }, // TODO
95         { "test",               no_argument,       NULL,  't' },
96         { "verbose",            no_argument,       NULL,  'v' },
97         { "compress",           no_argument,       NULL,  'z' },
98
99         // Filters
100         { "subblock",           optional_argument, NULL,   OPT_SUBBLOCK },
101         { "x86",                no_argument,       NULL,   OPT_X86 },
102         { "bcj",                no_argument,       NULL,   OPT_X86 },
103         { "powerpc",            no_argument,       NULL,   OPT_POWERPC },
104         { "ppc",                no_argument,       NULL,   OPT_POWERPC },
105         { "ia64",               no_argument,       NULL,   OPT_IA64 },
106         { "itanium",            no_argument,       NULL,   OPT_IA64 },
107         { "arm",                no_argument,       NULL,   OPT_ARM },
108         { "armthumb",           no_argument,       NULL,   OPT_ARMTHUMB },
109         { "sparc",              no_argument,       NULL,   OPT_SPARC },
110         { "delta",              optional_argument, NULL,   OPT_DELTA },
111         { "lzma1",              optional_argument, NULL,   OPT_LZMA1 },
112         { "lzma2",              optional_argument, NULL,   OPT_LZMA2 },
113
114         // Other
115         { "format",             required_argument, NULL,   'F' },
116         { "check",              required_argument, NULL,   'C' },
117         { "files",              optional_argument, NULL,   OPT_FILES },
118         { "files0",             optional_argument, NULL,   OPT_FILES0 },
119
120         { NULL,                 0,                 NULL,   0 }
121 };
122
123
124 static void
125 add_filter(lzma_vli id, const char *opt_str)
126 {
127         if (filter_count == 7) {
128                 errmsg(V_ERROR, _("Maximum number of filters is seven"));
129                 my_exit(ERROR);
130         }
131
132         opt_filters[filter_count].id = id;
133
134         switch (id) {
135         case LZMA_FILTER_SUBBLOCK:
136                 opt_filters[filter_count].options
137                                 = parse_options_subblock(opt_str);
138                 break;
139
140         case LZMA_FILTER_DELTA:
141                 opt_filters[filter_count].options
142                                 = parse_options_delta(opt_str);
143                 break;
144
145         case LZMA_FILTER_LZMA:
146         case LZMA_FILTER_LZMA2:
147                 opt_filters[filter_count].options
148                                 = parse_options_lzma(opt_str);
149                 break;
150
151         default:
152                 assert(opt_str == NULL);
153                 opt_filters[filter_count].options = NULL;
154                 break;
155         }
156
157         ++filter_count;
158         preset_default = false;
159         return;
160 }
161
162
163 static void
164 parse_real(int argc, char **argv)
165 {
166         int c;
167
168         while ((c = getopt_long(argc, argv, short_opts, long_opts, NULL))
169                         != -1) {
170                 switch (c) {
171                 // gzip-like options
172
173                 case '1': case '2': case '3': case '4':
174                 case '5': case '6': case '7': case '8': case '9':
175                         preset_number = c - '1';
176                         preset_default = false;
177                         break;
178
179                 // --memory
180                 case 'M':
181                         opt_memory = str_to_uint64("memory", optarg,
182                                         1, SIZE_MAX);
183                         break;
184
185                 case 'N':
186                         opt_preserve_name = true;
187                         break;
188
189                 // --suffix
190                 case 'S':
191                         // Empty suffix and suffixes having a slash are
192                         // rejected. Such suffixes would break things later.
193                         if (optarg[0] == '\0' || strchr(optarg, '/') != NULL) {
194                                 errmsg(V_ERROR, _("%s: Invalid filename "
195                                                 "suffix"), optarg);
196                                 my_exit(ERROR);
197                         }
198
199                         free(opt_suffix);
200                         opt_suffix = xstrdup(optarg);
201                         break;
202
203                 case 'T':
204                         opt_threads = str_to_uint64("threads", optarg,
205                                         1, SIZE_MAX);
206                         break;
207
208                 // --version
209                 case 'V':
210                         // This doesn't return.
211                         show_version();
212
213                 // --stdout
214                 case 'c':
215                         opt_stdout = true;
216                         break;
217
218                 // --decompress
219                 case 'd':
220                         opt_mode = MODE_DECOMPRESS;
221                         break;
222
223                 // --force
224                 case 'f':
225                         opt_force = true;
226                         break;
227
228                 // --help
229                 case 'h':
230                         // This doesn't return.
231                         show_help();
232
233                 // --list
234                 case 'l':
235                         opt_mode = MODE_LIST;
236                         break;
237
238                 // --keep
239                 case 'k':
240                         opt_keep_original = true;
241                         break;
242
243                 case 'n':
244                         opt_preserve_name = false;
245                         break;
246
247                 // --quiet
248                 case 'q':
249                         if (verbosity > V_SILENT)
250                                 --verbosity;
251
252                         break;
253
254                 case 't':
255                         opt_mode = MODE_TEST;
256                         break;
257
258                 // --verbose
259                 case 'v':
260                         if (verbosity < V_DEBUG)
261                                 ++verbosity;
262
263                         break;
264
265                 case 'z':
266                         opt_mode = MODE_COMPRESS;
267                         break;
268
269                 // Filter setup
270
271                 case OPT_SUBBLOCK:
272                         add_filter(LZMA_FILTER_SUBBLOCK, optarg);
273                         break;
274
275                 case OPT_X86:
276                         add_filter(LZMA_FILTER_X86, NULL);
277                         break;
278
279                 case OPT_POWERPC:
280                         add_filter(LZMA_FILTER_POWERPC, NULL);
281                         break;
282
283                 case OPT_IA64:
284                         add_filter(LZMA_FILTER_IA64, NULL);
285                         break;
286
287                 case OPT_ARM:
288                         add_filter(LZMA_FILTER_ARM, NULL);
289                         break;
290
291                 case OPT_ARMTHUMB:
292                         add_filter(LZMA_FILTER_ARMTHUMB, NULL);
293                         break;
294
295                 case OPT_SPARC:
296                         add_filter(LZMA_FILTER_SPARC, NULL);
297                         break;
298
299                 case OPT_DELTA:
300                         add_filter(LZMA_FILTER_DELTA, optarg);
301                         break;
302
303                 case OPT_LZMA1:
304                         add_filter(LZMA_FILTER_LZMA, optarg);
305                         break;
306
307                 case OPT_LZMA2:
308                         add_filter(LZMA_FILTER_LZMA2, optarg);
309                         break;
310
311                 // Other
312
313                 // --format
314                 case 'F': {
315                         static const char *types[] = {
316                                 "auto",
317                                 "native",
318                                 "alone",
319                                 // "gzip",
320                                 "raw",
321                                 NULL
322                         };
323
324                         opt_header = 0;
325                         while (strcmp(types[opt_header], optarg) != 0) {
326                                 if (types[++opt_header] == NULL) {
327                                         errmsg(V_ERROR, _("%s: Unknown file "
328                                                         "format type"),
329                                                         optarg);
330                                         my_exit(ERROR);
331                                 }
332                         }
333
334                         break;
335                 }
336
337                 // --check
338                 case 'C': {
339                         static const struct {
340                                 const char *str;
341                                 unsigned int value;
342                         } types[] = {
343                                 { "none",   LZMA_CHECK_NONE },
344                                 { "crc32",  LZMA_CHECK_CRC32 },
345                                 { "crc64",  LZMA_CHECK_CRC64 },
346                                 { "sha256", LZMA_CHECK_SHA256 },
347                                 { NULL,     0 }
348                         };
349
350                         size_t i = 0;
351                         while (strcmp(types[i].str, optarg) != 0) {
352                                 if (types[++i].str == NULL) {
353                                         errmsg(V_ERROR, _("%s: Unknown "
354                                                         "integrity check "
355                                                         "type"), optarg);
356                                         my_exit(ERROR);
357                                 }
358                         }
359
360                         opt_check = types[i].value;
361                         break;
362                 }
363
364                 case OPT_FILES:
365                         opt_files_split = '\n';
366
367                 // Fall through
368
369                 case OPT_FILES0:
370                         if (opt_files_name != NULL) {
371                                 errmsg(V_ERROR, _("Only one file can be "
372                                                 "specified with `--files'"
373                                                 "or `--files0'."));
374                                 my_exit(ERROR);
375                         }
376
377                         if (optarg == NULL) {
378                                 opt_files_name = (char *)stdin_filename;
379                                 opt_files_file = stdin;
380                         } else {
381                                 opt_files_name = optarg;
382                                 opt_files_file = fopen(optarg,
383                                                 c == OPT_FILES ? "r" : "rb");
384                                 if (opt_files_file == NULL) {
385                                         errmsg(V_ERROR, "%s: %s", optarg,
386                                                         strerror(errno));
387                                         my_exit(ERROR);
388                                 }
389                         }
390
391                         break;
392
393                 default:
394                         show_try_help();
395                         my_exit(ERROR);
396                 }
397         }
398
399         return;
400 }
401
402
403 static void
404 parse_environment(void)
405 {
406         char *env = getenv("LZMA_OPT");
407         if (env == NULL)
408                 return;
409
410         env = xstrdup(env);
411
412         // Calculate the number of arguments in env.
413         unsigned int argc = 1;
414         bool prev_was_space = true;
415         for (size_t i = 0; env[i] != '\0'; ++i) {
416                 if (isspace(env[i])) {
417                         prev_was_space = true;
418                 } else if (prev_was_space) {
419                         prev_was_space = false;
420                         if (++argc > (unsigned int)(INT_MAX)) {
421                                 errmsg(V_ERROR, _("The environment variable "
422                                                 "LZMA_OPT contains too many "
423                                                 "arguments"));
424                                 my_exit(ERROR);
425                         }
426                 }
427         }
428
429         char **argv = xmalloc((argc + 1) * sizeof(char*));
430         argv[0] = argv0;
431         argv[argc] = NULL;
432
433         argc = 1;
434         prev_was_space = true;
435         for (size_t i = 0; env[i] != '\0'; ++i) {
436                 if (isspace(env[i])) {
437                         prev_was_space = true;
438                 } else if (prev_was_space) {
439                         prev_was_space = false;
440                         argv[argc++] = env + i;
441                 }
442         }
443
444         parse_real((int)(argc), argv);
445
446         free(env);
447
448         return;
449 }
450
451
452 static void
453 set_compression_settings(void)
454 {
455         if (filter_count == 0) {
456                 opt_filters[0].id = opt_header == HEADER_ALONE
457                                 ? LZMA_FILTER_LZMA : LZMA_FILTER_LZMA2;
458                 opt_filters[0].options = (lzma_options_lzma *)(
459                                 lzma_preset_lzma + preset_number);
460                 filter_count = 1;
461         }
462
463         // Terminate the filter options array.
464         opt_filters[filter_count].id = LZMA_VLI_UNKNOWN;
465
466         // If we are using the LZMA_Alone format, allow exactly one filter
467         // which has to be LZMA.
468         if (opt_header == HEADER_ALONE && (filter_count != 1
469                         || opt_filters[0].id != LZMA_FILTER_LZMA)) {
470                 errmsg(V_ERROR, _("With --format=alone only the LZMA filter "
471                                 "is supported"));
472                 my_exit(ERROR);
473         }
474
475         // If using --format=raw, we can be decoding.
476         uint64_t memory_usage = opt_mode == MODE_COMPRESS
477                         ? lzma_memusage_encoder(opt_filters)
478                         : lzma_memusage_decoder(opt_filters);
479
480         // Don't go over the memory limits when the default
481         // setting is used.
482         if (preset_default) {
483                 while (memory_usage > opt_memory) {
484                         if (preset_number == 0) {
485                                 errmsg(V_ERROR, _("Memory usage limit is too "
486                                                 "small for any internal "
487                                                 "filter preset"));
488                                 my_exit(ERROR);
489                         }
490
491                         --preset_number;
492                         opt_filters[0].options = (lzma_options_lzma *)(
493                                         lzma_preset_lzma
494                                         + preset_number);
495                         memory_usage = lzma_memusage_encoder(opt_filters);
496                 }
497         } else {
498                 if (memory_usage > opt_memory) {
499                         errmsg(V_ERROR, _("Memory usage limit is too small "
500                                         "for the given filter setup"));
501                         my_exit(ERROR);
502                 }
503         }
504
505         // Limit the number of worked threads so that memory usage
506         // limit isn't exceeded.
507         assert(memory_usage > 0);
508         size_t thread_limit = opt_memory / memory_usage;
509         if (thread_limit == 0)
510                 thread_limit = 1;
511
512         if (opt_threads > thread_limit)
513                 opt_threads = thread_limit;
514
515         return;
516 }
517
518
519 extern char **
520 parse_args(int argc, char **argv)
521 {
522         // Check how we were called.
523         {
524                 const char *name = str_filename(argv[0]);
525                 if (name != NULL) {
526                         if (strstr(name, "cat") != NULL) {
527                                 opt_mode = MODE_DECOMPRESS;
528                                 opt_stdout = true;
529                         } else if (strstr(name, "un") != NULL) {
530                                 opt_mode = MODE_DECOMPRESS;
531                         }
532                 }
533         }
534
535         // First the flags from environment
536         parse_environment();
537
538         // Then from the command line
539         optind = 1;
540         parse_real(argc, argv);
541
542         // Never remove the source file when the destination is not on disk.
543         // In test mode the data is written nowhere, but setting opt_stdout
544         // will make the rest of the code behave well.
545         if (opt_stdout || opt_mode == MODE_TEST) {
546                 opt_keep_original = true;
547                 opt_stdout = true;
548         }
549
550         if (opt_mode == MODE_COMPRESS || opt_header == HEADER_RAW)
551                 set_compression_settings();
552
553         // If no filenames are given, use stdin.
554         if (argv[optind] == NULL && opt_files_name == NULL) {
555                 // We don't modify or free() the "-" constant.
556                 static char *argv_stdin[2] = { (char *)"-", NULL };
557                 return argv_stdin;
558         }
559
560         return argv + optind;
561 }