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 / 1024 + 512) / 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 / 1024 + 512) / 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 } 220