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