/*                                                            -*- C -*-
 * Copyright (c) 1998-2006  Motoyuki Kasahara
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the project nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include "ebzip.h"
#include "ebutils.h"

#if defined(DOS_FILE_PATH) && defined(HAVE_MBSTRING_H)
/* a path may contain double-byte chars in SJIS. */
#include <mbstring.h>
#define strchr  _mbschr
#define strrchr _mbsrchr
#endif

/*
 * Program name and version.
 */
const char *program_name = "ebzip";
const char *program_version = VERSION;
const char *invoked_name;

/*
 * Command line options.
 */
static const char *short_options = "fhikl:no:qs:S:tT:uvw:z";
static struct option long_options[] = {
    {"force-overwrite",   no_argument,       NULL, 'f'},
    {"help",              no_argument,       NULL, 'h'},
    {"information",       no_argument,       NULL, 'i'},
    {"keep",              no_argument,       NULL, 'k'},
    {"level",             required_argument, NULL, 'l'},
    {"no-overwrite",      no_argument,       NULL, 'n'},
    {"output-directory",  required_argument, NULL, 'o'},
    {"quiet",             no_argument,       NULL, 'q'},
    {"silent",            no_argument,       NULL, 'q'},
    {"skip-content",      required_argument, NULL, 's'},
    {"subbook",           required_argument, NULL, 'S'},
    {"test",              no_argument,       NULL, 't'},
    {"uncompress",        no_argument,       NULL, 'u'},
    {"version",           no_argument,       NULL, 'v'},
    {"overwrite",         required_argument, NULL, 'w'},
    {"compress",          no_argument,       NULL, 'z'},
    {NULL, 0, NULL, 0}
};

/*
 * Zip level.
 */
int ebzip_level = EBZIP_DEFAULT_LEVEL;

/*
 * Keep mode flag.
 */
int ebzip_keep_flag = EBZIP_DEFAULT_KEEP;

/*
 * Quiet mode flag.
 */
int ebzip_quiet_flag = EBZIP_DEFAULT_QUIET;

/*
 * Test mode flag.
 */
int ebzip_test_flag = EBZIP_DEFAULT_TEST;

/*
 * Overwrite mode.
 */
int ebzip_overwrite_mode = EBZIP_DEFAULT_OVERWRITE;

/*
 * Target contents.
 */
int ebzip_skip_flag_font    = EBZIP_DEFAULT_SKIP_FONT;
int ebzip_skip_flag_graphic = EBZIP_DEFAULT_SKIP_GRAPHIC;
int ebzip_skip_flag_movie   = EBZIP_DEFAULT_SKIP_MOVIE;
int ebzip_skip_flag_sound   = EBZIP_DEFAULT_SKIP_SOUND;

/*
 * Operation modes.
 */
#define EBZIP_ACTION_ZIP		0
#define EBZIP_ACTION_UNZIP		1
#define EBZIP_ACTION_INFO		2

static int action_mode = EBZIP_ACTION_ZIP;

/*
 * A list of subbook names to be compressed/uncompressed.
 */
static char
subbook_name_list[EB_MAX_SUBBOOKS][EB_MAX_DIRECTORY_NAME_LENGTH + 1];
static int subbook_name_count = 0;

/*
 * Unexported functions.
 */
static int parse_zip_level(const char *argument, int *zip_level);
static int parse_skip_content_argument(const char *argument);
static void output_help(void);


