// SPDX-License-Identifier: 0BSD /* * Simple XZ decoder command line tool * * Author: Lasse Collin */ /* * This is a very limited .xz decoder. Only LZMA2 and the BCJ filters * are supported, and the BCJ filters cannot use Filter Properties. * SHA256 is not supported as an integrity check. The LZMA2 dictionary * sizes can be at most 64 MiB, but this can be modified by changing * DICT_SIZE_MAX. * * See xzdec from XZ Utils if a few KiB bigger tool is not a problem. */ #include #include #include #include "xz.h" #ifndef DICT_SIZE_MAX # define DICT_SIZE_MAX (64U << 20) #endif static uint8_t in[BUFSIZ]; static uint8_t out[BUFSIZ]; int main(int argc, char **argv) { struct xz_buf b; struct xz_dec *s; enum xz_ret ret; const char *msg; if (argc >= 2 && strcmp(argv[1], "--help") == 0) { fputs("Uncompress a .xz file from stdin to stdout.\n" "Arguments other than `--help' are ignored.\n", stdout); return 0; } xz_crc32_init(); #ifdef XZ_USE_CRC64 xz_crc64_init(); #endif /* * Support up to 64 MiB dictionary. The actually needed memory * is allocated once the headers have been parsed. */ s = xz_dec_init(XZ_DYNALLOC, DICT_SIZE_MAX); if (s == NULL) { msg = "Memory allocation failed\n"; goto error; } b.in = in; b.in_pos = 0; b.in_size = 0; b.out = out; b.out_pos = 0; b.out_size = BUFSIZ; while (true) { if (b.in_pos == b.in_size) { b.in_size = fread(in, 1, sizeof(in), stdin); if (ferror(stdin)) { msg = "Read error\n"; goto error; } b.in_pos = 0; } /* * There are a few ways to set the "finish" (the third) * argument. We could use feof(stdin) but testing in_size * is fine too and may also work in applications that don't * use FILEs. */ ret = xz_dec_catrun(s, &b, b.in_size == 0); if (b.out_pos == sizeof(out)) { if (fwrite(out, 1, b.out_pos, stdout) != b.out_pos) { msg = "Write error\n"; goto error; } b.out_pos = 0; } if (ret == XZ_OK) continue; #ifdef XZ_DEC_ANY_CHECK if (ret == XZ_UNSUPPORTED_CHECK) { fputs(argv[0], stderr); fputs(": ", stderr); fputs("Unsupported check; not verifying " "file integrity\n", stderr); continue; } #endif if (fwrite(out, 1, b.out_pos, stdout) != b.out_pos || fclose(stdout)) { msg = "Write error\n"; goto error; } switch (ret) { case XZ_STREAM_END: xz_dec_end(s); return 0; case XZ_MEM_ERROR: msg = "Memory allocation failed\n"; goto error; case XZ_MEMLIMIT_ERROR: msg = "Memory usage limit reached\n"; goto error; case XZ_FORMAT_ERROR: msg = "Not a .xz file\n"; goto error; case XZ_OPTIONS_ERROR: msg = "Unsupported options in the .xz headers\n"; goto error; case XZ_DATA_ERROR: case XZ_BUF_ERROR: msg = "File is corrupt\n"; goto error; default: msg = "Bug!\n"; goto error; } } error: xz_dec_end(s); fputs(argv[0], stderr); fputs(": ", stderr); fputs(msg, stderr); return 1; }