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