/////////////////////////////////////////////////////////////////////////////// // /// \file lzmainfo.c /// \brief lzmainfo tool for compatibility with LZMA Utils // // Author: Lasse Collin // // This file has been put into the public domain. // You can do whatever you want with this file. // /////////////////////////////////////////////////////////////////////////////// #include "sysdefs.h" #include #include #include "lzma.h" #include "getopt.h" #include "tuklib_gettext.h" #include "tuklib_progname.h" #include "tuklib_exit.h" #ifdef TUKLIB_DOSLIKE # include # include #endif static void lzma_attribute((__noreturn__)) help(void) { printf( _("Usage: %s [--help] [--version] [FILE]...\n" "Show information stored in the .lzma file header"), progname); printf(_( "\nWith no FILE, or when FILE is -, read standard input.\n")); printf("\n"); printf(_("Report bugs to <%s> (in English or Finnish).\n"), PACKAGE_BUGREPORT); printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL); tuklib_exit(EXIT_SUCCESS, EXIT_FAILURE, true); } static void lzma_attribute((__noreturn__)) version(void) { puts("lzmainfo (" PACKAGE_NAME ") " LZMA_VERSION_STRING); tuklib_exit(EXIT_SUCCESS, EXIT_FAILURE, true); } /// Parse command line options. static void parse_args(int argc, char **argv) { enum { OPT_HELP, OPT_VERSION, }; static const struct option long_opts[] = { { "help", no_argument, NULL, OPT_HELP }, { "version", no_argument, NULL, OPT_VERSION }, { NULL, 0, NULL, 0 } }; int c; while ((c = getopt_long(argc, argv, "", long_opts, NULL)) != -1) { switch (c) { case OPT_HELP: help(); case OPT_VERSION: version(); default: exit(EXIT_FAILURE); } } return; } /// Primitive base-2 logarithm for integers static uint32_t my_log2(uint32_t n) { uint32_t e; for (e = 0; n > 1; ++e, n /= 2) ; return e; } /// Parse the .lzma header and display information about it. static bool lzmainfo(const char *name, FILE *f) { uint8_t buf[13]; const size_t size = fread(buf, 1, sizeof(buf), f); if (size != 13) { fprintf(stderr, "%s: %s: %s\n", progname, name, ferror(f) ? strerror(errno) : _("File is too small to be a .lzma file")); return true; } lzma_filter filter = { .id = LZMA_FILTER_LZMA1 }; // Parse the first five bytes. switch (lzma_properties_decode(&filter, NULL, buf, 5)) { case LZMA_OK: break; case LZMA_OPTIONS_ERROR: fprintf(stderr, "%s: %s: %s\n", progname, name, _("Not a .lzma file")); return true; case LZMA_MEM_ERROR: fprintf(stderr, "%s: %s\n", progname, strerror(ENOMEM)); exit(EXIT_FAILURE); default: fprintf(stderr, "%s: %s\n", progname, _("Internal error (bug)")); exit(EXIT_FAILURE); } // Uncompressed size uint64_t uncompressed_size = 0; for (size_t i = 0; i < 8; ++i) uncompressed_size |= (uint64_t)(buf[5 + i]) << (i * 8); // Display the results. We don't want to translate these and also // will use MB instead of MiB, because someone could be parsing // this output and we don't want to break that when people move // from LZMA Utils to XZ Utils. if (f != stdin) printf("%s\n", name); printf("Uncompressed size: "); if (uncompressed_size == UINT64_MAX) printf("Unknown"); else printf("%" PRIu64 " MB (%" PRIu64 " bytes)", (uncompressed_size + 512 * 1024) / (1024 * 1024), uncompressed_size); lzma_options_lzma *opt = filter.options; printf("\nDictionary size: " "%" PRIu32 " MB (2^%" PRIu32 " bytes)\n" "Literal context bits (lc): %" PRIu32 "\n" "Literal pos bits (lp): %" PRIu32 "\n" "Number of pos bits (pb): %" PRIu32 "\n", (opt->dict_size + 512 * 1024) / (1024 * 1024), my_log2(opt->dict_size), opt->lc, opt->lp, opt->pb); free(opt); return false; } extern int main(int argc, char **argv) { tuklib_progname_init(argv); tuklib_gettext_init(PACKAGE, LOCALEDIR); parse_args(argc, argv); #ifdef TUKLIB_DOSLIKE setmode(fileno(stdin), O_BINARY); #endif int ret = EXIT_SUCCESS; // We print empty lines around the output only when reading from // files specified on the command line. This is due to how // LZMA Utils did it. if (optind == argc) { if (lzmainfo("(stdin)", stdin)) ret = EXIT_FAILURE; } else { printf("\n"); do { if (strcmp(argv[optind], "-") == 0) { if (lzmainfo("(stdin)", stdin)) ret = EXIT_FAILURE; } else { FILE *f = fopen(argv[optind], "r"); if (f == NULL) { ret = EXIT_FAILURE; fprintf(stderr, "%s: %s: %s\n", progname, argv[optind], strerror(errno)); continue; } if (lzmainfo(argv[optind], f)) ret = EXIT_FAILURE; printf("\n"); fclose(f); } } while (++optind < argc); } tuklib_exit(ret, EXIT_FAILURE, true); }