/* * 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: * * */ #include "ChkTeX.h" #ifdef KPATHSEA #include #else #include #endif #include "OpSys.h" #include "Utility.h" #include "FindErrs.h" #include "Resource.h" #include #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, \n" #elif defined(__MSDOS__) "MS-DOS port by Bj\\o rn Ove Thue, \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 darthandrus@gmail.com\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 ] [-[wemn] <[1-42]|all>]\n" " [-d[0-...]] [-p ] [-o ] [-[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); }