CI: Update Upload Artifact Action.
[xz.git] / src / lzmainfo / lzmainfo.c
blob71e62958ad6756e307d8936f086f4600c2a9eb68
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.
11 ///////////////////////////////////////////////////////////////////////////////
13 #include "sysdefs.h"
14 #include <stdio.h>
15 #include <errno.h>
17 #include "lzma.h"
18 #include "getopt.h"
19 #include "tuklib_gettext.h"
20 #include "tuklib_progname.h"
21 #include "tuklib_exit.h"
23 #ifdef TUKLIB_DOSLIKE
24 # include <fcntl.h>
25 # include <io.h>
26 #endif
29 tuklib_attr_noreturn
30 static void
31 help(void)
33 printf(
34 _("Usage: %s [--help] [--version] [FILE]...\n"
35 "Show information stored in the .lzma file header"), progname);
37 printf(_(
38 "\nWith no FILE, or when FILE is -, read standard input.\n"));
39 printf("\n");
41 printf(_("Report bugs to <%s> (in English or Finnish).\n"),
42 PACKAGE_BUGREPORT);
43 printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
45 tuklib_exit(EXIT_SUCCESS, EXIT_FAILURE, true);
49 tuklib_attr_noreturn
50 static void
51 version(void)
53 puts("lzmainfo (" PACKAGE_NAME ") " LZMA_VERSION_STRING);
54 tuklib_exit(EXIT_SUCCESS, EXIT_FAILURE, true);
58 /// Parse command line options.
59 static void
60 parse_args(int argc, char **argv)
62 enum {
63 OPT_HELP,
64 OPT_VERSION,
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 }
73 int c;
74 while ((c = getopt_long(argc, argv, "", long_opts, NULL)) != -1) {
75 switch (c) {
76 case OPT_HELP:
77 help();
79 case OPT_VERSION:
80 version();
82 default:
83 exit(EXIT_FAILURE);
87 return;
91 /// Primitive base-2 logarithm for integers
92 static uint32_t
93 my_log2(uint32_t n)
95 uint32_t e;
96 for (e = 0; n > 1; ++e, n /= 2) ;
97 return e;
101 /// Parse the .lzma header and display information about it.
102 static bool
103 lzmainfo(const char *name, FILE *f)
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;
114 lzma_filter filter = { .id = LZMA_FILTER_LZMA1 };
116 // Parse the first five bytes.
117 switch (lzma_properties_decode(&filter, NULL, buf, 5)) {
118 case LZMA_OK:
119 break;
121 case LZMA_OPTIONS_ERROR:
122 fprintf(stderr, "%s: %s: %s\n", progname, name,
123 _("Not a .lzma file"));
124 return true;
126 case LZMA_MEM_ERROR:
127 fprintf(stderr, "%s: %s\n", progname, strerror(ENOMEM));
128 exit(EXIT_FAILURE);
130 default:
131 fprintf(stderr, "%s: %s\n", progname,
132 _("Internal error (bug)"));
133 exit(EXIT_FAILURE);
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);
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);
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);
157 lzma_options_lzma *opt = filter.options;
159 printf("\nDictionary size: "
160 "%" PRIu32 " MB (2^%" PRIu32 " 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);
167 free(opt);
169 return false;
173 extern int
174 main(int argc, char **argv)
176 tuklib_progname_init(argv);
177 tuklib_gettext_init(PACKAGE, LOCALEDIR);
179 parse_args(argc, argv);
181 #ifdef TUKLIB_DOSLIKE
182 setmode(fileno(stdin), O_BINARY);
183 #endif
185 int ret = EXIT_SUCCESS;
187 // We print empty lines around the output only when reading from
188 // files specified on the command line. This is due to how
189 // LZMA Utils did it.
190 if (optind == argc) {
191 if (lzmainfo("(stdin)", stdin))
192 ret = EXIT_FAILURE;
193 } else {
194 printf("\n");
196 do {
197 if (strcmp(argv[optind], "-") == 0) {
198 if (lzmainfo("(stdin)", stdin))
199 ret = EXIT_FAILURE;
200 } else {
201 FILE *f = fopen(argv[optind], "r");
202 if (f == NULL) {
203 ret = EXIT_FAILURE;
204 fprintf(stderr, "%s: %s: %s\n",
205 progname,
206 argv[optind],
207 strerror(errno));
208 continue;
211 if (lzmainfo(argv[optind], f))
212 ret = EXIT_FAILURE;
214 printf("\n");
215 fclose(f);
217 } while (++optind < argc);
220 tuklib_exit(ret, EXIT_FAILURE, true);