xref: /freebsd/contrib/xz/src/lzmainfo/lzmainfo.c (revision 128836d304d93f2d00eb14069c27089ab46c38d4)
1 // SPDX-License-Identifier: 0BSD
2 
3 ///////////////////////////////////////////////////////////////////////////////
4 //
5 /// \file       lzmainfo.c
6 /// \brief      lzmainfo tool for compatibility with LZMA Utils
7 //
8 //  Author:     Lasse Collin
9 //
10 ///////////////////////////////////////////////////////////////////////////////
11 
12 #include "sysdefs.h"
13 #include <stdio.h>
14 #include <errno.h>
15 
16 #include "lzma.h"
17 #include "getopt.h"
18 #include "tuklib_gettext.h"
19 #include "tuklib_progname.h"
20 #include "tuklib_mbstr_nonprint.h"
21 #include "tuklib_mbstr_wrap.h"
22 #include "tuklib_exit.h"
23 
24 #ifdef TUKLIB_DOSLIKE
25 #	include <fcntl.h>
26 #	include <io.h>
27 #endif
28 
29 
30 tuklib_attr_noreturn
31 static void
help(void)32 help(void)
33 {
34 	// A few languages use so long strings that we need automatic
35 	// wrapping. A few strings are the same as in xz/message.c and
36 	// should be kept in sync.
37 	static const struct tuklib_wrap_opt wrap0 = {  0,  0,  0,  0, 79 };
38 	int e = 0;
39 
40 	printf(_("Usage: %s [--help] [--version] [FILE]...\n"), progname);
41 
42 	e |= tuklib_wraps(stdout, &wrap0,
43 		W_("Show information stored in the .lzma file header."));
44 	e |= tuklib_wraps(stdout, &wrap0,
45 		W_("With no FILE, or when FILE is -, read standard input."));
46 
47 	putchar('\n');
48 
49 	e |= tuklib_wrapf(stdout, &wrap0,
50 			W_("Report bugs to <%s> (in English or Finnish)."),
51 			PACKAGE_BUGREPORT);
52 
53 	e |= tuklib_wrapf(stdout, &wrap0,
54 			W_("%s home page: <%s>"), PACKAGE_NAME, PACKAGE_URL);
55 
56 	if (e != 0) {
57 		// Avoid new translatable strings by printing the message
58 		// in pieces.
59 		fprintf(stderr, _("%s: "), progname);
60 		fprintf(stderr, _("Error printing the help text "
61 				"(error code %d)"), e);
62 		fprintf(stderr, "\n");
63 	}
64 
65 	tuklib_exit(EXIT_SUCCESS, EXIT_FAILURE, true);
66 }
67 
68 
69 tuklib_attr_noreturn
70 static void
version(void)71 version(void)
72 {
73 	puts("lzmainfo (" PACKAGE_NAME ") " LZMA_VERSION_STRING);
74 	tuklib_exit(EXIT_SUCCESS, EXIT_FAILURE, true);
75 }
76 
77 
78 /// Parse command line options.
79 static void
parse_args(int argc,char ** argv)80 parse_args(int argc, char **argv)
81 {
82 	enum {
83 		OPT_HELP,
84 		OPT_VERSION,
85 	};
86 
87 	static const struct option long_opts[] = {
88 		{ "help",    no_argument, NULL, OPT_HELP },
89 		{ "version", no_argument, NULL, OPT_VERSION },
90 		{ NULL,      0,           NULL, 0 }
91 	};
92 
93 	int c;
94 	while ((c = getopt_long(argc, argv, "", long_opts, NULL)) != -1) {
95 		switch (c) {
96 		case OPT_HELP:
97 			help();
98 
99 		case OPT_VERSION:
100 			version();
101 
102 		default:
103 			exit(EXIT_FAILURE);
104 		}
105 	}
106 
107 	return;
108 }
109 
110 
111 /// Primitive base-2 logarithm for integers
112 static uint32_t
my_log2(uint32_t n)113 my_log2(uint32_t n)
114 {
115 	uint32_t e;
116 	for (e = 0; n > 1; ++e, n /= 2) ;
117 	return e;
118 }
119 
120 
121 /// Parse the .lzma header and display information about it.
122 static bool
lzmainfo(const char * name,FILE * f)123 lzmainfo(const char *name, FILE *f)
124 {
125 	uint8_t buf[13];
126 	const size_t size = fread(buf, 1, sizeof(buf), f);
127 	if (size != 13) {
128 		fprintf(stderr, "%s: %s: %s\n", progname,
129 				tuklib_mask_nonprint(name),
130 				ferror(f) ? strerror(errno)
131 				: _("File is too small to be a .lzma file"));
132 		return true;
133 	}
134 
135 	lzma_filter filter = { .id = LZMA_FILTER_LZMA1 };
136 
137 	// Parse the first five bytes.
138 	switch (lzma_properties_decode(&filter, NULL, buf, 5)) {
139 	case LZMA_OK:
140 		break;
141 
142 	case LZMA_OPTIONS_ERROR:
143 		fprintf(stderr, "%s: %s: %s\n", progname,
144 				tuklib_mask_nonprint(name),
145 				_("Not a .lzma file"));
146 		return true;
147 
148 	case LZMA_MEM_ERROR:
149 		fprintf(stderr, "%s: %s\n", progname, strerror(ENOMEM));
150 		exit(EXIT_FAILURE);
151 
152 	default:
153 		fprintf(stderr, "%s: %s\n", progname,
154 				_("Internal error (bug)"));
155 		exit(EXIT_FAILURE);
156 	}
157 
158 	// Uncompressed size
159 	uint64_t uncompressed_size = 0;
160 	for (size_t i = 0; i < 8; ++i)
161 		uncompressed_size |= (uint64_t)(buf[5 + i]) << (i * 8);
162 
163 	// Display the results. We don't want to translate these and also
164 	// will use MB instead of MiB, because someone could be parsing
165 	// this output and we don't want to break that when people move
166 	// from LZMA Utils to XZ Utils.
167 	if (f != stdin)
168 		printf("%s\n", tuklib_mask_nonprint(name));
169 
170 	printf("Uncompressed size:             ");
171 	if (uncompressed_size == UINT64_MAX)
172 		printf("Unknown");
173 	else
174 		printf("%" PRIu64 " MB (%" PRIu64 " bytes)",
175 				(uncompressed_size / 1024 + 512) / 1024,
176 				uncompressed_size);
177 
178 	lzma_options_lzma *opt = filter.options;
179 
180 	printf("\nDictionary size:               "
181 			"%" PRIu32 " MB (2^%" PRIu32 " bytes)\n"
182 			"Literal context bits (lc):     %" PRIu32 "\n"
183 			"Literal pos bits (lp):         %" PRIu32 "\n"
184 			"Number of pos bits (pb):       %" PRIu32 "\n",
185 			(opt->dict_size / 1024 + 512) / 1024,
186 			my_log2(opt->dict_size), opt->lc, opt->lp, opt->pb);
187 
188 	free(opt);
189 
190 	return false;
191 }
192 
193 
194 extern int
main(int argc,char ** argv)195 main(int argc, char **argv)
196 {
197 	tuklib_progname_init(argv);
198 	tuklib_gettext_init(PACKAGE, LOCALEDIR);
199 
200 	parse_args(argc, argv);
201 
202 #ifdef TUKLIB_DOSLIKE
203 	setmode(fileno(stdin), O_BINARY);
204 #endif
205 
206 	int ret = EXIT_SUCCESS;
207 
208 	// We print empty lines around the output only when reading from
209 	// files specified on the command line. This is due to how
210 	// LZMA Utils did it.
211 	if (optind == argc) {
212 		if (lzmainfo("(stdin)", stdin))
213 			ret = EXIT_FAILURE;
214 	} else {
215 		printf("\n");
216 
217 		do {
218 			if (strcmp(argv[optind], "-") == 0) {
219 				if (lzmainfo("(stdin)", stdin))
220 					ret = EXIT_FAILURE;
221 			} else {
222 				FILE *f = fopen(argv[optind], "r");
223 				if (f == NULL) {
224 					ret = EXIT_FAILURE;
225 					fprintf(stderr, "%s: %s: %s\n",
226 						progname,
227 						tuklib_mask_nonprint(
228 							argv[optind]),
229 						strerror(errno));
230 					continue;
231 				}
232 
233 				if (lzmainfo(argv[optind], f))
234 					ret = EXIT_FAILURE;
235 
236 				printf("\n");
237 				fclose(f);
238 			}
239 		} while (++optind < argc);
240 	}
241 
242 	tuklib_exit(ret, EXIT_FAILURE, true);
243 }
244