#include <config.h>
#include "xstdlib.h"
#include "xstring.h"

RCSID("$Id: option.c,v 1.6 1996/05/03 20:22:51 beebe Exp beebe $")

#include "ch.h"
#include "yesorno.h"

#define BIBCLEAN_VERSION	"bibclean Version 2.11.3 [01-May-1996]"

typedef struct s_option_function_entry
{
    const char *name;		/* option name */
    size_t min_match;		/* minimum length string match */
    void (*function)(VOID);	/* function to call when option matched */
} OPTION_FUNCTION_ENTRY;

extern YESorNO	align_equals;		/* NO: left-adjust equals */
extern YESorNO	check_values;		/* NO: suppress value checks */
extern YESorNO	delete_empty_values; 	/* YES: delete empty values */
extern YESorNO	fix_initials;		/* reformat A.U. Thor?	*/
extern YESorNO	fix_names;		/* reformat Bach, P.D.Q? */
extern YESorNO	fix_font_changes;	/* brace {\em E. Coli}? */
extern YESorNO	German_style;		/* YES: " inside braced string
						value obeys german.sty style */
extern YESorNO	keep_linebreaks;	/* YES: keep linebreaks in values */
extern YESorNO	keep_parbreaks;		/* YES: keep parbreaks in values */
extern YESorNO	keep_preamble_spaces;	/* YES: keep spaces in @Preamble{} */
extern YESorNO	keep_spaces;		/* YES: keep spaces in values */
extern YESorNO	keep_string_spaces;	/* YES: keep spaces in @String{} */
extern long	max_width;
extern YESorNO	parbreaks;		/* NO: parbreaks forbidden */
					/* in strings and entries */
extern YESorNO	prettyprint;		/* NO: do lexical analysis */
extern YESorNO	print_patterns;		/* YES: print value patterns */
extern char	*program_name;		/* set to argv[0] */
extern YESorNO	read_initialization_files; /* -[no-]read-init-files sets */
extern YESorNO	remove_OPT_prefixes; 	/* YES: remove OPT prefix */
extern int	screen_lines;		/* kbopen() and out_lines() reset */
extern YESorNO	Scribe;			/* Scribe format input */
extern YESorNO	show_file_position; 	/* messages usually brief */
extern FILE	*stdlog;		/* usually stderr */
extern YESorNO	trace_file_opening; /* -[no-]trace-file-opening sets */
extern YESorNO	warnings;		/* NO: suppress warnings */

static int	current_index;			/* argv[] index in do_args() */
static char	*current_option;		/* set by do_args() */
static char	*next_option;			/* set in do_args() */

void		do_args ARGS((int argc_, char *argv_[]));
void		do_preargs ARGS((int argc_, char *argv_[]));
int		isoptionprefix ARGS((int c));

extern YESorNO	apply_function ARGS((const char *option_,
		    OPTION_FUNCTION_ENTRY table_[]));
extern void	check_inodes ARGS((void));
extern void	do_initfile ARGS((const char *pathlist_,const char *name_));

#if (SCREEN_LINES > 0)
extern int	get_screen_lines ARGS((void));
#endif /* (SCREEN_LINES > 0) */

extern void	out_lines ARGS((FILE *fpout_,const char *lines_[],
		    YESorNO pausing_));
extern FILE	*tfopen ARGS((const char *filename_, const char *mode_));
extern void	warning ARGS((const char *msg_));

static void	opt_align_equals ARGS((void));
static void	opt_author ARGS((void));
static void	opt_check_values ARGS((void));
static void	opt_delete_empty_values ARGS((void));
static void	opt_error_log ARGS((void));
static void	opt_file_position ARGS((void));
static void	opt_fix_font_changes ARGS((void));
static void	opt_fix_initials ARGS((void));
static void	opt_fix_names ARGS((void));
static void	opt_help ARGS((void));
static void	opt_init_file ARGS((void));
static void	opt_German_style ARGS((void));
static void	opt_keep_linebreaks ARGS((void));
static void	opt_keep_parbreaks ARGS((void));
static void	opt_keep_preamble_spaces ARGS((void));
static void	opt_keep_spaces ARGS((void));
static void	opt_keep_string_spaces ARGS((void));
static void	opt_max_width ARGS((void));
static void	opt_parbreaks ARGS((void));
static void	opt_prettyprint ARGS((void));
static void	opt_print_patterns ARGS((void));
static void	opt_read_init_files ARGS((void));
static void	opt_remove_OPT_prefixes ARGS((void));
static void	opt_scribe ARGS((void));
static void	opt_trace_file_opening ARGS((void));
static void	opt_version ARGS((void));
static void	opt_warnings ARGS((void));
static void	usage ARGS((void));
static void	version ARGS((void));
static YESorNO	YESorNOarg ARGS((void));

