Home | History | Annotate | Download | only in userspace
      1 /*
      2  * Simple XZ decoder command line tool
      3  *
      4  * Author: Lasse Collin <lasse.collin (at) tukaani.org>
      5  *
      6  * This file has been put into the public domain.
      7  * You can do whatever you want with this file.
      8  */
      9 
     10 /*
     11  * This is really limited: Not all filters from .xz format are supported,
     12  * only CRC32 is supported as the integrity check, and decoding of
     13  * concatenated .xz streams is not supported. Thus, you may want to look
     14  * at xzdec from XZ Utils if a few KiB bigger tool is not a problem.
     15  */
     16 
     17 #include <stdbool.h>
     18 #include <stdio.h>
     19 #include <string.h>
     20 #include "xz.h"
     21 
     22 static uint8_t in[BUFSIZ];
     23 static uint8_t out[BUFSIZ];
     24 
     25 int main(int argc, char **argv)
     26 {
     27 	struct xz_buf b;
     28 	struct xz_dec *s;
     29 	enum xz_ret ret;
     30 	const char *msg;
     31 
     32 	if (argc >= 2 && strcmp(argv[1], "--help") == 0) {
     33 		fputs("Uncompress a .xz file from stdin to stdout.\n"
     34 				"Arguments other than `--help' are ignored.\n",
     35 				stdout);
     36 		return 0;
     37 	}
     38 
     39 	xz_crc32_init();
     40 #ifdef XZ_USE_CRC64
     41 	xz_crc64_init();
     42 #endif
     43 
     44 	/*
     45 	 * Support up to 64 MiB dictionary. The actually needed memory
     46 	 * is allocated once the headers have been parsed.
     47 	 */
     48 	s = xz_dec_init(XZ_DYNALLOC, 1 << 26);
     49 	if (s == NULL) {
     50 		msg = "Memory allocation failed\n";
     51 		goto error;
     52 	}
     53 
     54 	b.in = in;
     55 	b.in_pos = 0;
     56 	b.in_size = 0;
     57 	b.out = out;
     58 	b.out_pos = 0;
     59 	b.out_size = BUFSIZ;
     60 
     61 	while (true) {
     62 		if (b.in_pos == b.in_size) {
     63 			b.in_size = fread(in, 1, sizeof(in), stdin);
     64 			b.in_pos = 0;
     65 		}
     66 
     67 		ret = xz_dec_run(s, &b);
     68 
     69 		if (b.out_pos == sizeof(out)) {
     70 			if (fwrite(out, 1, b.out_pos, stdout) != b.out_pos) {
     71 				msg = "Write error\n";
     72 				goto error;
     73 			}
     74 
     75 			b.out_pos = 0;
     76 		}
     77 
     78 		if (ret == XZ_OK)
     79 			continue;
     80 
     81 #ifdef XZ_DEC_ANY_CHECK
     82 		if (ret == XZ_UNSUPPORTED_CHECK) {
     83 			fputs(argv[0], stderr);
     84 			fputs(": ", stderr);
     85 			fputs("Unsupported check; not verifying "
     86 					"file integrity\n", stderr);
     87 			continue;
     88 		}
     89 #endif
     90 
     91 		if (fwrite(out, 1, b.out_pos, stdout) != b.out_pos
     92 				|| fclose(stdout)) {
     93 			msg = "Write error\n";
     94 			goto error;
     95 		}
     96 
     97 		switch (ret) {
     98 		case XZ_STREAM_END:
     99 			xz_dec_end(s);
    100 			return 0;
    101 
    102 		case XZ_MEM_ERROR:
    103 			msg = "Memory allocation failed\n";
    104 			goto error;
    105 
    106 		case XZ_MEMLIMIT_ERROR:
    107 			msg = "Memory usage limit reached\n";
    108 			goto error;
    109 
    110 		case XZ_FORMAT_ERROR:
    111 			msg = "Not a .xz file\n";
    112 			goto error;
    113 
    114 		case XZ_OPTIONS_ERROR:
    115 			msg = "Unsupported options in the .xz headers\n";
    116 			goto error;
    117 
    118 		case XZ_DATA_ERROR:
    119 		case XZ_BUF_ERROR:
    120 			msg = "File is corrupt\n";
    121 			goto error;
    122 
    123 		default:
    124 			msg = "Bug!\n";
    125 			goto error;
    126 		}
    127 	}
    128 
    129 error:
    130 	xz_dec_end(s);
    131 	fputs(argv[0], stderr);
    132 	fputs(": ", stderr);
    133 	fputs(msg, stderr);
    134 	return 1;
    135 }
    136