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