#if defined(HAVE_STDC)
void
do_args(int argc, char *argv[])
#else /* K&R style */
void
do_args(argc,argv)
int argc;
char *argv[];
#endif
{
    int k;				/* index into argv[] */
#define MSG_PREFIX	"Unrecognized option switch: "
#define MAX_OPTION_LENGTH	100
    char msg[sizeof(MSG_PREFIX) + MAX_OPTION_LENGTH + 1];
					/* for error messages */
    int nfiles;				/* number of files found in argv[] */

    static OPTION_FUNCTION_ENTRY options[] =
    {
	{"?",				1,	opt_help},
	{"align-equals",		2,	opt_align_equals},
	{"author",			2,	opt_author},
	{"check-values",		1,	opt_check_values},
	{"delete-empty-values",		1,	opt_delete_empty_values},
	{"error-log",			1,	opt_error_log},
	{"file-position",		3,	opt_file_position},
	{"fix-font-changes",		5,	opt_fix_font_changes},
	{"fix-initials",		5,	opt_fix_initials},
	{"fix-names",			5,	opt_fix_names},
	{"German-style",		1,	opt_German_style},
	{"help",			1,	opt_help},
	{"init-file",			1,	opt_init_file},
	{"keep-linebreaks",		6,	opt_keep_linebreaks},
	{"keep-parbreaks",		7,	opt_keep_parbreaks},
	{"keep-preamble-spaces",	7,	opt_keep_preamble_spaces},
	{"keep-spaces",			7,	opt_keep_spaces},
	{"keep-string-spaces",		7,	opt_keep_string_spaces},
	{"max-width",			1,	opt_max_width},
	{"no-align-equals",		4,	opt_align_equals},
	{"no-check-values",		4,	opt_check_values},
	{"no-delete-empty-values",	4,	opt_delete_empty_values},
	{"no-file-position",		6,	opt_file_position},
	{"no-fix-font-changes",		8,	opt_fix_font_changes},
	{"no-fix-initials",		8,	opt_fix_initials},
	{"no-fix-names",		8,	opt_fix_names},
	{"no-German-style",		4,	opt_German_style},
	{"no-keep-linebreaks",		9,	opt_keep_linebreaks},
	{"no-keep-parbreaks",		9,	opt_keep_parbreaks},
	{"no-keep-spaces",		9,	opt_keep_spaces},
	{"no-parbreaks",		5,	opt_parbreaks},
	{"no-prettyprint",		6,	opt_prettyprint},
	{"no-print-patterns",		6,	opt_print_patterns},
	{"no-read-init-files",		6,	opt_read_init_files},
	{"no-remove-OPT-prefixes",	6,	opt_remove_OPT_prefixes},
	{"no-scribe",			4,	opt_scribe},
	{"no-trace-file-opening",	4,	opt_trace_file_opening},
	{"no-warnings",			4,	opt_warnings},
	{"parbreaks",			2,	opt_parbreaks},
	{"prettyprint",			3,	opt_prettyprint},
	{"print-patterns",		3,	opt_print_patterns},
	{"read-init-files",		3,	opt_read_init_files},
	{"remove-OPT-prefixes",		3,	opt_remove_OPT_prefixes},
	{"scribe",			1,	opt_scribe},
	{"trace-file-opening",		1,	opt_trace_file_opening},
	{"version",			1,	opt_version},
	{"warnings",			1,	opt_warnings},
	{(const char*)NULL,		0,	(void (*)(VOID))NULL},
    };

    for (nfiles = 1, k = 1; k < argc; ++k)
    {
	if ( (argv[k][1] != '\0') && isoptionprefix(argv[k][0]) )
	{				/* then process command-line switch */
	    current_index = k;		/* needed by opt_init_file() and */
	    next_option = argv[k+1];	/* opt_error_log() */
	    current_option = argv[k];	/* needed by YESorNOarg() */
	    if (apply_function(current_option+1,options) == NO)
	    {
		(void)sprintf(msg, "%s%.*s", MSG_PREFIX, MAX_OPTION_LENGTH,
			      current_option);
		warning(msg);
		usage();
		exit(EXIT_FAILURE);
	    }
	    k = current_index;		/* some opt_xxx() functions update it */
	}
	else				/* save file names */
	    argv[nfiles++] = argv[k];	/* shuffle file names down */
    }
    argv[nfiles] = (char*)NULL;		/* terminate new argument list */
}


