/* * ChkTeX, resource file reader. * 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" #include "OpSys.h" #include "Utility.h" #include "Resource.h" #define LNEMPTY(a) struct WordList a = {0, 1, {0}, {0}}; #define LIST(a) struct WordList a = {0, 0, {0}, {0}}; #define LCASE(a) LIST(a) LIST(a ## Case) #define KEY(a,def) const char *a = def; RESOURCE_INFO #undef KEY #undef LCASE #undef LNEMPTY #undef LIST struct KeyWord { const char *Name; const char **String; /* Keyword = item */ struct WordList *List, /* Case-sensitive strings */ *CaseList; /* Case-insensitive strings */ }; #define LNEMPTY LIST #define LIST(name) {#name, NULL, &name, NULL}, #define LCASE(name) {#name, NULL, &name, &name ## Case}, #define KEY(name,def) {#name, &name, NULL, NULL}, struct KeyWord Keys[] = { RESOURCE_INFO {NULL, NULL, NULL, NULL} }; #undef KEY #undef LCASE #undef LNEMPTY #undef LIST /***************************** RESOURCE HANDLING **************************/ /* We don't include a trailing semicolon here, so that we can add it * at the calling site, thereby preserving proper indentation. Double * semicolons are undesirable since they have been known to break some * compilers. */ #define TOKENBITS(name) enum name { \ BIT(Eof), /* End-of-file */ \ BIT(Open), /* { */ \ BIT(Close), /* } */ \ BIT(BrOpen), /* [ */ \ BIT(BrClose), /* ] */ \ BIT(Equal), /* = */ \ BIT(Word), /* Keyword */ \ BIT(Item) /* List item */ \ } #undef BIT #define BIT BITDEF1 TOKENBITS(Token_BIT); #undef BIT #define BIT BITDEF2 TOKENBITS(Token); static enum Token Expect; static unsigned long RsrcLine; static enum Token ReadWord(char *, FILE *, char *(fgetsfun)(char *, int, FILE *)); static char MapChars(char **String); /* * Parses the contents of a resource file. * * Format: * Keyword { item1 item2 ... } [ item1 item2 ... ] * Keyword [ item1 item2 ... ] { item1 item2 ... } * Keyword = { item1 item2 ... } * Keyword = [ item1 item2 ... ] * Keyword = item * * Returns whether the attempt was a successful one. */ int ProcessRC(FILE *fh, const char *Filename, char *(fgetsfun)(char *, int, FILE *)) { const char *String = NULL; int Success = TRUE; enum Token Token; unsigned long Counter; struct KeyWord *CurWord = NULL; /* Interpret incoming words as ... */ enum { whList, /* List elements */ whCaseList, /* Case insensitive list elements */ whEqual, /* Solo elements */ whNone /* List items not accepted */ } What = whNone; RsrcLine = 0; Expect = FLG_Word | FLG_Eof; do { Token = ReadWord(ReadBuffer, fh, fgetsfun); if (!(Expect & Token)) { switch (Token) { case FLG_Item: String = "item"; break; case FLG_Word: String = "word"; break; case FLG_Equal: String = "`='"; break; case FLG_Open: String = "`{'"; break; case FLG_Close: String = "`}'"; break; case FLG_BrOpen: String = "`['"; break; case FLG_BrClose: String = "`]'"; break; case FLG_Eof: String = "EOF"; break; } PrintPrgErr(pmFaultFmt, Filename, RsrcLine, String); Success = FALSE; Token = FLG_Eof; } switch (Token) { case FLG_Word: for (Counter = 0; Keys[Counter].Name; Counter++) { if (!strcasecmp(ReadBuffer, Keys[Counter].Name)) { CurWord = &Keys[Counter]; Expect = (CurWord->List ? FLG_Open : 0) | (CurWord->CaseList ? FLG_BrOpen : 0) | FLG_Equal; break; } } if (!Keys[Counter].Name) { PrintPrgErr(pmKeyWord, ReadBuffer, Filename); Success = FALSE; Token = FLG_Eof; } break; case FLG_Item: switch (What) { case whEqual: if (!(*(CurWord->String) = strdup(ReadBuffer))) { PrintPrgErr(pmStrDupErr); Token = FLG_Eof; Success = FALSE; } What = whNone; Expect = FLG_Word | FLG_Eof; break; case whCaseList: if (!InsertWord(ReadBuffer, CurWord->CaseList)) { Token = FLG_Eof; Success = FALSE; } break; case whList: if (!InsertWord(ReadBuffer, CurWord->List)) { Token = FLG_Eof; Success = FALSE; } break; case whNone: PrintPrgErr(pmAssert); } break; case FLG_Equal: What = whEqual; Expect = (CurWord->List ? FLG_Open : 0) | (CurWord->CaseList ? FLG_BrOpen : 0) | (CurWord->String ? FLG_Item : 0); break; case FLG_BrOpen: if (What == whEqual) ClearWord(CurWord->CaseList); What = whCaseList; Expect = FLG_Item | FLG_BrClose; break; case FLG_Open: if (What == whEqual) ClearWord(CurWord->List); What = whList; Expect = FLG_Item | FLG_Close; break; case FLG_BrClose: case FLG_Close: Expect = (CurWord->List ? FLG_Open : 0) | (CurWord->CaseList ? FLG_BrOpen : 0) | FLG_Equal | FLG_Word | FLG_Eof; What = whNone; break; case FLG_Eof: break; } } while (Token != FLG_Eof); return (Success); } /* * Opens a file and passes to ProcessRC(). */ int ReadRC(const char *Filename) { int Success = FALSE; FILE *fh; if ((fh = fopen(Filename, "r"))) { Success = ProcessRC(fh, Filename, &fgets); fclose(fh); } else PrintPrgErr(pmRsrcOpen, Filename); return (Success); } const char *FGETS_TMP = NULL; char *fgets_from_string(char *out, int size, FILE *fh) { char *res; if (FGETS_TMP == NULL) return NULL; res = strncpy(out, FGETS_TMP, size - 1); if (size - 1 < strlen(FGETS_TMP)) { /* It wasn't all read, so null terminate it, and get ready for * next time. */ res[size] = '\0'; FGETS_TMP = FGETS_TMP + (size - 1); } else { /* We're done, so signal that */ FGETS_TMP = NULL; } return res; } /* * Opens a file and passes to ProcessRC(). */ int ReadRCFromCmdLine(const char *CmdLineArg) { if (!CmdLineArg) return FALSE; FGETS_TMP = CmdLineArg; return ProcessRC(NULL, "CommandLineArg", &fgets_from_string); } /* * Reads a token from the `.chktexrc' file; if the token is * FLG_Item or FLG_Word, Buffer will contain the plaintext of the * token. If not, the contents are undefined. */ static enum Token ReadWord(char *Buffer, FILE *fh, char *(fgetsfun)(char *, int, FILE *)) { static char *String = NULL; static char StatBuf[BUFFER_SIZE]; enum Token Retval = FLG_Eof; unsigned short Chr; char *Ptr; int OnceMore = TRUE, Cont = TRUE; if (Buffer) { do { if (!(String && *String)) { if ((fgetsfun)(StatBuf, BUFFER_SIZE - 1, fh)) String = strip(StatBuf, STRP_RGT); RsrcLine++; } Ptr = Buffer; if (String && (String = strip(String, STRP_LFT))) { switch (Chr = *String) { case 0: case CMNT: String = NULL; break; case QUOTE: /* Quoted argument */ Cont = TRUE; String++; while (Cont) { switch (Chr = *String++) { case 0: case QUOTE: Cont = FALSE; break; case ESCAPE: if (!(Chr = MapChars(&String))) break; /* FALLTHRU */ default: *Ptr++ = Chr; } } *Ptr = 0; Retval = FLG_Item; OnceMore = FALSE; break; #define DONEKEY (FLG_Open | FLG_Equal | FLG_BrOpen) #define DONELIST (FLG_Close | FLG_BrClose) #define TOKEN(c, ctxt, tk) case c: if(Expect & (ctxt)) Retval = tk; LAST(token) LOOP(token, TOKEN('{', DONEKEY, FLG_Open); TOKEN('[', DONEKEY, FLG_BrOpen); TOKEN('=', DONEKEY, FLG_Equal); TOKEN(']', DONELIST, FLG_BrClose); TOKEN('}', DONELIST, FLG_Close); ) if (Retval != FLG_Eof) { OnceMore = FALSE; String++; break; } /* FALLTHRU */ default: /* Non-quoted argument */ OnceMore = FALSE; if (Expect & FLG_Word) { while (Cont) { Chr = *String++; if (isalpha((unsigned char)Chr)) *Ptr++ = Chr; else Cont = FALSE; } String--; Retval = FLG_Word; } else /* Expect & FLG_Item */ { while (Cont) { switch (Chr = *String++) { case CMNT: case 0: String = NULL; Cont = FALSE; break; case ESCAPE: if (!(Chr = MapChars(&String))) break; *Ptr++ = Chr; break; default: if (!isspace((unsigned char)Chr)) *Ptr++ = Chr; else Cont = FALSE; } } Retval = FLG_Item; } if (!(Buffer[0])) { PrintPrgErr(pmEmptyToken); if (*String) String++; } *Ptr = 0; break; } } else OnceMore = FALSE; } while (OnceMore); } return (Retval); } /* * Translates escape codes. Give it a pointer to the char after the * escape char, and we'll return what it maps to. */ #define MAP(a,b) case a: Tmp = b; break; static char MapChars(char **String) { int Chr, Tmp = 0; unsigned short Cnt; Chr = *((char *) (*String)++); switch (tolower((unsigned char)Chr)) { MAP(QUOTE, QUOTE); MAP(ESCAPE, ESCAPE); MAP(CMNT, CMNT); MAP('n', '\n'); MAP('r', '\r'); MAP('b', '\b'); MAP('t', '\t'); MAP('f', '\f'); MAP('{', '{'); MAP('}', '}'); MAP('[', '['); MAP(']', ']'); MAP('=', '='); MAP(' ', ' '); case 'x': Tmp = 0; for (Cnt = 0; Cnt < 2; Cnt++) { Chr = *((*String)++); if (isxdigit((unsigned char)Chr)) { Chr = toupper((unsigned char)Chr); Tmp = (Tmp << 4) + Chr; if (isdigit((unsigned char)Chr)) Tmp -= '0'; else Tmp -= 'A' - 10; } else { if (Chr) { PrintPrgErr(pmNotPSDigit, Chr, "hex"); Tmp = 0; } break; } } break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': Tmp = Chr - '0'; for (Cnt = 0; Cnt < 2; Cnt++) { Chr = *((*String)++); if (within('0', Chr, '7')) Tmp = (Tmp * 8) + Chr - '0'; else { if (Chr) { PrintPrgErr(pmNotPSDigit, Chr, "octal"); Tmp = 0; } break; } } break; case 'd': for (Cnt = 0; Cnt < 3; Cnt++) { if (isdigit((unsigned char)(Chr = *((*String)++)))) Tmp = (Tmp * 10) + Chr - '0'; else { if (Chr) { PrintPrgErr(pmNotPSDigit, Chr, ""); Tmp = 0; } break; } } break; default: PrintPrgErr(pmEscCode, ESCAPE, Chr); } return (Tmp); }