lzmainfo.c revision 207753
1207753Smm///////////////////////////////////////////////////////////////////////////////
2207753Smm//
3207753Smm/// \file       lzmainfo.c
4207753Smm/// \brief      lzmainfo tool for compatibility with LZMA Utils
5207753Smm//
6207753Smm//  Author:     Lasse Collin
7207753Smm//
8207753Smm//  This file has been put into the public domain.
9207753Smm//  You can do whatever you want with this file.
10207753Smm//
11207753Smm///////////////////////////////////////////////////////////////////////////////
12207753Smm
13207753Smm#include "sysdefs.h"
14207753Smm#include <stdio.h>
15207753Smm#include <errno.h>
16207753Smm
17207753Smm#include "lzma.h"
18207753Smm#include "getopt.h"
19207753Smm#include "tuklib_gettext.h"
20207753Smm#include "tuklib_progname.h"
21207753Smm#include "tuklib_exit.h"
22207753Smm
23207753Smm
24207753Smmstatic void lzma_attribute((noreturn))
25207753Smmhelp(void)
26207753Smm{
27207753Smm	printf(
28207753Smm_("Usage: %s [--help] [--version] [FILE]...\n"
29207753Smm"Show information stored in the .lzma file header"), progname);
30207753Smm
31207753Smm	printf(_(
32207753Smm"\nWith no FILE, or when FILE is -, read standard input.\n"));
33207753Smm	printf("\n");
34207753Smm
35207753Smm	printf(_("Report bugs to <%s> (in English or Finnish).\n"),
36207753Smm			PACKAGE_BUGREPORT);
37207753Smm	printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
38207753Smm
39207753Smm	tuklib_exit(EXIT_SUCCESS, EXIT_FAILURE, true);
40207753Smm}
41207753Smm
42207753Smm
43207753Smmstatic void lzma_attribute((noreturn))
44207753Smmversion(void)
45207753Smm{
46207753Smm	puts("lzmainfo (" PACKAGE_NAME ") " PACKAGE_VERSION);
47207753Smm	tuklib_exit(EXIT_SUCCESS, EXIT_FAILURE, true);
48207753Smm}
49207753Smm
50207753Smm
51207753Smm/// Parse command line options.
52207753Smmstatic void
53207753Smmparse_args(int argc, char **argv)
54207753Smm{
55207753Smm	enum {
56207753Smm		OPT_HELP,
57207753Smm		OPT_VERSION,
58207753Smm	};
59207753Smm
60207753Smm	static const struct option long_opts[] = {
61207753Smm		{ "help",    no_argument, NULL, OPT_HELP },
62207753Smm		{ "version", no_argument, NULL, OPT_VERSION },
63207753Smm		{ NULL,      0,           NULL, 0 }
64207753Smm	};
65207753Smm
66207753Smm	int c;
67207753Smm	while ((c = getopt_long(argc, argv, "", long_opts, NULL)) != -1) {
68207753Smm		switch (c) {
69207753Smm		case OPT_HELP:
70207753Smm			help();
71207753Smm
72207753Smm		case OPT_VERSION:
73207753Smm			version();
74207753Smm
75207753Smm		default:
76207753Smm			exit(EXIT_FAILURE);
77207753Smm		}
78207753Smm	}
79207753Smm
80207753Smm	return;
81207753Smm}
82207753Smm
83207753Smm
84207753Smm/// Primitive base-2 logarithm for integers
85207753Smmstatic uint32_t
86207753Smmmy_log2(uint32_t n)
87207753Smm{
88207753Smm	uint32_t e;
89207753Smm	for (e = 0; n > 1; ++e, n /= 2) ;
90207753Smm	return e;
91207753Smm}
92207753Smm
93207753Smm
94207753Smm/// Parse the .lzma header and display information about it.
95207753Smmstatic bool
96207753Smmlzmainfo(const char *name, FILE *f)
97207753Smm{
98207753Smm	uint8_t buf[13];
99207753Smm	const size_t size = fread(buf, 1, sizeof(buf), f);
100207753Smm	if (size != 13) {
101207753Smm		fprintf(stderr, "%s: %s: %s\n", progname, name,
102207753Smm				ferror(f) ? strerror(errno)
103207753Smm				: _("File is too small to be a .lzma file"));
104207753Smm		return true;
105207753Smm	}
106207753Smm
107207753Smm	lzma_filter filter = { .id = LZMA_FILTER_LZMA1 };
108207753Smm
109207753Smm	// Parse the first five bytes.
110207753Smm	switch (lzma_properties_decode(&filter, NULL, buf, 5)) {
111207753Smm	case LZMA_OK:
112207753Smm		break;
113207753Smm
114207753Smm	case LZMA_OPTIONS_ERROR:
115207753Smm		fprintf(stderr, "%s: %s: %s\n", progname, name,
116207753Smm				_("Not a .lzma file"));
117207753Smm		return true;
118207753Smm
119207753Smm	case LZMA_MEM_ERROR:
120207753Smm		fprintf(stderr, "%s: %s\n", progname, strerror(ENOMEM));
121207753Smm		exit(EXIT_FAILURE);
122207753Smm
123207753Smm	default:
124207753Smm		fprintf(stderr, "%s: %s\n", progname,
125207753Smm				_("Internal error (bug)"));
126207753Smm		exit(EXIT_FAILURE);
127207753Smm	}
128207753Smm
129207753Smm	// Uncompressed size
130207753Smm	uint64_t uncompressed_size = 0;
131207753Smm	for (size_t i = 0; i < 8; ++i)
132207753Smm		uncompressed_size |= (uint64_t)(buf[5 + i]) << (i * 8);
133207753Smm
134207753Smm	// Display the results. We don't want to translate these and also
135207753Smm	// will use MB instead of MiB, because someone could be parsing
136207753Smm	// this output and we don't want to break that when people move
137207753Smm	// from LZMA Utils to XZ Utils.
138207753Smm	if (f != stdin)
139207753Smm		printf("%s\n", name);
140207753Smm
141207753Smm	printf("Uncompressed size:             ");
142207753Smm	if (uncompressed_size == UINT64_MAX)
143207753Smm		printf("Unknown");
144207753Smm	else
145207753Smm		printf("%" PRIu64 " MB (%" PRIu64 " bytes)",
146207753Smm				(uncompressed_size + 512 * 1024)
147207753Smm					/ (1024 * 1024),
148207753Smm				uncompressed_size);
149207753Smm
150207753Smm	lzma_options_lzma *opt = filter.options;
151207753Smm
152207753Smm	printf("\nDictionary size:               "
153207753Smm			"%u MB (2^%u bytes)\n"
154207753Smm			"Literal context bits (lc):     %" PRIu32 "\n"
155207753Smm			"Literal pos bits (lp):         %" PRIu32 "\n"
156207753Smm			"Number of pos bits (pb):       %" PRIu32 "\n",
157207753Smm			(opt->dict_size + 512 * 1024) / (1024 * 1024),
158207753Smm			my_log2(opt->dict_size), opt->lc, opt->lp, opt->pb);
159207753Smm
160207753Smm	free(opt);
161207753Smm
162207753Smm	return false;
163207753Smm}
164207753Smm
165207753Smm
166207753Smmextern int
167207753Smmmain(int argc, char **argv)
168207753Smm{
169207753Smm	tuklib_progname_init(argv);
170207753Smm	tuklib_gettext_init(PACKAGE, LOCALEDIR);
171207753Smm
172207753Smm	parse_args(argc, argv);
173207753Smm
174207753Smm	int ret = EXIT_SUCCESS;
175207753Smm
176207753Smm	// We print empty lines around the output only when reading from
177207753Smm	// files specified on the command line. This is due to how
178207753Smm	// LZMA Utils did it.
179207753Smm	if (optind == argc) {
180207753Smm		if (lzmainfo("(stdin)", stdin))
181207753Smm			ret = EXIT_FAILURE;
182207753Smm	} else {
183207753Smm		printf("\n");
184207753Smm
185207753Smm		do {
186207753Smm			if (strcmp(argv[optind], "-") == 0) {
187207753Smm				if (lzmainfo("(stdin)", stdin))
188207753Smm					ret = EXIT_FAILURE;
189207753Smm			} else {
190207753Smm				FILE *f = fopen(argv[optind], "r");
191207753Smm				if (f == NULL) {
192207753Smm					ret = EXIT_FAILURE;
193207753Smm					fprintf(stderr, "%s: %s: %s\n",
194207753Smm							progname,
195207753Smm							argv[optind],
196207753Smm							strerror(errno));
197207753Smm					continue;
198207753Smm				}
199207753Smm
200207753Smm				if (lzmainfo(argv[optind], f))
201207753Smm					ret = EXIT_FAILURE;
202207753Smm
203207753Smm				printf("\n");
204207753Smm				fclose(f);
205207753Smm			}
206207753Smm		} while (++optind < argc);
207207753Smm	}
208207753Smm
209207753Smm	tuklib_exit(ret, EXIT_FAILURE, true);
210207753Smm}
211