aboutsummaryrefslogtreecommitdiff
path: root/src/lzmainfo/lzmainfo.c
diff options
context:
space:
mode:
authorLasse Collin <lasse.collin@tukaani.org>2009-08-13 12:55:45 +0300
committerLasse Collin <lasse.collin@tukaani.org>2009-08-13 12:55:45 +0300
commite51b4e49e800bd84e6d589dca2964d3985e88139 (patch)
treeb1aa08694cb4b00a220ca35d5ce62ec7f7b8792e /src/lzmainfo/lzmainfo.c
parentSync some error messages from xz to xzdec. (diff)
downloadxz-e51b4e49e800bd84e6d589dca2964d3985e88139.tar.xz
Add lzmainfo for backward compatibility with LZMA Utils.
lzmainfo now links against static liblzma. In contrast to other command line tools in XZ Utils, linking lzmainfo against static liblzma by default is dumb. This will be fixed once I have fixed some related issues in configure.ac.
Diffstat (limited to 'src/lzmainfo/lzmainfo.c')
-rw-r--r--src/lzmainfo/lzmainfo.c242
1 files changed, 242 insertions, 0 deletions
diff --git a/src/lzmainfo/lzmainfo.c b/src/lzmainfo/lzmainfo.c
new file mode 100644
index 00000000..d9ae311a
--- /dev/null
+++ b/src/lzmainfo/lzmainfo.c
@@ -0,0 +1,242 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+/// \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>
+
+#ifdef ENABLE_NLS
+# include <libintl.h>
+# define _(msgid) gettext(msgid)
+#else
+# define _(msgid) msgid
+#endif
+
+#include "lzma.h"
+#include "getopt.h"
+
+
+/// Name of the program from argv[0]
+static const char *argv0;
+
+
+/// Close stdout unless we are already going to exit with EXIT_FAILURE.
+/// If closing stdout fails, set exit status to EXIT_FAILURE and print
+/// an error message to stderr. We don't care about closing stderr,
+/// because we don't print anything to stderr unless we are going to
+/// use EXIT_FAILURE anyway.
+static void lzma_attribute((noreturn))
+my_exit(int status)
+{
+ if (status != EXIT_FAILURE) {
+ const int ferror_err = ferror(stdout);
+ const int fclose_err = fclose(stdout);
+
+ if (ferror_err || fclose_err) {
+ // If it was fclose() that failed, we have the reason
+ // in errno. If only ferror() indicated an error,
+ // we have no idea what the reason was.
+ fprintf(stderr, "%s: %s: %s\n", argv0,
+ _("Writing to standard output "
+ "failed"),
+ fclose_err ? strerror(errno)
+ : _("Unknown error"));
+ status = EXIT_FAILURE;
+ }
+ }
+
+ exit(status);
+}
+
+
+static void lzma_attribute((noreturn))
+help(void)
+{
+ printf(
+_("Usage: %s [--help] [--version] [FILE]...\n"
+"Show information stored in the .lzma file header"), argv0);
+
+ 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_HOMEPAGE);
+
+ my_exit(EXIT_SUCCESS);
+}
+
+
+static void lzma_attribute((noreturn))
+version(void)
+{
+ puts("lzmainfo (" PACKAGE_NAME ") " PACKAGE_VERSION);
+ my_exit(EXIT_SUCCESS);
+}
+
+
+/// 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", argv0, 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", argv0, name,
+ _("Not a .lzma file"));
+ return true;
+
+ case LZMA_MEM_ERROR:
+ fprintf(stderr, "%s: %s\n", argv0, strerror(ENOMEM));
+ exit(EXIT_FAILURE);
+
+ default:
+ fprintf(stderr, "%s: %s\n", argv0, _("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: "
+ "%u MB (2^%u 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)
+{
+ int ret = EXIT_SUCCESS;
+ argv0 = argv[0];
+
+ parse_args(argc, argv);
+
+ // 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) {
+ lzmainfo("(stdin)", stdin);
+ } 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",
+ argv0, argv[optind],
+ strerror(errno));
+ continue;
+ }
+
+ if (lzmainfo(argv[optind], f))
+ ret = EXIT_FAILURE;
+
+ printf("\n");
+ fclose(f);
+ }
+ } while (++optind < argc);
+ }
+
+ my_exit(ret);
+}