/*
 *  ChkTeX v1.2, finds typographic errors in (La)TeX files.
 *  Copyright (C) 1995-96 Jens T. Berger Thielemann
 *
 *  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 2 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, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *  Contact the author at:
 *		Jens Berger
 *		Spektrumvn. 4
 *		N-0666 Oslo
 *		Norway
 *		E-mail: <jensthi@ifi.uio.no>
 *
 *
 */

#include "ChkTeX.h"



struct WordList
	Silent		= {0L},
	Abbrev		= {0L},
	Linker 		= {0L},
	IJAccent	= {0L},
	Italic		= {0L},
	UserWarn	= {0L},
	CmdLine		= {0L},
	PostLink	= {0L};

struct Stack
	CharStack	= {0L};


struct WordList *CurRead = NULL;		/* Where we'll put the words we find */

/************************************************************************/

const UBYTE BrOrder	[NUMBRACKETS + 1] = "()[]{}";

/******************** VARS CHANGED RUNTIME ******************************/

ULONG Brackets 	[NUMBRACKETS];

BOOL	AtLetter,				/* Whether `@' is a letter or not.		*/
		MathMode;				/* Whether we're in math mode or not	*/

ULONG	ErrPrint,				/* # errors printed 					*/
		WarnPrint,				/* # warnings printed					*/
		UserSupp;				/* # user suppressed warnings			*/

enum ItState					/* Are we doing italics?				*/
		ItState;

/***************************** ERROR MESSAGES ***************************/

#define	INTFAULTMSG		"INTERNAL FAULT OCCURED! PLEASE SUBMIT A BUG REPORT!\n"

#ifndef __LOCALIZED
	char InternFault [] = 	INTFAULTMSG;
#	define INTERNFAULT	&InternFault[0]
#else
#	define INTERNFAULT	INTFAULTMSG
#endif

struct ErrMsg PrgMsgs [pmMaxFault + 1] =
{
	{pmMinFault,	etErr,	TRUE,
		INTERNFAULT},
	{pmUnknownTerm,	etErr,	TRUE,
		"Unknown terminal type - using normal verbosity."},

	{pmNoFileMatch,	etWarn,	TRUE,
        "No files matched the pattern `%s'."},
	{pmNoTeXOpen,	etErr,	TRUE,
		"Unable to open TeX files."},

	{pmRename,		etMsg,	TRUE,
		"Renaming `%s' as `%s'."},
	{pmRenameErr,	etErr,	TRUE,
		"Could not rename `%s' to `%s'."},

	{pmOutOpen,		etErr,	TRUE,
		"Unable to open output file."},
	{pmOutTwice,	etErr,	TRUE,
		"You can specify output file only once."},

	{pmStrDupErr, 	etErr,	TRUE,
		"Unable to duplicate strings - no memory?"},
	{pmWordListErr,	etErr,	TRUE,
		"Unable to create wordlist - no memory?"},
	{pmNoStackMem,	etErr,	TRUE,
		"Unable to create stack - no memory?\n"},

	{pmWarnNumErr,	etErr,	TRUE,
		"Illegal warning number used."},
	{pmVerbLevErr,	etErr,	TRUE,
		"Illegal verbosity level."},

	{pmNotPSDigit,	etWarn,	TRUE,
		"`%c' is not a %s digit - ignored!"},
	{pmEscCode,		etWarn,	TRUE,
		"Unknown escape code `%c%c' - ignored!"},

	{pmKeyWord,		etErr,	TRUE,
		"Unsupported control word (`%s') encountered in file `%s'."},
	{pmBraceCnt,	etErr,	TRUE,
/*{*/	"ERROR: `%s' has a faulty format - too many }'s! Unable to read.\n"},
	{pmRsrcOpen,	etWarn,	TRUE,
		"Could not open `%s', may cause unwanted behaviour."},


	{pmMaxFault,	etErr,	TRUE,
		INTERNFAULT},
};


