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