/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following definitions.
- lzma_attribute
 - lzma_attribute
 - parse_args
 - my_log2
 - lzmainfo
 - main
 
///////////////////////////////////////////////////////////////////////////////
//
/// \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);
}