int
main(int argc, char *argv[])
{
    EB_Error_Code error_code;
    char out_top_path[PATH_MAX + 1];
    char book_path[PATH_MAX + 1];
    int ch;
    char *last_slash, *last_backslash;
    char *invoked_base_name;

    invoked_name = argv[0];
    strcpy(out_top_path, EBZIP_DEFAULT_OUTPUT_DIRECTORY);

    /*
     * Initialize locale data.
     */
#ifdef ENABLE_NLS
#ifdef HAVE_SETLOCALE
       setlocale(LC_ALL, "");
#endif
       bindtextdomain(TEXT_DOMAIN_NAME, LOCALEDIR);
       textdomain(TEXT_DOMAIN_NAME);
#endif

    /*
     * Determine the default action.
     */
    last_slash = strrchr(argv[0], '/');
#ifndef DOS_FILE_PATH
    last_backslash = NULL;
#else
    last_backslash = strrchr(argv[0], '\\');
#endif

    if (last_slash == NULL && last_backslash == NULL)
	invoked_base_name = argv[0];
    else if (last_slash == NULL)
	invoked_base_name = last_backslash + 1;
    else if (last_backslash == NULL)
	invoked_base_name = last_slash + 1;
    else if (last_slash < last_backslash)
	invoked_base_name = last_backslash + 1;
    else
	invoked_base_name = last_slash + 1;

#ifndef EXEEXT_EXE
    if (strcmp(invoked_base_name, "ebunzip") == 0)
	action_mode = EBZIP_ACTION_UNZIP;
    else if (strcmp(invoked_base_name, "ebzipinfo") == 0)
	action_mode = EBZIP_ACTION_INFO;
#else /* EXEEXT_EXE */
    if (strcasecmp(invoked_base_name, "ebunzip") == 0
	|| strcasecmp(invoked_base_name, "ebunzip.exe") == 0) {
	action_mode = EBZIP_ACTION_UNZIP;
    } else if (strcasecmp(invoked_base_name, "ebzipinfo") == 0
	|| strcasecmp(invoked_base_name, "ebzipinfo.exe") == 0
	|| strcasecmp(invoked_base_name, "ebzipinf") == 0
	|| strcasecmp(invoked_base_name, "ebzipinf.exe") == 0) {
	action_mode = EBZIP_ACTION_INFO;
    }
#endif /* EXEEXT_EXE */

    /*
     * Set overwrite mode.
     */
    if (!isatty(0))
	ebzip_overwrite_mode = EBZIP_OVERWRITE_NO;

    /*
     * Initialize EB Library.
     */
    error_code = eb_initialize_library();
    if (error_code != EB_SUCCESS) {
	fprintf(stderr, "%s: %s\n", invoked_name,
	    eb_error_message(error_code));
	goto die;
    }

    /*
     * Parse command line options.
     */
    for (;;) {
        ch = getopt_long(argc, argv, short_options, long_options, NULL);
        if (ch == -1)
            break;
        switch (ch) {
        case 'f':
            /*
             * Obsolete option `-f'.  Set `force' to the overwrite flag.
             */
	    ebzip_overwrite_mode = EBZIP_OVERWRITE_FORCE;
	    break;

        case 'h':
            /*
             * Option `-h'.  Display help message, then exit.
             */
            output_help();
            exit(0);

        case 'i':
	    /*
	     * Option `-i'.  Information mode.
	     */
	    action_mode = EBZIP_ACTION_INFO;
	    break;

        case 'k':
            /*
             * Option `-k'.  Keep (don't delete) input files.
             */
	    ebzip_keep_flag = 1;
	    break;

        case 'l':
            /*
             * Option `-l'.  Specify compression level.
             */
	    if (parse_zip_level(optarg, &ebzip_level) < 0)
		exit(1);
	    break;

        case 'n':
            /*
             * Obsolete option `-n'.  Set `no' to the overwrite flag.
             */
	    ebzip_overwrite_mode = EBZIP_OVERWRITE_NO;
	    break;

        case 'o':
            /*
             * Option `-o'.  Output files under DIRECOTRY.
	     * The length of the file name
	     *    "<out_top_path>/subdir/subsubdir/file.ebz;1"
	     * must not exceed PATH_MAX.
             */
            if (PATH_MAX < strlen(optarg)) {
                fprintf(stderr, _("%s: too long output directory path\n"),
                    invoked_name);
                exit(1);
            }
            strcpy(out_top_path, optarg);
	    canonicalize_path(out_top_path);
	    if (PATH_MAX < strlen(out_top_path) + 1
		+ EB_MAX_DIRECTORY_NAME_LENGTH + 1
		+ EB_MAX_DIRECTORY_NAME_LENGTH + 1
		+ EB_MAX_FILE_NAME_LENGTH) {
		fprintf(stderr, _("%s: too long output directory path\n"),
		    invoked_name);
		goto die;
	    }
	    break;

        case 'q':
            /*
             * Option `-q'.  Set quiet flag.
             */
	    ebzip_quiet_flag = 1;
	    break;

        case 's':
            /*
             * Option `-s'.  Specify content type to be skipped.
             */
            if (parse_skip_content_argument(optarg) < 0)
                exit(1);
	    break;

        case 'S':
            /*
             * Option `-S'.  Specify target subbooks.
             */
            if (parse_subbook_name_argument(invoked_name, optarg,
		subbook_name_list, &subbook_name_count) < 0)
                exit(1);
            break;

        case 't':
            /*
             * Option `-t'.  Set test mode.
             */
	    ebzip_test_flag = 1;
	    break;

        case 'u':
            /*
             * Option `-u'.  Decompression mode.
             */
	    action_mode = EBZIP_ACTION_UNZIP;
	    break;

        case 'v':
            /*
             * Option `-v'.  Display version number, then exit.
             */
            output_version(program_name, program_version);
            exit(0);

	case 'w':
            /*
             * Option `-w'.  Set overwrite mode.
             */
	    if (strcasecmp(optarg, "confirm") == 0)
		ebzip_overwrite_mode = EBZIP_OVERWRITE_CONFIRM;
	    else if (strcasecmp(optarg, "force") == 0)
		ebzip_overwrite_mode = EBZIP_OVERWRITE_FORCE;
	    else if (strcasecmp(optarg, "no") == 0)
		ebzip_overwrite_mode = EBZIP_OVERWRITE_NO;
	    else {
		fprintf(stderr, _("%s: invalid overwrite mode: %s\n"),
		    invoked_name, optarg);
		output_try_help(invoked_name);
		goto die;
	    }
	    break;
		
        case 'z':
            /*
             * Option `-z'.  Compression mode.
             */
	    action_mode = EBZIP_ACTION_ZIP;
	    break;

        default:
            output_try_help(invoked_name);
	    goto die;
	}
    }

    /*
     * Check the number of rest arguments.
     */
    if (1 < argc - optind) {
        fprintf(stderr, _("%s: too many arguments\n"), invoked_name);
        output_try_help(invoked_name);
	goto die;
    }

    /*
     * Set a book path.
     */
    if (argc == optind)
        strcpy(book_path, EBZIP_DEFAULT_BOOK_DIRECTORY);
    else
        strcpy(book_path, argv[optind]);

    if (is_ebnet_url(book_path)) {
        fprintf(stderr, "%s: %s\n", invoked_name,
	    eb_error_message(EB_ERR_EBNET_UNSUPPORTED));
	goto die;
    }
    canonicalize_path(book_path);

    /*
     * Compress the book.
     */
    switch (action_mode) {
    case EBZIP_ACTION_ZIP:
	if (ebzip_zip_book(out_top_path, book_path, subbook_name_list,
	    subbook_name_count) < 0) {
	    goto die;
	}
	break;
    case EBZIP_ACTION_UNZIP:
	if (ebzip_unzip_book(out_top_path, book_path, subbook_name_list,
	    subbook_name_count) < 0) {
	    goto die;
	}
	exit(1);
	break;
    case EBZIP_ACTION_INFO:
	if (ebzip_zipinfo_book(book_path, subbook_name_list,
	    subbook_name_count) < 0) {
	    goto die;
	}
	break;
    }

    eb_finalize_library();

    return 0;

    /*
     * A critical error occurs...
     */
  die:
    eb_finalize_library();
    exit(1);
}


