1 /*
2  *  ChkTeX, resource file reader.
3  *  Copyright (C) 1995-96 Jens T. Berger Thielemann
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  *
19  *  Contact the author at:
20  *              Jens Berger
21  *              Spektrumvn. 4
22  *              N-0666 Oslo
23  *              Norway
24  *              E-mail: <jensthi@ifi.uio.no>
25  *
26  *
27  */
28 
29 #include "ChkTeX.h"
30 #include "OpSys.h"
31 #include "Utility.h"
32 #include "Resource.h"
33 
34 #define LNEMPTY(a) struct WordList a = {0, 1, {0}, {0}};
35 #define LIST(a)    struct WordList a = {0, 0, {0}, {0}};
36 #define LCASE(a)   LIST(a) LIST(a ## Case)
37 #define KEY(a,def) const char *a = def;
38 
39 RESOURCE_INFO
40 #undef KEY
41 #undef LCASE
42 #undef LNEMPTY
43 #undef LIST
44 struct KeyWord
45 {
46     const char *Name;
47     const char **String;        /* Keyword = item */
48     struct WordList *List,      /* Case-sensitive strings */
49      *CaseList;                 /* Case-insensitive strings */
50 };
51 
52 #define LNEMPTY LIST
53 #define LIST(name)       {#name, NULL, &name, NULL},
54 #define LCASE(name)      {#name, NULL, &name, &name ## Case},
55 #define KEY(name,def)    {#name, &name, NULL, NULL},
56 
57 struct KeyWord Keys[] = {
58     RESOURCE_INFO {NULL, NULL, NULL, NULL}
59 };
60 
61 #undef KEY
62 #undef LCASE
63 #undef LNEMPTY
64 #undef LIST
65 
66 
67 /***************************** RESOURCE HANDLING **************************/
68 
69 /* We don't include a trailing semicolon here, so that we can add it
70  * at the calling site, thereby preserving proper indentation.  Double
71  * semicolons are undesirable since they have been known to break some
72  * compilers. */
73 #define TOKENBITS(name) enum name { \
74   BIT(Eof),      /* End-of-file */ \
75   BIT(Open),     /* { */ \
76   BIT(Close),    /* } */ \
77   BIT(BrOpen),   /* [ */ \
78   BIT(BrClose),  /* ] */ \
79   BIT(Equal),    /* = */ \
80   BIT(Word),     /* Keyword */ \
81   BIT(Item)      /* List item */ \
82 }
83 
84 #undef BIT
85 #define BIT BITDEF1
86 TOKENBITS(Token_BIT);
87 #undef BIT
88 #define BIT BITDEF2
89 TOKENBITS(Token);
90 static enum Token Expect;
91 static unsigned long RsrcLine;
92 
93 static enum Token ReadWord(char *, FILE *);
94 static char MapChars(char **String);
95 
96 
97 
98 /*
99  * Parses the contents of a resource file.
100  *
101  * Format:
102  * Keyword { item1 item2 ... } [ item1 item2 ... ]
103  * Keyword [ item1 item2 ... ] { item1 item2 ... }
104  * Keyword = { item1 item2 ...�}
105  * Keyword = [ item1 item2 ...�]
106  * Keyword = item
107  *
108  * Returns whether the attempt was a successful one.
109  */
110 
ReadRC(const char * Filename)111 int ReadRC(const char *Filename)
112 {
113     const char *String = NULL;
114     int Success = FALSE;
115     FILE *fh;
116     enum Token Token;
117     unsigned long Counter;
118 
119     struct KeyWord *CurWord = NULL;
120 
121     /* Interpret incoming words as ... */
122     enum
123     {
124         whList,                 /* List elements */
125         whCaseList,             /* Case insensitive list elements */
126         whEqual,                /* Solo elements */
127         whNone                  /* List items not accepted */
128     } What = whNone;
129 
130 
131     RsrcLine = 0;
132     Expect = FLG_Word | FLG_Eof;
133 
134     if ((fh = fopen(Filename, "r")))
135     {
136         Success = TRUE;
137         do
138         {
139             Token = ReadWord(ReadBuffer, fh);
140             if (!(Expect & Token))
141             {
142                 switch (Token)
143                 {
144                 case FLG_Item:
145                     String = "item";
146                     break;
147                 case FLG_Word:
148                     String = "word";
149                     break;
150                 case FLG_Equal:
151                     String = "`='";
152                     break;
153                 case FLG_Open:
154                     String = "`{'";
155                     break;
156                 case FLG_Close:
157                     String = "`}'";
158                     break;
159                 case FLG_BrOpen:
160                     String = "`['";
161                     break;
162                 case FLG_BrClose:
163                     String = "`]'";
164                     break;
165                 case FLG_Eof:
166                     String = "EOF";
167                     break;
168                 }
169                 PrintPrgErr(pmFaultFmt, Filename, RsrcLine, String);
170                 Success = FALSE;
171                 Token = FLG_Eof;
172             }
173 
174             switch (Token)
175             {
176             case FLG_Word:
177                 for (Counter = 0; Keys[Counter].Name; Counter++)
178                 {
179                     if (!strcasecmp(ReadBuffer, Keys[Counter].Name))
180                     {
181                         CurWord = &Keys[Counter];
182                         Expect = (CurWord->List ? FLG_Open : 0) |
183                             (CurWord->CaseList ? FLG_BrOpen : 0) | FLG_Equal;
184                         break;
185                     }
186                 }
187                 if (!Keys[Counter].Name)
188                 {
189                     PrintPrgErr(pmKeyWord, ReadBuffer, Filename);
190                     Success = FALSE;
191                     Token = FLG_Eof;
192                 }
193                 break;
194             case FLG_Item:
195                 switch (What)
196                 {
197                 case whEqual:
198                     if (!(*(CurWord->String) = strdup(ReadBuffer)))
199                     {
200                         PrintPrgErr(pmStrDupErr);
201                         Token = FLG_Eof;
202                         Success = FALSE;
203                     }
204 
205                     What = whNone;
206                     Expect = FLG_Word | FLG_Eof;
207                     break;
208                 case whCaseList:
209                     if (!InsertWord(ReadBuffer, CurWord->CaseList))
210                     {
211                         Token = FLG_Eof;
212                         Success = FALSE;
213                     }
214                     break;
215                 case whList:
216                     if (!InsertWord(ReadBuffer, CurWord->List))
217                     {
218                         Token = FLG_Eof;
219                         Success = FALSE;
220                     }
221                     break;
222                 case whNone:
223                     PrintPrgErr(pmAssert);
224                 }
225                 break;
226             case FLG_Equal:
227                 What = whEqual;
228                 Expect = (CurWord->List ? FLG_Open : 0) |
229                     (CurWord->CaseList ? FLG_BrOpen : 0) |
230                     (CurWord->String ? FLG_Item : 0);
231                 break;
232             case FLG_BrOpen:
233                 if (What == whEqual)
234                     ClearWord(CurWord->CaseList);
235                 What = whCaseList;
236                 Expect = FLG_Item | FLG_BrClose;
237                 break;
238             case FLG_Open:
239                 if (What == whEqual)
240                     ClearWord(CurWord->List);
241                 What = whList;
242                 Expect = FLG_Item | FLG_Close;
243                 break;
244             case FLG_BrClose:
245             case FLG_Close:
246                 Expect = (CurWord->List ? FLG_Open : 0) |
247                     (CurWord->CaseList ? FLG_BrOpen : 0) |
248                     FLG_Equal | FLG_Word | FLG_Eof;
249                 What = whNone;
250                 break;
251             case FLG_Eof:
252                 break;
253             }
254         }
255         while (Token != FLG_Eof);
256 
257         fclose(fh);
258     }
259     else
260         PrintPrgErr(pmRsrcOpen, Filename);
261 
262     return (Success);
263 }
264 
265 /*
266  * Reads a token from the `.chktexrc' file; if the token is
267  * FLG_Item or FLG_Word, Buffer will contain the plaintext of the
268  * token. If not, the contents are undefined.
269  */
270 
ReadWord(char * Buffer,FILE * fh)271 static enum Token ReadWord(char *Buffer, FILE * fh)
272 {
273     static char *String = NULL;
274     static char StatBuf[BUFSIZ];
275     enum Token Retval = FLG_Eof;
276 
277     unsigned short Chr;
278 
279     char *Ptr;
280     int OnceMore = TRUE, Cont = TRUE;
281 
282     if (Buffer)
283     {
284         do
285         {
286             if (!(String && *String))
287             {
288                 if (fgets(StatBuf, BUFSIZ - 1, fh))
289                     String = strip(StatBuf, STRP_RGT);
290                 RsrcLine++;
291             }
292 
293             Ptr = Buffer;
294             if (String && (String = strip(String, STRP_LFT)))
295             {
296                 switch (Chr = *String)
297                 {
298                 case 0:
299                 case CMNT:
300                     String = NULL;
301                     break;
302                 case QUOTE:    /* Quoted argument */
303                     Cont = TRUE;
304                     String++;
305 
306                     while (Cont)
307                     {
308                         switch (Chr = *String++)
309                         {
310                         case 0:
311                         case QUOTE:
312                             Cont = FALSE;
313                             break;
314                         case ESCAPE:
315                             if (!(Chr = MapChars(&String)))
316                                 break;
317 
318                             /* FALLTHRU */
319                         default:
320                             *Ptr++ = Chr;
321                         }
322                     }
323                     *Ptr = 0;
324                     Retval = FLG_Item;
325                     OnceMore = FALSE;
326                     break;
327 
328 #define DONEKEY (FLG_Open | FLG_Equal | FLG_BrOpen)
329 #define DONELIST (FLG_Close | FLG_BrClose)
330 #define TOKEN(c, ctxt, tk) case c: if(Expect & (ctxt)) Retval = tk; LAST(token)
331 
332                     LOOP(token,
333                          TOKEN('{', DONEKEY, FLG_Open);
334                          TOKEN('[', DONEKEY, FLG_BrOpen);
335                          TOKEN('=', DONEKEY, FLG_Equal);
336                          TOKEN(']', DONELIST, FLG_BrClose);
337                          TOKEN('}', DONELIST, FLG_Close);
338                         )
339                     if (Retval != FLG_Eof)
340                     {
341                         OnceMore = FALSE;
342                         String++;
343                         break;
344                     }
345 
346                     /* FALLTHRU */
347 
348                 default:       /* Non-quoted argument */
349                     OnceMore = FALSE;
350                     if (Expect & FLG_Word)
351                     {
352                         while (Cont)
353                         {
354                             Chr = *String++;
355                             if (isalpha((unsigned char)Chr))
356                                 *Ptr++ = Chr;
357                             else
358                                 Cont = FALSE;
359                         }
360                         String--;
361                         Retval = FLG_Word;
362                     }
363                     else        /* Expect & FLG_Item */
364                     {
365                         while (Cont)
366                         {
367                             switch (Chr = *String++)
368                             {
369                             case CMNT:
370                             case 0:
371                                 String = NULL;
372                                 Cont = FALSE;
373                                 break;
374                             case ESCAPE:
375                                 if (!(Chr = MapChars(&String)))
376                                     break;
377 
378                                 *Ptr++ = Chr;
379                                 break;
380                             default:
381                                 if (!isspace((unsigned char)Chr))
382                                     *Ptr++ = Chr;
383                                 else
384                                     Cont = FALSE;
385                             }
386                         }
387                         Retval = FLG_Item;
388                     }
389 
390                     if (!(Buffer[0]))
391                     {
392                         PrintPrgErr(pmEmptyToken);
393                         if (*String)
394                             String++;
395                     }
396                     *Ptr = 0;
397                     break;
398                 }
399             }
400             else
401                 OnceMore = FALSE;
402         }
403         while (OnceMore);
404     }
405     return (Retval);
406 }
407 
408 
409 
410 /*
411  * Translates escape codes. Give it a pointer to the char after the
412  * escape char, and we'll return what it maps to.
413  */
414 
415 #define MAP(a,b)        case a: Tmp = b; break;
416 
MapChars(char ** String)417 static char MapChars(char **String)
418 {
419     int Chr, Tmp = 0;
420     unsigned short Cnt;
421 
422     Chr = *((char *) (*String)++);
423 
424     switch (tolower((unsigned char)Chr))
425     {
426         MAP(QUOTE, QUOTE);
427         MAP(ESCAPE, ESCAPE);
428         MAP(CMNT, CMNT);
429         MAP('n', '\n');
430         MAP('r', '\r');
431         MAP('b', '\b');
432         MAP('t', '\t');
433         MAP('f', '\f');
434         MAP('{', '{');
435         MAP('}', '}');
436         MAP('[', '[');
437         MAP(']', ']');
438         MAP('=', '=');
439         MAP(' ', ' ');
440     case 'x':
441         Tmp = 0;
442 
443         for (Cnt = 0; Cnt < 2; Cnt++)
444         {
445             Chr = *((*String)++);
446             if (isxdigit((unsigned char)Chr))
447             {
448                 Chr = toupper((unsigned char)Chr);
449                 Tmp = (Tmp << 4) + Chr;
450 
451                 if (isdigit((unsigned char)Chr))
452                     Tmp -= '0';
453                 else
454                     Tmp -= 'A' - 10;
455             }
456             else
457             {
458                 if (Chr)
459                 {
460                     PrintPrgErr(pmNotPSDigit, Chr, "hex");
461                     Tmp = 0;
462                 }
463                 break;
464             }
465         }
466         break;
467     case '0':
468     case '1':
469     case '2':
470     case '3':
471     case '4':
472     case '5':
473     case '6':
474     case '7':
475 
476         Tmp = Chr - '0';
477 
478         for (Cnt = 0; Cnt < 2; Cnt++)
479         {
480             Chr = *((*String)++);
481             if (within('0', Chr, '7'))
482                 Tmp = (Tmp * 8) + Chr - '0';
483             else
484             {
485                 if (Chr)
486                 {
487                     PrintPrgErr(pmNotPSDigit, Chr, "octal");
488                     Tmp = 0;
489                 }
490                 break;
491             }
492         }
493         break;
494     case 'd':
495         for (Cnt = 0; Cnt < 3; Cnt++)
496         {
497             if (isdigit((unsigned char)(Chr = *((*String)++))))
498                 Tmp = (Tmp * 10) + Chr - '0';
499             else
500             {
501                 if (Chr)
502                 {
503                     PrintPrgErr(pmNotPSDigit, Chr, "");
504                     Tmp = 0;
505                 }
506                 break;
507             }
508         }
509         break;
510     default:
511         PrintPrgErr(pmEscCode, ESCAPE, Chr);
512     }
513     return (Tmp);
514 }
515