/[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.10 - (show annotations)
Fri Mar 14 01:38:39 2003 UTC (14 years, 7 months ago) by bertho
Branch: MAIN
Changes since 1.9: +1 -0 lines
File MIME type: text/plain
- Fixed a bug in the initial placing of left to right displaying.
- Half-fixed a bug in the kerning code, where a loop-safeguard was triggered
  way too soon (at 100 iterations). This has now been changed to 10000, but
  should be dependent on the number of drawable branches, as the function is
  _at least_ order O(N^2). However, more analysis is required to ensure
  safe guarding under all circumstances.
- Implemented folding of empty branches. Many uses of CVS create many
  branches on the same revision but have no commits on them. This occurs
  often with stable files like .cvsignore files and the like. A new
  configuration option branch_fold enables imaging consequtive brances
  with no commits in the same branch-box. This reduces images by a huge
  factor (28000x1700 -> 2100x2300, i.e. a factor of 10). It also speeds up
  drawing considerably. The branch_fold option is *on* by default.
- Fix duplicate branch-boxes (branch_dupbox=true). No duplicates should be
  created if there are no commits on a specific branch. There is no reason
  for having two boxes on top of each other.
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 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 int val; /* FIXME: Oops, sizeof(int) must be sizeof(void *) */
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 { "upside_down", TYPE_BOOLEAN, { &conf.upside_down } },
101 { "left_right", TYPE_BOOLEAN, { &conf.left_right } },
102 { "auto_stretch", TYPE_BOOLEAN, { &conf.auto_stretch } },
103 { "color_bg", TYPE_COLOR, { &conf.color_bg } },
104 { "transparent_bg", TYPE_BOOLEAN, { &conf.transparent_bg } },
105 { "cvsmodule", TYPE_STRING, { &conf.cvsmodule } },
106 { "cvsroot", TYPE_STRING, { &conf.cvsroot } },
107 { "date_format", TYPE_STRING, { &conf.date_format } },
108 { "box_shadow", TYPE_BOOLEAN, { &conf.box_shadow } },
109 { "strip_untagged", TYPE_BOOLEAN, { &conf.strip_untagged } },
110 { "strip_first_rev", TYPE_BOOLEAN, { &conf.strip_first_rev } },
111 { "anti_alias", TYPE_BOOLEAN, { &conf.anti_alias } },
112 { "use_ttf", TYPE_BOOLEAN, { &conf.use_ttf } },
113 { "parse_logs", TYPE_BOOLEAN, { &conf.parse_logs } },
114 { "html_level", TYPE_NUMBER, { &conf.html_level } },
115 { "thick_lines", TYPE_NUMBER, { &conf.thick_lines } },
116 { "rev_color", TYPE_COLOR, { &conf.rev_color } },
117 { "rev_bgcolor", TYPE_COLOR, { &conf.rev_bgcolor } },
118 { "rev_font", TYPE_FONT, { &conf.rev_font.gdfont } },
119 { "rev_ttfont", TYPE_STRING, { &conf.rev_font.ttfont } },
120 { "rev_ttsize", TYPE_DOUBLE, { &conf.rev_font.ttsize } },
121 { "rev_separator", TYPE_NUMBER, { &conf.rev_separator } },
122 { "rev_minline", TYPE_NUMBER, { &conf.rev_minline } },
123 { "rev_maxline", TYPE_NUMBER, { &conf.rev_maxline } },
124 { "rev_lspace", TYPE_NUMBER, { &conf.rev_lspace } },
125 { "rev_rspace", TYPE_NUMBER, { &conf.rev_rspace } },
126 { "rev_tspace", TYPE_NUMBER, { &conf.rev_tspace } },
127 { "rev_bspace", TYPE_NUMBER, { &conf.rev_bspace } },
128 { "rev_text", TYPE_STRING, { &conf.rev_text } },
129 { "rev_text_color", TYPE_COLOR, { &conf.rev_text_color } },
130 { "rev_text_font", TYPE_FONT, { &conf.rev_text_font.gdfont } },
131 { "rev_text_ttfont", TYPE_STRING, { &conf.rev_text_font.ttfont } },
132 { "rev_text_ttsize", TYPE_DOUBLE, { &conf.rev_text_font.ttsize } },
133 { "rev_maxtags", TYPE_NUMBER, { &conf.rev_maxtags } },
134 { "merge_color", TYPE_COLOR, { &conf.merge_color } },
135 { "merge_from", TYPE_STRING, { &conf.merge_from } },
136 { "merge_to", TYPE_STRING, { &conf.merge_to } },
137 { "merge_front", TYPE_BOOLEAN, { &conf.merge_front } },
138 { "merge_nocase", TYPE_BOOLEAN, { &conf.merge_nocase } },
139 { "tag_color", TYPE_COLOR, { &conf.tag_color } },
140 { "tag_font", TYPE_FONT, { &conf.tag_font.gdfont } },
141 { "tag_ttfont", TYPE_STRING, { &conf.tag_font.ttfont } },
142 { "tag_ttsize", TYPE_DOUBLE, { &conf.tag_font.ttsize } },
143 { "title", TYPE_STRING, { &conf.title } },
144 { "title_x", TYPE_NUMBER, { &conf.title_x } },
145 { "title_y", TYPE_NUMBER, { &conf.title_y } },
146 { "title_font", TYPE_FONT, { &conf.title_font.gdfont } },
147 { "title_ttfont", TYPE_STRING, { &conf.title_font.ttfont } },
148 { "title_ttsize", TYPE_DOUBLE, { &conf.title_font.ttsize } },
149 { "title_align", TYPE_NUMBER, { &conf.title_align } },
150 { "title_color", TYPE_COLOR, { &conf.title_color } },
151 { "margin_top", TYPE_NUMBER, { &conf.margin_top } },
152 { "margin_bottom", TYPE_NUMBER, { &conf.margin_bottom } },
153 { "margin_left", TYPE_NUMBER, { &conf.margin_left } },
154 { "margin_right", TYPE_NUMBER, { &conf.margin_right } },
155 { "image_type", TYPE_NUMBER, { &conf.image_type } },
156 { "image_quality", TYPE_NUMBER, { &conf.image_quality } },
157 { "map_name", TYPE_STRING, { &conf.map_name } },
158 { "map_branch_href", TYPE_STRING, { &conf.map_branch_href } },
159 { "map_branch_alt", TYPE_STRING, { &conf.map_branch_alt } },
160 { "map_rev_href", TYPE_STRING, { &conf.map_rev_href } },
161 { "map_rev_alt", TYPE_STRING, { &conf.map_rev_alt } },
162 { "map_diff_href", TYPE_STRING, { &conf.map_diff_href } },
163 { "map_diff_alt", TYPE_STRING, { &conf.map_diff_alt } },
164 { "jpeg", TYPE_VALUE, { (void *)IMAGE_JPEG } },
165 { "png", TYPE_VALUE, { (void *)IMAGE_PNG } },
166 { "gif", TYPE_VALUE, { (void *)IMAGE_GIF } },
167 { "true", TYPE_VALUE, { (void *)1 } },
168 { "false", TYPE_VALUE, { (void *)0 } },
169 { "not", TYPE_VALUE, { (void *)-1 } },
170 { "left", TYPE_VALUE, { (void *)0 } },
171 { "center", TYPE_VALUE, { (void *)1 } },
172 { "right", TYPE_VALUE, { (void *)2 } },
173 { "tiny", TYPE_VALUE, { (void *)0 } },
174 { "small", TYPE_VALUE, { (void *)1 } },
175 { "medium", TYPE_VALUE, { (void *)2 } },
176 { "large", TYPE_VALUE, { (void *)3 } },
177 { "giant", TYPE_VALUE, { (void *)4 } },
178 { "HTML3", TYPE_VALUE, { (void *)1 } },
179 { "HTML4", TYPE_VALUE, { (void *)2 } },
180 { "XHTML", TYPE_VALUE, { (void *)3 } },
181 };
182
183 #define NKEYWORDS (sizeof(keywords) / sizeof(keywords[0]))
184
185 static int cmp_kw(const void *k1, const void *k2)
186 {
187 return strcmp(((keyword_t *)k1)->keyword, ((keyword_t *)k2)->keyword);
188 }
189
190 /*
191 **************************************************************************
192 * Debug routines
193 **************************************************************************
194 */
195 #ifdef DEBUG
196 #define DEBUGSTREAM stdout
197 static void debug_pname(const char *n)
198 {
199 fprintf(DEBUGSTREAM, "%-16s: ", n);
200 }
201
202 static void debug_pstring(const char *n, const char *a)
203 {
204 debug_pname(n);
205 if(!a)
206 fprintf(DEBUGSTREAM, "<not-set>\n");
207 else
208 {
209 fputc('\'', DEBUGSTREAM);
210 for(; *a; a++)
211 {
212 if(isprint(*a))
213 fputc(*a, DEBUGSTREAM);
214 else
215 {
216 fputc('\\', DEBUGSTREAM);
217 switch(*a)
218 {
219 case '\a': fputc('a', DEBUGSTREAM); break;
220 case '\b': fputc('b', DEBUGSTREAM); break;
221 case '\f': fputc('f', DEBUGSTREAM); break;
222 case '\n': fputc('n', DEBUGSTREAM); break;
223 case '\r': fputc('r', DEBUGSTREAM); break;
224 case '\t': fputc('t', DEBUGSTREAM); break;
225 case '\v': fputc('v', DEBUGSTREAM); break;
226 default:
227 fprintf(DEBUGSTREAM, "x%02x", (unsigned char)*a);
228 }
229 }
230 }
231 fprintf(DEBUGSTREAM, "'\n");
232 }
233 }
234
235 static void debug_pbool(const char *n, int b)
236 {
237 debug_pname(n);
238 fprintf(DEBUGSTREAM, "%s\n", b ? "true" : "false");
239 }
240
241 static void debug_pint(const char *n, int i)
242 {
243 debug_pname(n);
244 fprintf(DEBUGSTREAM, "%i\n", i);
245 }
246
247 static void debug_pdouble(const char *n, double d)
248 {
249 debug_pname(n);
250 fprintf(DEBUGSTREAM, "%g\n", d);
251 }
252
253 static void debug_pfont(const char *n, gdFontPtr f)
254 {
255 const char *s = "<Unknown font>";
256 debug_pname(n);
257 if(f == gdFontTiny)
258 s = "gdFontTiny";
259 else if(f == gdFontSmall)
260 s = "gdFontSmall";
261 else if(f == gdFontMediumBold)
262 s = "gdFontMediumBold";
263 else if(f == gdFontLarge)
264 s = "gdFontLarge";
265 else if(f == gdFontGiant)
266 s = "gdFontGiant";
267 fprintf(DEBUGSTREAM, "%s\n", s);
268 }
269
270 static void debug_pcolor(const char *n, color_t *c)
271 {
272 debug_pname(n);
273 fprintf(DEBUGSTREAM, "#%02x%02x%02x\n", c->r, c->g, c->b);
274 }
275
276 void dump_config(void)
277 {
278 debug_pstring("cvsroot", conf.cvsroot);
279 debug_pstring("cvsmodule", conf.cvsmodule);
280 debug_pstring("date_format", conf.date_format);
281
282 debug_pcolor("color_bg", &conf.color_bg);
283 debug_pbool("box_shadow", conf.box_shadow);
284 debug_pbool("upside_down", conf.upside_down);
285 debug_pbool("left_right", conf.left_right);
286 debug_pbool("strip_untagged", conf.strip_untagged);
287 debug_pbool("strip_first_rev", conf.strip_first_rev);
288 debug_pbool("auto_stretch", conf.auto_stretch);
289 debug_pbool("anti_alias", conf.anti_alias);
290 debug_pbool("use_ttf", conf.use_ttf);
291 debug_pint("thick_lines", conf.thick_lines);
292
293 debug_pfont("tag_font", conf.tag_font.gdfont);
294 debug_pstring("tag_ttfont", conf.tag_font.ttfont);
295 debug_psouble("tag_ttsize", conf.tag_font.ttsize);
296 debug_pcolor("tag_color", &conf.tag_color);
297
298 debug_pfont("rev_font", conf.rev_font.gdfont);
299 debug_pstring("rev_ttfont", conf.rev_font.ttfont);
300 debug_pdouble("rev_ttsize", conf.rev_font.ttsize);
301 debug_pcolor("rev_color", &conf.rev_color);
302 debug_pcolor("rev_bgcolor", &conf.rev_bgcolor);
303 debug_pint("rev_separator", conf.rev_separator);
304 debug_pint("rev_minline", conf.rev_minline);
305 debug_pint("rev_maxline", conf.rev_maxline);
306 debug_pint("rev_lspace", conf.rev_lspace);
307 debug_pint("rev_rspace", conf.rev_rspace);
308 debug_pint("rev_tspace", conf.rev_tspace);
309 debug_pint("rev_bspace", conf.rev_bspace);
310 debug_pstring("rev_text", conf.rev_text);
311 debug_pcolor("rev_text_color", &conf.rev_text_color);
312 debug_pfont("rev_text_font", conf.rev_text_font.gdfont);
313 debug_pstring("rev_text_ttfont", conf.rev_text_font.ttfont);
314 debug_pdouble("rev_text_ttsize", conf.rev_text_font.ttsize);
315
316 debug_pfont("branch_font", conf.branch_font.gdfont);
317 debug_pstring("branch_ttfont", conf.branch_font.ttfont);
318 debug_pdouble("branch_ttsize", conf.branch_font.ttsize);
319 debug_pcolor("branch_color", &conf.branch_color);
320 debug_pfont("branch_tag_font", conf.branch_tag_font.gdfont);
321 debug_pstring("branch_tag_ttfont", conf.branch_tag_font.ttfont);
322 debug_pdouble("branch_tag_ttsize", conf.branch_tag_font.ttsize);
323 debug_pcolor("branch_tag_color", &conf.branch_tag_color);
324 debug_pcolor("branch_bgcolor", &conf.branch_bgcolor);
325 debug_pint("branch_lspace", conf.branch_lspace);
326 debug_pint("branch_rspace", conf.branch_rspace);
327 debug_pint("branch_tspace", conf.branch_tspace);
328 debug_pint("branch_bspace", conf.branch_bspace);
329 debug_pint("branch_connect", conf.branch_connect);
330 debug_pint("branch_margin", conf.branch_margin);
331 debug_pint("branch_dupbox", conf.branch_dupbox);
332
333 debug_pstring("title", conf.title);
334 debug_pint("title_x", conf.title_x);
335 debug_pint("title_y", conf.title_y);
336 debug_pfont("title_font", conf.title_font.gdfont);
337 debug_pstring("title_ttfont", conf.title_font.ttfont);
338 debug_pdouble("title_ttsize", conf.title_font.ttsize);
339 debug_pint("title_align", conf.title_align);
340 debug_pcolor("title_color", &conf.title_color);
341
342 debug_pint("margin_top", conf.margin_top);
343 debug_pint("margin_bottom", conf.margin_bottom);
344 debug_pint("margin_left", conf.margin_left);
345 debug_pint("margin_right", conf.margin_right);
346
347 debug_pint("image_type", conf.image_type);
348 debug_pint("image_quality", conf.image_quality);
349
350 debug_pstring("map_name", conf.map_name);
351 debug_pstring("map_branch_href", conf.map_branch_href);
352 debug_pstring("map_branch_alt", conf.map_branch_alt);
353 debug_pstring("map_rev_href", conf.map_rev_href);
354 debug_pstring("map_rev_alt", conf.map_rev_alt);
355 debug_pstring("map_diff_href", conf.map_diff_href);
356 debug_pstring("map_diff_alt", conf.map_diff_alt);
357
358 debug_pstring("expand[0]", conf.expand[0]);
359 debug_pstring("expand[1]", conf.expand[1]);
360 debug_pstring("expand[2]", conf.expand[2]);
361 debug_pstring("expand[3]", conf.expand[3]);
362 debug_pstring("expand[4]", conf.expand[4]);
363 debug_pstring("expand[5]", conf.expand[5]);
364 debug_pstring("expand[6]", conf.expand[6]);
365 debug_pstring("expand[7]", conf.expand[7]);
366 debug_pstring("expand[8]", conf.expand[8]);
367 debug_pstring("expand[9]", conf.expand[9]);
368 }
369 #endif
370
371 /*
372 **************************************************************************
373 * String collection routines
374 **************************************************************************
375 */
376 #define STRALLOCSIZE 128
377 static char *str;
378 static int nstr;
379 static int nastr;
380
381 static void reset_str(void)
382 {
383 nstr = 0;
384 }
385
386 static void add_str(int c)
387 {
388 if(nstr + 1 + 1 > nastr)
389 {
390 str = xrealloc(str, nastr+STRALLOCSIZE);
391 nastr += STRALLOCSIZE;
392 }
393 str[nstr++] = c;
394 }
395
396 static char *get_str(void)
397 {
398 if(!str)
399 return xstrdup("");
400
401 str[nstr] = '\0';
402 return xstrdup(str);
403 }
404
405 /*
406 **************************************************************************
407 * Input routines
408 **************************************************************************
409 */
410 static char *buf = NULL;
411 static int bufsize = 0;
412 static int bufalloc = 0;
413 static int bufpos = 0;
414 static int bufunput = -1;
415 static FILE *buffp;
416
417 static void set_input(FILE *fp, char *s)
418 {
419 assert((fp == NULL && s != NULL) || (fp != NULL && s == NULL));
420 buffp = fp;
421 bufsize = bufpos = 0;
422 if(s)
423 {
424 if(!buf)
425 {
426 bufalloc = 8192;
427 buf = xmalloc(bufalloc * sizeof(*buf));
428 }
429 bufsize = strlen(s);
430 assert(bufsize < bufalloc);
431 strcpy(buf, s);
432 }
433 }
434
435 static int get_input(void)
436 {
437 if(bufunput != -1)
438 {
439 int c = bufunput;
440 bufunput = -1;
441 return c;
442 }
443
444 if(bufpos < bufsize)
445 {
446 assert(buf != NULL);
447 retry_input:
448 return (int)((unsigned char)buf[bufpos++]);
449 }
450
451 if(!buf)
452 {
453 bufalloc = 8192;
454 buf = xmalloc(bufalloc * sizeof(*buf));
455 bufsize = bufpos = 0;
456 }
457 if(buffp)
458 {
459 bufsize = fread(buf, 1, bufalloc, buffp);
460 bufpos = 0;
461 if(!bufsize)
462 return EOF;
463 goto retry_input;
464 }
465 return EOF;
466 }
467
468 static void unget_input(int c)
469 {
470 bufunput = c;
471 }
472
473 /*
474 **************************************************************************
475 * Lexical scanner
476 **************************************************************************
477 */
478 static int config_lex(void)
479 {
480 int ch;
481 while(1)
482 {
483 ch = get_input();
484 if(ch == '\n')
485 line_number++;
486
487 if(isspace(ch))
488 continue;
489
490 switch(ch)
491 {
492 case EOF:
493 case '=':
494 case ';':
495 return ch;
496
497 case '#': /* Comment */
498 while((ch = get_input()) != '\n' && ch != EOF)
499 ;
500 if(ch != EOF)
501 unget_input(ch);
502 break;
503
504 case '"':
505 reset_str();
506 while(1)
507 {
508 char c[4];
509 ch = get_input();
510 switch(ch)
511 {
512 case '\\': /* Start an escape sequence */
513 switch(ch = get_input())
514 {
515 default: /* This includes '\\', '"' and embedded newlines */
516 add_str(ch);
517 break;
518 case 'a': add_str('\a'); break;
519 case 'b': add_str('\b'); break;
520 case 'f': add_str('\f'); break;
521 case 'n': add_str('\n'); break;
522 case 'r': add_str('\r'); break;
523 case 't': add_str('\t'); break;
524 case 'v': add_str('\v'); break;
525 case 'x':
526 case 'X': /* Hex escape */
527 c[0] = get_input();
528 c[1] = get_input();
529 c[2] = '\0';
530 if(!isxdigit((int)(unsigned char)c[0]) || !isxdigit((int)(unsigned char)c[1]))
531 yyerror("Invalid hex escape");
532 add_str((int)strtol(c, NULL, 16));
533 break;
534 case '0':
535 case '1':
536 case '2': /* Octal escape */
537 c[0] = ch;
538 c[1] = c[2] = c[3] = '\0';
539 if((ch = get_input()) >= '0' && ch <= '7')
540 c[1] = ch;
541 else
542 unget_input(ch);
543 if((ch = get_input()) >= '0' && ch <= '7')
544 c[2] = ch;
545 else
546 unget_input(ch);
547 add_str((int)strtol(c, NULL, 8));
548 break;
549 case EOF:
550 yyerror("Unexpected EOF in escape");
551 break;
552 }
553 break;
554 case '"':
555 yylval.str = get_str();
556 return TYPE_STRING;
557 case '\n':
558 yyerror("Newline in string");
559 break;
560 case EOF:
561 yyerror("Unexpected EOF in string");
562 break;
563 default:
564 add_str(ch);
565 break;
566 }
567 }
568 break;
569
570 default:
571 if(isalpha(ch) || ch == '_')
572 {
573 keyword_t skw;
574 keyword_t *kw;
575 /* Collect keyword */
576 reset_str();
577 add_str(ch);
578 while(1)
579 {
580 ch = get_input();
581 if(isalnum(ch) || ch == '_')
582 add_str(ch);
583 else
584 {
585 unget_input(ch);
586 break;
587 }
588 }
589 skw.keyword = get_str();
590 kw = bsearch(&skw, keywords, NKEYWORDS, sizeof(keywords[0]), cmp_kw);
591 if(!kw)
592 yyerror("Unknown keyword '%s'", skw.keyword);
593 xfree(skw.keyword);
594 if(kw->type == TYPE_VALUE)
595 {
596 yylval.i = kw->confref.val;
597 return TYPE_NUMBER;
598 }
599 yylval.kw = kw;
600 return TYPE_KEYWORD;
601 }
602 else if(isdigit(ch) || ch == '+' || ch == '-')
603 {
604 char *s;
605 char *eptr;
606 int type = TYPE_NUMBER;
607 /* Collect number */
608 reset_str();
609 add_str(ch);
610 while(1)
611 {
612 ch = get_input();
613 if(isxdigit(ch) || ch == 'x' || ch == 'X' || ch == '.') /* Not exact, but close enough */
614 add_str(ch);
615 else
616 {
617 unget_input(ch);
618 break;
619 }
620 if(ch == '.')
621 type = TYPE_DOUBLE;
622 }
623 s = get_str();
624 if(type == TYPE_DOUBLE)
625 {
626 yylval.d = strtod(s, &eptr);
627 if(*eptr)
628 yyerror("Invalid floating point number");
629 }
630 else
631 {
632 yylval.i = strtol(s, &eptr, 0);
633 if(*eptr)
634 yyerror("Invalid number");
635 }
636 xfree(s);
637 return type;
638 }
639 else
640 yyerror("Unmatched text '%c' (0x%02x)", isprint(ch) ? ch : ' ', ch);
641 break;
642 }
643 }
644 }
645
646 static void set_color(color_t *c, char *s)
647 {
648 char *cptr;
649 if(*s != '#' || strlen(s) != 7)
650 {
651 colorerror:
652 yyerror("Invalid color value");
653 }
654 c->b = strtol(s+5, &cptr, 16);
655 if(*cptr)
656 goto colorerror;
657 s[5] = '\0';
658 c->g = strtol(s+3, &cptr, 16);
659 if(*cptr)
660 goto colorerror;
661 s[3] = '\0';
662 c->r = strtol(s+1, &cptr, 16);
663 if(*cptr)
664 goto colorerror;
665 }
666
667 static gdFontPtr get_font(int id)
668 {
669 switch(id)
670 {
671 case 0: return gdFontTiny;
672 case 1: return gdFontSmall;
673 default:
674 case 2: return gdFontMediumBold;
675 case 3: return gdFontLarge;
676 case 4: return gdFontGiant;
677 }
678 }
679
680 /*
681 **************************************************************************
682 * The config parser
683 * Grammar:
684 * file : <Empty>
685 * | lines
686 * ;
687 *
688 * lines : line
689 * | lines line
690 * ;
691 *
692 * line : <keyword> '=' <value> ';'
693 * | ';'
694 * ;
695 **************************************************************************
696 */
697 static void config_parse(void)
698 {
699 int state = 0;
700 int token;
701 int t;
702 keyword_t *kw = NULL;
703
704 while(1)
705 {
706 token = config_lex();
707 if(token == EOF)
708 {
709 if(state)
710 yyerror("Unexpected EOF");
711 break;
712 }
713
714 switch(state)
715 {
716 case 0:
717 if(token == TYPE_KEYWORD)
718 {
719 kw = yylval.kw;
720 state = 1;
721 }
722 else if(token != ';')
723 yyerror("Keyword expected");
724 break;
725 case 1:
726 if(token != '=')
727 yyerror("'=' expected");
728 state = 2;
729 break;
730 case 2:
731 if(kw->type == TYPE_FONT || kw->type == TYPE_BOOLEAN)
732 t = TYPE_NUMBER;
733 else if(kw->type == TYPE_COLOR)
734 t = TYPE_STRING;
735 else
736 t = kw->type;
737
738 if(token == TYPE_NUMBER && kw->type == TYPE_DOUBLE)
739 {
740 /* Auto promote numbers to doubles if required */
741 yylval.d = (double)yylval.i;
742 token = TYPE_DOUBLE;
743 }
744
745 if(token != t)
746 {
747 char *e;
748 switch(kw->type)
749 {
750 case TYPE_STRING: e = "String"; break;
751 case TYPE_NUMBER: e = "Number"; break;
752 case TYPE_COLOR: e = "Color"; break;
753 case TYPE_FONT: e = "Font"; break;
754 case TYPE_BOOLEAN: e = "Boolean"; break;
755 case TYPE_DOUBLE: e = "Double"; break;
756 default: e = "Internal error: Unknown type"; break;
757 }
758 yyerror("%s expected", e);
759 }
760 #ifdef DEBUG
761 printf("processing: '%s'\n", kw->keyword);
762 #endif
763 switch(kw->type)
764 {
765 case TYPE_STRING:
766 *kw->confref.s = yylval.str;
767 break;
768 case TYPE_NUMBER:
769 *kw->confref.i = yylval.i;
770 break;
771 case TYPE_BOOLEAN:
772 if(yylval.i == -1)
773 *kw->confref.i = !*kw->confref.i;
774 else
775 *kw->confref.i = yylval.i != 0;
776 break;
777 case TYPE_COLOR:
778 set_color(kw->confref.c, yylval.str);
779 break;
780 case TYPE_FONT:
781 kw->confref.f->gdfont = get_font(yylval.i);
782 break;
783 case TYPE_DOUBLE:
784 *kw->confref.d = yylval.d;
785 break;
786 default:
787 yyerror("Internal error: Unknown type passed %d", kw->type);
788 break;
789 }
790 state = 3;
791 break;
792 case 3:
793 if(token != ';')
794 yyerror("';' expected");
795 state = 0;
796 break;
797 default:
798 yyerror("Internal error: invalid state %d", state);
799 break;
800 }
801 }
802 }
803
804 /*
805 **************************************************************************
806 * Configuration
807 **************************************************************************
808 */
809 void stack_option(const char *opt)
810 {
811 stacked_opts = xrealloc(stacked_opts, sizeof(*stacked_opts) * (nstacked_opts + 1));
812 stacked_opts[nstacked_opts] = xmalloc(strlen(opt) + 2);
813 strcpy(stacked_opts[nstacked_opts], opt);
814 strcat(stacked_opts[nstacked_opts], ";");
815 nstacked_opts++;
816 #ifdef DEBUG
817 printf("stacking option: '%s'\n", stacked_opts[nstacked_opts-1]);
818 #endif
819 }
820
821 void read_config(const char *path)
822 {
823 FILE *fp;
824
825 /* Make sure we have them sorted for bsearch */
826 qsort(keywords, NKEYWORDS, sizeof(keywords[0]), cmp_kw);
827
828 if(path)
829 {
830 if((fp = fopen(path, "r")) != NULL)
831 input_file = path;
832 }
833 else
834 {
835 if((fp = fopen("./" CONFFILENAME, "r")) == NULL)
836 {
837 if((fp = fopen(ETCDIR "/" CONFFILENAME, "r")) != NULL)
838 input_file = ETCDIR "/" CONFFILENAME;
839 }
840 else
841 input_file = "./" CONFFILENAME;
842 }
843
844 if(fp)
845 {
846 line_number = 1;
847 set_input(fp, NULL);
848 config_parse();
849 fclose(fp);
850 input_file = NULL;
851 }
852
853 if(nstacked_opts)
854 {
855 int i;
856 for(i = 0; i < nstacked_opts; i++)
857 {
858 line_number = 0;
859 set_input(NULL, stacked_opts[i]);
860 input_file = stacked_opts[i];
861 #ifdef DEBUG
862 printf("parsing stacked option: '%s'\n", stacked_opts[i]);
863 #endif
864 config_parse();
865 }
866 input_file = NULL;
867 }
868 #ifdef DEBUG
869 dump_config();
870 #endif
871 }

  ViewVC Help
Powered by ViewVC 1.1.0 with CvsGraph 1.7.0