/*
 * Parse an argument to option `--level (-l)'.
 * If the argument is valid form, 0 is returned.
 * Otherwise -1 is returned.
 */
static int
parse_zip_level(const char *argument, int *zip_level)
{
    char *end_p;
    int level;

    level = (int)strtol(argument, &end_p, 10);
    if (!ASCII_ISDIGIT(*argument) || *end_p != '\0'
	|| level < 0 || ZIO_MAX_EBZIP_LEVEL < level) {
	fprintf(stderr, _("%s: invalid compression level `%s'\n"),
	    invoked_name, argument);
	fflush(stderr);
	return -1;
    }

    *zip_level = level;

    return 0;
}


/*
 * Parse an argument to option `--skip-content (-S)'.
 * If the argument is valid form, 0 is returned.
 * Otherwise -1 is returned.
 */
static int
parse_skip_content_argument(const char *argument)
{
    const char *argument_p = argument;
    char name[EB_MAX_DIRECTORY_NAME_LENGTH + 1];
    char *name_p;
    int i;

    while (*argument_p != '\0') {
	/*
	 * Take a next element in the argument.
	 */
	i = 0;
	name_p = name;
	while (*argument_p != ',' && *argument_p != '\0'
	    && i < EB_MAX_DIRECTORY_NAME_LENGTH) {
		*name_p = ASCII_TOLOWER(*argument_p);
	    i++;
	    name_p++;
	    argument_p++;
	}
	*name_p = '\0';
	if (*argument_p == ',')
	    argument_p++;
	else if (*argument_p != '\0') {
	    fprintf(stderr, _("%s: invalid content name `%s'\n"),
		invoked_name, name);
	    fflush(stderr);
	    return -1;
	}

	/*
	 * If the font name is not found in `font_list', it is added to
	 * the list.
	 */
	if (strcasecmp(name, "font") == 0) {
	    ebzip_skip_flag_font = 1;
	} else if (strcasecmp(name, "sound") == 0) {
	    ebzip_skip_flag_sound = 1;
	} else if (strcasecmp(name, "graphic") == 0) {
	    ebzip_skip_flag_graphic = 1;
	} else if (strcasecmp(name, "movie") == 0) {
	    ebzip_skip_flag_movie = 1;
	} else {
	    fprintf(stderr, _("%s: invalid content name `%s'\n"),
		invoked_name, name);
	    fflush(stderr);
	    return -1;
	}
    }

    return 0;
}


