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 29static void lzma_attribute((__noreturn__)) 30help(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 48static void lzma_attribute((__noreturn__)) 49version(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. 57static void 58parse_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 90static uint32_t 91my_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. 100static bool 101lzmainfo(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 171extern int 172main(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