lzmainfo.c revision 207753
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
24static void lzma_attribute((noreturn))
25help(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
43static void lzma_attribute((noreturn))
44version(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.
52static void
53parse_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
85static uint32_t
86my_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.
95static bool
96lzmainfo(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
166extern int
167main(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