Update THANKS
[xz/debian.git] / src / lzmainfo / lzmainfo.c
blobd917f371c3ba7b592d771e469926445a11c73c97
1 // SPDX-License-Identifier: 0BSD
3 ///////////////////////////////////////////////////////////////////////////////
4 //
5 /// \file lzmainfo.c
6 /// \brief lzmainfo tool for compatibility with LZMA Utils
7 //
8 // Author: Lasse Collin
9 //
10 ///////////////////////////////////////////////////////////////////////////////
12 #include "sysdefs.h"
13 #include <stdio.h>
14 #include <errno.h>
16 #include "lzma.h"
17 #include "getopt.h"
18 #include "tuklib_gettext.h"
19 #include "tuklib_progname.h"
20 #include "tuklib_exit.h"
22 #ifdef TUKLIB_DOSLIKE
23 # include <fcntl.h>
24 # include <io.h>
25 #endif
28 tuklib_attr_noreturn
29 static void
30 help(void)
32 printf(
33 _("Usage: %s [--help] [--version] [FILE]...\n"
34 "Show information stored in the .lzma file header"), progname);
36 printf(_(
37 "\nWith no FILE, or when FILE is -, read standard input.\n"));
38 printf("\n");
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);
44 tuklib_exit(EXIT_SUCCESS, EXIT_FAILURE, true);
48 tuklib_attr_noreturn
49 static void
50 version(void)
52 puts("lzmainfo (" PACKAGE_NAME ") " LZMA_VERSION_STRING);
53 tuklib_exit(EXIT_SUCCESS, EXIT_FAILURE, true);
57 /// Parse command line options.
58 static void
59 parse_args(int argc, char **argv)
61 enum {
62 OPT_HELP,
63 OPT_VERSION,
66 static const struct option long_opts[] = {
67 { "help", no_argument, NULL, OPT_HELP },
68 { "version", no_argument, NULL, OPT_VERSION },
69 { NULL, 0, NULL, 0 }
72 int c;
73 while ((c = getopt_long(argc, argv, "", long_opts, NULL)) != -1) {
74 switch (c) {
75 case OPT_HELP:
76 help();
78 case OPT_VERSION:
79 version();
81 default:
82 exit(EXIT_FAILURE);
86 return;
90 /// Primitive base-2 logarithm for integers
91 static uint32_t
92 my_log2(uint32_t n)
94 uint32_t e;
95 for (e = 0; n > 1; ++e, n /= 2) ;
96 return e;
100 /// Parse the .lzma header and display information about it.
101 static bool
102 lzmainfo(const char *name, FILE *f)
104 uint8_t buf[13];
105 const size_t size = fread(buf, 1, sizeof(buf), f);
106 if (size != 13) {
107 fprintf(stderr, "%s: %s: %s\n", progname, name,
108 ferror(f) ? strerror(errno)
109 : _("File is too small to be a .lzma file"));
110 return true;
113 lzma_filter filter = { .id = LZMA_FILTER_LZMA1 };
115 // Parse the first five bytes.
116 switch (lzma_properties_decode(&filter, NULL, buf, 5)) {
117 case LZMA_OK:
118 break;
120 case LZMA_OPTIONS_ERROR:
121 fprintf(stderr, "%s: %s: %s\n", progname, name,
122 _("Not a .lzma file"));
123 return true;
125 case LZMA_MEM_ERROR:
126 fprintf(stderr, "%s: %s\n", progname, strerror(ENOMEM));
127 exit(EXIT_FAILURE);
129 default:
130 fprintf(stderr, "%s: %s\n", progname,
131 _("Internal error (bug)"));
132 exit(EXIT_FAILURE);
135 // Uncompressed size
136 uint64_t uncompressed_size = 0;
137 for (size_t i = 0; i < 8; ++i)
138 uncompressed_size |= (uint64_t)(buf[5 + i]) << (i * 8);
140 // Display the results. We don't want to translate these and also
141 // will use MB instead of MiB, because someone could be parsing
142 // this output and we don't want to break that when people move
143 // from LZMA Utils to XZ Utils.
144 if (f != stdin)
145 printf("%s\n", name);
147 printf("Uncompressed size: ");
148 if (uncompressed_size == UINT64_MAX)
149 printf("Unknown");
150 else
151 printf("%" PRIu64 " MB (%" PRIu64 " bytes)",
152 (uncompressed_size / 1024 + 512) / 1024,
153 uncompressed_size);
155 lzma_options_lzma *opt = filter.options;
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 / 1024 + 512) / 1024,
163 my_log2(opt->dict_size), opt->lc, opt->lp, opt->pb);
165 free(opt);
167 return false;
171 extern int
172 main(int argc, char **argv)
174 tuklib_progname_init(argv);
175 tuklib_gettext_init(PACKAGE, LOCALEDIR);
177 parse_args(argc, argv);
179 #ifdef TUKLIB_DOSLIKE
180 setmode(fileno(stdin), O_BINARY);
181 #endif
183 int ret = EXIT_SUCCESS;
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");
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;
209 if (lzmainfo(argv[optind], f))
210 ret = EXIT_FAILURE;
212 printf("\n");
213 fclose(f);
215 } while (++optind < argc);
218 tuklib_exit(ret, EXIT_FAILURE, true);