]> icculus.org git repositories - icculus/xz.git/blob - src/xzdec/xzdec.c
add gitignore files
[icculus/xz.git] / src / xzdec / xzdec.c
1 ///////////////////////////////////////////////////////////////////////////////
2 //
3 /// \file       xzdec.c
4 /// \brief      Simple single-threaded tool to uncompress .xz or .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 #include "lzma.h"
22
23 #include <errno.h>
24 #include <stdio.h>
25 #include <unistd.h>
26
27 #ifdef _WIN32
28 #       include <fcntl.h>
29 #endif
30
31 #include "getopt.h"
32 #include "physmem.h"
33
34
35 #ifdef LZMADEC
36 #       define TOOL_FORMAT "lzma"
37 #else
38 #       define TOOL_FORMAT "xz"
39 #endif
40
41
42 /// Number of bytes to use memory at maximum
43 static uint64_t memlimit;
44
45 /// Program name to be shown in error messages
46 static const char *argv0;
47
48
49 static void lzma_attribute((noreturn))
50 my_exit(void)
51 {
52         int status = EXIT_SUCCESS;
53
54         // Close stdout. We don't care about stderr, because we write to it
55         // only when an error has already occurred.
56         const int ferror_err = ferror(stdout);
57         const int fclose_err = fclose(stdout);
58
59         if (ferror_err || fclose_err) {
60                 // If it was fclose() that failed, we have the reason
61                 // in errno. If only ferror() indicated an error,
62                 // we have no idea what the reason was.
63                 fprintf(stderr, "%s: Cannot write to standard output: %s\n",
64                                 argv0, fclose_err
65                                         ? strerror(errno) : "Unknown error");
66                 status = EXIT_FAILURE;
67         }
68
69         exit(status);
70 }
71
72
73 static void lzma_attribute((noreturn))
74 help(void)
75 {
76         printf(
77 "Usage: %s [OPTION]... [FILE]...\n"
78 "Uncompress files in the ." TOOL_FORMAT " format to the standard output.\n"
79 "\n"
80 "  -c, --stdout       (ignored)\n"
81 "  -d, --decompress   (ignored)\n"
82 "  -k, --keep         (ignored)\n"
83 "  -f, --force        (ignored)\n"
84 "  -M, --memory=NUM   use NUM bytes of memory at maximum (0 means default);\n"
85 "                     the suffixes k, M, G, Ki, Mi, and Gi are supported.\n"
86 "  -h, --help         display this help and exit\n"
87 "  -V, --version      display version and license information and exit\n"
88 "\n"
89 "With no FILE, or when FILE is -, read standard input.\n"
90 "\n"
91 "On this configuration, the tool will use about %" PRIu64
92                 " MiB of memory at maximum.\n"
93 "\n"
94 "Report bugs to <" PACKAGE_BUGREPORT "> (in English or Finnish).\n",
95                 argv0, (memlimit + 512 * 1024) / (1024 * 1024));
96         my_exit();
97 }
98
99
100 static void lzma_attribute((noreturn))
101 version(void)
102 {
103         printf(TOOL_FORMAT "dec " PACKAGE_VERSION "\n"
104                         "liblzma %s\n", lzma_version_string());
105
106         my_exit();
107 }
108
109
110 /// Finds out the amount of physical memory in the system, and sets
111 /// a default memory usage limit.
112 static void
113 set_default_memlimit(void)
114 {
115         const uint64_t mem = physmem();
116
117         if (mem == 0)
118                 // Cannot autodetect, use 10 MiB as the default limit.
119                 memlimit = (1U << 23) + (1U << 21);
120         else
121                 // Limit is 33 % of RAM.
122                 memlimit = mem / 3;
123
124         return;
125 }
126
127
128 /// \brief      Converts a string to uint64_t
129 ///
130 /// This is rudely copied from src/xz/util.c and modified a little. :-(
131 ///
132 static uint64_t
133 str_to_uint64(const char *value)
134 {
135         uint64_t result = 0;
136
137         if (*value < '0' || *value > '9') {
138                 fprintf(stderr, "%s: %s: Not a number\n", argv0, value);
139                 exit(EXIT_FAILURE);
140         }
141
142         do {
143                 // Don't overflow.
144                 if (result > (UINT64_MAX - 9) / 10)
145                         return UINT64_MAX;
146
147                 result *= 10;
148                 result += *value - '0';
149                 ++value;
150         } while (*value >= '0' && *value <= '9');
151
152         if (*value != '\0') {
153                 // Look for suffix.
154                 static const struct {
155                         const char name[4];
156                         uint32_t multiplier;
157                 } suffixes[] = {
158                         { "k",   1000 },
159                         { "kB",  1000 },
160                         { "M",   1000000 },
161                         { "MB",  1000000 },
162                         { "G",   1000000000 },
163                         { "GB",  1000000000 },
164                         { "Ki",  1024 },
165                         { "KiB", 1024 },
166                         { "Mi",  1048576 },
167                         { "MiB", 1048576 },
168                         { "Gi",  1073741824 },
169                         { "GiB", 1073741824 }
170                 };
171
172                 uint32_t multiplier = 0;
173                 for (size_t i = 0; i < ARRAY_SIZE(suffixes); ++i) {
174                         if (strcmp(value, suffixes[i].name) == 0) {
175                                 multiplier = suffixes[i].multiplier;
176                                 break;
177                         }
178                 }
179
180                 if (multiplier == 0) {
181                         fprintf(stderr, "%s: %s: Invalid suffix\n",
182                                         argv0, value);
183                         exit(EXIT_FAILURE);
184                 }
185
186                 // Don't overflow here either.
187                 if (result > UINT64_MAX / multiplier)
188                         result = UINT64_MAX;
189                 else
190                         result *= multiplier;
191         }
192
193         return result;
194 }
195
196
197 /// Parses command line options.
198 static void
199 parse_options(int argc, char **argv)
200 {
201         static const char short_opts[] = "cdkfM:hV";
202         static const struct option long_opts[] = {
203                 { "stdout",       no_argument,         NULL, 'c' },
204                 { "to-stdout",    no_argument,         NULL, 'c' },
205                 { "decompress",   no_argument,         NULL, 'd' },
206                 { "uncompress",   no_argument,         NULL, 'd' },
207                 { "force",        no_argument,         NULL, 'f' },
208                 { "keep",         no_argument,         NULL, 'k' },
209                 { "memory",       required_argument,   NULL, 'M' },
210                 { "help",         no_argument,         NULL, 'h' },
211                 { "version",      no_argument,         NULL, 'V' },
212                 { NULL,           0,                   NULL, 0   }
213         };
214
215         int c;
216
217         while ((c = getopt_long(argc, argv, short_opts, long_opts, NULL))
218                         != -1) {
219                 switch (c) {
220                 case 'c':
221                 case 'd':
222                 case 'f':
223                 case 'k':
224                         break;
225
226                 case 'M':
227                         memlimit = str_to_uint64(optarg);
228                         if (memlimit == 0)
229                                 set_default_memlimit();
230
231                         break;
232
233                 case 'h':
234                         help();
235
236                 case 'V':
237                         version();
238
239                 default:
240                         exit(EXIT_FAILURE);
241                 }
242         }
243
244         return;
245 }
246
247
248 static void
249 uncompress(lzma_stream *strm, FILE *file, const char *filename)
250 {
251         lzma_ret ret;
252
253         // Initialize the decoder
254 #ifdef LZMADEC
255         ret = lzma_alone_decoder(strm, memlimit);
256 #else
257         ret = lzma_stream_decoder(strm, memlimit, LZMA_CONCATENATED);
258 #endif
259
260         // The only reasonable error here is LZMA_MEM_ERROR.
261         // FIXME: Maybe also LZMA_MEMLIMIT_ERROR in future?
262         if (ret != LZMA_OK) {
263                 fprintf(stderr, "%s: ", argv0);
264
265                 if (ret == LZMA_MEM_ERROR)
266                         fprintf(stderr, "%s\n", strerror(ENOMEM));
267                 else
268                         fprintf(stderr, "Internal program error (bug)\n");
269
270                 exit(EXIT_FAILURE);
271         }
272
273         // Input and output buffers
274         uint8_t in_buf[BUFSIZ];
275         uint8_t out_buf[BUFSIZ];
276
277         strm->avail_in = 0;
278         strm->next_out = out_buf;
279         strm->avail_out = BUFSIZ;
280
281         lzma_action action = LZMA_RUN;
282
283         while (true) {
284                 if (strm->avail_in == 0) {
285                         strm->next_in = in_buf;
286                         strm->avail_in = fread(in_buf, 1, BUFSIZ, file);
287
288                         if (ferror(file)) {
289                                 // POSIX says that fread() sets errno if
290                                 // an error occurred. ferror() doesn't
291                                 // touch errno.
292                                 fprintf(stderr, "%s: %s: Error reading "
293                                                 "input file: %s\n",
294                                                 argv0, filename,
295                                                 strerror(errno));
296                                 exit(EXIT_FAILURE);
297                         }
298
299 #ifndef LZMADEC
300                         // When using LZMA_CONCATENATED, we need to tell
301                         // liblzma when it has got all the input.
302                         if (feof(file))
303                                 action = LZMA_FINISH;
304 #endif
305                 }
306
307                 ret = lzma_code(strm, action);
308
309                 // Write and check write error before checking decoder error.
310                 // This way as much data as possible gets written to output
311                 // even if decoder detected an error.
312                 if (strm->avail_out == 0 || ret != LZMA_OK) {
313                         const size_t write_size = BUFSIZ - strm->avail_out;
314
315                         if (fwrite(out_buf, 1, write_size, stdout)
316                                         != write_size) {
317                                 // Wouldn't be a surprise if writing to stderr
318                                 // would fail too but at least try to show an
319                                 // error message.
320                                 fprintf(stderr, "%s: Cannot write to "
321                                                 "standard output: %s\n", argv0,
322                                                 strerror(errno));
323                                 exit(EXIT_FAILURE);
324                         }
325
326                         strm->next_out = out_buf;
327                         strm->avail_out = BUFSIZ;
328                 }
329
330                 if (ret != LZMA_OK) {
331                         if (ret == LZMA_STREAM_END) {
332 #ifdef LZMADEC
333                                 // Check that there's no trailing garbage.
334                                 if (strm->avail_in != 0
335                                                 || fread(in_buf, 1, 1, file)
336                                                         != 0
337                                                 || !feof(file))
338                                         ret = LZMA_DATA_ERROR;
339                                 else
340                                         return;
341 #else
342                                 // lzma_stream_decoder() already guarantees
343                                 // that there's no trailing garbage.
344                                 assert(strm->avail_in == 0);
345                                 assert(action == LZMA_FINISH);
346                                 assert(feof(file));
347                                 return;
348 #endif
349                         }
350
351                         const char *msg;
352                         switch (ret) {
353                         case LZMA_MEM_ERROR:
354                                 msg = strerror(ENOMEM);
355                                 break;
356
357                         case LZMA_MEMLIMIT_ERROR:
358                                 msg = "Memory usage limit reached";
359                                 break;
360
361                         case LZMA_FORMAT_ERROR:
362                                 msg = "File format not recognized";
363                                 break;
364
365                         case LZMA_OPTIONS_ERROR:
366                                 // FIXME: Better message?
367                                 msg = "Unsupported compression options";
368                                 break;
369
370                         case LZMA_DATA_ERROR:
371                                 msg = "File is corrupt";
372                                 break;
373
374                         case LZMA_BUF_ERROR:
375                                 msg = "Unexpected end of input";
376                                 break;
377
378                         default:
379                                 msg = "Internal program error (bug)";
380                                 break;
381                         }
382
383                         fprintf(stderr, "%s: %s: %s\n", argv0, filename, msg);
384
385                         exit(EXIT_FAILURE);
386                 }
387         }
388 }
389
390
391 int
392 main(int argc, char **argv)
393 {
394         // Set the argv0 global so that we can print the command name in
395         // error and help messages.
396         argv0 = argv[0];
397
398         // Detect amount of installed RAM and set the memory usage limit.
399         // This is needed before parsing the command line arguments.
400         set_default_memlimit();
401
402         // Parse the command line options.
403         parse_options(argc, argv);
404
405         // The same lzma_stream is used for all files that we decode. This way
406         // we don't need to reallocate memory for every file if they use same
407         // compression settings.
408         lzma_stream strm = LZMA_STREAM_INIT;
409
410         // Some systems require setting stdin and stdout to binary mode.
411 #ifdef _WIN32
412         setmode(fileno(stdin), O_BINARY);
413         setmode(fileno(stdout), O_BINARY);
414 #endif
415
416         if (optind == argc) {
417                 // No filenames given, decode from stdin.
418                 uncompress(&strm, stdin, "(stdin)");
419         } else {
420                 // Loop through the filenames given on the command line.
421                 do {
422                         // "-" indicates stdin.
423                         if (strcmp(argv[optind], "-") == 0) {
424                                 uncompress(&strm, stdin, "(stdin)");
425                         } else {
426                                 FILE *file = fopen(argv[optind], "rb");
427                                 if (file == NULL) {
428                                         fprintf(stderr, "%s: %s: %s\n",
429                                                         argv0, argv[optind],
430                                                         strerror(errno));
431                                         exit(EXIT_FAILURE);
432                                 }
433
434                                 uncompress(&strm, file, argv[optind]);
435                                 fclose(file);
436                         }
437                 } while (++optind < argc);
438         }
439
440 #ifndef NDEBUG
441         // Free the memory only when debugging. Freeing wastes some time,
442         // but allows detecting possible memory leaks with Valgrind.
443         lzma_end(&strm);
444 #endif
445
446         my_exit();
447 }