]> icculus.org git repositories - icculus/xz.git/blob - src/lzmadec/lzmadec.c
Renamed constants:
[icculus/xz.git] / src / lzmadec / lzmadec.c
1 ///////////////////////////////////////////////////////////////////////////////
2 //
3 /// \file       lzmadec.c
4 /// \brief      Simple single-threaded tool to uncompress .lzma files
5 //
6 //  Copyright (C) 2007 Lasse Collin
7 //
8 //  This program is free software; you can redistribute it and/or
9 //  modify it under the terms of the GNU Lesser General Public
10 //  License as published by the Free Software Foundation; either
11 //  version 2.1 of the License, or (at your option) any later version.
12 //
13 //  This program is distributed in the hope that it will be useful,
14 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
15 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 //  Lesser General Public License for more details.
17 //
18 ///////////////////////////////////////////////////////////////////////////////
19
20 #include "sysdefs.h"
21
22 #ifdef HAVE_ERRNO_H
23 #       include <errno.h>
24 #else
25 extern int errno;
26 #endif
27
28 #include <stdio.h>
29 #include <unistd.h>
30
31 #ifdef WIN32
32 #       include <fcntl.h>
33 #endif
34
35 #include "getopt.h"
36 #include "physmem.h"
37
38
39 enum return_code {
40         SUCCESS,
41         ERROR,
42         WARNING,
43 };
44
45
46 enum format_type {
47         FORMAT_AUTO,
48         FORMAT_NATIVE,
49         FORMAT_ALONE,
50 };
51
52
53 enum {
54         OPTION_FORMAT = INT_MIN,
55 };
56
57
58 /// Input buffer
59 static uint8_t in_buf[BUFSIZ];
60
61 /// Output buffer
62 static uint8_t out_buf[BUFSIZ];
63
64 /// Decoder
65 static lzma_stream strm = LZMA_STREAM_INIT;
66
67 /// Number of bytes to use memory at maximum
68 static size_t memlimit;
69
70 /// Program name to be shown in error messages
71 static const char *argv0;
72
73 /// File currently being processed
74 static FILE *file;
75
76 /// Name of the file currently being processed
77 static const char *filename;
78
79 static enum return_code exit_status = SUCCESS;
80
81 static enum format_type format_type = FORMAT_AUTO;
82
83 static bool force = false;
84
85
86 static void lzma_attribute((noreturn))
87 help(void)
88 {
89         printf(
90 "Usage: %s [OPTION]... [FILE]...\n"
91 "Uncompress files in the .lzma format to the standard output.\n"
92 "\n"
93 "  -c, --stdout       (ignored)\n"
94 "  -d, --decompress   (ignored)\n"
95 "  -k, --keep         (ignored)\n"
96 "  -f, --force        allow reading compressed data from a terminal\n"
97 "  -M, --memory=NUM   use NUM bytes of memory at maximum; the suffixes\n"
98 "                     k, M, G, Ki, Mi, and Gi are supported.\n"
99 "      --format=FMT   accept only files in the given file format;\n"
100 "                     possible FMTs are `auto', `native', and alone',\n"
101 "  -h, --help         display this help and exit\n"
102 "  -V, --version      display version and license information and exit\n"
103 "\n"
104 "With no FILE, or when FILE is -, read standard input.\n"
105 "\n"
106 "On this configuration, the tool will use about %" PRIu64
107                 " MiB of memory at maximum.\n"
108 "\n"
109 "Report bugs to <" PACKAGE_BUGREPORT "> (in English or Finnish).\n",
110                 argv0, ((uint64_t)(memlimit) + 512 * 1024) / (1024 * 1024));
111                 // Using PRIu64 above instead of %zu to support pre-C99 libc.
112         exit(0);
113 }
114
115
116 static void lzma_attribute((noreturn))
117 version(void)
118 {
119         printf(
120 "lzmadec (LZMA Utils) " PACKAGE_VERSION "\n"
121 "\n"
122 "Copyright (C) 1999-2006 Igor Pavlov\n"
123 "Copyright (C) 2007 Lasse Collin\n"
124 "\n"
125 "This program is free software; you can redistribute it and/or\n"
126 "modify it under the terms of the GNU Lesser General Public\n"
127 "License as published by the Free Software Foundation; either\n"
128 "version 2.1 of the License, or (at your option) any later version.\n"
129 "\n"
130 "This program is distributed in the hope that it will be useful,\n"
131 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
132 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n"
133 "Lesser General Public License for more details.\n"
134 "\n");
135         exit(0);
136 }
137
138
139 /// Finds out the amount of physical memory in the system, and sets
140 /// a default memory usage limit.
141 static void
142 set_default_memlimit(void)
143 {
144         uint64_t mem = physmem();
145         if (mem != 0) {
146                 mem /= 3;
147
148 #if UINT64_MAX > SIZE_MAX
149                 if (mem > SIZE_MAX)
150                         mem = SIZE_MAX;
151 #endif
152
153                 memlimit = mem / 3;
154         } else {
155                 // Cannot autodetect, use 10 MiB as the default limit.
156                 memlimit = (1U << 23) + (1U << 21);
157         }
158
159         return;
160 }
161
162
163 /// \brief      Converts a string to size_t
164 ///
165 /// This is rudely copied from src/lzma/util.c and modified a little. :-(
166 ///
167 static size_t
168 str_to_size(const char *value)
169 {
170         size_t result = 0;
171
172         if (*value < '0' || *value > '9') {
173                 fprintf(stderr, "%s: %s: Not a number", argv0, value);
174                 exit(ERROR);
175         }
176
177         do {
178                 // Don't overflow.
179                 if (result > (SIZE_MAX - 9) / 10)
180                         return SIZE_MAX;
181
182                 result *= 10;
183                 result += *value - '0';
184                 ++value;
185         } while (*value >= '0' && *value <= '9');
186
187         if (*value != '\0') {
188                 // Look for suffix.
189                 static const struct {
190                         const char name[4];
191                         size_t multiplier;
192                 } suffixes[] = {
193                         { "k",   1000 },
194                         { "kB",  1000 },
195                         { "M",   1000000 },
196                         { "MB",  1000000 },
197                         { "G",   1000000000 },
198                         { "GB",  1000000000 },
199                         { "Ki",  1024 },
200                         { "KiB", 1024 },
201                         { "Mi",  1048576 },
202                         { "MiB", 1048576 },
203                         { "Gi",  1073741824 },
204                         { "GiB", 1073741824 }
205                 };
206
207                 size_t multiplier = 0;
208                 for (size_t i = 0; i < ARRAY_SIZE(suffixes); ++i) {
209                         if (strcmp(value, suffixes[i].name) == 0) {
210                                 multiplier = suffixes[i].multiplier;
211                                 break;
212                         }
213                 }
214
215                 if (multiplier == 0) {
216                         fprintf(stderr, "%s: %s: Invalid suffix",
217                                         argv0, value);
218                         exit(ERROR);
219                 }
220
221                 // Don't overflow here either.
222                 if (result > SIZE_MAX / multiplier)
223                         result = SIZE_MAX;
224                 else
225                         result *= multiplier;
226         }
227
228         return result;
229 }
230
231
232 /// Parses command line options.
233 static void
234 parse_options(int argc, char **argv)
235 {
236         static const char short_opts[] = "cdkfM:hV";
237         static const struct option long_opts[] = {
238                 { "stdout",       no_argument,         NULL, 'c' },
239                 { "to-stdout",    no_argument,         NULL, 'c' },
240                 { "decompress",   no_argument,         NULL, 'd' },
241                 { "uncompress",   no_argument,         NULL, 'd' },
242                 { "force",        no_argument,         NULL, 'f' },
243                 { "keep",         no_argument,         NULL, 'k' },
244                 { "memory",       required_argument,   NULL, 'M' },
245                 { "format",       required_argument,   NULL, OPTION_FORMAT },
246                 { "help",         no_argument,         NULL, 'h' },
247                 { "version",      no_argument,         NULL, 'V' },
248                 { NULL,           0,                   NULL, 0   }
249         };
250
251         int c;
252
253         while ((c = getopt_long(argc, argv, short_opts, long_opts, NULL))
254                         != -1) {
255                 switch (c) {
256                 case 'c':
257                 case 'd':
258                 case 'k':
259                         break;
260
261                 case 'f':
262                         force = true;
263                         break;
264
265                 case 'M':
266                         memlimit = str_to_size(optarg);
267                         break;
268
269                 case 'h':
270                         help();
271
272                 case 'V':
273                         version();
274
275                 case OPTION_FORMAT: {
276                         if (strcmp("auto", optarg) == 0) {
277                                 format_type = FORMAT_AUTO;
278                         } else if (strcmp("native", optarg) == 0) {
279                                 format_type = FORMAT_NATIVE;
280                         } else if (strcmp("alone", optarg) == 0) {
281                                 format_type = FORMAT_ALONE;
282                         } else {
283                                 fprintf(stderr, "%s: %s: Unknown file format "
284                                                 "name\n", argv0, optarg);
285                                 exit(ERROR);
286                         }
287                         break;
288                 }
289
290                 default:
291                         exit(ERROR);
292                 }
293         }
294
295         return;
296 }
297
298
299 /// Initializes lzma_stream structure for decoding of a new Stream.
300 static void
301 init(void)
302 {
303         const uint32_t flags = LZMA_TELL_UNSUPPORTED_CHECK | LZMA_CONCATENATED;
304         lzma_ret ret;
305
306         switch (format_type) {
307         case FORMAT_AUTO:
308                 ret = lzma_auto_decoder(&strm, memlimit, flags);
309                 break;
310
311         case FORMAT_NATIVE:
312                 ret = lzma_stream_decoder(&strm, memlimit, flags);
313                 break;
314
315         case FORMAT_ALONE:
316                 ret = lzma_alone_decoder(&strm, memlimit);
317                 break;
318
319         default:
320                 assert(0);
321                 ret = LZMA_PROG_ERROR;
322         }
323
324         if (ret != LZMA_OK) {
325                 fprintf(stderr, "%s: ", argv0);
326
327                 if (ret == LZMA_MEM_ERROR)
328                         fprintf(stderr, "%s\n", strerror(ENOMEM));
329                 else
330                         fprintf(stderr, "Internal program error (bug)\n");
331
332                 exit(ERROR);
333         }
334
335         return;
336 }
337
338
339 static void
340 uncompress(void)
341 {
342         if (file == stdin && !force && isatty(STDIN_FILENO)) {
343                 fprintf(stderr, "%s: Compressed data not read from "
344                                 "a terminal.\n%s: Use `-f' to force reading "
345                                 "from a terminal, or `-h' for help.\n",
346                                 argv0, argv0);
347                 exit(ERROR);
348         }
349
350         init();
351
352         strm.avail_in = 0;
353         strm.next_out = out_buf;
354         strm.avail_out = BUFSIZ;
355
356         lzma_action action = LZMA_RUN;
357
358         while (true) {
359                 if (strm.avail_in == 0) {
360                         strm.next_in = in_buf;
361                         strm.avail_in = fread(in_buf, 1, BUFSIZ, file);
362
363                         if (ferror(file)) {
364                                 // POSIX says that fread() sets errno if
365                                 // an error occurred. ferror() doesn't
366                                 // touch errno.
367                                 fprintf(stderr, "%s: %s: Error reading "
368                                                 "input file: %s\n",
369                                                 argv0, filename,
370                                                 strerror(errno));
371                                 exit(ERROR);
372                         }
373
374                         if (feof(file))
375                                 action = LZMA_FINISH;
376                 }
377
378                 const lzma_ret ret = lzma_code(&strm, action);
379
380                 // Write and check write error before checking decoder error.
381                 // This way as much data as possible gets written to output
382                 // even if decoder detected an error.
383                 if (strm.avail_out == 0 || ret != LZMA_OK) {
384                         const size_t write_size = BUFSIZ - strm.avail_out;
385
386                         if (fwrite(out_buf, 1, write_size, stdout)
387                                         != write_size) {
388                                 // Wouldn't be a surprise if writing to stderr
389                                 // would fail too but at least try to show an
390                                 // error message.
391                                 fprintf(stderr, "%s: Cannot write to "
392                                                 "standard output: %s\n", argv0,
393                                                 strerror(errno));
394                                 exit(ERROR);
395                         }
396
397                         strm.next_out = out_buf;
398                         strm.avail_out = BUFSIZ;
399                 }
400
401                 if (ret != LZMA_OK) {
402                         // FIXME !!! Doesn't work with LZMA_Alone for the
403                         // same reason as in process.c.
404                         if (ret == LZMA_STREAM_END)
405                                 return;
406
407                         fprintf(stderr, "%s: %s: ", argv0, filename);
408
409                         // FIXME Add LZMA_*_CHECK and LZMA_FORMAT_ERROR.
410                         switch (ret) {
411                         case LZMA_DATA_ERROR:
412                                 fprintf(stderr, "File is corrupt\n");
413                                 exit(ERROR);
414
415                         case LZMA_OPTIONS_ERROR:
416                                 fprintf(stderr, "Unsupported file "
417                                                 "format or filters\n");
418                                 exit(ERROR);
419
420                         case LZMA_MEM_ERROR:
421                                 fprintf(stderr, "%s\n", strerror(ENOMEM));
422                                 exit(ERROR);
423
424                         case LZMA_MEMLIMIT_ERROR:
425                                 fprintf(stderr, "Memory usage limit "
426                                                 "reached\n");
427                                 exit(ERROR);
428
429                         case LZMA_BUF_ERROR:
430                                 fprintf(stderr, "Unexpected end of input\n");
431                                 exit(ERROR);
432
433                         case LZMA_UNSUPPORTED_CHECK:
434                                 fprintf(stderr, "Unsupported type of "
435                                                 "integrity check; not "
436                                                 "verifying file integrity\n");
437                                 exit_status = WARNING;
438                                 break;
439
440                         case LZMA_PROG_ERROR:
441                         default:
442                                 fprintf(stderr, "Internal program "
443                                                 "error (bug)\n");
444                                 exit(ERROR);
445                         }
446                 }
447         }
448 }
449
450
451 int
452 main(int argc, char **argv)
453 {
454         argv0 = argv[0];
455
456         set_default_memlimit();
457
458         parse_options(argc, argv);
459
460         lzma_init_decoder();
461
462 #ifdef WIN32
463         setmode(fileno(stdin), O_BINARY);
464         setmode(fileno(stdout), O_BINARY);
465 #endif
466
467         if (optind == argc) {
468                 file = stdin;
469                 filename = "(stdin)";
470                 uncompress();
471         } else {
472                 do {
473                         if (strcmp(argv[optind], "-") == 0) {
474                                 file = stdin;
475                                 filename = "(stdin)";
476                                 uncompress();
477                         } else {
478                                 filename = argv[optind];
479                                 file = fopen(filename, "rb");
480                                 if (file == NULL) {
481                                         fprintf(stderr, "%s: %s: %s\n",
482                                                         argv0, filename,
483                                                         strerror(errno));
484                                         exit(ERROR);
485                                 }
486
487                                 uncompress();
488                                 fclose(file);
489                         }
490                 } while (++optind < argc);
491         }
492
493 #ifndef NDEBUG
494         // Free the memory only when debugging. Freeing wastes some time,
495         // but allows detecting possible memory leaks with Valgrind.
496         lzma_end(&strm);
497 #endif
498
499         return exit_status;
500 }