Import xz-5.0.3.
[dragonfly.git] / contrib / xz / 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 #include "lzma.h"
18 #include "getopt.h"
19 #include "tuklib_gettext.h"
20 #include "tuklib_progname.h"
21 #include "tuklib_exit.h"
22
23 #ifdef TUKLIB_DOSLIKE
24 #       include <fcntl.h>
25 #       include <io.h>
26 #endif
27
28
29 static void lzma_attribute((__noreturn__))
30 help(void)
31 {
32         printf(
33 _("Usage: %s [--help] [--version] [FILE]...\n"
34 "Show information stored in the .lzma file header"), progname);
35
36         printf(_(
37 "\nWith no FILE, or when FILE is -, read standard input.\n"));
38         printf("\n");
39
40         printf(_("Report bugs to <%s> (in English or Finnish).\n"),
41                         PACKAGE_BUGREPORT);
42         printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
43
44         tuklib_exit(EXIT_SUCCESS, EXIT_FAILURE, true);
45 }
46
47
48 static void lzma_attribute((__noreturn__))
49 version(void)
50 {
51         puts("lzmainfo (" PACKAGE_NAME ") " LZMA_VERSION_STRING);
52         tuklib_exit(EXIT_SUCCESS, EXIT_FAILURE, true);
53 }
54
55
56 /// Parse command line options.
57 static void
58 parse_args(int argc, char **argv)
59 {
60         enum {
61                 OPT_HELP,
62                 OPT_VERSION,
63         };
64
65         static const struct option long_opts[] = {
66                 { "help",    no_argument, NULL, OPT_HELP },
67                 { "version", no_argument, NULL, OPT_VERSION },
68                 { NULL,      0,           NULL, 0 }
69         };
70
71         int c;
72         while ((c = getopt_long(argc, argv, "", long_opts, NULL)) != -1) {
73                 switch (c) {
74                 case OPT_HELP:
75                         help();
76
77                 case OPT_VERSION:
78                         version();
79
80                 default:
81                         exit(EXIT_FAILURE);
82                 }
83         }
84
85         return;
86 }
87
88
89 /// Primitive base-2 logarithm for integers
90 static uint32_t
91 my_log2(uint32_t n)
92 {
93         uint32_t e;
94         for (e = 0; n > 1; ++e, n /= 2) ;
95         return e;
96 }
97
98
99 /// Parse the .lzma header and display information about it.
100 static bool
101 lzmainfo(const char *name, FILE *f)
102 {
103         uint8_t buf[13];
104         const size_t size = fread(buf, 1, sizeof(buf), f);
105         if (size != 13) {
106                 fprintf(stderr, "%s: %s: %s\n", progname, name,
107                                 ferror(f) ? strerror(errno)
108                                 : _("File is too small to be a .lzma file"));
109                 return true;
110         }
111
112         lzma_filter filter = { .id = LZMA_FILTER_LZMA1 };
113
114         // Parse the first five bytes.
115         switch (lzma_properties_decode(&filter, NULL, buf, 5)) {
116         case LZMA_OK:
117                 break;
118
119         case LZMA_OPTIONS_ERROR:
120                 fprintf(stderr, "%s: %s: %s\n", progname, name,
121                                 _("Not a .lzma file"));
122                 return true;
123
124         case LZMA_MEM_ERROR:
125                 fprintf(stderr, "%s: %s\n", progname, strerror(ENOMEM));
126                 exit(EXIT_FAILURE);
127
128         default:
129                 fprintf(stderr, "%s: %s\n", progname,
130                                 _("Internal error (bug)"));
131                 exit(EXIT_FAILURE);
132         }
133
134         // Uncompressed size
135         uint64_t uncompressed_size = 0;
136         for (size_t i = 0; i < 8; ++i)
137                 uncompressed_size |= (uint64_t)(buf[5 + i]) << (i * 8);
138
139         // Display the results. We don't want to translate these and also
140         // will use MB instead of MiB, because someone could be parsing
141         // this output and we don't want to break that when people move
142         // from LZMA Utils to XZ Utils.
143         if (f != stdin)
144                 printf("%s\n", name);
145
146         printf("Uncompressed size:             ");
147         if (uncompressed_size == UINT64_MAX)
148                 printf("Unknown");
149         else
150                 printf("%" PRIu64 " MB (%" PRIu64 " bytes)",
151                                 (uncompressed_size + 512 * 1024)
152                                         / (1024 * 1024),
153                                 uncompressed_size);
154
155         lzma_options_lzma *opt = filter.options;
156
157         printf("\nDictionary size:               "
158                         "%" PRIu32 " MB (2^%" PRIu32 " bytes)\n"
159                         "Literal context bits (lc):     %" PRIu32 "\n"
160                         "Literal pos bits (lp):         %" PRIu32 "\n"
161                         "Number of pos bits (pb):       %" PRIu32 "\n",
162                         (opt->dict_size + 512 * 1024) / (1024 * 1024),
163                         my_log2(opt->dict_size), opt->lc, opt->lp, opt->pb);
164
165         free(opt);
166
167         return false;
168 }
169
170
171 extern int
172 main(int argc, char **argv)
173 {
174         tuklib_progname_init(argv);
175         tuklib_gettext_init(PACKAGE, LOCALEDIR);
176
177         parse_args(argc, argv);
178
179 #ifdef TUKLIB_DOSLIKE
180         setmode(fileno(stdin), O_BINARY);
181 #endif
182
183         int ret = EXIT_SUCCESS;
184
185         // We print empty lines around the output only when reading from
186         // files specified on the command line. This is due to how
187         // LZMA Utils did it.
188         if (optind == argc) {
189                 if (lzmainfo("(stdin)", stdin))
190                         ret = EXIT_FAILURE;
191         } else {
192                 printf("\n");
193
194                 do {
195                         if (strcmp(argv[optind], "-") == 0) {
196                                 if (lzmainfo("(stdin)", stdin))
197                                         ret = EXIT_FAILURE;
198                         } else {
199                                 FILE *f = fopen(argv[optind], "r");
200                                 if (f == NULL) {
201                                         ret = EXIT_FAILURE;
202                                         fprintf(stderr, "%s: %s: %s\n",
203                                                         progname,
204                                                         argv[optind],
205                                                         strerror(errno));
206                                         continue;
207                                 }
208
209                                 if (lzmainfo(argv[optind], f))
210                                         ret = EXIT_FAILURE;
211
212                                 printf("\n");
213                                 fclose(f);
214                         }
215                 } while (++optind < argc);
216         }
217
218         tuklib_exit(ret, EXIT_FAILURE, true);
219 }