struct ErrMsg LaTeXMsgs [emMaxFault + 1] =
{
	{emMinFault,	etErr,	TRUE,
		INTERNFAULT},
	{emSpaceTerm,	etWarn, TRUE,
		"Command terminated with space."},
	{emNBSpace,		etWarn, TRUE,
		"Non-breaking space (`~') should have been used."},
	{emEnclosePar,	etWarn,	TRUE,
		"You should enclose the previous parenthesis with `{}\'."},
	{emItInNoIt,	etWarn, TRUE,
		"Italic correction (`\\/') found in non-italic buffer."},
	{emItDup,		etWarn, TRUE,
		"Italic correction (`\\/') found more than once."},
	{emNoItFound,	etWarn, TRUE,
		"No italic correction (`\\/') found."},
	{emAccent,		etWarn,	TRUE,
		"Accent command `%s' needs use of `\\%c%s'."},
	{emWrongDash,	etWarn, TRUE,
		"Wrong length of dash may have been used."},
	{emExpectC,		etWarn, TRUE,
		"`%c' expected, found `%c'."},
	{emSoloC,		etWarn, TRUE,
		"Solo `%c' found."},
	{emEllipsis,	etWarn, TRUE,
		"You should use `\\dots' to achieve an ellipsis."},
	{emInterWord,	etWarn, TRUE,
		"Interword spacing (`\\ ') should been used."},
	{emInterSent,	etWarn,	TRUE,
		"Intersentence spacing (`\\@') should perhaps been used."},
	{emNoArgFound,	etErr,	TRUE,
		"Could not find argument for command `%s'."},
	{emNoMatchC,	etWarn, TRUE,
		"No match found for character `%c'."},
	{emMathStillOn,	etWarn,	TRUE,
		"Mathmode still on at end of LaTeX file."},
	{emNoMatchCC,	etWarn,	TRUE,
		"Number of `%c' doesn't match the number of `%c'!"},
	{emUseQuoteLiga, etWarn, TRUE,
		"Use either `` or '' as an alternative to `\"'."},
	{emUseOtherQuote, etWarn, TRUE,
		"Use \"'\" (ASCII 39) instead  of \"´\" (ASCII 180)."},
	{emUserWarn, etWarn, TRUE,
		"User-specified pattern found."},
	{emNotIntended, etWarn, TRUE,
		"This command might not be intended."},
	{emComment, etMsg, FALSE,
		"Comment displayed."},
	{emThreeQuotes, etWarn, TRUE,
		"Either %c\\,%c%c or %c%c\\,%c will look better."},
	{emFalsePage, etWarn, TRUE,
		"Delete this space to maintain correct pagereferences."},
	{emEmbrace, etWarn, TRUE,
		"You might wish to put this between a pair of `{}'"},
	{emMaxFault,	etErr,	TRUE,
		INTERNFAULT}                                         
};

/************************************************************************/


/*
 * Have to do things this way, to ease some checking throughout the
 * program.
 */


NEWBUF(TmpBuffer,	BUFLEN);
NEWBUF(CmdBuffer,	BUFLEN);
NEWBUF(ReadBuffer,	BUFLEN);

const STRPTR
	Banner  =
			"ChkTeX v1.2 - Copyright 1995-96 Jens T. Berger Thielemann\n",
	BigBanner  =
			"\n"
			"ChkTeX comes with ABSOLUTELY NO WARRANTY; details on this and\n"
			"distribution conditions in the GNU General Public License file.\n"
			"Type \"ChkTeX -h\" for help, \"ChkTeX -i\" for distribution info.\n"
			"To contact the author, write to:\n"
			"Jens Berger, Spektrumvn. 4, N-0666 Oslo, Norway.\n"
			"E-mail: <jensthi@ifi.uio.no>\n"
#ifdef AMIGA
			"Press Ctrl-\\ to abort stdin input.\n"
