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