/[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.16 - (show annotations)
Sun Aug 15 16:58:22 2004 UTC (13 years, 4 months ago) by bertho
Branch: MAIN
CVS Tags: REL_1_5_0
Changes since 1.15: +50 -18 lines
File MIME type: text/plain
- Added drawing of partial trees. You now can select a subtree based on a
  revision number, branch number or symbolic tag to show only that part of the
  tree. New config option branch_subtree selects which part is shown. If the
  subtree is empty, then the whole tree is shown.

- Most error and warning messages are now displayed in the graph, instead of
  being written to stderr. This enables you to see an image eventhough errors
  might be present. The old method would generate a corrupt image.
  New configuration options include msg_color and msg_font to control the looks
  of it. Messages are always printed at the bottom of the image.

- Fixed a bug in the folding code where subtrees would not fold correctly.

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

  ViewVC Help
Powered by ViewVC 1.1.0 with CvsGraph 1.7.0