/*
 * Output help message to stdandard out.
 */
static void
output_help(void)
{
    printf(_("Usage: %s [option...] [book-directory]\n"), program_name);
    printf(_("Options:\n"));
    printf(_("  -f  --force-overwrite      set overwrite mode to `force'\n"));
    printf(_("                             (same as `--overwrite force')\n"));
    printf(_("  -h  --help                 display this help, then exit\n"));
    printf(_("  -i  --information          list information of compressed files\n"));
    printf(_("  -k  --keep                 don't delete original files\n"));
    printf(_("  -l INTEGER  --level INTEGER\n"));
    printf(_("                             compression level; 0..%d\n"),
	ZIO_MAX_EBZIP_LEVEL);
    printf(_("                             (default: %d)\n"),
	EBZIP_DEFAULT_LEVEL);
    printf(_("  -n  --no-overwrite         set overwrite mode to `no'\n"));
    printf(_("                             (same as `--overwrite no')\n"));
    printf(_("  -o DIRECTORY  --output-directory DIRECTORY\n"));
    printf(_("                             ouput files under DIRECTORY\n"));
    printf(_("                             (default: %s)\n"),
	EBZIP_DEFAULT_OUTPUT_DIRECTORY);
    printf(_("  -q  --quiet  --silence     suppress all warnings\n"));
    printf(_("  -s TYPE[,TYPE]  --skip-content TYPE[,TYPE...]\n"));
    printf(_("                             skip content; font, graphic, sound or movie\n"));
    printf(_("                             (default: none is skipped)\n"));
    printf(_("  -S SUBBOOK[,SUBBOOK...]  --subbook SUBBOOK[,SUBBOOK...]\n"));
    printf(_("                             target subbook\n"));
    printf(_("                             (default: all subbooks)\n"));
    printf(_("  -t  --test                 only check for input files\n"));
    printf(_("  -u  --uncompress           uncompress files\n"));
    printf(_("  -v  --version              display version number, then exit\n"));
    printf(_("  -w MODE  --overwrite MODE  set overwrite mode of output files;\n"));
    printf(_("                             confirm, force or no\n"));
    printf(_("                             (default: confirm)\n"));
    printf(_("  -z  --compress             compress files\n"));
    printf(_("\nArgument:\n"));
    printf(_("  book-directory             top directory of a CD-ROM book\n"));
    printf(_("                             (default: %s)\n"),
	EBZIP_DEFAULT_BOOK_DIRECTORY);

    printf(_("\nDefault action:\n"));
#ifndef EXEEXT
    printf(_("  When invoked as `ebunzip', uncompression is the default action.\n"));
    printf(_("  When invoked as `ebzipinfo', listing information is the default action.\n"));
#else
    printf(_("  When invoked as `ebunzip.exe', uncompression is the default action.\n"));
    printf(_("  When invoked as `ebzipinf.exe', listing information is the default action.\n"));
#endif
    printf(_("  Otherwise, compression is the default action.\n"));
    printf(_("\nReport bugs to %s.\n"), MAILING_ADDRESS);
    fflush(stdout);
}
