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