/[CvsGraph]/cvsgraph/readconf.c
ViewVC logotype

Annotate of /cvsgraph/readconf.c

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.22 - (show annotations)
Wed Jun 22 22:51:51 2005 UTC (12 years, 4 months ago) by bertho
Branch: MAIN
Changes since 1.21: +0 -3 lines
File MIME type: text/plain
- Fix fixed background color
- Fix width adaption of image width if error strings are displayed
1 /*
2 * CvsGraph graphical representation generator of brances and revisions
3 * of a file in cvs/rcs.
4 *
5 * Copyright (C) 2001,2002 B. Stultiens
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22 /*#define DEBUG 1*/
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <ctype.h>
28 #include <assert.h>
29
30 #include <gd.h>
31 #include <gdfontt.h>
32 #include <gdfonts.h>
33 #include <gdfontmb.h>
34 #include <gdfontl.h>
35 #include <gdfontg.h>
36
37 #include "utils.h"
38 #include "cvsgraph.h"
39 #include "readconf.h"
40
41 int line_number;
42
43 typedef struct
44 {
45 const char *keyword;
46 int type;
47 union {
48 void *v; /* join of other values */
49 int *i;
50 font_t *f;
51 char **s;
52 color_t *c;
53 double *d;
54 INTTYPE val;
55 } confref;
56 } keyword_t;
57
58 typedef union {
59 keyword_t *kw;
60 int i;
61 double d;
62 char *str;
63 } YYSTYPE;
64
65 static YYSTYPE yylval;
66
67 static int nstacked_opts;
68 static char **stacked_opts;
69
70 enum {
71 TYPE_dummy = 256,
72 TYPE_KEYWORD,
73 TYPE_NUMBER,
74 TYPE_VALUE,
75 TYPE_BOOLEAN,
76 TYPE_COLOR,
77 TYPE_FONT,
78 TYPE_STRING,
79 TYPE_DOUBLE
80 };
81
82 static keyword_t keywords[] = {
83 { "branch_bgcolor", TYPE_COLOR, { &conf.branch_bgcolor } },
84 { "branch_bspace", TYPE_NUMBER, { &conf.branch_bspace } },
85 { "branch_color", TYPE_COLOR, { &conf.branch_color } },
86 { "branch_font", TYPE_FONT, { &conf.branch_font.gdfont } },
87 { "branch_ttfont", TYPE_STRING, { &conf.branch_font.ttfont } },
88 { "branch_ttsize", TYPE_DOUBLE, { &conf.branch_font.ttsize } },
89 { "branch_tag_color", TYPE_COLOR, { &conf.branch_tag_color } },
90 { "branch_tag_font", TYPE_FONT, { &conf.branch_tag_font.gdfont } },
91 { "branch_tag_ttfont", TYPE_STRING, { &conf.branch_tag_font.ttfont } },
92 { "branch_tag_ttsize", TYPE_DOUBLE, { &conf.branch_tag_font.ttsize } },
93 { "branch_lspace", TYPE_NUMBER, { &conf.branch_lspace } },
94 { "branch_rspace", TYPE_NUMBER, { &conf.branch_rspace } },
95 { "branch_tspace", TYPE_NUMBER, { &conf.branch_tspace } },
96 { "branch_connect", TYPE_NUMBER, { &conf.branch_connect } },
97 { "branch_margin", TYPE_NUMBER, { &conf.branch_margin } },
98 { "branch_dupbox", TYPE_BOOLEAN, { &conf.branch_dupbox } },
99 { "branch_fold", TYPE_BOOLEAN, { &conf.branch_fold } },
100 { "branch_foldall", TYPE_BOOLEAN, { &conf.branch_foldall } },
101 { "branch_resort", TYPE_BOOLEAN, { &conf.branch_resort } },
102 { "branch_subtree", TYPE_STRING, { &conf.branch_subtree } },
103 { "upside_down", TYPE_BOOLEAN, { &conf.upside_down } },
104 { "left_right", TYPE_BOOLEAN, { &conf.left_right } },
105 { "auto_stretch", TYPE_BOOLEAN, { &conf.auto_stretch } },
106 { "color_bg", TYPE_COLOR, { &conf.color_bg } },
107 { "transparent_bg", TYPE_BOOLEAN, { &conf.transparent_bg } },
108 { "cvsmodule", TYPE_STRING, { &conf.cvsmodule } },
109 { "cvsroot", TYPE_STRING, { &conf.cvsroot } },
110 { "date_format", TYPE_STRING, { &conf.date_format } },
111 { "box_shadow", TYPE_BOOLEAN, { &conf.box_shadow } },
112 { "strip_untagged", TYPE_BOOLEAN, { &conf.strip_untagged } },
113 { "strip_first_rev", TYPE_BOOLEAN, { &conf.strip_first_rev } },
114 { "anti_alias", TYPE_BOOLEAN, { &conf.anti_alias } },
115 { "use_ttf", TYPE_BOOLEAN, { &conf.use_ttf } },
116 { "parse_logs", TYPE_BOOLEAN, { &conf.parse_logs } },
117 { "html_level", TYPE_NUMBER, { &conf.html_level } },
118 { "thick_lines", TYPE_NUMBER, { &conf.thick_lines } },
119 { "msg_color", TYPE_COLOR, { &conf.msg_color } },
120 { "msg_font", TYPE_FONT, { &conf.msg_font.gdfont } },
121 { "msg_ttfont", TYPE_STRING, { &conf.msg_font.ttfont } },
122 { "msg_ttsize", TYPE_DOUBLE, { &conf.msg_font.ttsize } },
123 { "rev_hidenumber", TYPE_BOOLEAN, { &conf.rev_hidenumber } },
124 { "rev_color", TYPE_COLOR, { &conf.rev_color } },
125 { "rev_bgcolor", TYPE_COLOR, { &conf.rev_bgcolor } },
126 { "rev_font", TYPE_FONT, { &conf.rev_font.gdfont } },
127 { "rev_ttfont", TYPE_STRING, { &conf.rev_font.ttfont } },
128 { "rev_ttsize", TYPE_DOUBLE, { &conf.rev_font.ttsize } },
129 { "rev_separator", TYPE_NUMBER, { &conf.rev_separator } },
130 { "rev_minline", TYPE_NUMBER, { &conf.rev_minline } },
131 { "rev_maxline", TYPE_NUMBER, { &conf.rev_maxline } },
132 { "rev_lspace", TYPE_NUMBER, { &conf.rev_lspace } },
133 { "rev_rspace", TYPE_NUMBER, { &conf.rev_rspace } },
134 { "rev_tspace", TYPE_NUMBER, { &conf.rev_tspace } },
135 { "rev_bspace", TYPE_NUMBER, { &conf.rev_bspace } },
136 { "rev_text", TYPE_STRING, { &conf.rev_text } },
137 { "rev_text_color", TYPE_COLOR, { &conf.rev_text_color } },
138 { "rev_text_font", TYPE_FONT, { &conf.rev_text_font.gdfont } },
139 { "rev_text_ttfont", TYPE_STRING, { &conf.rev_text_font.ttfont } },
140 { "rev_text_ttsize", TYPE_DOUBLE, { &conf.rev_text_font.ttsize } },
141 { "rev_maxtags", TYPE_NUMBER, { &conf.rev_maxtags } },
142 { "merge_color", TYPE_COLOR, { &conf.merge_color } },
143 { "merge_from", TYPE_STRING, { &conf.merge_from } },
144 { "merge_to", TYPE_STRING, { &conf.merge_to } },
145 { "merge_findall", TYPE_BOOLEAN, { &conf.merge_findall } },
146 { "merge_front", TYPE_BOOLEAN, { &conf.merge_front } },
147 { "merge_nocase", TYPE_BOOLEAN, { &conf.merge_nocase } },
148 { "merge_arrows", TYPE_BOOLEAN, { &conf.merge_arrows } },
149 { "merge_cvsnt", TYPE_BOOLEAN, { &conf.merge_cvsnt } },
150 { "merge_cvsnt_color", TYPE_COLOR, { &conf.merge_cvsnt_color } },
151 { "arrow_width", TYPE_NUMBER, { &conf.arrow_width } },
152 { "arrow_length", TYPE_NUMBER, { &conf.arrow_length } },
153 { "tag_color", TYPE_COLOR, { &conf.tag_color } },
154 { "tag_font", TYPE_FONT, { &conf.tag_font.gdfont } },
155 { "tag_ttfont", TYPE_STRING, { &conf.tag_font.ttfont } },
156 { "tag_ttsize", TYPE_DOUBLE, { &conf.tag_font.ttsize } },
157 { "tag_ignore", TYPE_STRING, { &conf.tag_ignore } },
158 { "tag_ignore_merge", TYPE_BOOLEAN, { &conf.tag_ignore_merge } },
159 { "tag_nocase", TYPE_BOOLEAN, { &conf.tag_nocase } },
160 { "tag_negate", TYPE_BOOLEAN, { &conf.tag_negate } },
161 { "title", TYPE_STRING, { &conf.title } },
162 { "title_x", TYPE_NUMBER, { &conf.title_x } },
163 { "title_y", TYPE_NUMBER, { &conf.title_y } },
164 { "title_font", TYPE_FONT, { &conf.title_font.gdfont } },
165 { "title_ttfont", TYPE_STRING, { &conf.title_font.ttfont } },
166 { "title_ttsize", TYPE_DOUBLE, { &conf.title_font.ttsize } },
167 { "title_align", TYPE_NUMBER, { &conf.title_align } },
168 { "title_color", TYPE_COLOR, { &conf.title_color } },
169 { "margin_top", TYPE_NUMBER, { &conf.margin_top } },
170 { "margin_bottom", TYPE_NUMBER, { &conf.margin_bottom } },
171 { "margin_left", TYPE_NUMBER, { &conf.margin_left } },
172 { "margin_right", TYPE_NUMBER, { &conf.margin_right } },
173 { "image_type", TYPE_NUMBER, { &conf.image_type } },
174 { "image_quality", TYPE_NUMBER, { &conf.image_quality } },
175 { "image_compress", TYPE_NUMBER, { &conf.image_compress } },
176 { "image_interlace", TYPE_BOOLEAN, { &conf.image_interlace } },
177 { "map_name", TYPE_STRING, { &conf.map_name } },
178 { "map_branch_href", TYPE_STRING, { &conf.map_branch_href } },
179 { "map_branch_alt", TYPE_STRING, { &conf.map_branch_alt } },
180 { "map_rev_href", TYPE_STRING, { &conf.map_rev_href } },
181 { "map_rev_alt", TYPE_STRING, { &conf.map_rev_alt } },
182 { "map_diff_href", TYPE_STRING, { &conf.map_diff_href } },
183 { "map_diff_alt", TYPE_STRING, { &conf.map_diff_alt } },
184 { "map_merge_href", TYPE_STRING, { &conf.map_merge_href } },
185 { "map_merge_alt", TYPE_STRING, { &conf.map_merge_alt } },
186 { "jpeg", TYPE_VALUE, { (void *)IMAGE_JPEG } },
187 { "png", TYPE_VALUE, { (void *)IMAGE_PNG } },
188 { "gif", TYPE_VALUE, { (void *)IMAGE_GIF } },
189 { "true", TYPE_VALUE, { (void *)1 } },
190 { "false", TYPE_VALUE, { (void *)0 } },
191 { "not", TYPE_VALUE, { (void *)-1 } },
192 { "left", TYPE_VALUE, { (void *)0 } },
193 { "center", TYPE_VALUE, { (void *)1 } },
194 { "right", TYPE_VALUE, { (void *)2 } },
195 { "tiny", TYPE_VALUE, { (void *)0 } },
196 { "small", TYPE_VALUE, { (void *)1 } },
197 { "medium", TYPE_VALUE, { (void *)2 } },
198 { "large", TYPE_VALUE, { (void *)3 } },
199 { "giant", TYPE_VALUE, { (void *)4 } },
200 { "HTML3", TYPE_VALUE, { (void *)1 } },
201 { "HTML4", TYPE_VALUE, { (void *)2 } },
202 { "XHTML", TYPE_VALUE, { (void *)3 } },
203 };
204
205 #define NKEYWORDS (sizeof(keywords) / sizeof(keywords[0]))
206
207 static int cmp_kw(const void *k1, const void *k2)
208 {
209 return strcmp(((keyword_t *)k1)->keyword, ((keyword_t *)k2)->keyword);
210 }
211
212 /*
213 **************************************************************************
214 * Debug routines
215 **************************************************************************
216 */
217 #ifdef DEBUG
218 #define DEBUGSTREAM stdout
219 static void debug_pname(const char *n)
220 {
221 fprintf(DEBUGSTREAM, "%-16s: ", n);
222 }
223
224 static void debug_pstring(const char *n, const char *a)
225 {
226 debug_pname(n);
227 if(!a)
228 fprintf(DEBUGSTREAM, "<not-set>\n");
229 else
230 {
231 fputc('\'', DEBUGSTREAM);
232 for(; *a; a++)
233 {
234 if(isprint(*a))
235 fputc(*a, DEBUGSTREAM);
236 else
237 {
238 fputc('\\', DEBUGSTREAM);
239 switch(*a)
240 {
241 case '\a': fputc('a', DEBUGSTREAM); break;
242 case '\b': fputc('b', DEBUGSTREAM); break;
243 case '\f': fputc('f', DEBUGSTREAM); break;
244 case '\n': fputc('n', DEBUGSTREAM); break;
245 case '\r': fputc('r', DEBUGSTREAM); break;
246 case '\t': fputc('t', DEBUGSTREAM); break;
247 case '\v': fputc('v', DEBUGSTREAM); break;
248 default:
249 fprintf(DEBUGSTREAM, "x%02x", (unsigned char)*a);
250 }
251 }
252 }
253 fprintf(DEBUGSTREAM, "'\n");
254 }
255 }
256
257 static void debug_pbool(const char *n, int b)
258 {
259 debug_pname(n);
260 fprintf(DEBUGSTREAM, "%s\n", b ? "true" : "false");
261 }
262
263 static void debug_pint(const char *n, int i)
264 {
265 debug_pname(n);
266 fprintf(DEBUGSTREAM, "%i\n", i);
267 }
268
269 static void debug_pdouble(const char *n, double d)
270 {
271 debug_pname(n);
272 fprintf(DEBUGSTREAM, "%g\n", d);
273 }
274
275 static void debug_pfont(const char *n, gdFontPtr f)
276 {
277 const char *s = "<Unknown font>";
278 debug_pname(n);
279 if(f == gdFontTiny)
280 s = "gdFontTiny";
281 else if(f == gdFontSmall)
282 s = "gdFontSmall";
283 else if(f == gdFontMediumBold)
284 s = "gdFontMediumBold";
285 else if(f == gdFontLarge)
286 s = "gdFontLarge";
287 else if(f == gdFontGiant)
288 s = "gdFontGiant";
289 fprintf(DEBUGSTREAM, "%s\n", s);
290 }
291
292 static void debug_pcolor(const char *n, color_t *c)
293 {
294 debug_pname(n);
295 fprintf(DEBUGSTREAM, "#%02x%02x%02x\n", c->r, c->g, c->b);
296 }
297
298 void dump_config(void)
299 {
300 debug_pstring("cvsroot", conf.cvsroot);
301 debug_pstring("cvsmodule", conf.cvsmodule);
302 debug_pstring("date_format", conf.date_format);
303
304 debug_pcolor("color_bg", &conf.color_bg);
305 debug_pbool("box_shadow", conf.box_shadow);
306 debug_pbool("upside_down", conf.upside_down);
307 debug_pbool("left_right", conf.left_right);
308 debug_pbool("strip_untagged", conf.strip_untagged);
309 debug_pbool("strip_first_rev", conf.strip_first_rev);
310 debug_pbool("auto_stretch", conf.auto_stretch);
311 debug_pbool("anti_alias", conf.anti_alias);
312 debug_pbool("use_ttf", conf.use_ttf);
313 debug_pint("thick_lines", conf.thick_lines);
314
315 debug_pfont("tag_font", conf.tag_font.gdfont);
316 debug_pstring("tag_ttfont", conf.tag_font.ttfont);
317 debug_psouble("tag_ttsize", conf.tag_font.ttsize);
318 debug_pcolor("tag_color", &conf.tag_color);
319
320 debug_pfont("rev_font", conf.rev_font.gdfont);
321 debug_pstring("rev_ttfont", conf.rev_font.ttfont);
322 debug_pdouble("rev_ttsize", conf.rev_font.ttsize);
323 debug_pcolor("rev_color", &conf.rev_color);
324 debug_pcolor("rev_bgcolor", &conf.rev_bgcolor);
325 debug_pint("rev_separator", conf.rev_separator);
326 debug_pint("rev_minline", conf.rev_minline);
327 debug_pint("rev_maxline", conf.rev_maxline);
328 debug_pint("rev_lspace", conf.rev_lspace);
329 debug_pint("rev_rspace", conf.rev_rspace);
330 debug_pint("rev_tspace", conf.rev_tspace);
331 debug_pint("rev_bspace", conf.rev_bspace);
332 debug_pstring("rev_text", conf.rev_text);
333 debug_pcolor("rev_text_color", &conf.rev_text_color);
334 debug_pfont("rev_text_font", conf.rev_text_font.gdfont);
335 debug_pstring("rev_text_ttfont", conf.rev_text_font.ttfont);
336 debug_pdouble("rev_text_ttsize", conf.rev_text_font.ttsize);
337
338 debug_pfont("branch_font", conf.branch_font.gdfont);
339 debug_pstring("branch_ttfont", conf.branch_font.ttfont);
340 debug_pdouble("branch_ttsize", conf.branch_font.ttsize);
341 debug_pcolor("branch_color", &conf.branch_color);
342 debug_pfont("branch_tag_font", conf.branch_tag_font.gdfont);
343 debug_pstring("branch_tag_ttfont", conf.branch_tag_font.ttfont);
344 debug_pdouble("branch_tag_ttsize", conf.branch_tag_font.ttsize);
345 debug_pcolor("branch_tag_color", &conf.branch_tag_color);
346 debug_pcolor("branch_bgcolor", &conf.branch_bgcolor);
347 debug_pint("branch_lspace", conf.branch_lspace);
348 debug_pint("branch_rspace", conf.branch_rspace);
349 debug_pint("branch_tspace", conf.branch_tspace);
350 debug_pint("branch_bspace", conf.branch_bspace);
351 debug_pint("branch_connect", conf.branch_connect);
352 debug_pint("branch_margin", conf.branch_margin);
353 debug_pint("branch_dupbox", conf.branch_dupbox);
354
355 debug_pstring("title", conf.title);
356 debug_pint("title_x", conf.title_x);
357 debug_pint("title_y", conf.title_y);
358 debug_pfont("title_font", conf.title_font.gdfont);
359 debug_pstring("title_ttfont", conf.title_font.ttfont);
360 debug_pdouble("title_ttsize", conf.title_font.ttsize);
361 debug_pint("title_align", conf.title_align);
362 debug_pcolor("title_color", &conf.title_color);
363
364 debug_pint("margin_top", conf.margin_top);
365 debug_pint("margin_bottom", conf.margin_bottom);
366 debug_pint("margin_left", conf.margin_left);
367 debug_pint("margin_right", conf.margin_right);
368
369 debug_pint("image_type", conf.image_type);
370 debug_pint("image_quality", conf.image_quality);
371
372 debug_pstring("map_name", conf.map_name);
373 debug_pstring("map_branch_href", conf.map_branch_href);
374 debug_pstring("map_branch_alt", conf.map_branch_alt);
375 debug_pstring("map_rev_href", conf.map_rev_href);
376 debug_pstring("map_rev_alt", conf.map_rev_alt);
377 debug_pstring("map_diff_href", conf.map_diff_href);
378 debug_pstring("map_diff_alt", conf.map_diff_alt);
379
380 debug_pstring("expand[0]", conf.expand[0]);
381 debug_pstring("expand[1]", conf.expand[1]);
382 debug_pstring("expand[2]", conf.expand[2]);
383 debug_pstring("expand[3]", conf.expand[3]);
384 debug_pstring("expand[4]", conf.expand[4]);
385 debug_pstring("expand[5]", conf.expand[5]);
386 debug_pstring("expand[6]", conf.expand[6]);
387 debug_pstring("expand[7]", conf.expand[7]);
388 debug_pstring("expand[8]", conf.expand[8]);
389 debug_pstring("expand[9]", conf.expand[9]);
390 }
391 #endif
392
393 /*
394 **************************************************************************
395 * String collection routines
396 **************************************************************************
397 */
398 #define STRALLOCSIZE 128
399 static char *str;
400 static int nstr;
401 static int nastr;
402
403 static void reset_str(void)
404 {
405 nstr = 0;
406 }
407
408 static void add_str(int c)
409 {
410 if(nstr + 1 + 1 > nastr)
411 {
412 str = xrealloc(str, nastr+STRALLOCSIZE);
413 nastr += STRALLOCSIZE;
414 }
415 str[nstr++] = c;
416 }
417
418 static char *get_str(void)
419 {
420 if(!str)
421 return xstrdup("");
422
423 str[nstr] = '\0';
424 return xstrdup(str);
425 }
426
427 /*
428 **************************************************************************
429 * Input routines
430 **************************************************************************
431 */
432 static char *buf = NULL;
433 static int bufsize = 0;
434 static int bufalloc = 0;
435 static int bufpos = 0;
436 static int bufunput = -1;
437 static FILE *buffp;
438
439 static void set_input(FILE *fp, char *s)
440 {
441 assert((fp == NULL && s != NULL) || (fp != NULL && s == NULL));
442 buffp = fp;
443 bufsize = bufpos = 0;
444 if(s)
445 {
446 if(!buf)
447 {
448 bufalloc = 8192;
449 buf = xmalloc(bufalloc * sizeof(*buf));
450 }
451 bufsize = strlen(s);
452 assert(bufsize < bufalloc);
453 strcpy(buf, s);
454 }
455 }
456
457 static int get_input(void)
458 {
459 if(bufunput != -1)
460 {
461 int c = bufunput;
462 bufunput = -1;
463 return c;
464 }
465
466 if(bufpos < bufsize)
467 {
468 assert(buf != NULL);
469 retry_input:
470 return (int)((unsigned char)buf[bufpos++]);
471 }
472
473 if(!buf)
474 {
475 bufalloc = 8192;
476 buf = xmalloc(bufalloc * sizeof(*buf));
477 bufsize = bufpos = 0;
478 }
479 if(buffp)
480 {
481 bufsize = fread(buf, 1, bufalloc, buffp);
482 bufpos = 0;
483 if(!bufsize)
484 return EOF;
485 goto retry_input;
486 }
487 return EOF;
488 }
489
490 static void unget_input(int c)
491 {
492 bufunput = c;
493 }
494
495 /*
496 **************************************************************************
497 * Lexical scanner
498 **************************************************************************
499 */
500 static int config_lex(void)
501 {
502 int ch;
503 while(1)
504 {
505 ch = get_input();
506 if(ch == '\n')
507 line_number++;
508
509 if(isspace(ch))
510 continue;
511
512 switch(ch)
513 {
514 case EOF:
515 case '=':
516 case ';':
517 return ch;
518
519 case '#': /* Comment */
520 while((ch = get_input()) != '\n' && ch != EOF)
521 ;
522 if(ch != EOF)
523 unget_input(ch);
524 break;
525
526 case '"':
527 reset_str();
528 while(1)
529 {
530 char c[4];
531 ch = get_input();
532 switch(ch)
533 {
534 case '\\': /* Start an escape sequence */
535 switch(ch = get_input())
536 {
537 default: /* This includes '\\', '"' and embedded newlines */
538 add_str(ch);
539 break;
540 case 'a': add_str('\a'); break;
541 case 'b': add_str('\b'); break;
542 case 'f': add_str('\f'); break;
543 case 'n': add_str('\n'); break;
544 case 'r': add_str('\r'); break;
545 case 't': add_str('\t'); break;
546 case 'v': add_str('\v'); break;
547 case 'x':
548 case 'X': /* Hex escape */
549 c[0] = get_input();
550 c[1] = get_input();
551 c[2] = '\0';
552 if(!isxdigit((int)(unsigned char)c[0]) || !isxdigit((int)(unsigned char)c[1]))
553 stack_msg(MSG_ERR, "config: %d: Invalid hex escape", line_number);
554 add_str((int)strtol(c, NULL, 16));
555 break;
556 case '0':
557 case '1':
558 case '2': /* Octal escape */
559 c[0] = ch;
560 c[1] = c[2] = c[3] = '\0';
561 if((ch = get_input()) >= '0' && ch <= '7')
562 c[1] = ch;
563 else
564 unget_input(ch);
565 if((ch = get_input()) >= '0' && ch <= '7')
566 c[2] = ch;
567 else
568 unget_input(ch);
569 add_str((int)strtol(c, NULL, 8));
570 break;
571 case EOF:
572 yyerror("Unexpected EOF in escape");
573 break;
574 }
575 break;
576 case '"':
577 yylval.str = get_str();
578 return TYPE_STRING;
579 case '\n':
580 yyerror("Newline in string");
581 break;
582 case EOF:
583 yyerror("Unexpected EOF in string");
584 break;
585 default:
586 add_str(ch);
587 break;
588 }
589 }
590 break;
591
592 default:
593 if(isalpha(ch) || ch == '_')
594 {
595 keyword_t skw;
596 keyword_t *kw;
597 /* Collect keyword */
598 reset_str();
599 add_str(ch);
600 while(1)
601 {
602 ch = get_input();
603 if(isalnum(ch) || ch == '_')
604 add_str(ch);
605 else
606 {
607 unget_input(ch);
608 break;
609 }
610 }
611 skw.keyword = get_str();
612 kw = bsearch(&skw, keywords, NKEYWORDS, sizeof(keywords[0]), cmp_kw);
613 if(!kw)
614 {
615 stack_msg(MSG_ERR, "config: %d: Unknown keyword '%s'", line_number, skw.keyword);
616 yylval.kw = NULL;
617 return TYPE_KEYWORD;
618 }
619 xfree((void *)skw.keyword);
620 if(kw->type == TYPE_VALUE)
621 {
622 yylval.i = (int)kw->confref.val;
623 return TYPE_NUMBER;
624 }
625 yylval.kw = kw;
626 return TYPE_KEYWORD;
627 }
628 else if(isdigit(ch) || ch == '+' || ch == '-')
629 {
630 char *s;
631 char *eptr;
632 int type = TYPE_NUMBER;
633 /* Collect number */
634 reset_str();
635 add_str(ch);
636 while(1)
637 {
638 ch = get_input();
639 if(isxdigit(ch) || ch == 'x' || ch == 'X' || ch == '.') /* Not exact, but close enough */
640 add_str(ch);
641 else
642 {
643 unget_input(ch);
644 break;
645 }
646 if(ch == '.')
647 type = TYPE_DOUBLE;
648 }
649 s = get_str();
650 if(type == TYPE_DOUBLE)
651 {
652 yylval.d = strtod(s, &eptr);
653 if(*eptr)
654 stack_msg(MSG_ERR, "config: %d: Invalid floating point number", line_number);
655 }
656 else
657 {
658 yylval.i = strtol(s, &eptr, 0);
659 if(*eptr)
660 stack_msg(MSG_ERR, "config: %d: Invalid number", line_number);
661 }
662 xfree(s);
663 return type;
664 }
665 else
666 yyerror("Unmatched text '%c' (0x%02x)", isprint(ch) ? ch : ' ', ch);
667 break;
668 }
669 }
670 }
671
672 static void set_color(color_t *c, char *s)
673 {
674 char *cptr;
675 if(*s != '#' || strlen(s) != 7)
676 {
677 colorerror:
678 stack_msg(MSG_ERR, "config: %d: Invalid color value '%s'", line_number, s);
679 return;
680 }
681 c->b = strtol(s+5, &cptr, 16);
682 if(*cptr)
683 goto colorerror;
684 s[5] = '\0';
685 c->g = strtol(s+3, &cptr, 16);
686 if(*cptr)
687 goto colorerror;
688 s[3] = '\0';
689 c->r = strtol(s+1, &cptr, 16);
690 if(*cptr)
691 goto colorerror;
692 }
693
694 static gdFontPtr get_font(int id)
695 {
696 switch(id)
697 {
698 case 0: return gdFontTiny;
699 case 1: return gdFontSmall;
700 default:
701 case 2: return gdFontMediumBold;
702 case 3: return gdFontLarge;
703 case 4: return gdFontGiant;
704 }
705 }
706
707 /*
708 **************************************************************************
709 * The config parser
710 * Grammar:
711 * file : <Empty>
712 * | lines
713 * ;
714 *
715 * lines : line
716 * | lines line
717 * ;
718 *
719 * line : <keyword> '=' <value> ';'
720 * | ';'
721 * ;
722 **************************************************************************
723 */
724 static void config_parse(void)
725 {
726 int state = 0;
727 int token;
728 int t;
729 keyword_t *kw = NULL;
730
731 while(1)
732 {
733 token = config_lex();
734 if(token == EOF)
735 {
736 if(state)
737 stack_msg(MSG_ERR, "config: %d: Unexpected EOF", line_number);
738 break;
739 }
740
741 switch(state)
742 {
743 case 0:
744 if(token == TYPE_KEYWORD)
745 {
746 kw = yylval.kw;
747 state = 1;
748 }
749 else if(token != ';')
750 stack_msg(MSG_ERR, "config: %d: Keyword expected", line_number);
751 break;
752 case 1:
753 if(token != '=')
754 {
755 stack_msg(MSG_ERR, "config: %d: '=' expected", line_number);
756 while(1)
757 {
758 token = config_lex();
759 if(token == ';')
760 {
761 state = 0;
762 break;
763 }
764 else if(token == EOF)
765 break;
766 }
767 }
768 else
769 state = 2;
770 break;
771 case 2:
772 if(!kw)
773 {
774 /* Error recovery of failed keyword */
775 state = 3;
776 break;
777 }
778 if(kw->type == TYPE_FONT || kw->type == TYPE_BOOLEAN)
779 t = TYPE_NUMBER;
780 else if(kw->type == TYPE_COLOR)
781 t = TYPE_STRING;
782 else
783 t = kw->type;
784
785 if(token == TYPE_NUMBER && kw->type == TYPE_DOUBLE)
786 {
787 /* Auto promote numbers to doubles if required */
788 yylval.d = (double)yylval.i;
789 token = TYPE_DOUBLE;
790 }
791
792 if(token != t)
793 {
794 char *e;
795 switch(kw->type)
796 {
797 case TYPE_STRING: e = "String"; yylval.str = xstrdup("error recovery"); break;
798 case TYPE_NUMBER: e = "Number"; yylval.i = 0; break;
799 case TYPE_COLOR: e = "Color"; yylval.str = xstrdup("#123456"); break;
800 case TYPE_FONT: e = "Font"; yylval.i = 0; break;
801 case TYPE_BOOLEAN: e = "Boolean"; yylval.i = 0; break;
802 case TYPE_DOUBLE: e = "Double"; yylval.d = 0.0; break;
803 default: e = "Internal error: Unknown type"; yylval.i = 0; break;
804 }
805 stack_msg(MSG_ERR, "config: %d: %s expected", line_number, e);
806 }
807 #ifdef DEBUG
808 printf("processing: '%s'\n", kw->keyword);
809 #endif
810 switch(kw->type)
811 {
812 case TYPE_STRING:
813 *kw->confref.s = yylval.str;
814 break;
815 case TYPE_NUMBER:
816 *kw->confref.i = yylval.i;
817 break;
818 case TYPE_BOOLEAN:
819 if(yylval.i == -1)
820 *kw->confref.i = !*kw->confref.i;
821 else
822 *kw->confref.i = yylval.i != 0;
823 break;
824 case TYPE_COLOR:
825 set_color(kw->confref.c, yylval.str);
826 break;
827 case TYPE_FONT:
828 kw->confref.f->gdfont = get_font(yylval.i);
829 break;
830 case TYPE_DOUBLE:
831 *kw->confref.d = yylval.d;
832 break;
833 default:
834 yyerror("Internal error: Unknown type passed %d", kw->type);
835 break;
836 }
837 state = 3;
838 break;
839 case 3:
840 if(token != ';')
841 stack_msg(MSG_ERR, "config: %d: ';' expected", line_number);
842 state = 0;
843 break;
844 default:
845 yyerror("Internal error: invalid state %d", state);
846 break;
847 }
848 }
849 }
850
851 /*
852 **************************************************************************
853 * Configuration
854 **************************************************************************
855 */
856 void stack_option(const char *opt)
857 {
858 stacked_opts = xrealloc(stacked_opts, sizeof(*stacked_opts) * (nstacked_opts + 1));
859 stacked_opts[nstacked_opts] = xmalloc(strlen(opt) + 2);
860 strcpy(stacked_opts[nstacked_opts], opt);
861 strcat(stacked_opts[nstacked_opts], ";");
862 nstacked_opts++;
863 #ifdef DEBUG
864 printf("stacking option: '%s'\n", stacked_opts[nstacked_opts-1]);
865 #endif
866 }
867
868 void read_config(const char *path)
869 {
870 FILE *fp;
871
872 /* Make sure we have them sorted for bsearch */
873 qsort(keywords, NKEYWORDS, sizeof(keywords[0]), cmp_kw);
874
875 if(path)
876 {
877 if((fp = fopen(path, "r")) != NULL)
878 input_file = path;
879 }
880 else
881 {
882 if((fp = fopen("./" CONFFILENAME, "r")) == NULL)
883 {
884 if((fp = fopen(ETCDIR "/" CONFFILENAME, "r")) != NULL)
885 input_file = ETCDIR "/" CONFFILENAME;
886 }
887 else
888 input_file = "./" CONFFILENAME;
889 }
890
891 if(fp)
892 {
893 line_number = 1;
894 set_input(fp, NULL);
895 config_parse();
896 fclose(fp);
897 input_file = NULL;
898 }
899
900 if(nstacked_opts)
901 {
902 int i;
903 for(i = 0; i < nstacked_opts; i++)
904 {
905 line_number = 0;
906 set_input(NULL, stacked_opts[i]);
907 input_file = stacked_opts[i];
908 #ifdef DEBUG
909 printf("parsing stacked option: '%s'\n", stacked_opts[i]);
910 #endif
911 config_parse();
912 }
913 input_file = NULL;
914 }
915 #ifdef DEBUG
916 dump_config();
917 #endif
918 }
919
920 /*
921 **************************************************************************
922 * Color reference by name for late-binding color allocation
923 **************************************************************************
924 */
925 color_t *get_colorref(const char *confcolor)
926 {
927 keyword_t skw;
928 keyword_t *kw;
929
930 assert(confcolor != NULL);
931
932 skw.keyword = confcolor;
933 kw = bsearch(&skw, keywords, NKEYWORDS, sizeof(keywords[0]), cmp_kw);
934 if(!kw || kw->type != TYPE_COLOR)
935 return NULL;
936 return kw->confref.c;
937 }

  ViewVC Help
Powered by ViewVC 1.1.0 with CvsGraph 1.7.0