| /////////////////////////////////////////////////////////////////////////////// |
| // |
| /// \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 <stdio.h> |
| #include <errno.h> |
| |
| #include "lzma.h" |
| #include "getopt.h" |
| #include "tuklib_gettext.h" |
| #include "tuklib_progname.h" |
| #include "tuklib_exit.h" |
| |
| #ifdef TUKLIB_DOSLIKE |
| # include <fcntl.h> |
| # include <io.h> |
| #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); |
| } |