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 tuklib_attr_noreturn 30 static void 31 help(void) 32 { 33 printf( 34 _("Usage: %s [--help] [--version] [FILE]...\n" 35 "Show information stored in the .lzma file header"), progname); 36 37 printf(_( 38 "\nWith no FILE, or when FILE is -, read standard input.\n")); 39 printf("\n"); 40 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); 44 45 tuklib_exit(EXIT_SUCCESS, EXIT_FAILURE, true); 46 } 47 48 49 tuklib_attr_noreturn 50 static void 51 version(void) 52 { 53 puts("lzmainfo (" PACKAGE_NAME ") " LZMA_VERSION_STRING); 54 tuklib_exit(EXIT_SUCCESS, EXIT_FAILURE, true); 55 } 56 57 58 /// Parse command line options. 59 static void 60 parse_args(int argc, char **argv) 61 { 62 enum { 63 OPT_HELP, 64 OPT_VERSION, 65 }; 66 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 } 71 }; 72 73 int c; 74 while ((c = getopt_long(argc, argv, "", long_opts, NULL)) != -1) { 75 switch (c) { 76 case OPT_HELP: 77 help(); 78 79 case OPT_VERSION: 80 version(); 81 82 default: 83 exit(EXIT_FAILURE); 84 } 85 } 86 87 return; 88 } 89 90 91 /// Primitive base-2 logarithm for integers 92 static uint32_t 93 my_log2(uint32_t n) 94 { 95 uint32_t e; 96 for (e = 0; n > 1; ++e, n /= 2) ; 97 return e; 98 } 99 100 101 /// Parse the .lzma header and display information about it. 102 static bool 103 lzmainfo(const char *name, FILE *f) 104 { 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; 112 } 113 114 lzma_filter filter = { .id = LZMA_FILTER_LZMA1 }; 115 116 // Parse the first five bytes. 117 switch (lzma_properties_decode(&filter, NULL, buf, 5)) { 118 case LZMA_OK: 119 break; 120 121 case LZMA_OPTIONS_ERROR: 122 fprintf(stderr, "%s: %s: %s\n", progname, name, 123 _("Not a .lzma file")); 124 return true; 125 126 case LZMA_MEM_ERROR: 127 fprintf(stderr, "%s: %s\n", progname, strerror(ENOMEM)); 128 exit(EXIT_FAILURE); 129 130 default: 131 fprintf(stderr, "%s: %s\n", progname, 132 _("Internal error (bug)")); 133 exit(EXIT_FAILURE); 134 } 135 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); 140 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); 147 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); 156 157 lzma_options_lzma *opt = filter.options; 158 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); 166 167 free(opt); 168 169 return false; 170 } 171 172 173 extern int 174 main(int argc, char **argv) 175 { 176 tuklib_progname_init(argv); 177 tuklib_gettext_init(PACKAGE, LOCALEDIR); 178 179 parse_args(argc, argv); 180 181 #ifdef TUKLIB_DOSLIKE 182 setmode(fileno(stdin), O_BINARY); 183 #endif 184 185 int ret = EXIT_SUCCESS; 186 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"); 195 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; 209 } 210 211 if (lzmainfo(argv[optind], f)) 212 ret = EXIT_FAILURE; 213 214 printf("\n"); 215 fclose(f); 216 } 217 } while (++optind < argc); 218 } 219 220 tuklib_exit(ret, EXIT_FAILURE, true); 221 } 222