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