]> icculus.org git repositories - icculus/xz.git/blob - src/lzmainfo/lzmainfo.c
Update tuklib_integer.h with bit scan functions.
[icculus/xz.git] / src / lzmainfo / lzmainfo.c
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 #ifdef ENABLE_NLS
18 #       include <libintl.h>
19 #       define _(msgid) gettext(msgid)
20 #else
21 #       define _(msgid) msgid
22 #endif
23
24 #include "lzma.h"
25 #include "getopt.h"
26 #include "tuklib_gettext.h"
27 #include "tuklib_progname.h"
28 #include "tuklib_exit.h"
29
30
31 static void lzma_attribute((noreturn))
32 help(void)
33 {
34         printf(
35 _("Usage: %s [--help] [--version] [FILE]...\n"
36 "Show information stored in the .lzma file header"), progname);
37
38         printf(_(
39 "\nWith no FILE, or when FILE is -, read standard input.\n"));
40         printf("\n");
41
42         printf(_("Report bugs to <%s> (in English or Finnish).\n"),
43                         PACKAGE_BUGREPORT);
44         printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_HOMEPAGE);
45
46         tuklib_exit(EXIT_SUCCESS, EXIT_FAILURE, true);
47 }
48
49
50 static void lzma_attribute((noreturn))
51 version(void)
52 {
53         puts("lzmainfo (" PACKAGE_NAME ") " PACKAGE_VERSION);
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                         "%u MB (2^%u 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         int ret = EXIT_SUCCESS;
182
183         // We print empty lines around the output only when reading from
184         // files specified on the command line. This is due to how
185         // LZMA Utils did it.
186         if (optind == argc) {
187                 if (lzmainfo("(stdin)", stdin))
188                         ret = EXIT_FAILURE;
189         } else {
190                 printf("\n");
191
192                 do {
193                         if (strcmp(argv[optind], "-") == 0) {
194                                 if (lzmainfo("(stdin)", stdin))
195                                         ret = EXIT_FAILURE;
196                         } else {
197                                 FILE *f = fopen(argv[optind], "r");
198                                 if (f == NULL) {
199                                         ret = EXIT_FAILURE;
200                                         fprintf(stderr, "%s: %s: %s\n",
201                                                         progname,
202                                                         argv[optind],
203                                                         strerror(errno));
204                                         continue;
205                                 }
206
207                                 if (lzmainfo(argv[optind], f))
208                                         ret = EXIT_FAILURE;
209
210                                 printf("\n");
211                                 fclose(f);
212                         }
213                 } while (++optind < argc);
214         }
215
216         tuklib_exit(ret, EXIT_FAILURE, true);
217 }