#endif
			,
	Distrib  =
			"\n"
			"This program is free software; you can redistribute it and/or modify\n"
			"it under the terms of the GNU General Public License as published by\n"
			"the Free Software Foundation; either version 2 of the License, or\n"
			"(at your option) any later version.\n"
			"\n"
			"This program is distributed in the hope that it will be useful,\n"
			"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
			"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
			"GNU General Public License for more details.\n"
			"\n"
			"You should have received a copy of the GNU General Public License\n"
			"along with this program; if not, write to the Free Software\n"
			"Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n"
			"\n",
	OnText  =
			"On",
	OffText  =
			"Off",
	HelpText  =
			"\n"
			"\n"
			"                         Usage of ChkTeX v1.2\n"
			"                         ~~~~~~~~~~~~~~~~~~~~\n"
			"\n"
			"                               Template\n"
			"                               ~~~~~~~~\n"
			"chktex [-hiqr] [-v[0|1|2]] [-l <rcfile>] [-[w|e|m|n] <[0-24]>]\n"
			"       [-d <number>] [-o <outputfile>] [-b[0|1]] [-t[0|1]]\n"
			"       [-x[0|1]] [-g[0|1]]\n"
			"\n"
			"----------------------------------------------------------------------\n"
			"                       Description of options:\n"
			"                       ~~~~~~~~~~~~~~~~~~~~~~~\n"
			"Misc. options\n"
			"~~~~~~~~~~~~~\n"
			"    -h  --help      : This text.\n"
			"    -i  --license   : Show distribution information\n"
			"    -l  --localrc   : Read local .chktexrc formatted  file.\n"
			"    -d  --debug     : Debug information. Give it a number.\n"
			"    -r  --reset     : Reset settings to default.\n"
			"\n"
			"Muting warning messages:\n"
			"~~~~~~~~~~~~~~~~~~~~~~~~\n"
			"    -w  --warnon    : Makes msg # given a warning and turns it on.\n"
			"    -e  --erroron   : Makes msg # given an error and turns it on.\n"
			"    -m  --msgon     : Makes msg # given a message and turns it on.\n"
			"    -n  --nowarn    : Mutes msg # given.\n"
			"\n"
			"Output control flags:\n"
			"~~~~~~~~~~~~~~~~~~~~~\n"
			"    -v  --verbosity : How errors are displayed.\n"
			"                      Default 1, 0=Less, 2=More.\n"
			"    -s  --splitchar : String used to split fields when doing -v0\n"
			"    -o  --output    : Redirect error report to a file.\n"
			"    -q  --quiet     : Shuts up about version information.\n"
			"\n"
			"Boolean switches (1 -> enables / 0 -> disables):\n"
			"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
			"    -b  --backup    : Backup output file.\n"
			"    -t  --tictoc    : Display a twirling baton.\n"
			"                      -v0 does an -t0, too.\n"
			"    -x  --wipeverb  : Ignore contents of `\\verb' commands.\n"
			"    -g  --globalrc  : Read global .chktexrc file.\n"
			"\n"
			"----------------------------------------------------------------------\n"
			"If no LaTeX files are specified on the command line, we will read from\n"
			"stdin.   For explanation of warning/error messages, please consult the\n"
			"main document ChkTeX.dvi or ChkTeX.ps.\n";



UBYTE EmptyString [] = "";


/*
 * Options we will set.
 *
 */

LONG Verbosity = vbNormal;


BOOL 	GlobalRC = TRUE, WipeVerb = TRUE, TicToc = TRUE, BackupOut = TRUE,
		Quiet = FALSE, LicenseOnly = FALSE, UsingStdIn = FALSE;

LONG	DebugLevel = 0;

FILE *OutputFile = NULL, *InputFile = NULL;

STRPTR OutputName = EmptyString, InputName = EmptyString;

UBYTE	ConfigFile [BUFLEN] = RCFILE;

STRPTR	Delimit = ":";

STRPTR	PrgName;

/*
 * End of config params.
 */


