/*
 * fhist - file history and comparison tools
 * Copyright (C) 1991-2008, 2010, 2012 Peter Miller
 *
 * Derived from a work Copyright (C) 1990 David I. Bell.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

#include <common/ac/errno.h>
#include <common/ac/stdio.h>
#include <common/ac/stdlib.h>
#include <common/ac/string.h>
#include <libexplain/program_name.h>
#include <libexplain/strdup.h>

#include <common/arglex.h>
#include <common/cmalloc.h>
#include <common/compare.h>
#include <common/error_intl.h>
#include <common/expand.h>
#include <common/fileio.h>
#include <common/help.h>
#include <common/quit.h>
#include <common/version.h>

#include <fmerge/work.h>

short           outputflag;
short           conflictflag;

enum
{
    arglex_token_conflicts = arglex_token_MAX1,
    arglex_token_debug,
    arglex_token_failure,
    arglex_token_ignore,
    arglex_token_ignore_identical,
    arglex_token_ignore_identical_not,
    arglex_token_output,
    arglex_token_unchanged,
    arglex_token_verbose,
    arglex_token_MAX2
};

static arglex_table_ty argtab[] =
{
    { "-Conflicts", arglex_token_conflicts, },
    { "-Debug", arglex_token_debug, },
    { "-Failure", arglex_token_failure, },
    { "-Ignore", arglex_token_ignore, },
    { "-Ignore_Identical_Conflicts", arglex_token_ignore_identical, },
    { "-Not_Ignore_Identical_Conflicts", arglex_token_ignore_identical_not, },
    { "-Output", arglex_token_output, },
    { "-Unchanged", arglex_token_unchanged, },
    { "-Verbose", arglex_token_verbose, },
    { 0, 0, },    /* end marker */
};


static void
usage(void)
{
    const char      *progname;

    progname = explain_program_name_get();
    fprintf(stderr, "usage: %s [ <option>... ] <basefile> <file1> <file2>\n",
        progname);
    fprintf(stderr, "       %s -Help\n", progname);
    fprintf(stderr, "       %s -VERSion\n", progname);
    quit(1);
}


static void
main_help(void)
{
    help(NULL, usage);
}


/*
 * This is the main program.
 */

int
main(int argc, char **argv)
{
    char            *cp;            /* current option character */
    char            *base_name;     /* file which is the base file */
    char            *nameA;
    char            *nameB;         /* the two divergent files */
    char            *ofile;         /* output merged filename */
    char            *cfile;         /* output conflict filename */
    int             filecount;      /* number of files specified */
    sub_context_ty  *scp;

    arglex_init(argc, argv, argtab);
    switch (arglex())
    {
    case arglex_token_help:
        main_help();
        quit(0);

    case arglex_token_version:
        version();
        quit(0);

    default:
        break;
    }
    base_name = NULL;
    nameA = NULL;
    nameB = NULL;
    ofile = NULL;
    cfile = NULL;
    fc.verbosity = VERBOSE_DEFAULT;
    fc.debugflag = 0;
    outputflag = 0;
    conflictflag = 0;
    ignoreflag = 0;
    filecount = 0;
    failcount = INFINITY;
    unchangecount = INFINITY;
    ignore_identical = -1;
    while (arglex_token != arglex_token_eoln)
    {
        switch (arglex_token)
        {
        default:
            bad_argument(usage);
            /* NOTREACHED */

        case arglex_token_string:
            /* a file name */
            cp = expand(arglex_value.alv_string);
            filecount++;
            if (!base_name)
                base_name = cp;
            else if (!nameA)
                nameA = cp;
            else if (!nameB)
                nameB = cp;
            else
            {
                fatal_intl(0, i18n("only three input files allowed"));
            }
            break;

        case arglex_token_debug:
            /* type debugging information */
            fc.debugflag = 1;
            break;

        case arglex_token_ignore:
            /* ignore conflicts */
            ignoreflag = 1;
            break;

        case arglex_token_ignore_identical:
            if (ignore_identical >= 0)
            {
                bad_ii:
                mutually_exclusive
                (
                    arglex_token_ignore_identical,
                    arglex_token_ignore_identical_not,
                    usage
                );
            }
            ignore_identical = 1;
            break;

        case arglex_token_ignore_identical_not:
            if (ignore_identical >= 0)
                goto bad_ii;
            ignore_identical = 0;
            break;

        case arglex_token_verbose:
            /* be verbose */
            if (arglex() != arglex_token_number)
            {
                fc.verbosity = VERBOSE_FULL;
                continue;
            }
            fc.verbosity = arglex_value.alv_number;
            if (fc.verbosity < 0)
            {
                scp = sub_context_new();
                sub_var_set_long(scp, "Number", fc.verbosity);
                fatal_intl(scp, i18n("verbosity $number out of range"));
                /* NOTREACHED */
                sub_context_delete(scp);
            }
            break;

        case arglex_token_output:
            /* specify output file */
            if (arglex() != arglex_token_string)
                usage();
            outputflag = 1;
            ofile = expand(arglex_value.alv_string);
            break;

        case arglex_token_conflicts:
            /* specify conflict file */
            conflictflag = 1;
            if (arglex() != arglex_token_string)
                continue;
            cfile = expand(arglex_value.alv_string);
            break;

        case arglex_token_failure:
            /* fail if too many conflicts */
            if (arglex() != arglex_token_number)
            {
                fatal_intl(0, i18n("please specify maximum change count"));
            }
            failcount = arglex_value.alv_number;
            if (failcount < 0)
            {
                scp = sub_context_new();
                sub_var_set_long(scp, "Number", failcount);
                fatal_intl
                (
                    scp,
                    i18n("failure count of $number is out of range")
                );
                /* NOTREACHED */
                sub_context_delete(scp);
            }
            break;

        case arglex_token_unchanged:
            /* limit number of unchanged lines */
            if (arglex() != arglex_token_number)
                fatal_intl(0, i18n("please specify maximum unchanged count"));
            unchangecount = arglex_value.alv_number;
            if (unchangecount < 0)
            {
                scp = sub_context_new();
                sub_var_set_long(scp, "Number", unchangecount);
                fatal_intl(scp, i18n("unchange count $number out of range"));
                /* NOTREACHED */
                sub_context_delete(scp);
            }
            break;
        }
        arglex();
    }
    if (ignore_identical < 0)
        ignore_identical = 0;
    if (filecount == 1)
    {
        /* converting conflict file */
        if (conflictflag)
        {
            fatal_intl
            (
                0,
                i18n("conflicting options for conflict file conversion")
            );
        }
        convertconflicts(base_name, ofile);
        quit(0);
    }
    if (filecount != 3)
        fatal_intl(0, i18n("must have three input files for merge"));
    if (!outputflag && !conflictflag)
        outputflag = 1;
    fmerge(base_name, nameA, nameB);
    if (outputflag)
        dumpmergedfile(ofile);
    if (conflictflag)
        dumpconflicts(cfile);
    if (conflicts && !conflictflag)
    {
        scp = sub_context_new();
        sub_var_set_long(scp, "Number", (long)conflicts);
        sub_var_optional(scp, "Number");
        fatal_intl(scp, i18n("conflict detected"));
        /* NOTREACHED */
        sub_context_delete(scp);
    }
    if (fc.verbosity && conflicts)
    {
        scp = sub_context_new();
        sub_var_set_long(scp, "Number", conflicts);
        sub_var_optional(scp, "Number");
        error_intl(scp, i18n("warning: conflict detected"));
        sub_context_delete(scp);
    }
    quit(0);
    return 0;
}


/* vim: set ts=8 sw=4 et : */
