diff options
Diffstat (limited to 'src/lzmadec/lzmadec.c')
-rw-r--r-- | src/lzmadec/lzmadec.c | 157 |
1 files changed, 60 insertions, 97 deletions
diff --git a/src/lzmadec/lzmadec.c b/src/lzmadec/lzmadec.c index 1fc561b7..ed5947ad 100644 --- a/src/lzmadec/lzmadec.c +++ b/src/lzmadec/lzmadec.c @@ -65,14 +65,7 @@ static uint8_t out_buf[BUFSIZ]; static lzma_stream strm = LZMA_STREAM_INIT; /// Number of bytes to use memory at maximum -static size_t mem_limit; - -/// Memory allocation hooks -static lzma_allocator allocator = { - .alloc = (void *(*)(void *, size_t, size_t))(&lzma_memlimit_alloc), - .free = (void (*)(void *, void *))(&lzma_memlimit_free), - .opaque = NULL, -}; +static size_t memlimit; /// Program name to be shown in error messages static const char *argv0; @@ -116,7 +109,7 @@ help(void) " MiB of memory at maximum.\n" "\n" "Report bugs to <" PACKAGE_BUGREPORT "> (in English or Finnish).\n", - argv0, ((uint64_t)(mem_limit) + 512 * 1024) / (1024 * 1024)); + argv0, ((uint64_t)(memlimit) + 512 * 1024) / (1024 * 1024)); // Using PRIu64 above instead of %zu to support pre-C99 libc. exit(0); } @@ -148,7 +141,7 @@ version(void) /// Finds out the amount of physical memory in the system, and sets /// a default memory usage limit. static void -set_default_mem_limit(void) +set_default_memlimit(void) { uint64_t mem = physmem(); if (mem != 0) { @@ -159,10 +152,10 @@ set_default_mem_limit(void) mem = SIZE_MAX; #endif - mem_limit = mem / 3; + memlimit = mem / 3; } else { // Cannot autodetect, use 10 MiB as the default limit. - mem_limit = (1U << 23) + (1U << 21); + memlimit = (1U << 23) + (1U << 21); } return; @@ -272,7 +265,7 @@ parse_options(int argc, char **argv) break; case 'M': - mem_limit = str_to_size(optarg); + memlimit = str_to_size(optarg); break; case 'h': @@ -309,19 +302,20 @@ parse_options(int argc, char **argv) static void init(void) { + const uint32_t flags = LZMA_WARN_UNSUPPORTED_CHECK | LZMA_CONCATENATED; lzma_ret ret; switch (format_type) { case FORMAT_AUTO: - ret = lzma_auto_decoder(&strm); + ret = lzma_auto_decoder(&strm, memlimit, flags); break; case FORMAT_NATIVE: - ret = lzma_stream_decoder(&strm); + ret = lzma_stream_decoder(&strm, memlimit, flags); break; case FORMAT_ALONE: - ret = lzma_alone_decoder(&strm); + ret = lzma_alone_decoder(&strm, memlimit); break; default: @@ -345,50 +339,6 @@ init(void) static void -read_input(void) -{ - strm.next_in = in_buf; - strm.avail_in = fread(in_buf, 1, BUFSIZ, file); - - if (ferror(file)) { - // POSIX says that fread() sets errno if an error occurred. - // ferror() doesn't touch errno. - fprintf(stderr, "%s: %s: Error reading input file: %s\n", - argv0, filename, strerror(errno)); - exit(ERROR); - } - - return; -} - - -static bool -skip_padding(void) -{ - // Handle concatenated Streams. There can be arbitrary number of - // nul-byte padding between the Streams, which must be ignored. - // - // NOTE: Concatenating LZMA_Alone files works only if at least - // one of lc, lp, and pb is non-zero. Using the concatenation - // on LZMA_Alone files is strongly discouraged. - while (true) { - while (strm.avail_in > 0) { - if (*strm.next_in != '\0') - return true; - - ++strm.next_in; - --strm.avail_in; - } - - if (feof(file)) - return false; - - read_input(); - } -} - - -static void uncompress(void) { if (file == stdin && !force && isatty(STDIN_FILENO)) { @@ -400,44 +350,65 @@ uncompress(void) } init(); + strm.avail_in = 0; + strm.next_out = out_buf; + strm.avail_out = BUFSIZ; + + lzma_action action = LZMA_RUN; while (true) { - if (strm.avail_in == 0) - read_input(); + if (strm.avail_in == 0) { + strm.next_in = in_buf; + strm.avail_in = fread(in_buf, 1, BUFSIZ, file); + + if (ferror(file)) { + // POSIX says that fread() sets errno if + // an error occurred. ferror() doesn't + // touch errno. + fprintf(stderr, "%s: %s: Error reading " + "input file: %s\n", + argv0, filename, + strerror(errno)); + exit(ERROR); + } - strm.next_out = out_buf; - strm.avail_out = BUFSIZ; + if (feof(file)) + action = LZMA_FINISH; + } - const lzma_ret ret = lzma_code(&strm, LZMA_RUN); + const lzma_ret ret = lzma_code(&strm, action); // Write and check write error before checking decoder error. // This way as much data as possible gets written to output - // even if decoder detected an error. Checking write error - // needs to be done before checking decoder error due to - // how concatenated Streams are handled a few lines later. - const size_t write_size = BUFSIZ - strm.avail_out; - if (fwrite(out_buf, 1, write_size, stdout) != write_size) { - // Wouldn't be a surprise if writing to stderr would - // fail too but at least try to show an error message. - fprintf(stderr, "%s: Cannot write to " - "standard output: %s\n", argv0, - strerror(errno)); - exit(ERROR); + // even if decoder detected an error. + if (strm.avail_out == 0 || ret != LZMA_OK) { + const size_t write_size = BUFSIZ - strm.avail_out; + + if (fwrite(out_buf, 1, write_size, stdout) + != write_size) { + // Wouldn't be a surprise if writing to stderr + // would fail too but at least try to show an + // error message. + fprintf(stderr, "%s: Cannot write to " + "standard output: %s\n", argv0, + strerror(errno)); + exit(ERROR); + } + + strm.next_out = out_buf; + strm.avail_out = BUFSIZ; } if (ret != LZMA_OK) { - if (ret == LZMA_STREAM_END) { - if (skip_padding()) { - init(); - continue; - } - + // FIXME !!! Doesn't work with LZMA_Alone for the + // same reason as in process.c. + if (ret == LZMA_STREAM_END) return; - } fprintf(stderr, "%s: %s: ", argv0, filename); + // FIXME Add LZMA_*_CHECK and LZMA_FORMAT_ERROR. switch (ret) { case LZMA_DATA_ERROR: fprintf(stderr, "File is corrupt\n"); @@ -452,6 +423,11 @@ uncompress(void) fprintf(stderr, "%s\n", strerror(ENOMEM)); exit(ERROR); + case LZMA_MEMLIMIT_ERROR: + fprintf(stderr, "Memory usage limit " + "reached\n"); + exit(ERROR); + case LZMA_BUF_ERROR: fprintf(stderr, "Unexpected end of input\n"); exit(ERROR); @@ -479,23 +455,12 @@ main(int argc, char **argv) { argv0 = argv[0]; - set_default_mem_limit(); + set_default_memlimit(); parse_options(argc, argv); lzma_init_decoder(); - lzma_memlimit *mem_limiter = lzma_memlimit_create(mem_limit); - if (mem_limiter == NULL) { - fprintf(stderr, "%s: %s\n", argv0, strerror(ENOMEM)); - exit(ERROR); - } - - assert(lzma_memlimit_count(mem_limiter) == 0); - - allocator.opaque = mem_limiter; - strm.allocator = &allocator; - #ifdef WIN32 setmode(fileno(stdin), O_BINARY); setmode(fileno(stdout), O_BINARY); @@ -531,8 +496,6 @@ main(int argc, char **argv) // Free the memory only when debugging. Freeing wastes some time, // but allows detecting possible memory leaks with Valgrind. lzma_end(&strm); - assert(lzma_memlimit_count(mem_limiter) == 0); - lzma_memlimit_end(mem_limiter, false); #endif return exit_status; |