int main(int argc, char **argv)
{
	int		retval = EXIT_FAILURE, CurArg = 0L;
	ULONG	LineCount, Count;


#ifdef __LOCALIZED
	InitStrings();
#endif

	OutputFile = stdout;
	PrgName = argv[0];

	SetupVars();
	ReadRC(ConfigFile);

	if(CmdLine.Stack.Used)
	{
		ParseArgs(CmdLine.Stack.Used, (STRPTR *) CmdLine.Stack.Data);
		CmdLine.Stack.Used = 0L;
	}

	if(CurArg = ParseArgs((ULONG) argc, argv))
	{
		if(CmdLine.Stack.Used)
		{
			ParseArgs(CmdLine.Stack.Used, (STRPTR *) CmdLine.Stack.Data);
			CmdLine.Stack.Used = 0L;
		}

		if(!Quiet || LicenseOnly)
			fprintf(stderr, Banner);

		if(((CurArg == argc) && !Quiet) || LicenseOnly)
			fprintf(stderr, BigBanner);

		if(LicenseOnly)
			fprintf(stderr, Distrib);
		else
		{
			if(CurArg == argc)
				UsingStdIn = TRUE;

			for(Count = 0L;
				Count < Abbrev.Stack.Used;
				Count++)
				strupr(Abbrev.Stack.Data[Count]);

			if(DebugLevel)
				ShowIntStatus();

			if(OpenOut())
			{
				for(;;)
				{
					for(Count = 0; Count < NUMBRACKETS; Count++)
						Brackets[Count] = 0L;

					LineCount = 1;
					ItState  = itOff;
					AtLetter = MathMode  = FALSE;
					ErrPrint = WarnPrint = UserSupp = 0L;

					if(UsingStdIn) {
						if(InputFile)
							break;
						else {
							InputFile = stdin;
							InputName = "stdin";
						}
					} else {
						if((CurArg <= argc) || InputName) {
							ifn(InputName = MatchFileName(NULL)) {
								if(CurArg < argc)
									InputName = MatchFileName(argv[CurArg++]);
							}

							if(InputName)
								InputFile = fopen(InputName, "r");
							else
								break;

							if(!InputFile) {
								strcpy(TmpBuffer, InputName);
								strcat(TmpBuffer, ".tex");

								ifn(InputFile = fopen(TmpBuffer, "r")) {
									PrintPrgErr(pmNoTeXOpen);
									break;
								}
							}
						}
					}

					if(InputFile && OutputFile)
					{
						if(Verbosity > vbSilent)
							fprintf(OutputFile, "Scanning %s...\n",
									InputName);

						while(!ferror(OutputFile) && !ferror(InputFile) &&
								fgets(ReadBuffer, BUFLEN-1, InputFile))
						{

							/* Make all spaces ordinary spaces */

							strrep(ReadBuffer, '\n', ' ');
							strrep(ReadBuffer, '\r', ' ');
							strrep(ReadBuffer, '\t', ' ');

							strcat(ReadBuffer, " ");
							FindErr(ReadBuffer, LineCount);

							if((Verbosity != vbSilent) && TicToc)
							{
								if(LineCount & 1)
									tictoc();
							}
							LineCount++;
						}

						PrintStatus(LineCount);
						retval = EXIT_SUCCESS;
					}
				}
			}
		}
	}
	exit(retval);
}

/*
 * Opens the output file handle & possibly renames
 */

BOOL OpenOut(void)
{
	BOOL Success = TRUE;

	if(*OutputName)
	{
		if(BackupOut && fexists(OutputName))
		{
			strncpy(TmpBuffer, OutputName, BUFLEN - 4);
			strcat(TmpBuffer, ".bak");

			if(fexists(TmpBuffer))
				remove(TmpBuffer);

			if(!rename(OutputName, TmpBuffer))
				PrintPrgErr(pmRename,
						OutputName, TmpBuffer);
			else
			{
				PrintPrgErr(pmRenameErr,
						OutputName, TmpBuffer);
				Success = FALSE;
			}

		}

		if(Success)
		{
			ifn(OutputFile = fopen(OutputName, "w"))
			{
				PrintPrgErr(pmOutOpen);
				Success = FALSE;
			}
		}
	}
	else
		OutputFile = stdout;

	return(Success);
}


#define BOOLDISP(var)		((var)? OnText : OffText)
#define SHOWSTAT(text, arg)	fprintf(stderr, "\t" text ": %s\n", arg)
#define BOOLSTAT(name, var)	SHOWSTAT(name, BOOLDISP(var))
#define SHOWWORD(text, list)	fprintf(stderr, "\n" text "\n"); \
							for(Cnt = 0L; \
								Cnt < list.Stack.Used; \
								Cnt++) \
								fprintf(stderr, "\t%s\n", list.Stack.Data[Cnt])

/*
 * Prints some of the internal flags; mainly for debugging purposes
 */

void ShowIntStatus(void)
{
	ULONG	Cnt;
	STRPTR	String;

	fprintf(stderr, "There are %d warnings/error messages available:\n", emMaxFault - emMinFault - 1);

	for(Cnt = emMinFault + 1; Cnt < emMaxFault; Cnt++)
	{
		switch(LaTeXMsgs[Cnt].Type)
		{
		case etWarn:	String = "Warning"; break;
		case etErr:		String = "Error"; break;
		case etMsg:		String = "Message"; break;
		}

		fprintf(stderr, "Number: %2d, Type: %s, Status: %s\n"
						"\tText: %s\n\n",
						Cnt, String,
						BOOLDISP(LaTeXMsgs[Cnt].InUse),
						LaTeXMsgs[Cnt].Message);
	}

	fprintf(stderr, "Current flags include:\n");

	switch(Verbosity)
	{
	case vbSilent:	String = "Silent"; break;
	case vbNormal:	String = "Normal"; break;
	case vbFancy:	String = "Fancy"; break;
	}

	SHOWSTAT("Verbosity", String);

	BOOLSTAT("Read global resource", GlobalRC);
	BOOLSTAT("Wipe verbose commands", WipeVerb);
	BOOLSTAT("Show twirling baton", TicToc);
	BOOLSTAT("Backup outfile", BackupOut);
	BOOLSTAT("Quiet mode", Quiet);
	BOOLSTAT("Show license", LicenseOnly);
	BOOLSTAT("Use stdin", UsingStdIn);

	SHOWWORD("`Silent' commands:", Silent);
	SHOWWORD("Abbreviations searched for:", Abbrev);
	SHOWWORD("`Linked' commands:", Linker);
	SHOWWORD("Accenting commands needing `\\i' and `\\j':", IJAccent);
	SHOWWORD("Italizing commands:", Italic);
	SHOWWORD("User patterns:", UserWarn);
}