#if defined(HAVE_STDC)
void
do_preargs(int argc, char *argv[])
#else /* K&R style */
void
do_preargs(argc,argv)
int argc;
char *argv[];
#endif
{
    int k;

    static OPTION_FUNCTION_ENTRY options[] =
    {
	{"no-print-patterns",		5,	opt_print_patterns},
	{"no-read-init-files",		6,	opt_read_init_files},
	{"no-trace-file-opening",	4,	opt_trace_file_opening},
	{"print-patterns",		2,	opt_print_patterns},
	{"read-init-files",		3,	opt_read_init_files},
	{"trace-file-opening",		1,	opt_trace_file_opening},
	{(const char*)NULL,		0,	(void (*)(VOID))NULL},
    };

    for (k = 1; k < argc; ++k)
    {
	/* Do argument scan for options that must be known BEFORE
	initializations are attempted. */

	if ( (argv[k][1] != '\0') && isoptionprefix(argv[k][0]) )
	{				/* then process command-line switch */
	    current_index = k;
	    current_option = argv[k];
	    next_option = argv[k+1];
	    (void)apply_function(current_option+1,options);
	}
    }
}


#if defined(HAVE_STDC)
int
isoptionprefix(int c)
#else /* K&R style */
int
isoptionprefix(c)
int c;
#endif
{
    int result;

    result = 0;

#if OS_PCDOS
    result = (((c) == '-') || ((c) == '/'));
#endif /* OS_PCDOS */

#if OS_UNIX
    result = ((c) == '-');
#endif /* OS_UNIX */

#if OS_VAXVMS
    result = (((c) == '-') || ((c) == '/'));
#endif /* OS_VAXVMS */

    return (result);
}


static void
opt_align_equals(VOID)
{
    align_equals = YESorNOarg();
}


static void
opt_author(VOID)
{
    static CONST char *author[] =
    {
	"Author:\n",
	"\tNelson H. F. Beebe\n",
	"\tCenter for Scientific Computing\n",
	"\tDepartment of Mathematics\n",
	"\tUniversity of Utah\n",
	"\tSalt Lake City, UT 84112\n",
	"\tUSA\n",
	"\tTel: +1 801 581 5254\n",
	"\tFAX: +1 801 581 4801\n",
	"\tEmail: <beebe@math.utah.edu>\n",
	(const char*)NULL,
    };

    out_lines(stdlog, author, NO);
}


static void
opt_check_values(VOID)
{
    check_values = YESorNOarg();
}


static void
opt_delete_empty_values(VOID)
{
    delete_empty_values = YESorNOarg();
}


static void
opt_error_log(VOID)
{
    current_index++;
    if ((stdlog = tfopen(next_option,"w")) == (FILE*)NULL)
    {
	fprintf(stderr, "%s cannot open error log file [%s]",
	    WARNING_PREFIX, next_option);
	fprintf(stderr, " -- using stderr instead\n");
	perror("perror() says");
	stdlog = stderr;
    }
    else
	check_inodes();				/* stdlog changed */
}


static void
opt_file_position(VOID)
{
    show_file_position = YESorNOarg();
}


static void
opt_fix_font_changes(VOID)
{
    fix_font_changes = YESorNOarg();
}


static void
opt_fix_initials(VOID)
{
    fix_initials = YESorNOarg();
}


static void
opt_fix_names(VOID)
{
    fix_names = YESorNOarg();
}


static void
opt_help(VOID)
{
    static CONST char *help_lines[] =
    {
	"\nUsage: ",
	(const char*)NULL,
	" [ -author ] [ -error-log filename ] [ -help ] [ '-?' ]\n",
	"\t[ -init-file filename ] [ -max-width width ]\n",
	"\t[ -[no-]align-equals ] [ -[no-]check-values ]\n",
	"\t[ -[no-]delete-empty-values ] [ -[no-]file-position ]\n",
	"\t[ -[no-]fix-font-changes ] [ -[no-]fix-initials ]\n",
	"\t[ -[no-]fix-names ] [ -[no-]German-style ]\n",
	"\t[ -[no-]keep-linebreaks ] [ -[no-]keep-parbreaks ]\n",
	"\t[ -[no-]keep-preamble-spaces ] [ -[no-]keep-spaces ]\n",
	"\t[ -[no-]keep-string-spaces ] [ -[no-]parbreaks ]\n",
	"\t[ -[no-]prettyprint ] [ -[no-]print-patterns ]\n",
	"\t[ -[no-]read-init-files ] [ -[no-]remove-OPT-prefixes ]\n",
	"\t[ -[no-]scribe ] [ -[no-]trace-file-opening ]\n",
	"\t[ -[no-]warnings ] [ -version ]\n",
	"\t[ <infile or bibfile1 bibfile2 bibfile3 ...] >outfile\n",
	"\n",
#include "bibclean.h"
    };

    help_lines[1] = program_name;	/* cannot have this in initializer */
    out_lines(stdlog, help_lines, (screen_lines > 0) ? YES : NO);
    exit(EXIT_SUCCESS);
}


