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