/*
 * Resets all flags (not wordlists) to their default values. Sets
 * Outputfile to stdout.
 *
 */

void ResetSettings(void)
{
	GlobalRC = TRUE;
	WipeVerb = TRUE;
	TicToc = TRUE;
	BackupOut = TRUE;
	Quiet = FALSE;
	LicenseOnly = FALSE;
	UsingStdIn = FALSE;
	DebugLevel = 0;

	Verbosity = vbNormal;

	if(OutputFile != stdout)
	{
		fclose(OutputFile);
		OutputFile = stdout;
	}

	if(InputFile)
	{
		fclose(InputFile);
		InputFile = NULL;
	}

	OutputName = EmptyString;
	InputName = EmptyString;
}

/*
 * Reads a numerical argument from the argument. Supports concatenation
 * of arguments (main purpose)
 */

int ParseNumArg(LONG *Dest,			/* Where to put the value */
				LONG Default,		/* Value to put in if no in argue */
				STRPTR *Argument)	/* optarg or similar */
{
	if(Argument && *Argument && **Argument && isdigit(**Argument))
		*Dest = strtol(*Argument, Argument, 10);
	else
		*Dest = Default;

	return(ShiftArg(Argument));
}

/*
 * Same as above; however, will toggle the boolean if user doesn't
 * supply value
 */

int ParseBoolArg(BOOL *Dest,			/* Boolean value */
				 STRPTR *Argument)		/* optarg or similar */
{
	LONG	D = *Dest? 1L : 0L;
	int		Retval;

	Retval = ParseNumArg(&D, *Dest? 0L : 1L, Argument);

	*Dest = D ? TRUE : FALSE;

	return(Retval);
}

/*
 * Returns the first character in the string passed, updates the
 * string pointer, if the string is non-empty.
 *
 * 0 if the string is empty.
 */

int ShiftArg(STRPTR *Argument)			/* optarg or similar */
{
	if(Argument && *Argument && **Argument)
		return(*((STRPTR) (*Argument)++));
	else
		return 0;
}

/*
 * Parses an argv similar array.
 */