static void
opt_init_file(VOID)
{
    current_index++;
    do_initfile((const char*)NULL,next_option);
}


static void
opt_German_style(VOID)
{
    German_style = YESorNOarg();
}


static void
opt_keep_linebreaks(VOID)
{
    keep_linebreaks = YESorNOarg();
}


static void
opt_keep_parbreaks(VOID)
{
    keep_parbreaks = YESorNOarg();
}


static void
opt_keep_preamble_spaces(VOID)
{
    keep_preamble_spaces = YESorNOarg();
}


static void
opt_keep_spaces(VOID)
{
    keep_spaces = YESorNOarg();
}


static void
opt_keep_string_spaces(VOID)
{
    keep_string_spaces = YESorNOarg();
}


static void
opt_max_width(VOID)
{
    current_index++;
    max_width = strtol(next_option,(char**)NULL,0);
    if (max_width <= 0L)		/* width <= 0 means unlimited width */
	max_width = LONG_MAX;
}


static void
opt_parbreaks(VOID)
{
    parbreaks = YESorNOarg();
}


static void
opt_prettyprint(VOID)
{
    prettyprint = YESorNOarg();
}


static void
opt_print_patterns(VOID)
{
    print_patterns = YESorNOarg();
}


static void
opt_read_init_files(VOID)
{
    read_initialization_files = YESorNOarg();
}


static void
opt_remove_OPT_prefixes(VOID)
{
    remove_OPT_prefixes = YESorNOarg();
}


static void
opt_scribe(VOID)
{
    Scribe = YESorNOarg();
}


static void
opt_trace_file_opening(VOID)
{
    trace_file_opening = YESorNOarg();
}


static void
opt_version(VOID)
{
    version();
}


static void
opt_warnings(VOID)
{
    warnings = YESorNOarg();
}


static void
usage(VOID)
{
    static CONST char *usage_lines[] =
    {
	"\nUsage: ",
	(const char*)NULL,
	" [ -author ] [ -error-log filename ] [ -help ] [ '-?' ]\n",
	"\t[ -init-file filename ] [ -max-width width ]\n",
	"\t[ -[no-]align-equals ] [ -[no-]check-values ]\n",
	"\t[ -[no-]delete-empty-values ] [ -[no-]file-position ]\n",
	"\t[ -[no-]fix-font-changes ] [ -[no-]fix-initials ]\n",
	"\t[ -[no-]fix-names ] [ -[no-]German-style ]\n",
	"\t[ -[no-]keep-linebreaks ] [ -[no-]keep-parbreaks ]\n",
	"\t[ -[no-]keep-preamble-spaces ] [ -[no-]keep-spaces ]\n",
	"\t[ -[no-]keep-string-spaces ] [ -[no-]parbreaks ]\n",
	"\t[ -[no-]prettyprint ] [ -[no-]print-patterns ]\n",
	"\t[ -[no-]read-init-files ] [ -[no-]remove-OPT-prefixes ]\n",
	"\t[ -[no-]scribe ] [ -[no-]trace-file-opening ]\n",
	"\t[ -[no-]warnings ] [ -version ]\n",
	"\t[ <infile or bibfile1 bibfile2 bibfile3 ...] >outfile\n",
	(const char*)NULL,
    };

    version();
    usage_lines[1] = program_name;	/* cannot have this in initializer */
    out_lines(stdlog, usage_lines, NO);
}


static void
version(VOID)
{
    static CONST char *version_string[] =
    {
	BIBCLEAN_VERSION,
	"\n",

#if defined(HOST) || defined(USER) || defined(__DATE__) || defined(__TIME__)
	"Compiled",

#if defined(USER)
	" by <", USER,

#if defined(HOST)
	"@", HOST,
#endif /* defined(HOST) */

	">",
#endif /* defined(USER) */

#if defined(__DATE__)
	" on ", __DATE__,
#endif /* defined(__DATE__) */

#if defined(__TIME__)
	" ", __TIME__,
#endif /* defined(__TIME__) */

#if defined(HAVE_PATTERNS)
	"\nwith native pattern matching",
#endif /* defined(HAVE_PATTERNS) */

#if defined(HAVE_RECOMP) || defined(HAVE_REGEXP)
	"\nwith regular-expression pattern matching",
#endif /* defined(HAVE_RECOMP) || defined(HAVE_REGEXP) */

#if defined(HAVE_OLDCODE)
	"\nwith old matching code",
#endif /* defined(HAVE_OLDCODE) */

	"\n",
#endif /* defined(HOST)||defined(USER)||defined(__DATE__)||defined(__TIME__) */

	(const char*)NULL,
    };

    out_lines(stdlog, version_string, NO);
}


static YESorNO
YESorNOarg(VOID)
{
    return ((strnicmp(current_option+1,"no-",3) == 0) ? NO : YES);
}
