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 #ifdef TUKLIB_DOSLIKE 24 # include <fcntl.h> 25 # include <io.h> 26 #endif 27 28 29 static void lzma_attribute((__noreturn__)) 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 static void lzma_attribute((__noreturn__)) 49 version(void) 50 { 51 puts("lzmainfo (" PACKAGE_NAME ") " LZMA_VERSION_STRING); 52 tuklib_exit(EXIT_SUCCESS, EXIT_FAILURE, true); 53 } 54 55 56 /// Parse command line options. 57 static void 58 parse_args(int argc, char **argv) 59 { 60 enum { 61 OPT_HELP, 62 OPT_VERSION, 63 }; 64 65 static const struct option long_opts[] = { 66 { "help", no_argument, NULL, OPT_HELP }, 67 { "version", no_argument, NULL, OPT_VERSION }, 68 { NULL, 0, NULL, 0 } 69 }; 70 71 int c; 72 while ((c = getopt_long(argc, argv, "", long_opts, NULL)) != -1) { 73 switch (c) { 74 case OPT_HELP: 75 help(); 76 77 case OPT_VERSION: 78 version(); 79 80 default: 81 exit(EXIT_FAILURE); 82 } 83 } 84 85 return; 86 } 87 88 89 /// Primitive base-2 logarithm for integers 90 static uint32_t 91 my_log2(uint32_t n) 92 { 93 uint32_t e; 94 for (e = 0; n > 1; ++e, n /= 2) ; 95 return e; 96 } 97 98 99 /// Parse the .lzma header and display information about it. 100 static bool 101 lzmainfo(const char *name, FILE *f) 102 { 103 uint8_t buf[13]; 104 const size_t size = fread(buf, 1, sizeof(buf), f); 105 if (size != 13) { 106 fprintf(stderr, "%s: %s: %s\n", progname, name, 107 ferror(f) ? strerror(errno) 108 : _("File is too small to be a .lzma file")); 109 return true; 110 } 111 112 lzma_filter filter = { .id = LZMA_FILTER_LZMA1 }; 113 114 // Parse the first five bytes. 115 switch (lzma_properties_decode(&filter, NULL, buf, 5)) { 116 case LZMA_OK: 117 break; 118 119 case LZMA_OPTIONS_ERROR: 120 fprintf(stderr, "%s: %s: %s\n", progname, name, 121 _("Not a .lzma file")); 122 return true; 123 124 case LZMA_MEM_ERROR: 125 fprintf(stderr, "%s: %s\n", progname, strerror(ENOMEM)); 126 exit(EXIT_FAILURE); 127 128 default: 129 fprintf(stderr, "%s: %s\n", progname, 130 _("Internal error (bug)")); 131 exit(EXIT_FAILURE); 132 } 133 134 // Uncompressed size 135 uint64_t uncompressed_size = 0; 136 for (size_t i = 0; i < 8; ++i) 137 uncompressed_size |= (uint64_t)(buf[5 + i]) << (i * 8); 138 139 // Display the results. We don't want to translate these and also 140 // will use MB instead of MiB, because someone could be parsing 141 // this output and we don't want to break that when people move 142 // from LZMA Utils to XZ Utils. 143 if (f != stdin) 144 printf("%s\n", name); 145 146 printf("Uncompressed size: "); 147 if (uncompressed_size == UINT64_MAX) 148 printf("Unknown"); 149 else 150 printf("%" PRIu64 " MB (%" PRIu64 " bytes)", 151 (uncompressed_size + 512 * 1024) 152 / (1024 * 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 + 512 * 1024) / (1024 * 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