xref: /aosp_15_r20/external/xz-embedded/userspace/xzminidec.c (revision d2c16535d139cb185e89120452531bba6b36d3c6)
1 // SPDX-License-Identifier: 0BSD
2 
3 /*
4  * Simple XZ decoder command line tool
5  *
6  * Author: Lasse Collin <[email protected]>
7  */
8 
9 /*
10  * This is a very limited .xz decoder. Only LZMA2 and the BCJ filters
11  * are supported, and the BCJ filters cannot use Filter Properties.
12  * SHA256 is not supported as an integrity check. The LZMA2 dictionary
13  * sizes can be at most 64 MiB, but this can be modified by changing
14  * DICT_SIZE_MAX.
15  *
16  * See xzdec from XZ Utils if a few KiB bigger tool is not a problem.
17  */
18 
19 #include <stdbool.h>
20 #include <stdio.h>
21 #include <string.h>
22 #include "xz.h"
23 
24 #ifndef DICT_SIZE_MAX
25 #	define DICT_SIZE_MAX (64U << 20)
26 #endif
27 
28 static uint8_t in[BUFSIZ];
29 static uint8_t out[BUFSIZ];
30 
main(int argc,char ** argv)31 int main(int argc, char **argv)
32 {
33 	struct xz_buf b;
34 	struct xz_dec *s;
35 	enum xz_ret ret;
36 	const char *msg;
37 
38 	if (argc >= 2 && strcmp(argv[1], "--help") == 0) {
39 		fputs("Uncompress a .xz file from stdin to stdout.\n"
40 				"Arguments other than `--help' are ignored.\n",
41 				stdout);
42 		return 0;
43 	}
44 
45 	xz_crc32_init();
46 #ifdef XZ_USE_CRC64
47 	xz_crc64_init();
48 #endif
49 
50 	/*
51 	 * Support up to 64 MiB dictionary. The actually needed memory
52 	 * is allocated once the headers have been parsed.
53 	 */
54 	s = xz_dec_init(XZ_DYNALLOC, DICT_SIZE_MAX);
55 	if (s == NULL) {
56 		msg = "Memory allocation failed\n";
57 		goto error;
58 	}
59 
60 	b.in = in;
61 	b.in_pos = 0;
62 	b.in_size = 0;
63 	b.out = out;
64 	b.out_pos = 0;
65 	b.out_size = BUFSIZ;
66 
67 	while (true) {
68 		if (b.in_pos == b.in_size) {
69 			b.in_size = fread(in, 1, sizeof(in), stdin);
70 
71 			if (ferror(stdin)) {
72 				msg = "Read error\n";
73 				goto error;
74 			}
75 
76 			b.in_pos = 0;
77 		}
78 
79 		/*
80 		 * There are a few ways to set the "finish" (the third)
81 		 * argument. We could use feof(stdin) but testing in_size
82 		 * is fine too and may also work in applications that don't
83 		 * use FILEs.
84 		 */
85 		ret = xz_dec_catrun(s, &b, b.in_size == 0);
86 
87 		if (b.out_pos == sizeof(out)) {
88 			if (fwrite(out, 1, b.out_pos, stdout) != b.out_pos) {
89 				msg = "Write error\n";
90 				goto error;
91 			}
92 
93 			b.out_pos = 0;
94 		}
95 
96 		if (ret == XZ_OK)
97 			continue;
98 
99 #ifdef XZ_DEC_ANY_CHECK
100 		if (ret == XZ_UNSUPPORTED_CHECK) {
101 			fputs(argv[0], stderr);
102 			fputs(": ", stderr);
103 			fputs("Unsupported check; not verifying "
104 					"file integrity\n", stderr);
105 			continue;
106 		}
107 #endif
108 
109 		if (fwrite(out, 1, b.out_pos, stdout) != b.out_pos
110 				|| fclose(stdout)) {
111 			msg = "Write error\n";
112 			goto error;
113 		}
114 
115 		switch (ret) {
116 		case XZ_STREAM_END:
117 			xz_dec_end(s);
118 			return 0;
119 
120 		case XZ_MEM_ERROR:
121 			msg = "Memory allocation failed\n";
122 			goto error;
123 
124 		case XZ_MEMLIMIT_ERROR:
125 			msg = "Memory usage limit reached\n";
126 			goto error;
127 
128 		case XZ_FORMAT_ERROR:
129 			msg = "Not a .xz file\n";
130 			goto error;
131 
132 		case XZ_OPTIONS_ERROR:
133 			msg = "Unsupported options in the .xz headers\n";
134 			goto error;
135 
136 		case XZ_DATA_ERROR:
137 		case XZ_BUF_ERROR:
138 			msg = "File is corrupt\n";
139 			goto error;
140 
141 		default:
142 			msg = "Bug!\n";
143 			goto error;
144 		}
145 	}
146 
147 error:
148 	xz_dec_end(s);
149 	fputs(argv[0], stderr);
150 	fputs(": ", stderr);
151 	fputs(msg, stderr);
152 	return 1;
153 }
154