(root)/
xz-5.4.5/
src/
lzmainfo/
lzmainfo.c
       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  
      29  tuklib_attr_noreturn
      30  static void
      31  help(void)
      32  {
      33  	printf(
      34  _("Usage: %s [--help] [--version] [FILE]...\n"
      35  "Show information stored in the .lzma file header"), progname);
      36  
      37  	printf(_(
      38  "\nWith no FILE, or when FILE is -, read standard input.\n"));
      39  	printf("\n");
      40  
      41  	printf(_("Report bugs to <%s> (in English or Finnish).\n"),
      42  			PACKAGE_BUGREPORT);
      43  	printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
      44  
      45  	tuklib_exit(EXIT_SUCCESS, EXIT_FAILURE, true);
      46  }
      47  
      48  
      49  tuklib_attr_noreturn
      50  static void
      51  version(void)
      52  {
      53  	puts("lzmainfo (" PACKAGE_NAME ") " LZMA_VERSION_STRING);
      54  	tuklib_exit(EXIT_SUCCESS, EXIT_FAILURE, true);
      55  }
      56  
      57  
      58  /// Parse command line options.
      59  static void
      60  parse_args(int argc, char **argv)
      61  {
      62  	enum {
      63  		OPT_HELP,
      64  		OPT_VERSION,
      65  	};
      66  
      67  	static const struct option long_opts[] = {
      68  		{ "help",    no_argument, NULL, OPT_HELP },
      69  		{ "version", no_argument, NULL, OPT_VERSION },
      70  		{ NULL,      0,           NULL, 0 }
      71  	};
      72  
      73  	int c;
      74  	while ((c = getopt_long(argc, argv, "", long_opts, NULL)) != -1) {
      75  		switch (c) {
      76  		case OPT_HELP:
      77  			help();
      78  
      79  		case OPT_VERSION:
      80  			version();
      81  
      82  		default:
      83  			exit(EXIT_FAILURE);
      84  		}
      85  	}
      86  
      87  	return;
      88  }
      89  
      90  
      91  /// Primitive base-2 logarithm for integers
      92  static uint32_t
      93  my_log2(uint32_t n)
      94  {
      95  	uint32_t e;
      96  	for (e = 0; n > 1; ++e, n /= 2) ;
      97  	return e;
      98  }
      99  
     100  
     101  /// Parse the .lzma header and display information about it.
     102  static bool
     103  lzmainfo(const char *name, FILE *f)
     104  {
     105  	uint8_t buf[13];
     106  	const size_t size = fread(buf, 1, sizeof(buf), f);
     107  	if (size != 13) {
     108  		fprintf(stderr, "%s: %s: %s\n", progname, name,
     109  				ferror(f) ? strerror(errno)
     110  				: _("File is too small to be a .lzma file"));
     111  		return true;
     112  	}
     113  
     114  	lzma_filter filter = { .id = LZMA_FILTER_LZMA1 };
     115  
     116  	// Parse the first five bytes.
     117  	switch (lzma_properties_decode(&filter, NULL, buf, 5)) {
     118  	case LZMA_OK:
     119  		break;
     120  
     121  	case LZMA_OPTIONS_ERROR:
     122  		fprintf(stderr, "%s: %s: %s\n", progname, name,
     123  				_("Not a .lzma file"));
     124  		return true;
     125  
     126  	case LZMA_MEM_ERROR:
     127  		fprintf(stderr, "%s: %s\n", progname, strerror(ENOMEM));
     128  		exit(EXIT_FAILURE);
     129  
     130  	default:
     131  		fprintf(stderr, "%s: %s\n", progname,
     132  				_("Internal error (bug)"));
     133  		exit(EXIT_FAILURE);
     134  	}
     135  
     136  	// Uncompressed size
     137  	uint64_t uncompressed_size = 0;
     138  	for (size_t i = 0; i < 8; ++i)
     139  		uncompressed_size |= (uint64_t)(buf[5 + i]) << (i * 8);
     140  
     141  	// Display the results. We don't want to translate these and also
     142  	// will use MB instead of MiB, because someone could be parsing
     143  	// this output and we don't want to break that when people move
     144  	// from LZMA Utils to XZ Utils.
     145  	if (f != stdin)
     146  		printf("%s\n", name);
     147  
     148  	printf("Uncompressed size:             ");
     149  	if (uncompressed_size == UINT64_MAX)
     150  		printf("Unknown");
     151  	else
     152  		printf("%" PRIu64 " MB (%" PRIu64 " bytes)",
     153  				(uncompressed_size + 512 * 1024)
     154  					/ (1024 * 1024),
     155  				uncompressed_size);
     156  
     157  	lzma_options_lzma *opt = filter.options;
     158  
     159  	printf("\nDictionary size:               "
     160  			"%" PRIu32 " MB (2^%" PRIu32 " bytes)\n"
     161  			"Literal context bits (lc):     %" PRIu32 "\n"
     162  			"Literal pos bits (lp):         %" PRIu32 "\n"
     163  			"Number of pos bits (pb):       %" PRIu32 "\n",
     164  			(opt->dict_size + 512 * 1024) / (1024 * 1024),
     165  			my_log2(opt->dict_size), opt->lc, opt->lp, opt->pb);
     166  
     167  	free(opt);
     168  
     169  	return false;
     170  }
     171  
     172  
     173  extern int
     174  main(int argc, char **argv)
     175  {
     176  	tuklib_progname_init(argv);
     177  	tuklib_gettext_init(PACKAGE, LOCALEDIR);
     178  
     179  	parse_args(argc, argv);
     180  
     181  #ifdef TUKLIB_DOSLIKE
     182  	setmode(fileno(stdin), O_BINARY);
     183  #endif
     184  
     185  	int ret = EXIT_SUCCESS;
     186  
     187  	// We print empty lines around the output only when reading from
     188  	// files specified on the command line. This is due to how
     189  	// LZMA Utils did it.
     190  	if (optind == argc) {
     191  		if (lzmainfo("(stdin)", stdin))
     192  			ret = EXIT_FAILURE;
     193  	} else {
     194  		printf("\n");
     195  
     196  		do {
     197  			if (strcmp(argv[optind], "-") == 0) {
     198  				if (lzmainfo("(stdin)", stdin))
     199  					ret = EXIT_FAILURE;
     200  			} else {
     201  				FILE *f = fopen(argv[optind], "r");
     202  				if (f == NULL) {
     203  					ret = EXIT_FAILURE;
     204  					fprintf(stderr, "%s: %s: %s\n",
     205  							progname,
     206  							argv[optind],
     207  							strerror(errno));
     208  					continue;
     209  				}
     210  
     211  				if (lzmainfo(argv[optind], f))
     212  					ret = EXIT_FAILURE;
     213  
     214  				printf("\n");
     215  				fclose(f);
     216  			}
     217  		} while (++optind < argc);
     218  	}
     219  
     220  	tuklib_exit(ret, EXIT_FAILURE, true);
     221  }