1207753Smm/////////////////////////////////////////////////////////////////////////////// 2207753Smm// 3207753Smm/// \file lzmainfo.c 4207753Smm/// \brief lzmainfo tool for compatibility with LZMA Utils 5207753Smm// 6207753Smm// Author: Lasse Collin 7207753Smm// 8207753Smm// This file has been put into the public domain. 9207753Smm// You can do whatever you want with this file. 10207753Smm// 11207753Smm/////////////////////////////////////////////////////////////////////////////// 12207753Smm 13207753Smm#include "sysdefs.h" 14207753Smm#include <stdio.h> 15207753Smm#include <errno.h> 16207753Smm 17207753Smm#include "lzma.h" 18207753Smm#include "getopt.h" 19207753Smm#include "tuklib_gettext.h" 20207753Smm#include "tuklib_progname.h" 21207753Smm#include "tuklib_exit.h" 22207753Smm 23213700Smm#ifdef TUKLIB_DOSLIKE 24213700Smm# include <fcntl.h> 25213700Smm# include <io.h> 26213700Smm#endif 27207753Smm 28213700Smm 29223935Smmstatic void lzma_attribute((__noreturn__)) 30207753Smmhelp(void) 31207753Smm{ 32207753Smm printf( 33207753Smm_("Usage: %s [--help] [--version] [FILE]...\n" 34207753Smm"Show information stored in the .lzma file header"), progname); 35207753Smm 36207753Smm printf(_( 37207753Smm"\nWith no FILE, or when FILE is -, read standard input.\n")); 38207753Smm printf("\n"); 39207753Smm 40207753Smm printf(_("Report bugs to <%s> (in English or Finnish).\n"), 41207753Smm PACKAGE_BUGREPORT); 42207753Smm printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL); 43207753Smm 44207753Smm tuklib_exit(EXIT_SUCCESS, EXIT_FAILURE, true); 45207753Smm} 46207753Smm 47207753Smm 48223935Smmstatic void lzma_attribute((__noreturn__)) 49207753Smmversion(void) 50207753Smm{ 51213700Smm puts("lzmainfo (" PACKAGE_NAME ") " LZMA_VERSION_STRING); 52207753Smm tuklib_exit(EXIT_SUCCESS, EXIT_FAILURE, true); 53207753Smm} 54207753Smm 55207753Smm 56207753Smm/// Parse command line options. 57207753Smmstatic void 58207753Smmparse_args(int argc, char **argv) 59207753Smm{ 60207753Smm enum { 61207753Smm OPT_HELP, 62207753Smm OPT_VERSION, 63207753Smm }; 64207753Smm 65207753Smm static const struct option long_opts[] = { 66207753Smm { "help", no_argument, NULL, OPT_HELP }, 67207753Smm { "version", no_argument, NULL, OPT_VERSION }, 68207753Smm { NULL, 0, NULL, 0 } 69207753Smm }; 70207753Smm 71207753Smm int c; 72207753Smm while ((c = getopt_long(argc, argv, "", long_opts, NULL)) != -1) { 73207753Smm switch (c) { 74207753Smm case OPT_HELP: 75207753Smm help(); 76207753Smm 77207753Smm case OPT_VERSION: 78207753Smm version(); 79207753Smm 80207753Smm default: 81207753Smm exit(EXIT_FAILURE); 82207753Smm } 83207753Smm } 84207753Smm 85207753Smm return; 86207753Smm} 87207753Smm 88207753Smm 89207753Smm/// Primitive base-2 logarithm for integers 90207753Smmstatic uint32_t 91207753Smmmy_log2(uint32_t n) 92207753Smm{ 93207753Smm uint32_t e; 94207753Smm for (e = 0; n > 1; ++e, n /= 2) ; 95207753Smm return e; 96207753Smm} 97207753Smm 98207753Smm 99207753Smm/// Parse the .lzma header and display information about it. 100207753Smmstatic bool 101207753Smmlzmainfo(const char *name, FILE *f) 102207753Smm{ 103207753Smm uint8_t buf[13]; 104207753Smm const size_t size = fread(buf, 1, sizeof(buf), f); 105207753Smm if (size != 13) { 106207753Smm fprintf(stderr, "%s: %s: %s\n", progname, name, 107207753Smm ferror(f) ? strerror(errno) 108207753Smm : _("File is too small to be a .lzma file")); 109207753Smm return true; 110207753Smm } 111207753Smm 112207753Smm lzma_filter filter = { .id = LZMA_FILTER_LZMA1 }; 113207753Smm 114207753Smm // Parse the first five bytes. 115207753Smm switch (lzma_properties_decode(&filter, NULL, buf, 5)) { 116207753Smm case LZMA_OK: 117207753Smm break; 118207753Smm 119207753Smm case LZMA_OPTIONS_ERROR: 120207753Smm fprintf(stderr, "%s: %s: %s\n", progname, name, 121207753Smm _("Not a .lzma file")); 122207753Smm return true; 123207753Smm 124207753Smm case LZMA_MEM_ERROR: 125207753Smm fprintf(stderr, "%s: %s\n", progname, strerror(ENOMEM)); 126207753Smm exit(EXIT_FAILURE); 127207753Smm 128207753Smm default: 129207753Smm fprintf(stderr, "%s: %s\n", progname, 130207753Smm _("Internal error (bug)")); 131207753Smm exit(EXIT_FAILURE); 132207753Smm } 133207753Smm 134207753Smm // Uncompressed size 135207753Smm uint64_t uncompressed_size = 0; 136207753Smm for (size_t i = 0; i < 8; ++i) 137207753Smm uncompressed_size |= (uint64_t)(buf[5 + i]) << (i * 8); 138207753Smm 139207753Smm // Display the results. We don't want to translate these and also 140207753Smm // will use MB instead of MiB, because someone could be parsing 141207753Smm // this output and we don't want to break that when people move 142207753Smm // from LZMA Utils to XZ Utils. 143207753Smm if (f != stdin) 144207753Smm printf("%s\n", name); 145207753Smm 146207753Smm printf("Uncompressed size: "); 147207753Smm if (uncompressed_size == UINT64_MAX) 148207753Smm printf("Unknown"); 149207753Smm else 150207753Smm printf("%" PRIu64 " MB (%" PRIu64 " bytes)", 151207753Smm (uncompressed_size + 512 * 1024) 152207753Smm / (1024 * 1024), 153207753Smm uncompressed_size); 154207753Smm 155207753Smm lzma_options_lzma *opt = filter.options; 156207753Smm 157207753Smm printf("\nDictionary size: " 158213700Smm "%" PRIu32 " MB (2^%" PRIu32 " bytes)\n" 159207753Smm "Literal context bits (lc): %" PRIu32 "\n" 160207753Smm "Literal pos bits (lp): %" PRIu32 "\n" 161207753Smm "Number of pos bits (pb): %" PRIu32 "\n", 162207753Smm (opt->dict_size + 512 * 1024) / (1024 * 1024), 163207753Smm my_log2(opt->dict_size), opt->lc, opt->lp, opt->pb); 164207753Smm 165207753Smm free(opt); 166207753Smm 167207753Smm return false; 168207753Smm} 169207753Smm 170207753Smm 171207753Smmextern int 172207753Smmmain(int argc, char **argv) 173207753Smm{ 174207753Smm tuklib_progname_init(argv); 175207753Smm tuklib_gettext_init(PACKAGE, LOCALEDIR); 176207753Smm 177207753Smm parse_args(argc, argv); 178207753Smm 179213700Smm#ifdef TUKLIB_DOSLIKE 180213700Smm setmode(fileno(stdin), O_BINARY); 181213700Smm#endif 182213700Smm 183207753Smm int ret = EXIT_SUCCESS; 184207753Smm 185207753Smm // We print empty lines around the output only when reading from 186207753Smm // files specified on the command line. This is due to how 187207753Smm // LZMA Utils did it. 188207753Smm if (optind == argc) { 189207753Smm if (lzmainfo("(stdin)", stdin)) 190207753Smm ret = EXIT_FAILURE; 191207753Smm } else { 192207753Smm printf("\n"); 193207753Smm 194207753Smm do { 195207753Smm if (strcmp(argv[optind], "-") == 0) { 196207753Smm if (lzmainfo("(stdin)", stdin)) 197207753Smm ret = EXIT_FAILURE; 198207753Smm } else { 199207753Smm FILE *f = fopen(argv[optind], "r"); 200207753Smm if (f == NULL) { 201207753Smm ret = EXIT_FAILURE; 202207753Smm fprintf(stderr, "%s: %s: %s\n", 203207753Smm progname, 204207753Smm argv[optind], 205207753Smm strerror(errno)); 206207753Smm continue; 207207753Smm } 208207753Smm 209207753Smm if (lzmainfo(argv[optind], f)) 210207753Smm ret = EXIT_FAILURE; 211207753Smm 212207753Smm printf("\n"); 213207753Smm fclose(f); 214207753Smm } 215207753Smm } while (++optind < argc); 216207753Smm } 217207753Smm 218207753Smm tuklib_exit(ret, EXIT_FAILURE, true); 219207753Smm} 220