/*
* ChkTeX, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contact the author at:
* Jens Berger
* Spektrumvn. 4
* N-0666 Oslo
* Norway
* E-mail: <
[email protected]>
*
*
*/
#include "ChkTeX.h"
#ifdef KPATHSEA
#include <kpathsea/getopt.h>
#else
#include <getopt.h>
#endif
#include "OpSys.h"
#include "Utility.h"
#include "FindErrs.h"
#include "Resource.h"
#include <string.h>
#undef MSG
#define MSG(num, type, inuse, ctxt, text) {(enum ErrNum)num, type, inuse, ctxt, text},
struct ErrMsg PrgMsgs[pmMaxFault + 1] = {
PRGMSGS {(enum ErrNum)pmMaxFault, etErr, TRUE, 0, INTERNFAULT}
};
struct Stack CharStack = {0L};
struct Stack InputStack = {0L};
struct Stack EnvStack = {0L};
struct Stack ConTeXtStack = {0L};
struct Stack FileSuppStack = {0L};
struct Stack UserFileSuppStack = {0L};
struct Stack MathModeStack = {0L};
/************************************************************************/
const char BrOrder[NUMBRACKETS + 1] = "()[]{}";
unsigned long Brackets[NUMBRACKETS];
/************************************************************************/
/*
* Have to do things this way, to ease some checking throughout the
* program.
*/
NEWBUF(TmpBuffer, BUFFER_SIZE);
NEWBUF(ReadBuffer, BUFFER_SIZE);
static const char *Banner =
"ChkTeX v" PACKAGE_VERSION " - Copyright 1995-96 Jens T. Berger Thielemann.\n"
#ifdef __OS2__
"OS/2 port generated with emx compiler, by Wolfgang Fritsch, <
[email protected]>\n"
#elif defined(__MSDOS__)
"MS-DOS port by Bj\\o rn Ove Thue, <
[email protected]>\n"
#endif
#if HAVE_PCRE
"Compiled with PCRE regex support."
#else
#if HAVE_POSIX_ERE
"Compiled with POSIX extended regex support."
#else
"Compiled with no regex support."
#endif
#endif
"\n";
static const char *BigBanner =
"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"
"Author: Jens Berger.\n"
"Bug reports:
https://savannah.nongnu.org/bugs/?group=chktex\n"
" or
[email protected]\n"
"Press " STDIN_BREAK " to terminate stdin input.\n";
static const char *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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n";
static const char *OnText = "On";
static const char *OffText = "Off";
static const char *HowHelp =
"-h or --help gives usage information. See also ChkTeX.{ps,dvi}.\n";
static const char *HelpText =
"\n"
"\n"
" Usage of ChkTeX v" PACKAGE_VERSION "\n"
" ~~~~~~~~~~~~~~~~~~~~~~\n"
"\n"
" Template\n"
" ~~~~~~~~\n"
"chktex [-hiqrW] [-v[0-...]] [-l <rcfile>] [-[wemn] <[1-42]|all>]\n"
" [-d[0-...]] [-p <name>] [-o <outfile>] [-[btxgI][0|1]]\n"
" file1 file2 ...\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. A bit field with 5 bits.\n"
" Each bit shows a different type of information.\n"
" -r --reset : Reset settings to default.\n"
" -S --set : Read it's argument as if from chktexrc.\n"
" e.g., -S TabSize=8 will override the TabSize.\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"
" -L --nolinesupp: Disables per-line and per-file suppressions.\n"
"\n"
"Output control flags:\n"
"~~~~~~~~~~~~~~~~~~~~~\n"
" -v --verbosity : How errors are displayed.\n"
" Default 1, 0=Less, 2=Fancy, 3=lacheck.\n"
" -V --pipeverb : How errors are displayed when stdout != tty.\n"
" Defaults to the same as -v.\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"
" -p --pseudoname: Input file-name when reporting.\n"
" -f --format : Format to use for output\n"
"\n"
"Boolean switches (1 -> enables / 0 -> disables):\n"
"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
" -b --backup : Backup output file.\n"
" -x --wipeverb : Ignore contents of `\\verb' commands.\n"
" -g --globalrc : Read global .chktexrc file.\n"
" -I --inputfiles: Execute \\input statements.\n"
" -H --headererr : Show errors found before \\begin{document}\n"
"\n"
"Miscellaneous switches:\n"
"~~~~~~~~~~~~~~~~~~~~~~~\n"
" -W --version : Version information\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 documentation ChkTeX.dvi, ChkTeX.ps or ChkTeX.pdf:\n"
"
http://www.nongnu.org/chktex/ChkTeX.pdf\n"
"\n"
"Any of the above arguments can be made permanent by setting them in the\n"
"chktexrc file "
#if defined(__unix__)
"(~/.chktexrc).\n"
#else
"(see documentation for location).\n"
#endif
;
/*
* Options we will set.
*
*/
enum Quote Quote;
enum CmdSpace CmdSpace;
char VerbNormal[] = "%k %n in %f line %l: %m\n" "%r%s%t\n" "%u\n";
#define DEF(type, name, value) type name = value
OPTION_DEFAULTS;
STATE_VARS;
#undef DEF
FILE *OutputFile = NULL;
char *PrgName;
int StdInTTY, StdOutTTY;
/*
* End of config params.
*/
static int ParseArgs(int argc, char **argv);
static void ShowIntStatus(void);
static int OpenOut(void);
static int ShiftArg(char **Argument);
/*
* Duplicates all arguments, and appends an asterix to each of them.
*/
static void AddStars(struct WordList *wl)
{
unsigned long Count, CmdLen;
char *Data;
FORWL(Count, *wl)
{
Data = wl->Stack.Data[Count];
CmdLen = strlen(Data);
if (Data[CmdLen - 1] != '*')
{
strcpy(TmpBuffer, Data);
strcat(TmpBuffer, "*");
InsertWord(TmpBuffer, wl);
}
}
}
/*
* Sets up all the lists.
*
*/
static void SetupLists(void)
{
unsigned long i;
AddStars(&VerbEnvir);
AddStars(&MathEnvir);
MakeLower(&UserWarnCase);
ListRep(&WipeArg, ':', 0);
ListRep(&NoCharNext, ':', 0);
#define ThisItem ((char *) AbbrevCase.Stack.Data[i])
FORWL(i, AbbrevCase)
{
if (isalpha((unsigned char)ThisItem[0]))
{
ThisItem[0] = toupper((unsigned char)ThisItem[0]);
InsertWord(ThisItem, &Abbrev);
ThisItem[0] = tolower((unsigned char)ThisItem[0]);
}
InsertWord(ThisItem, &Abbrev);
}
}
#define NOCOMMON(a,b) NoCommon(&a,#a,&b,#b)
/*
* Checks that two lists don't have any common element.
*/
static void
NoCommon(struct WordList *a, const char *aName,
struct WordList *b, const char *bName)
{
unsigned long i;
FORWL(i, *a)
{
if (HasWord((char *) a->Stack.Data[i], b))
PrintPrgErr(pmNoCommon, a->Stack.Data[i], aName, bName);
}
}
/*
* Expands the tabs in a string to regular intervals sized
* TSize.
*/
static void ExpandTabs(char *From, char *To, long TSize, long MaxDiff)
{
char *Next;
char *Orig;
unsigned long Diff;
static short HasExpandedTooLong = 0;
Next = From;
Orig = To;
while ((Next = strchr(From, '\t')))
{
if ((Diff = Next - From))
{
strncpy(To, From, Diff);
To += Diff;
Diff = TSize - ((To - Orig) % TSize);
}
else
Diff = TSize;
/* Make sure we don't expand this buffer out of the memory we
* have allocated for it. */
if ( Diff > MaxDiff+1 )
{
Diff = MaxDiff+1;
if ( !HasExpandedTooLong )
{
PrintPrgErr(pmTabExpands, BUFFER_SIZE);
}
HasExpandedTooLong = 1;
}
MaxDiff -= (Diff-1);
memset(To, ' ', Diff);
To += Diff;
From = ++Next;
}
strcpy(To, From);
}
void ReadRcFiles(void)
{
unsigned long i;
while (SetupVars())
{
InsertWord(ConfigFile, &ConfigFiles);
}
FORWL(i, ConfigFiles)
{
ReadRC(ConfigFiles.Stack.Data[i]);
}
}
int main(int argc, char **argv)
{
int retval = EXIT_FAILURE, ret, CurArg;
unsigned long Count;
int StdInUse = FALSE;
long Tab = 8;
#ifdef __LOCALIZED
InitStrings();
#endif
OutputFile = stdout;
#ifdef KPATHSEA
kpse_set_program_name(argv[0], "chktex");
PrgName = kpse_program_name;
#ifdef WIN32
setmode(fileno(stdout), _O_BINARY);
#endif
#else
PrgName = argv[0];
#endif
#undef KEY
#undef LCASE
#undef LIST
#undef LNEMPTY
#define KEY(a, def)
#define LCASE(a)
#define LIST(a)
#define LNEMPTY(a) InsertWord("", &a);
RESOURCE_INFO
ReadRcFiles();
if (CmdLine.Stack.Used)
{
ParseArgs(CmdLine.Stack.Used, (char **) CmdLine.Stack.Data);
CmdLine.Stack.Used = 1L;
}
if ((CurArg = ParseArgs((unsigned long) argc, argv)))
{
retval = EXIT_SUCCESS;
if (CmdLine.Stack.Used)
{
ParseArgs(CmdLine.Stack.Used, (char **) CmdLine.Stack.Data);
CmdLine.Stack.Used = 1L;
}
if (!Quiet || LicenseOnly)
fprintf(stderr, "%s", Banner);
if (CurArg == argc)
UsingStdIn = TRUE;
#if defined(HAVE_FILENO) && defined(HAVE_ISATTY)
StdInTTY = isatty(fileno(stdin));
StdOutTTY = isatty(fileno(stdout));
#else
StdInTTY = StdOutTTY = TRUE;
#endif
SetupTerm();
if ((UsingStdIn && StdInTTY && !Quiet) || LicenseOnly)
{
fprintf(stderr, "%s", BigBanner);
}
if (!StdOutTTY && PipeOutputFormat)
OutputFormat = PipeOutputFormat;
if (LicenseOnly)
{
fprintf(stderr, "%s", Distrib);
}
else
{
SetupLists();
if (QuoteStyle)
{
if (!strcasecmp(QuoteStyle, "LOGICAL"))
Quote = quLogic;
else if (!strcasecmp(QuoteStyle, "TRADITIONAL"))
Quote = quTrad;
else
{
PrintPrgErr(pmQuoteStyle, QuoteStyle);
Quote = quTrad;
}
}
if (CmdSpaceStyle)
{
if (!strcasecmp(CmdSpaceStyle, "IGNORE"))
CmdSpace = csIgnore;
else if (!strcasecmp(CmdSpaceStyle, "INTERWORD"))
CmdSpace = csInterWord;
else if (!strcasecmp(CmdSpaceStyle, "INTERSENTENCE"))
CmdSpace = csInterSentence;
else if (!strcasecmp(CmdSpaceStyle, "BOTH"))
CmdSpace = csBoth;
else
{
PrintPrgErr(pmCmdSpaceStyle, CmdSpaceStyle);
CmdSpace = csIgnore;
}
}
if (DebugLevel)
ShowIntStatus();
NOCOMMON(Italic, NonItalic);
NOCOMMON(Italic, ItalCmd);
NOCOMMON(LowDots, CenterDots);
if (TabSize && isdigit((unsigned char)*TabSize))
Tab = strtol(TabSize, NULL, 10);
if (OpenOut())
{
for (;;)
{
for (Count = 0; Count < NUMBRACKETS; Count++)
Brackets[Count] = 0L;
#define DEF(type, name, value) name = value
STATE_VARS;
#undef DEF
if (UsingStdIn)
{
if (StdInUse)
break;
else
{
StdInUse = TRUE;
PushFile("stdin", stdin, &InputStack);
}
}
else
{
if (CurArg <= argc)
{
const char *filename = NULL;
if (CurArg < argc)
filename = argv[CurArg++];
AddDirectoryFromRelativeFile(filename,&TeXInputs);
if (!PushFileName(filename, &InputStack))
break;
}
}
if (StkTop(&InputStack) && OutputFile)
{
while (!ferror(OutputFile)
&& StkTop(&InputStack)
&& !ferror(CurStkFile(&InputStack))
&& FGetsStk(ReadBuffer, BUFFER_SIZE - 1,
&InputStack))
{
/* Make all spaces ordinary spaces */
strrep(ReadBuffer, '\n', ' ');
strrep(ReadBuffer, '\r', ' ');
ExpandTabs(ReadBuffer, TmpBuffer, Tab,
BUFFER_SIZE - 1 - strlen(ReadBuffer));
strcpy(ReadBuffer, TmpBuffer);
strcat(ReadBuffer, " ");
ret = FindErr(ReadBuffer, CurStkLine(&InputStack));
if ( ret != EXIT_SUCCESS ) {
retval = ret;
}
}
PrintStatus(CurStkLine(&InputStack));
}
}
}
}
}
return retval;
}
/*
* Opens the output file handle & possibly renames
*/
static int OpenOut(void)
{
int Success = TRUE;
if (*OutputName)
{
if (BackupOut && fexists(OutputName))
{
strcpy(TmpBuffer, OutputName);
AddAppendix(TmpBuffer, BAKAPPENDIX);
if (fexists(TmpBuffer))
remove(TmpBuffer);
if (!rename(OutputName, TmpBuffer))
PrintPrgErr(pmRename, OutputName, TmpBuffer);
else
{
PrintPrgErr(pmRenameErr, OutputName, TmpBuffer);
Success = FALSE;
}
}
if (Success)
{
#ifdef KPATHSEA
if (!(OutputFile = fopen(OutputName, "wb")))
#else
if (!(OutputFile = fopen(OutputName, "w")))
#endif
{
PrintPrgErr(pmOutOpen);
Success = FALSE;
}
}
}
else
OutputFile = stdout;
return (Success);
}
#ifndef STRIP_DEBUG
static void ShowWL(const char *Name, const struct WordList *wl)
{
unsigned long i, j, percent;
fprintf(stderr, "Name: %12s", Name);
if (DebugLevel & FLG_DbgListInfo)
{
fprintf(stderr, ", MaxLen: %3ld, Entries: %3ld, ",
wl->MaxLen, wl->Stack.Used);
if (wl->Hash.Index && wl->Stack.Used)
{
for (i = j = 0; i < HASH_SIZE; i++)
{
if (wl->Hash.Index[i])
j++;
}
percent = (j * 10000) / wl->Stack.Used;
fprintf(stderr, "Hash usage: %3ld.%02ld%%",
percent / 100, percent % 100);
}
else
fprintf(stderr, "No hash table.");
}
fputc('\n', stderr);
if (DebugLevel & FLG_DbgListCont)
{
FORWL(i, *wl) fprintf(stderr, "\t%s\n", (char *) wl->Stack.Data[i]);
}
}
#endif
#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 SHOWSTR(text, arg) fprintf(stderr, "%s:\n\t%s\n", text, arg);
/*
* Prints some of the internal flags; mainly for debugging purposes
*/
static void ShowIntStatus(void)
{
#ifndef STRIP_DEBUG
unsigned long Cnt;
const char *String;
const char *iuStr = "";
if (DebugLevel & FLG_DbgMsgs)
{
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;
default:
String = "";
break;
}
switch (LaTeXMsgs[Cnt].InUse)
{
case iuOK:
iuStr = "In use";
break;
case iuNotUser:
iuStr = "User muted";
break;
case iuNotSys:
iuStr = "System muted";
break;
}
fprintf(stderr, "Number: %2ld, Type: %s, Status: %s\n"
"\tText: %s\n\n",
Cnt, String, iuStr, LaTeXMsgs[Cnt].Message);
}
}
#undef KEY
#undef LCASE
#undef LNEMPTY
#undef LIST
#define LNEMPTY LIST
#define LIST(a) ShowWL(#a, &a);
#define LCASE(a) LIST(a); LIST(a ## Case);
#define KEY(a,def) SHOWSTR(#a, a);
if (DebugLevel & (FLG_DbgListInfo | FLG_DbgListCont))
{
ShowWL("ConfigFilesRead", &ConfigFiles);
RESOURCE_INFO
}
if (DebugLevel & FLG_DbgOtherInfo)
{
SHOWSTR("Outputformat", OutputFormat);
fprintf(stderr, "Current flags include:\n");
BOOLSTAT("Read global resource", GlobalRC);
BOOLSTAT("Wipe verbose commands", WipeVerb);
BOOLSTAT("Backup outfile", BackupOut);
BOOLSTAT("Quiet mode", Quiet);
BOOLSTAT("Show license", LicenseOnly);
BOOLSTAT("Use stdin", UsingStdIn);
BOOLSTAT("\\input files", InputFiles);
BOOLSTAT("Output header errors", HeadErrOut);
BOOLSTAT("No line suppressions", NoLineSupp);
}
#endif
}
/*
* Resets all stacks.
*
*/
#undef KEY
#undef LCASE
#undef LNEMPTY
#undef LIST
#define LNEMPTY LIST
#define LIST(a) ClearWord(&a);
#define LCASE(a) LIST(a); LIST(a ## Case);
#define KEY(a,def) a = def;
static void ResetStacks(void)
{
RESOURCE_INFO
}
/*
* Resets all flags (not wordlists) to their default values. Sets
* Outputfile to stdout.
*
*/
static void ResetSettings(void)
{
#define DEF(type, name, value) name = value
OPTION_DEFAULTS;
#undef DEF
if (OutputFile != stdout)
{
fclose(OutputFile);
OutputFile = stdout;
}
}
/*
* Reads a numerical argument from the argument. Supports concatenation
* of arguments (main purpose)
*/
static int ParseNumArg(long *Dest, /* Where to put the value */
long Default, /* Value to put in if no in argue */
char **Argument) /* optarg or similar */
{
if (Argument && *Argument && isdigit((unsigned char)**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
*/
static int ParseBoolArg(int *Dest, /* Boolean value */
char **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.
*/
static int ShiftArg(char **Argument) /* optarg or similar */
{
if (Argument && *Argument && **Argument)
return (*((char *) (*Argument)++));
else
return 0;
}
/*
* Parses an argv similar array.
*/
static int ParseArgs(int 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'},
{"nolinesupp", no_argument, 0L, 'L'},
{"verbosity", optional_argument, 0L, 'v'},
{"pipeverb", optional_argument, 0L, 'V'},
{"debug", required_argument, 0L, 'd'},
{"reset", no_argument, 0L, 'r'},
{"set", required_argument, 0L, 'S'},
{"quiet", no_argument, 0L, 'q'},
{"license", no_argument, 0L, 'i'},
{"splitchar", required_argument, 0L, 's'},
{"format", required_argument, 0L, 'f'},
{"pseudoname", required_argument, 0L, 'p'},
{"inputfiles", optional_argument, 0L, 'I'},
{"backup", optional_argument, 0L, 'b'},
{"globalrc", optional_argument, 0L, 'g'},
{"wipeverb", optional_argument, 0L, 'x'},
{"tictoc", optional_argument, 0L, 't'},
{"headererr", optional_argument, 0L, 'H'},
{"version", no_argument, 0L, 'W'},
{0L, 0L, 0L, 0L}
};
int option_index = 0L, c, i, nextc, ErrType;
int Retval = FALSE, InUse;
int Success, Foo;
long Err, Verb = 1, PipeVerb = 1;
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,
"b::d:e:f:g::hH::I::il:m:n:Lo:p:qrs:S:t::v::V::w:Wx::",
long_options, &option_index)) != EOF))
{
while (c)
{
nextc = 0;
switch (c)
{
case 'S':
ReadRCFromCmdLine(optarg);
break;
case 's':
if (!(Delimit = strdup(optarg)))
{
PrintPrgErr(pmStrDupErr);
ArgErr = aeMem;
}
break;
case 'p':
if (!(PseudoInName = strdup(optarg)))
{
PrintPrgErr(pmStrDupErr);
ArgErr = aeMem;
}
break;
case 'd':
#ifdef STRIP_DEBUG
PrintPrgErr(pmNoDebugFlag);
#else
nextc = ParseNumArg(&DebugLevel, 0xffff, &optarg);
#endif
break;
case 'i':
LicenseOnly = TRUE;
nextc = ShiftArg(&optarg);
break;
case 'L':
NoLineSupp = TRUE;
nextc = ShiftArg(&optarg);
break;
case 'q':
Quiet = TRUE;
nextc = ShiftArg(&optarg);
break;
LOOP(warntype,
case 'w': ErrType = etWarn; InUse = iuOK; LAST(warntype);
case 'e': ErrType = etErr; InUse = iuOK; LAST(warntype);
case 'm': ErrType = etMsg; InUse = iuOK; LAST(warntype);
case 'n': ErrType = etMsg; InUse = iuNotUser; LAST(warntype);
)
if (isdigit((unsigned char)*optarg))
{
nextc = ParseNumArg(&Err, -1, &optarg);
if (betw(emMinFault, Err, emMaxFault))
{
LaTeXMsgs[Err].Type = ErrType;
LaTeXMsgs[Err].InUse = InUse;
}
else
{
ArgErr = aeOpt;
PrintPrgErr(pmWarnNumErr);
}
}
else if (!strcasecmp(optarg, "all"))
{
for (i = emMinFault + 1; i < emMaxFault; i++)
{
LaTeXMsgs[i].Type = ErrType;
LaTeXMsgs[i].InUse = InUse;
}
}
else
{
ArgErr = aeOpt;
PrintPrgErr(pmWarnNumErr);
}
break;
case 'g':
nextc = ParseBoolArg(&GlobalRC, &optarg);
if (!GlobalRC)
{
ResetStacks();
}
break;
case 'r':
ResetSettings();
nextc = ShiftArg(&optarg);
break;
case 'l':
if (optarg)
ReadRC(optarg);
break;
case 'f':
if (!(OutputFormat = strdup(optarg)))
{
PrintPrgErr(pmStrDupErr);
ArgErr = aeMem;
}
break;
case 'v':
nextc = ParseNumArg(&Verb, 2, &optarg);
if (Verb < (long) OutFormat.Stack.Used)
OutputFormat = strdup(OutFormat.Stack.Data[Verb]);
else
{
PrintPrgErr(pmVerbLevErr);
ArgErr = aeArg;
}
break;
case 'V':
nextc = ParseNumArg(&PipeVerb, 1, &optarg);
if (PipeVerb < (long) OutFormat.Stack.Used)
PipeOutputFormat = strdup(OutFormat.Stack.Data[PipeVerb]);
else
{
PrintPrgErr(pmVerbLevErr);
ArgErr = aeArg;
}
break;
case 'o':
if (optarg)
{
if (*OutputName)
{
PrintPrgErr(pmOutTwice);
ArgErr = aeOpt;
}
else
{
if (!(OutputName = strdup(optarg)))
{
PrintPrgErr(pmStrDupErr);
ArgErr = aeMem;
}
}
}
break;
case 't':
nextc = ParseBoolArg(&Foo, &optarg);
break;
case 'x':
nextc = ParseBoolArg(&WipeVerb, &optarg);
break;
case 'b':
nextc = ParseBoolArg(&BackupOut, &optarg);
break;
case 'I':
nextc = ParseBoolArg(&InputFiles, &optarg);
break;
case 'H':
nextc = ParseBoolArg(&HeadErrOut, &optarg);
break;
case 'W':
printf("%s", Banner);
exit(EXIT_SUCCESS);
case '?':
default:
fputs(Banner, stderr);
fputs(HowHelp, stderr);
exit(EXIT_FAILURE);
break;
case 'h':
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, ...)
{
const char *Type;
va_list MsgArgs;
if (betw(pmMinFault, Error, pmMaxFault))
{
switch (PrgMsgs[Error].Type)
{
case etWarn:
Type = "WARNING";
break;
case etMsg:
Type = "NOTE";
break;
default:
case etErr:
Type = "ERROR";
break;
}
fprintf(stderr, "%s: %s -- ", PrgName, Type);
va_start(MsgArgs, Error);
vfprintf(stderr, PrgMsgs[Error].Message, MsgArgs);
va_end(MsgArgs);
fputc('\n', stderr);
if (PrgMsgs[Error].Type == etErr)
exit(EXIT_FAILURE);
}
}
void ErrPrintf(const char *fmt, ...)
{
va_list MsgArgs;
va_start(MsgArgs, fmt);
vfprintf(stderr, fmt, MsgArgs);
va_end(MsgArgs);
}