int ParseArgs(ULONG argc, char **argv)
{
	/* Needed for option parsing. */

static const
	struct option long_options[] =
	{
		{"help",		no_argument,		0L,	'h'},
		{"localrc",		required_argument,	0L,	'l'},
		{"output",		required_argument,	0L,	'o'},
		{"warnon",		required_argument,	0L,	'w'},
		{"erroron",		required_argument,	0L,	'e'},
		{"msgon",		required_argument,	0L, 'm'},
		{"nowarn",		required_argument,	0L,	'n'},
		{"verbosity",	optional_argument,	0L,	'v'},
		{"debug",		required_argument,	0L, 'd'},
		{"reset",		no_argument,		0L,	'r'},
		{"quiet",		no_argument,		0L,	'q'},
		{"license",		no_argument,		0L,	'i'},
		{"splitchar",	required_argument,	0L,	's'},

		{"backup",		optional_argument,		0L,	'b'},
		{"globalrc",	optional_argument,		0L,	'g'},
		{"wipeverb",	optional_argument,		0L,	'x'},
		{"tictoc",		optional_argument,		0L,	't'},
		{0L,			0L,						0L, 0L}
	};

	int		option_index = 0L;

	int		c, nextc, ErrType;
	LONG	Err;

	int		Retval = FALSE;
	BOOL	Success = FALSE;

	int		InUse;

	enum
	{
		aeNoErr = 0,
		aeArg,				/* missing/bad required argument */
		aeOpt,				/* unknown option returned */
		aeHelp,				/* just a call for help */
		aeMem				/* no memory */
	} ArgErr = aeNoErr;

	optind = 0;

	while(!ArgErr && ((c = getopt_long((int) argc, argv,
							"hl:o:w:e:m:n:v::d:rqib::g::x::t::s:", long_options,
							&option_index)) != EOF))
	{
		while(c) {
			nextc = 0;
			switch(c)
			{
			case 's':
				ifn(Delimit = strdup(optarg)) {
					PrintPrgErr(pmStrDupErr);
					ArgErr = aeMem;
				}

				break;
			case 'd':
				nextc = ParseNumArg(&DebugLevel, 0, &optarg);

				break;
			case 'i':
				LicenseOnly = TRUE;

				nextc = ShiftArg(&optarg);
				break;
			case 'q':
				Quiet = TRUE;

				nextc = ShiftArg(&optarg);
				break;

				/* Variation of Duff's device... */
				do {
			case 'w':
					ErrType = etWarn;
					InUse = TRUE;
					break;
			case 'e':
					ErrType = etErr;
					InUse = TRUE;
					break;
			case 'm':
					ErrType	= etMsg;
					InUse = TRUE;
					break;
			case 'n':
					InUse = FALSE;
				} while(FALSE);

				nextc = ParseNumArg(&Err, -1, &optarg);

				if(betw(emMinFault, Err, emMaxFault)) {
					LaTeXMsgs[Err].Type	= ErrType;
					LaTeXMsgs[Err].InUse = InUse;
				} else {
					ArgErr = aeOpt;
					PrintPrgErr(pmWarnNumErr);
				}
				break;

			case 'g':
				nextc = ParseBoolArg(&GlobalRC, &optarg);
				if(!GlobalRC) {
					Silent.Stack.Used 	= 0L;
					Abbrev.Stack.Used 	= 0L;
					Linker.Stack.Used 	= 0L;
					IJAccent.Stack.Used = 0L;
					Italic.Stack.Used 	= 0L;
					UserWarn.Stack.Used = 0L;
				}
				break;

			case 'r':
				ResetSettings();
				nextc = ShiftArg(&optarg);
				break;

			case 'l':
				if(optarg)
					ReadRC(optarg);
				break;

			case 'v':
				nextc = ParseNumArg(&Verbosity, vbFancy, &optarg);
				switch(Verbosity)
				{
				case vbSilent:
				case vbNormal:
					break;
				case vbFancy:
					if(TermType != tmUnknown) {
						Verbosity = vbFancy;
					} else {
						PrintPrgErr(pmUnknownTerm);
						Verbosity = vbNormal;
					}
					break;
				default:
					PrintPrgErr(pmVerbLevErr);
					ArgErr = aeArg;
					break;
				}
				break;

			case 'o':
				if(optarg)
				{
					if(*OutputName)
					{
						PrintPrgErr(pmOutTwice);
						ArgErr = aeOpt;
					}
					else
					{
						ifn(OutputName = strdup(optarg))
						{
							PrintPrgErr(pmStrDupErr);
							ArgErr = aeMem;
						}
					}
				}

				break;

			case 't':
				nextc = ParseBoolArg(&TicToc, &optarg);
				break;
			case 'x':
				nextc = ParseBoolArg(&WipeVerb, &optarg);
				break;
			case 'b':
				nextc = ParseBoolArg(&BackupOut, &optarg);
				break;

			case 'h':
			case '?':
			default:
				ArgErr = aeHelp;
				break;
			}
			c = nextc;
		}
	}

	if((argc >= optind) && !strcmp(argv[optind], "?"))
		ArgErr = aeHelp;

	if(ArgErr)
	{
		fputs(Banner, stderr);
		fputs(BigBanner, stderr);
		fputs(HelpText, stderr);
		Success = FALSE;
	}
	else
		Success = TRUE;

	if(Success)
		Retval = optind;

	return(Retval);
}

/*
 * Outputs a program error.
 */


void PrintPrgErr(enum PrgErrNum Error, ...)
{
	STRPTR	Type;
	va_list	MsgArgs;

	if(betw(pmMinFault, Error, pmMaxFault)) {
		switch(PrgMsgs[Error].Type) {
		case etWarn:
			Type = "WARNING";
			break;
		case etErr:
			Type = "ERROR";
			break;
		case etMsg:
			Type = "NOTE";
			break;
		}
		fprintf(stderr, "%s: %s -- ", PrgName, Type);

		va_start(MsgArgs, Error);
		vfprintf(stderr, PrgMsgs[Error].Message, MsgArgs);
		va_end(MsgArgs);
		fputc('\n', stderr);
	}
}



