xref: /freebsd/contrib/xz/src/lzmainfo/lzmainfo.c (revision 128836d304d93f2d00eb14069c27089ab46c38d4)
13b35e7eeSXin LI // SPDX-License-Identifier: 0BSD
23b35e7eeSXin LI 
381ad8388SMartin Matuska ///////////////////////////////////////////////////////////////////////////////
481ad8388SMartin Matuska //
581ad8388SMartin Matuska /// \file       lzmainfo.c
681ad8388SMartin Matuska /// \brief      lzmainfo tool for compatibility with LZMA Utils
781ad8388SMartin Matuska //
881ad8388SMartin Matuska //  Author:     Lasse Collin
981ad8388SMartin Matuska //
1081ad8388SMartin Matuska ///////////////////////////////////////////////////////////////////////////////
1181ad8388SMartin Matuska 
1281ad8388SMartin Matuska #include "sysdefs.h"
1381ad8388SMartin Matuska #include <stdio.h>
1481ad8388SMartin Matuska #include <errno.h>
1581ad8388SMartin Matuska 
1681ad8388SMartin Matuska #include "lzma.h"
1781ad8388SMartin Matuska #include "getopt.h"
1881ad8388SMartin Matuska #include "tuklib_gettext.h"
1981ad8388SMartin Matuska #include "tuklib_progname.h"
20*128836d3SXin LI #include "tuklib_mbstr_nonprint.h"
21*128836d3SXin LI #include "tuklib_mbstr_wrap.h"
2281ad8388SMartin Matuska #include "tuklib_exit.h"
2381ad8388SMartin Matuska 
24e0f0e66dSMartin Matuska #ifdef TUKLIB_DOSLIKE
25e0f0e66dSMartin Matuska #	include <fcntl.h>
26e0f0e66dSMartin Matuska #	include <io.h>
27e0f0e66dSMartin Matuska #endif
28e0f0e66dSMartin Matuska 
2981ad8388SMartin Matuska 
30ca6a6373SXin LI tuklib_attr_noreturn
31ca6a6373SXin LI static void
help(void)3281ad8388SMartin Matuska help(void)
3381ad8388SMartin Matuska {
34*128836d3SXin LI 	// A few languages use so long strings that we need automatic
35*128836d3SXin LI 	// wrapping. A few strings are the same as in xz/message.c and
36*128836d3SXin LI 	// should be kept in sync.
37*128836d3SXin LI 	static const struct tuklib_wrap_opt wrap0 = {  0,  0,  0,  0, 79 };
38*128836d3SXin LI 	int e = 0;
3981ad8388SMartin Matuska 
40*128836d3SXin LI 	printf(_("Usage: %s [--help] [--version] [FILE]...\n"), progname);
4181ad8388SMartin Matuska 
42*128836d3SXin LI 	e |= tuklib_wraps(stdout, &wrap0,
43*128836d3SXin LI 		W_("Show information stored in the .lzma file header."));
44*128836d3SXin LI 	e |= tuklib_wraps(stdout, &wrap0,
45*128836d3SXin LI 		W_("With no FILE, or when FILE is -, read standard input."));
46*128836d3SXin LI 
47*128836d3SXin LI 	putchar('\n');
48*128836d3SXin LI 
49*128836d3SXin LI 	e |= tuklib_wrapf(stdout, &wrap0,
50*128836d3SXin LI 			W_("Report bugs to <%s> (in English or Finnish)."),
5181ad8388SMartin Matuska 			PACKAGE_BUGREPORT);
52*128836d3SXin LI 
53*128836d3SXin LI 	e |= tuklib_wrapf(stdout, &wrap0,
54*128836d3SXin LI 			W_("%s home page: <%s>"), PACKAGE_NAME, PACKAGE_URL);
55*128836d3SXin LI 
56*128836d3SXin LI 	if (e != 0) {
57*128836d3SXin LI 		// Avoid new translatable strings by printing the message
58*128836d3SXin LI 		// in pieces.
59*128836d3SXin LI 		fprintf(stderr, _("%s: "), progname);
60*128836d3SXin LI 		fprintf(stderr, _("Error printing the help text "
61*128836d3SXin LI 				"(error code %d)"), e);
62*128836d3SXin LI 		fprintf(stderr, "\n");
63*128836d3SXin LI 	}
6481ad8388SMartin Matuska 
6581ad8388SMartin Matuska 	tuklib_exit(EXIT_SUCCESS, EXIT_FAILURE, true);
6681ad8388SMartin Matuska }
6781ad8388SMartin Matuska 
6881ad8388SMartin Matuska 
69ca6a6373SXin LI tuklib_attr_noreturn
70ca6a6373SXin LI static void
version(void)7181ad8388SMartin Matuska version(void)
7281ad8388SMartin Matuska {
73e0f0e66dSMartin Matuska 	puts("lzmainfo (" PACKAGE_NAME ") " LZMA_VERSION_STRING);
7481ad8388SMartin Matuska 	tuklib_exit(EXIT_SUCCESS, EXIT_FAILURE, true);
7581ad8388SMartin Matuska }
7681ad8388SMartin Matuska 
7781ad8388SMartin Matuska 
7881ad8388SMartin Matuska /// Parse command line options.
7981ad8388SMartin Matuska static void
parse_args(int argc,char ** argv)8081ad8388SMartin Matuska parse_args(int argc, char **argv)
8181ad8388SMartin Matuska {
8281ad8388SMartin Matuska 	enum {
8381ad8388SMartin Matuska 		OPT_HELP,
8481ad8388SMartin Matuska 		OPT_VERSION,
8581ad8388SMartin Matuska 	};
8681ad8388SMartin Matuska 
8781ad8388SMartin Matuska 	static const struct option long_opts[] = {
8881ad8388SMartin Matuska 		{ "help",    no_argument, NULL, OPT_HELP },
8981ad8388SMartin Matuska 		{ "version", no_argument, NULL, OPT_VERSION },
9081ad8388SMartin Matuska 		{ NULL,      0,           NULL, 0 }
9181ad8388SMartin Matuska 	};
9281ad8388SMartin Matuska 
9381ad8388SMartin Matuska 	int c;
9481ad8388SMartin Matuska 	while ((c = getopt_long(argc, argv, "", long_opts, NULL)) != -1) {
9581ad8388SMartin Matuska 		switch (c) {
9681ad8388SMartin Matuska 		case OPT_HELP:
9781ad8388SMartin Matuska 			help();
9881ad8388SMartin Matuska 
9981ad8388SMartin Matuska 		case OPT_VERSION:
10081ad8388SMartin Matuska 			version();
10181ad8388SMartin Matuska 
10281ad8388SMartin Matuska 		default:
10381ad8388SMartin Matuska 			exit(EXIT_FAILURE);
10481ad8388SMartin Matuska 		}
10581ad8388SMartin Matuska 	}
10681ad8388SMartin Matuska 
10781ad8388SMartin Matuska 	return;
10881ad8388SMartin Matuska }
10981ad8388SMartin Matuska 
11081ad8388SMartin Matuska 
11181ad8388SMartin Matuska /// Primitive base-2 logarithm for integers
11281ad8388SMartin Matuska static uint32_t
my_log2(uint32_t n)11381ad8388SMartin Matuska my_log2(uint32_t n)
11481ad8388SMartin Matuska {
11581ad8388SMartin Matuska 	uint32_t e;
11681ad8388SMartin Matuska 	for (e = 0; n > 1; ++e, n /= 2) ;
11781ad8388SMartin Matuska 	return e;
11881ad8388SMartin Matuska }
11981ad8388SMartin Matuska 
12081ad8388SMartin Matuska 
12181ad8388SMartin Matuska /// Parse the .lzma header and display information about it.
12281ad8388SMartin Matuska static bool
lzmainfo(const char * name,FILE * f)12381ad8388SMartin Matuska lzmainfo(const char *name, FILE *f)
12481ad8388SMartin Matuska {
12581ad8388SMartin Matuska 	uint8_t buf[13];
12681ad8388SMartin Matuska 	const size_t size = fread(buf, 1, sizeof(buf), f);
12781ad8388SMartin Matuska 	if (size != 13) {
128*128836d3SXin LI 		fprintf(stderr, "%s: %s: %s\n", progname,
129*128836d3SXin LI 				tuklib_mask_nonprint(name),
13081ad8388SMartin Matuska 				ferror(f) ? strerror(errno)
13181ad8388SMartin Matuska 				: _("File is too small to be a .lzma file"));
13281ad8388SMartin Matuska 		return true;
13381ad8388SMartin Matuska 	}
13481ad8388SMartin Matuska 
13581ad8388SMartin Matuska 	lzma_filter filter = { .id = LZMA_FILTER_LZMA1 };
13681ad8388SMartin Matuska 
13781ad8388SMartin Matuska 	// Parse the first five bytes.
13881ad8388SMartin Matuska 	switch (lzma_properties_decode(&filter, NULL, buf, 5)) {
13981ad8388SMartin Matuska 	case LZMA_OK:
14081ad8388SMartin Matuska 		break;
14181ad8388SMartin Matuska 
14281ad8388SMartin Matuska 	case LZMA_OPTIONS_ERROR:
143*128836d3SXin LI 		fprintf(stderr, "%s: %s: %s\n", progname,
144*128836d3SXin LI 				tuklib_mask_nonprint(name),
14581ad8388SMartin Matuska 				_("Not a .lzma file"));
14681ad8388SMartin Matuska 		return true;
14781ad8388SMartin Matuska 
14881ad8388SMartin Matuska 	case LZMA_MEM_ERROR:
14981ad8388SMartin Matuska 		fprintf(stderr, "%s: %s\n", progname, strerror(ENOMEM));
15081ad8388SMartin Matuska 		exit(EXIT_FAILURE);
15181ad8388SMartin Matuska 
15281ad8388SMartin Matuska 	default:
15381ad8388SMartin Matuska 		fprintf(stderr, "%s: %s\n", progname,
15481ad8388SMartin Matuska 				_("Internal error (bug)"));
15581ad8388SMartin Matuska 		exit(EXIT_FAILURE);
15681ad8388SMartin Matuska 	}
15781ad8388SMartin Matuska 
15881ad8388SMartin Matuska 	// Uncompressed size
15981ad8388SMartin Matuska 	uint64_t uncompressed_size = 0;
16081ad8388SMartin Matuska 	for (size_t i = 0; i < 8; ++i)
16181ad8388SMartin Matuska 		uncompressed_size |= (uint64_t)(buf[5 + i]) << (i * 8);
16281ad8388SMartin Matuska 
16381ad8388SMartin Matuska 	// Display the results. We don't want to translate these and also
16481ad8388SMartin Matuska 	// will use MB instead of MiB, because someone could be parsing
16581ad8388SMartin Matuska 	// this output and we don't want to break that when people move
16681ad8388SMartin Matuska 	// from LZMA Utils to XZ Utils.
16781ad8388SMartin Matuska 	if (f != stdin)
168*128836d3SXin LI 		printf("%s\n", tuklib_mask_nonprint(name));
16981ad8388SMartin Matuska 
17081ad8388SMartin Matuska 	printf("Uncompressed size:             ");
17181ad8388SMartin Matuska 	if (uncompressed_size == UINT64_MAX)
17281ad8388SMartin Matuska 		printf("Unknown");
17381ad8388SMartin Matuska 	else
17481ad8388SMartin Matuska 		printf("%" PRIu64 " MB (%" PRIu64 " bytes)",
17526743408SXin LI 				(uncompressed_size / 1024 + 512) / 1024,
17681ad8388SMartin Matuska 				uncompressed_size);
17781ad8388SMartin Matuska 
17881ad8388SMartin Matuska 	lzma_options_lzma *opt = filter.options;
17981ad8388SMartin Matuska 
18081ad8388SMartin Matuska 	printf("\nDictionary size:               "
181e0f0e66dSMartin Matuska 			"%" PRIu32 " MB (2^%" PRIu32 " bytes)\n"
18281ad8388SMartin Matuska 			"Literal context bits (lc):     %" PRIu32 "\n"
18381ad8388SMartin Matuska 			"Literal pos bits (lp):         %" PRIu32 "\n"
18481ad8388SMartin Matuska 			"Number of pos bits (pb):       %" PRIu32 "\n",
18526743408SXin LI 			(opt->dict_size / 1024 + 512) / 1024,
18681ad8388SMartin Matuska 			my_log2(opt->dict_size), opt->lc, opt->lp, opt->pb);
18781ad8388SMartin Matuska 
18881ad8388SMartin Matuska 	free(opt);
18981ad8388SMartin Matuska 
19081ad8388SMartin Matuska 	return false;
19181ad8388SMartin Matuska }
19281ad8388SMartin Matuska 
19381ad8388SMartin Matuska 
19481ad8388SMartin Matuska extern int
main(int argc,char ** argv)19581ad8388SMartin Matuska main(int argc, char **argv)
19681ad8388SMartin Matuska {
19781ad8388SMartin Matuska 	tuklib_progname_init(argv);
19881ad8388SMartin Matuska 	tuklib_gettext_init(PACKAGE, LOCALEDIR);
19981ad8388SMartin Matuska 
20081ad8388SMartin Matuska 	parse_args(argc, argv);
20181ad8388SMartin Matuska 
202e0f0e66dSMartin Matuska #ifdef TUKLIB_DOSLIKE
203e0f0e66dSMartin Matuska 	setmode(fileno(stdin), O_BINARY);
204e0f0e66dSMartin Matuska #endif
205e0f0e66dSMartin Matuska 
20681ad8388SMartin Matuska 	int ret = EXIT_SUCCESS;
20781ad8388SMartin Matuska 
20881ad8388SMartin Matuska 	// We print empty lines around the output only when reading from
20981ad8388SMartin Matuska 	// files specified on the command line. This is due to how
21081ad8388SMartin Matuska 	// LZMA Utils did it.
21181ad8388SMartin Matuska 	if (optind == argc) {
21281ad8388SMartin Matuska 		if (lzmainfo("(stdin)", stdin))
21381ad8388SMartin Matuska 			ret = EXIT_FAILURE;
21481ad8388SMartin Matuska 	} else {
21581ad8388SMartin Matuska 		printf("\n");
21681ad8388SMartin Matuska 
21781ad8388SMartin Matuska 		do {
21881ad8388SMartin Matuska 			if (strcmp(argv[optind], "-") == 0) {
21981ad8388SMartin Matuska 				if (lzmainfo("(stdin)", stdin))
22081ad8388SMartin Matuska 					ret = EXIT_FAILURE;
22181ad8388SMartin Matuska 			} else {
22281ad8388SMartin Matuska 				FILE *f = fopen(argv[optind], "r");
22381ad8388SMartin Matuska 				if (f == NULL) {
22481ad8388SMartin Matuska 					ret = EXIT_FAILURE;
22581ad8388SMartin Matuska 					fprintf(stderr, "%s: %s: %s\n",
22681ad8388SMartin Matuska 						progname,
227*128836d3SXin LI 						tuklib_mask_nonprint(
228*128836d3SXin LI 							argv[optind]),
22981ad8388SMartin Matuska 						strerror(errno));
23081ad8388SMartin Matuska 					continue;
23181ad8388SMartin Matuska 				}
23281ad8388SMartin Matuska 
23381ad8388SMartin Matuska 				if (lzmainfo(argv[optind], f))
23481ad8388SMartin Matuska 					ret = EXIT_FAILURE;
23581ad8388SMartin Matuska 
23681ad8388SMartin Matuska 				printf("\n");
23781ad8388SMartin Matuska 				fclose(f);
23881ad8388SMartin Matuska 			}
23981ad8388SMartin Matuska 		} while (++optind < argc);
24081ad8388SMartin Matuska 	}
24181ad8388SMartin Matuska 
24281ad8388SMartin Matuska 	tuklib_exit(ret, EXIT_FAILURE, true);
24381ad8388SMartin Matuska }
244