1 // SPDX-License-Identifier: 0BSD 2 3 /////////////////////////////////////////////////////////////////////////////// 4 // 5 /// \file lzmainfo.c 6 /// \brief lzmainfo tool for compatibility with LZMA Utils 7 // 8 // Author: Lasse Collin 9 // 10 /////////////////////////////////////////////////////////////////////////////// 11 12 #include "sysdefs.h" 13 #include <stdio.h> 14 #include <errno.h> 15 16 #include "lzma.h" 17 #include "getopt.h" 18 #include "tuklib_gettext.h" 19 #include "tuklib_progname.h" 20 #include "tuklib_exit.h" 21 22 #ifdef TUKLIB_DOSLIKE 23 # include <fcntl.h> 24 # include <io.h> 25 #endif 26 27 28 tuklib_attr_noreturn 29 static void 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 tuklib_attr_noreturn 49 static void 50 version(void) 51 { 52 puts("lzmainfo (" PACKAGE_NAME ") " LZMA_VERSION_STRING); 53 tuklib_exit(EXIT_SUCCESS, EXIT_FAILURE, true); 54 } 55 56 57 /// Parse command line options. 58 static void 59 parse_args(int argc, char **argv) 60 { 61 enum { 62 OPT_HELP, 63 OPT_VERSION, 64 }; 65 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 } 70 }; 71 72 int c; 73 while ((c = getopt_long(argc, argv, "", long_opts, NULL)) != -1) { 74 switch (c) { 75 case OPT_HELP: 76 help(); 77 78 case OPT_VERSION: 79 version(); 80 81 default: 82 exit(EXIT_FAILURE); 83 } 84 } 85 86 return; 87 } 88 89 90 /// Primitive base-2 logarithm for integers 91 static uint32_t 92 my_log2(uint32_t n) 93 { 94 uint32_t e; 95 for (e = 0; n > 1; ++e, n /= 2) ; 96 return e; 97 } 98 99 100 /// Parse the .lzma header and display information about it. 101 static bool 102 lzmainfo(const char *name, FILE *f) 103 { 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; 111 } 112 113 lzma_filter filter = { .id = LZMA_FILTER_LZMA1 }; 114 115 // Parse the first five bytes. 116 switch (lzma_properties_decode(&filter, NULL, buf, 5)) { 117 case LZMA_OK: 118 break; 119 120 case LZMA_OPTIONS_ERROR: 121 fprintf(stderr, "%s: %s: %s\n", progname, name, 122 _("Not a .lzma file")); 123 return true; 124 125 case LZMA_MEM_ERROR: 126 fprintf(stderr, "%s: %s\n", progname, strerror(ENOMEM)); 127 exit(EXIT_FAILURE); 128 129 default: 130 fprintf(stderr, "%s: %s\n", progname, 131 _("Internal error (bug)")); 132 exit(EXIT_FAILURE); 133 } 134 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); 139 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); 146 147 printf("Uncompressed size: "); 148 if (uncompressed_size == UINT64_MAX) 149 printf("Unknown"); 150 else 151 printf("%" PRIu64 " MB (%" PRIu64 " bytes)", 152 (uncompressed_size + 512 * 1024) 153 / (1024 * 1024), 154 uncompressed_size); 155 156 lzma_options_lzma *opt = filter.options; 157 158 printf("\nDictionary size: " 159 "%" PRIu32 " MB (2^%" PRIu32 " bytes)\n" 160 "Literal context bits (lc): %" PRIu32 "\n" 161 "Literal pos bits (lp): %" PRIu32 "\n" 162 "Number of pos bits (pb): %" PRIu32 "\n", 163 (opt->dict_size + 512 * 1024) / (1024 * 1024), 164 my_log2(opt->dict_size), opt->lc, opt->lp, opt->pb); 165 166 free(opt); 167 168 return false; 169 } 170 171 172 extern int 173 main(int argc, char **argv) 174 { 175 tuklib_progname_init(argv); 176 tuklib_gettext_init(PACKAGE, LOCALEDIR); 177 178 parse_args(argc, argv); 179 180 #ifdef TUKLIB_DOSLIKE 181 setmode(fileno(stdin), O_BINARY); 182 #endif 183 184 int ret = EXIT_SUCCESS; 185 186 // We print empty lines around the output only when reading from 187 // files specified on the command line. This is due to how 188 // LZMA Utils did it. 189 if (optind == argc) { 190 if (lzmainfo("(stdin)", stdin)) 191 ret = EXIT_FAILURE; 192 } else { 193 printf("\n"); 194 195 do { 196 if (strcmp(argv[optind], "-") == 0) { 197 if (lzmainfo("(stdin)", stdin)) 198 ret = EXIT_FAILURE; 199 } else { 200 FILE *f = fopen(argv[optind], "r"); 201 if (f == NULL) { 202 ret = EXIT_FAILURE; 203 fprintf(stderr, "%s: %s: %s\n", 204 progname, 205 argv[optind], 206 strerror(errno)); 207 continue; 208 } 209 210 if (lzmainfo(argv[optind], f)) 211 ret = EXIT_FAILURE; 212 213 printf("\n"); 214 fclose(f); 215 } 216 } while (++optind < argc); 217 } 218 219 tuklib_exit(ret, EXIT_FAILURE, true); 220 } 221