/[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.25 - (show annotations)
Wed May 21 01:41:18 2008 UTC (9 years, 6 months ago) by bertho
Branch: MAIN
CVS Tags: REL_1_6_2, source_head_20080521
Changes since 1.24: +1 -0 lines
File MIME type: text/plain
- Do a better job at drawing the merge lines by selecting the shortest path
  from the revision boxes. An analysis is now done whether the source and
  destinations should be on the left or right side.
- Fix the left_right case for merge lines to display correctly.
- Fix a +/-1 error on the merge lines to account for both rounding errors and
  the shadow of the revision boxes.
- Add configuration option 'merge_on_tag' to force the left_right case to
  display merge lines on the tags instead of on the top/bottom sides. This
  also solves imagemap overlaps where multiple sources or destinations would
  be displayed at the same position.
- Fix the imagemap function to record the correct position of the merges.
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 stringlist_t *sl;
55 condstring_t *cs;
56 colorlist_t *cl;
57 INTTYPE val;
58 } confref;
59 } keyword_t;
60
61 typedef union {
62 keyword_t *kw;
63 int i;
64 double d;
65 char *str;
66 } YYSTYPE;
67
68 static YYSTYPE yylval;
69
70 static int nstacked_opts;
71 static char **stacked_opts;
72
73 static keyword_t keywords[] = {
74 { "branch_bgcolor", TYPE_COLOR, { &conf.branch_bgcolor } },
75 { "branch_bspace", TYPE_NUMBER, { &conf.branch_bspace } },
76 { "branch_color", TYPE_COLOR, { &conf.branch_color } },
77 { "branch_font", TYPE_FONT, { &conf.branch_font.gdfont } },
78 { "branch_ttfont", TYPE_STRING, { &conf.branch_font.ttfont } },
79 { "branch_ttsize", TYPE_DOUBLE, { &conf.branch_font.ttsize } },
80 { "branch_tag_color", TYPE_COLOR, { &conf.branch_tag_color } },
81 { "branch_tag_font", TYPE_FONT, { &conf.branch_tag_font.gdfont } },
82 { "branch_tag_ttfont", TYPE_STRING, { &conf.branch_tag_font.ttfont } },
83 { "branch_tag_ttsize", TYPE_DOUBLE, { &conf.branch_tag_font.ttsize } },
84 { "branch_lspace", TYPE_NUMBER, { &conf.branch_lspace } },
85 { "branch_rspace", TYPE_NUMBER, { &conf.branch_rspace } },
86 { "branch_tspace", TYPE_NUMBER, { &conf.branch_tspace } },
87 { "branch_connect", TYPE_NUMBER, { &conf.branch_connect } },
88 { "branch_margin", TYPE_NUMBER, { &conf.branch_margin } },
89 { "branch_dupbox", TYPE_BOOLEAN, { &conf.branch_dupbox } },
90 { "branch_fold", TYPE_BOOLEAN, { &conf.branch_fold } },
91 { "branch_foldall", TYPE_BOOLEAN, { &conf.branch_foldall } },
92 { "branch_resort", TYPE_BOOLEAN, { &conf.branch_resort } },
93 { "branch_subtree", TYPE_STRING, { &conf.branch_subtree } },
94 { "upside_down", TYPE_BOOLEAN, { &conf.upside_down } },
95 { "left_right", TYPE_BOOLEAN, { &conf.left_right } },
96 { "auto_stretch", TYPE_BOOLEAN, { &conf.auto_stretch } },
97 { "color_bg", TYPE_COLOR, { &conf.color_bg } },
98 { "transparent_bg", TYPE_BOOLEAN, { &conf.transparent_bg } },
99 { "cvsmodule", TYPE_STRING, { &conf.cvsmodule } },
100 { "cvsroot", TYPE_STRING, { &conf.cvsroot } },
101 { "date_format", TYPE_STRING, { &conf.date_format } },
102 { "box_shadow", TYPE_BOOLEAN, { &conf.box_shadow } },
103 { "strip_untagged", TYPE_BOOLEAN, { &conf.strip_untagged } },
104 { "strip_first_rev", TYPE_BOOLEAN, { &conf.strip_first_rev } },
105 { "anti_alias", TYPE_BOOLEAN, { &conf.anti_alias } },
106 { "use_ttf", TYPE_BOOLEAN, { &conf.use_ttf } },
107 { "parse_logs", TYPE_BOOLEAN, { &conf.parse_logs } },
108 { "html_level", TYPE_NUMBER, { &conf.html_level } },
109 { "thick_lines", TYPE_NUMBER, { &conf.thick_lines } },
110 { "msg_color", TYPE_COLOR, { &conf.msg_color } },
111 { "msg_font", TYPE_FONT, { &conf.msg_font.gdfont } },
112 { "msg_ttfont", TYPE_STRING, { &conf.msg_font.ttfont } },
113 { "msg_ttsize", TYPE_DOUBLE, { &conf.msg_font.ttsize } },
114 { "rev_hidenumber", TYPE_BOOLEAN, { &conf.rev_hidenumber } },
115 { "rev_color", TYPE_COLOR, { &conf.rev_color } },
116 { "rev_bgcolor", TYPE_COLOR, { &conf.rev_bgcolor } },
117 { "rev_font", TYPE_FONT, { &conf.rev_font.gdfont } },
118 { "rev_ttfont", TYPE_STRING, { &conf.rev_font.ttfont } },
119 { "rev_ttsize", TYPE_DOUBLE, { &conf.rev_font.ttsize } },
120 { "rev_separator", TYPE_NUMBER, { &conf.rev_separator } },
121 { "rev_minline", TYPE_NUMBER, { &conf.rev_minline } },
122 { "rev_maxline", TYPE_NUMBER, { &conf.rev_maxline } },
123 { "rev_lspace", TYPE_NUMBER, { &conf.rev_lspace } },
124 { "rev_rspace", TYPE_NUMBER, { &conf.rev_rspace } },
125 { "rev_tspace", TYPE_NUMBER, { &conf.rev_tspace } },
126 { "rev_bspace", TYPE_NUMBER, { &conf.rev_bspace } },
127 { "rev_text", TYPE_CSTRING, { &conf.rev_text } },
128 { "rev_idtext", TYPE_CSTRING, { &conf.rev_idtext } },
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_COLORLIST, { &conf.merge_color } },
135 { "merge_from", TYPE_STRINGLIST, { &conf.merge_from } },
136 { "merge_to", TYPE_STRINGLIST, { &conf.merge_to } },
137 { "merge_findall", TYPE_BOOLEAN, { &conf.merge_findall } },
138 { "merge_front", TYPE_BOOLEAN, { &conf.merge_front } },
139 { "merge_nocase", TYPE_BOOLEAN, { &conf.merge_nocase } },
140 { "merge_arrows", TYPE_BOOLEAN, { &conf.merge_arrows } },
141 { "merge_cvsnt", TYPE_BOOLEAN, { &conf.merge_cvsnt } },
142 { "merge_cvsnt_color", TYPE_COLOR, { &conf.merge_cvsnt_color } },
143 { "merge_on_tag", TYPE_BOOLEAN, { &conf.merge_on_tag } },
144 { "arrow_width", TYPE_NUMBER, { &conf.arrow_width } },
145 { "arrow_length", TYPE_NUMBER, { &conf.arrow_length } },
146 { "tag_color", TYPE_COLOR, { &conf.tag_color } },
147 { "tag_font", TYPE_FONT, { &conf.tag_font.gdfont } },
148 { "tag_ttfont", TYPE_STRING, { &conf.tag_font.ttfont } },
149 { "tag_ttsize", TYPE_DOUBLE, { &conf.tag_font.ttsize } },
150 { "tag_ignore", TYPE_STRING, { &conf.tag_ignore } },
151 { "tag_ignore_merge", TYPE_BOOLEAN, { &conf.tag_ignore_merge } },
152 { "tag_nocase", TYPE_BOOLEAN, { &conf.tag_nocase } },
153 { "tag_negate", TYPE_BOOLEAN, { &conf.tag_negate } },
154 { "title", TYPE_STRING, { &conf.title } },
155 { "title_x", TYPE_NUMBER, { &conf.title_x } },
156 { "title_y", TYPE_NUMBER, { &conf.title_y } },
157 { "title_font", TYPE_FONT, { &conf.title_font.gdfont } },
158 { "title_ttfont", TYPE_STRING, { &conf.title_font.ttfont } },
159 { "title_ttsize", TYPE_DOUBLE, { &conf.title_font.ttsize } },
160 { "title_align", TYPE_NUMBER, { &conf.title_align } },
161 { "title_color", TYPE_COLOR, { &conf.title_color } },
162 { "margin_top", TYPE_NUMBER, { &conf.margin_top } },
163 { "margin_bottom", TYPE_NUMBER, { &conf.margin_bottom } },
164 { "margin_left", TYPE_NUMBER, { &conf.margin_left } },
165 { "margin_right", TYPE_NUMBER, { &conf.margin_right } },
166 { "image_type", TYPE_NUMBER, { &conf.image_type } },
167 { "image_quality", TYPE_NUMBER, { &conf.image_quality } },
168 { "image_compress", TYPE_NUMBER, { &conf.image_compress } },
169 { "image_interlace", TYPE_BOOLEAN, { &conf.image_interlace } },
170 { "map_name", TYPE_STRING, { &conf.map_name } },
171 { "map_branch_href", TYPE_STRING, { &conf.map_branch_href } },
172 { "map_branch_alt", TYPE_STRING, { &conf.map_branch_alt } },
173 { "map_rev_href", TYPE_STRING, { &conf.map_rev_href } },
174 { "map_rev_alt", TYPE_STRING, { &conf.map_rev_alt } },
175 { "map_diff_href", TYPE_STRING, { &conf.map_diff_href } },
176 { "map_diff_alt", TYPE_STRING, { &conf.map_diff_alt } },
177 { "map_merge_href", TYPE_STRING, { &conf.map_merge_href } },
178 { "map_merge_alt", TYPE_STRING, { &conf.map_merge_alt } },
179 { "jpeg", TYPE_VALUE, { (void *)IMAGE_JPEG } },
180 { "png", TYPE_VALUE, { (void *)IMAGE_PNG } },
181 { "gif", TYPE_VALUE, { (void *)IMAGE_GIF } },
182 { "true", TYPE_VALUE, { (void *)1 } },
183 { "false", TYPE_VALUE, { (void *)0 } },
184 { "not", TYPE_VALUE, { (void *)-1 } },
185 { "left", TYPE_VALUE, { (void *)0 } },
186 { "center", TYPE_VALUE, { (void *)1 } },
187 { "right", TYPE_VALUE, { (void *)2 } },
188 { "tiny", TYPE_VALUE, { (void *)0 } },
189 { "small", TYPE_VALUE, { (void *)1 } },
190 { "medium", TYPE_VALUE, { (void *)2 } },
191 { "large", TYPE_VALUE, { (void *)3 } },
192 { "giant", TYPE_VALUE, { (void *)4 } },
193 { "HTML3", TYPE_VALUE, { (void *)1 } },
194 { "HTML4", TYPE_VALUE, { (void *)2 } },
195 { "XHTML", TYPE_VALUE, { (void *)3 } },
196 };
197
198 #define NKEYWORDS (sizeof(keywords) / sizeof(keywords[0]))
199
200 static int cmp_kw(const void *k1, const void *k2)
201 {
202 return strcmp(((keyword_t *)k1)->keyword, ((keyword_t *)k2)->keyword);
203 }
204
205 /*
206 **************************************************************************
207 * Debug routines
208 **************************************************************************
209 */
210 #ifdef DEBUG
211 #define DEBUGSTREAM stdout
212 static void debug_pname(const char *n)
213 {
214 fprintf(DEBUGSTREAM, "%-16s: ", n);
215 }
216
217 static void debug_pstring(const char *n, const char *a)
218 {
219 debug_pname(n);
220 if(!a)
221 fprintf(DEBUGSTREAM, "<not-set>\n");
222 else
223 {
224 fputc('\'', DEBUGSTREAM);
225 for(; *a; a++)
226 {
227 if(isprint(*a))
228 fputc(*a, DEBUGSTREAM);
229 else
230 {
231 fputc('\\', DEBUGSTREAM);
232 switch(*a)
233 {
234 case '\a': fputc('a', DEBUGSTREAM); break;
235 case '\b': fputc('b', DEBUGSTREAM); break;
236 case '\f': fputc('f', DEBUGSTREAM); break;
237 case '\n': fputc('n', DEBUGSTREAM); break;
238 case '\r': fputc('r', DEBUGSTREAM); break;
239 case '\t': fputc('t', DEBUGSTREAM); break;
240 case '\v': fputc('v', DEBUGSTREAM); break;
241 default:
242 fprintf(DEBUGSTREAM, "x%02x", (unsigned char)*a);
243 }
244 }
245 }
246 fprintf(DEBUGSTREAM, "'\n");
247 }
248 }
249
250 static void debug_pbool(const char *n, int b)
251 {
252 debug_pname(n);
253 fprintf(DEBUGSTREAM, "%s\n", b ? "true" : "false");
254 }
255
256 static void debug_pint(const char *n, int i)
257 {
258 debug_pname(n);
259 fprintf(DEBUGSTREAM, "%i\n", i);
260 }
261
262 static void debug_pdouble(const char *n, double d)
263 {
264 debug_pname(n);
265 fprintf(DEBUGSTREAM, "%g\n", d);
266 }
267
268 static void debug_pfont(const char *n, gdFontPtr f)
269 {
270 const char *s = "<Unknown font>";
271 debug_pname(n);
272 if(f == gdFontTiny)
273 s = "gdFontTiny";
274 else if(f == gdFontSmall)
275 s = "gdFontSmall";
276 else if(f == gdFontMediumBold)
277 s = "gdFontMediumBold";
278 else if(f == gdFontLarge)
279 s = "gdFontLarge";
280 else if(f == gdFontGiant)
281 s = "gdFontGiant";
282 fprintf(DEBUGSTREAM, "%s\n", s);
283 }
284
285 static char *debug_op[] = { "=~", "=*", "!~", "!*", "==", "!_", ">=", ">", "<=", "<" };
286
287 static void debug_pcolornode(node_t *n)
288 {
289 if(!n)
290 return;
291 if(n->op == TYPE_STRING)
292 fprintf(DEBUGSTREAM, "%s ", n->value.str);
293 else if(n->op == TYPE_COLOR)
294 fprintf(DEBUGSTREAM, "#%02x%02x%02x ", n->value.clr.r, n->value.clr.g, n->value.clr.b);
295 else
296 {
297 char *key;
298 fprintf(DEBUGSTREAM, "[ ");
299 switch(n->key)
300 {
301 case KEY_STATE: key = "state"; break;
302 case KEY_AUTHOR: key = "author"; break;
303 case KEY_TAG: key = "tag"; break;
304 case KEY_DATE: key = "date"; break;
305 case KEY_REV: key = "rev"; break;
306 case KEY_UNKNOWN: key = "unknown"; break;
307 default: key = "<undefined>"; break;
308 }
309 fprintf(DEBUGSTREAM, "%s ", key);
310 if(n->op > OP_FIRST && n->op < OP_LAST)
311 fprintf(DEBUGSTREAM, "%s ", debug_op[n->op - OP_FIRST - 1]);
312 else
313 fprintf(DEBUGSTREAM, "op(-?-) ");
314 fprintf(DEBUGSTREAM, "%s ", n->content);
315 debug_pcolornode(n->tcase);
316 debug_pcolornode(n->fcase);
317 fprintf(DEBUGSTREAM, "]");
318 }
319 }
320
321 static void debug_pcolor(const char *n, color_t *c)
322 {
323 debug_pname(n);
324 if(c->node)
325 {
326 debug_pcolornode(c->node);
327 fprintf(DEBUGSTREAM, "\n");
328 }
329 else
330 fprintf(DEBUGSTREAM, "#%02x%02x%02x\n", c->r, c->g, c->b);
331 }
332
333 static void debug_pcolorlist(const char *n, colorlist_t *cl)
334 {
335 int i;
336 char buf[128];
337 for(i = 0; i < cl->n; i++)
338 {
339 sprintf(buf, "%s{%d}", n, i);
340 debug_pcolor(buf, &cl->clrs[i]);
341 }
342 }
343
344 static void debug_pstringlist(const char *n, stringlist_t *sl)
345 {
346 int i;
347 char buf[128];
348 for(i = 0; i < sl->n; i++)
349 {
350 sprintf(buf, "%s{%d}", n, i);
351 debug_pstring(buf, sl->strs[i]);
352 }
353 }
354
355 void dump_config(void)
356 {
357 debug_pstring("cvsroot", conf.cvsroot);
358 debug_pstring("cvsmodule", conf.cvsmodule);
359 debug_pstring("date_format", conf.date_format);
360
361 debug_pcolor("color_bg", &conf.color_bg);
362 debug_pbool("box_shadow", conf.box_shadow);
363 debug_pbool("upside_down", conf.upside_down);
364 debug_pbool("left_right", conf.left_right);
365 debug_pbool("strip_untagged", conf.strip_untagged);
366 debug_pbool("strip_first_rev", conf.strip_first_rev);
367 debug_pbool("auto_stretch", conf.auto_stretch);
368 debug_pbool("anti_alias", conf.anti_alias);
369 debug_pbool("use_ttf", conf.use_ttf);
370 debug_pint("thick_lines", conf.thick_lines);
371 debug_pint("html_level", conf.html_level);
372 debug_pbool("parse_logs", conf.parse_logs);
373 debug_pbool("transparent_bg", conf.transparent_bg);
374
375 debug_pint("arrow_length", conf.arrow_length);
376 debug_pint("arrow_width", conf.arrow_width);
377
378 debug_pbool("merge_arrows", conf.merge_arrows);
379 debug_pcolor("merge_cvsnt_color", &conf.merge_cvsnt_color);
380 debug_pbool("merge_cvsnt", conf.merge_cvsnt);
381 debug_pbool("merge_findall", conf.merge_findall);
382 debug_pcolorlist("merge_color", &conf.merge_color);
383 debug_pstringlist("merge_from", &conf.merge_from);
384 debug_pstringlist("merge_to", &conf.merge_to);
385 debug_pbool("merge_front", conf.merge_front);
386 debug_pbool("merge_nocase", conf.merge_nocase);
387
388 debug_pcolor("msg_color", &conf.msg_color);
389 debug_pfont("msg_font", conf.msg_font.gdfont);
390 debug_pstring("msg_ttfont", conf.msg_font.ttfont);
391 debug_pdouble("msg_ttsize", conf.msg_font.ttsize);
392
393 debug_pfont("tag_font", conf.tag_font.gdfont);
394 debug_pstring("tag_ttfont", conf.tag_font.ttfont);
395 debug_pdouble("tag_ttsize", conf.tag_font.ttsize);
396 debug_pcolor("tag_color", &conf.tag_color);
397 debug_pbool("tag_ignore_merge", conf.tag_ignore_merge);
398 debug_pstring("tag_ignore", conf.tag_ignore);
399 debug_pbool("tag_negate", conf.tag_negate);
400 debug_pbool("tag_nocase", conf.tag_nocase);
401
402 debug_pfont("rev_font", conf.rev_font.gdfont);
403 debug_pstring("rev_ttfont", conf.rev_font.ttfont);
404 debug_pdouble("rev_ttsize", conf.rev_font.ttsize);
405 debug_pcolor("rev_color", &conf.rev_color);
406 debug_pcolor("rev_bgcolor", &conf.rev_bgcolor);
407 debug_pint("rev_separator", conf.rev_separator);
408 debug_pint("rev_minline", conf.rev_minline);
409 debug_pint("rev_maxline", conf.rev_maxline);
410 debug_pint("rev_maxtags", conf.rev_maxtags);
411 debug_pint("rev_lspace", conf.rev_lspace);
412 debug_pint("rev_rspace", conf.rev_rspace);
413 debug_pint("rev_tspace", conf.rev_tspace);
414 debug_pint("rev_bspace", conf.rev_bspace);
415 debug_pcstring("rev_text", conf.rev_text);
416 debug_pcolor("rev_text_color", &conf.rev_text_color);
417 debug_pfont("rev_text_font", conf.rev_text_font.gdfont);
418 debug_pstring("rev_text_ttfont", conf.rev_text_font.ttfont);
419 debug_pdouble("rev_text_ttsize", conf.rev_text_font.ttsize);
420 debug_pbool("rev_hidenumber", conf.rev_hidenumber);
421
422 debug_pfont("branch_font", conf.branch_font.gdfont);
423 debug_pstring("branch_ttfont", conf.branch_font.ttfont);
424 debug_pdouble("branch_ttsize", conf.branch_font.ttsize);
425 debug_pcolor("branch_color", &conf.branch_color);
426 debug_pfont("branch_tag_font", conf.branch_tag_font.gdfont);
427 debug_pstring("branch_tag_ttfont", conf.branch_tag_font.ttfont);
428 debug_pdouble("branch_tag_ttsize", conf.branch_tag_font.ttsize);
429 debug_pcolor("branch_tag_color", &conf.branch_tag_color);
430 debug_pcolor("branch_bgcolor", &conf.branch_bgcolor);
431 debug_pint("branch_lspace", conf.branch_lspace);
432 debug_pint("branch_rspace", conf.branch_rspace);
433 debug_pint("branch_tspace", conf.branch_tspace);
434 debug_pint("branch_bspace", conf.branch_bspace);
435 debug_pint("branch_connect", conf.branch_connect);
436 debug_pint("branch_margin", conf.branch_margin);
437 debug_pint("branch_dupbox", conf.branch_dupbox);
438 debug_pbool("branch_fold", conf.branch_fold);
439 debug_pbool("branch_foldall", conf.branch_foldall);
440 debug_pbool("branch_resort", conf.branch_resort);
441 debug_pstring("branch_subtree", conf.branch_subtree);
442
443 debug_pstring("title", conf.title);
444 debug_pint("title_x", conf.title_x);
445 debug_pint("title_y", conf.title_y);
446 debug_pfont("title_font", conf.title_font.gdfont);
447 debug_pstring("title_ttfont", conf.title_font.ttfont);
448 debug_pdouble("title_ttsize", conf.title_font.ttsize);
449 debug_pint("title_align", conf.title_align);
450 debug_pcolor("title_color", &conf.title_color);
451
452 debug_pint("margin_top", conf.margin_top);
453 debug_pint("margin_bottom", conf.margin_bottom);
454 debug_pint("margin_left", conf.margin_left);
455 debug_pint("margin_right", conf.margin_right);
456
457 debug_pint("image_type", conf.image_type);
458 debug_pint("image_quality", conf.image_quality);
459 debug_pint("image_compress", conf.image_compress);
460 debug_pbool("image_interlace", conf.image_interlace);
461
462 debug_pstring("map_name", conf.map_name);
463 debug_pstring("map_branch_href", conf.map_branch_href);
464 debug_pstring("map_branch_alt", conf.map_branch_alt);
465 debug_pstring("map_rev_href", conf.map_rev_href);
466 debug_pstring("map_rev_alt", conf.map_rev_alt);
467 debug_pstring("map_diff_href", conf.map_diff_href);
468 debug_pstring("map_diff_alt", conf.map_diff_alt);
469 debug_pstring("map_merge_href", conf.map_merge_href);
470 debug_pstring("map_merge_alt", conf.map_merge_alt);
471
472 debug_pstring("expand[0]", conf.expand[0]);
473 debug_pstring("expand[1]", conf.expand[1]);
474 debug_pstring("expand[2]", conf.expand[2]);
475 debug_pstring("expand[3]", conf.expand[3]);
476 debug_pstring("expand[4]", conf.expand[4]);
477 debug_pstring("expand[5]", conf.expand[5]);
478 debug_pstring("expand[6]", conf.expand[6]);
479 debug_pstring("expand[7]", conf.expand[7]);
480 debug_pstring("expand[8]", conf.expand[8]);
481 debug_pstring("expand[9]", conf.expand[9]);
482 }
483 #endif
484
485 /*
486 **************************************************************************
487 * String collection routines
488 **************************************************************************
489 */
490 #define STRALLOCSIZE 128
491 static char *str;
492 static int nstr;
493 static int nastr;
494
495 static void reset_str(void)
496 {
497 nstr = 0;
498 }
499
500 static void add_str(int c)
501 {
502 if(nstr + 1 + 1 > nastr)
503 {
504 str = xrealloc(str, nastr+STRALLOCSIZE);
505 nastr += STRALLOCSIZE;
506 }
507 str[nstr++] = c;
508 }
509
510 static char *get_str(void)
511 {
512 if(!str)
513 return xstrdup("");
514
515 str[nstr] = '\0';
516 return xstrdup(str);
517 }
518
519 /*
520 **************************************************************************
521 * Input routines
522 **************************************************************************
523 */
524 static char *buf = NULL;
525 static int bufsize = 0;
526 static int bufalloc = 0;
527 static int bufpos = 0;
528 static int bufunput = -1;
529 static FILE *buffp;
530
531 static void set_input(FILE *fp, char *s)
532 {
533 assert((fp == NULL && s != NULL) || (fp != NULL && s == NULL));
534 buffp = fp;
535 bufsize = bufpos = 0;
536 if(s)
537 {
538 if(!buf)
539 {
540 bufalloc = 8192;
541 buf = xmalloc(bufalloc * sizeof(*buf));
542 }
543 bufsize = strlen(s);
544 assert(bufsize < bufalloc);
545 strcpy(buf, s);
546 }
547 }
548
549 static int get_input(void)
550 {
551 if(bufunput != -1)
552 {
553 int c = bufunput;
554 bufunput = -1;
555 return c;
556 }
557
558 if(bufpos < bufsize)
559 {
560 assert(buf != NULL);
561 retry_input:
562 return (int)((unsigned char)buf[bufpos++]);
563 }
564
565 if(!buf)
566 {
567 bufalloc = 8192;
568 buf = xmalloc(bufalloc * sizeof(*buf));
569 bufsize = bufpos = 0;
570 }
571 if(buffp)
572 {
573 bufsize = fread(buf, 1, bufalloc, buffp);
574 bufpos = 0;
575 if(!bufsize)
576 return EOF;
577 goto retry_input;
578 }
579 return EOF;
580 }
581
582 static void unget_input(int c)
583 {
584 bufunput = c;
585 }
586
587 /*
588 **************************************************************************
589 * Lexical scanner
590 **************************************************************************
591 */
592 static int config_lex(void)
593 {
594 int ch;
595 while(1)
596 {
597 ch = get_input();
598 if(ch == '\n')
599 line_number++;
600
601 if(isspace(ch))
602 continue;
603
604 switch(ch)
605 {
606 case '=':
607 ch = get_input();
608 switch(ch)
609 {
610 case '~': return OP_CONTAINED;
611 case '*': return OP_CONTAINEDI;
612 case '=': return OP_EQ;
613 default:
614 unget_input(ch);
615 return '=';
616 }
617 break;
618
619 case '!':
620 ch = get_input();
621 switch(ch)
622 {
623 case '~': return OP_NCONTAINED;
624 case '*': return OP_NCONTAINEDI;
625 case '=': return OP_NE;
626 default:
627 stack_msg(MSG_ERR, "config: %d: Invalid operator", line_number);
628 unget_input(ch);
629 return '!';
630 }
631 break;
632
633 case '>':
634 ch = get_input();
635 if(ch == '=')
636 return OP_GE;
637 unget_input(ch);
638 return OP_GT;
639
640 case '<':
641 ch = get_input();
642 if(ch == '=')
643 return OP_LE;
644 unget_input(ch);
645 return OP_LT;
646
647 case EOF:
648 case ';':
649 case '[':
650 case ']':
651 return ch;
652
653 case '#': /* Comment */
654 while((ch = get_input()) != '\n' && ch != EOF)
655 ;
656 if(ch != EOF)
657 unget_input(ch);
658 break;
659
660 case '"':
661 reset_str();
662 while(1)
663 {
664 char c[4];
665 ch = get_input();
666 switch(ch)
667 {
668 case '\\': /* Start an escape sequence */
669 switch(ch = get_input())
670 {
671 default: /* This includes '\\', '"' and embedded newlines */
672 add_str(ch);
673 break;
674 case 'a': add_str('\a'); break;
675 case 'b': add_str('\b'); break;
676 case 'f': add_str('\f'); break;
677 case 'n': add_str('\n'); break;
678 case 'r': add_str('\r'); break;
679 case 't': add_str('\t'); break;
680 case 'v': add_str('\v'); break;
681 case 'x':
682 case 'X': /* Hex escape */
683 c[0] = get_input();
684 c[1] = get_input();
685 c[2] = '\0';
686 if(!isxdigit((int)(unsigned char)c[0]) || !isxdigit((int)(unsigned char)c[1]))
687 stack_msg(MSG_ERR, "config: %d: Invalid hex escape", line_number);
688 add_str((int)strtol(c, NULL, 16));
689 break;
690 case '0':
691 case '1':
692 case '2': /* Octal escape */
693 c[0] = ch;
694 c[1] = c[2] = c[3] = '\0';
695 if((ch = get_input()) >= '0' && ch <= '7')
696 c[1] = ch;
697 else
698 unget_input(ch);
699 if((ch = get_input()) >= '0' && ch <= '7')
700 c[2] = ch;
701 else
702 unget_input(ch);
703 add_str((int)strtol(c, NULL, 8));
704 break;
705 case EOF:
706 yyerror("Unexpected EOF in escape");
707 break;
708 }
709 break;
710 case '"':
711 yylval.str = get_str();
712 return TYPE_STRING;
713 case '\n':
714 yyerror("Newline in string");
715 break;
716 case EOF:
717 yyerror("Unexpected EOF in string");
718 break;
719 default:
720 add_str(ch);
721 break;
722 }
723 }
724 break;
725
726 default:
727 if(isalpha(ch) || ch == '_')
728 {
729 keyword_t skw;
730 keyword_t *kw;
731 /* Collect keyword */
732 reset_str();
733 add_str(ch);
734 while(1)
735 {
736 ch = get_input();
737 if(isalnum(ch) || ch == '_')
738 add_str(ch);
739 else
740 {
741 unget_input(ch);
742 break;
743 }
744 }
745 skw.keyword = get_str();
746 kw = bsearch(&skw, keywords, NKEYWORDS, sizeof(keywords[0]), cmp_kw);
747 if(!kw)
748 {
749 stack_msg(MSG_ERR, "config: %d: Unknown keyword '%s'", line_number, skw.keyword);
750 yylval.kw = NULL;
751 return TYPE_KEYWORD;
752 }
753 xfree((void *)skw.keyword);
754 if(kw->type == TYPE_VALUE)
755 {
756 yylval.i = (int)kw->confref.val;
757 return TYPE_NUMBER;
758 }
759 yylval.kw = kw;
760 return TYPE_KEYWORD;
761 }
762 else if(isdigit(ch) || ch == '+' || ch == '-')
763 {
764 char *s;
765 char *eptr;
766 int type = TYPE_NUMBER;
767 /* Collect number */
768 reset_str();
769 add_str(ch);
770 while(1)
771 {
772 ch = get_input();
773 if(isxdigit(ch) || ch == 'x' || ch == 'X' || ch == '.') /* Not exact, but close enough */
774 add_str(ch);
775 else
776 {
777 unget_input(ch);
778 break;
779 }
780 if(ch == '.')
781 type = TYPE_DOUBLE;
782 }
783 s = get_str();
784 if(type == TYPE_DOUBLE)
785 {
786 yylval.d = strtod(s, &eptr);
787 if(*eptr)
788 stack_msg(MSG_ERR, "config: %d: Invalid floating point number", line_number);
789 }
790 else
791 {
792 yylval.i = strtol(s, &eptr, 0);
793 if(*eptr)
794 stack_msg(MSG_ERR, "config: %d: Invalid number", line_number);
795 }
796 xfree(s);
797 return type;
798 }
799 else
800 yyerror("Unmatched text '%c' (0x%02x)", isprint(ch) ? ch : ' ', ch);
801 break;
802 }
803 }
804 }
805
806 static void set_color(color_t *c, char *s)
807 {
808 char *cptr;
809 c->node = NULL;
810 if(*s != '#' || strlen(s) != 7)
811 {
812 colorerror:
813 stack_msg(MSG_ERR, "config: %d: Invalid color value '%s'", line_number, s);
814 return;
815 }
816 c->b = strtol(s+5, &cptr, 16);
817 if(*cptr)
818 goto colorerror;
819 s[5] = '\0';
820 c->g = strtol(s+3, &cptr, 16);
821 if(*cptr)
822 goto colorerror;
823 s[3] = '\0';
824 c->r = strtol(s+1, &cptr, 16);
825 if(*cptr)
826 goto colorerror;
827 }
828
829 static gdFontPtr get_font(int id)
830 {
831 switch(id)
832 {
833 case 0: return gdFontTiny;
834 case 1: return gdFontSmall;
835 default:
836 case 2: return gdFontMediumBold;
837 case 3: return gdFontLarge;
838 case 4: return gdFontGiant;
839 }
840 }
841
842 /*
843 **************************************************************************
844 * The config parser
845 * Grammar:
846 * file : <Empty>
847 * | lines
848 * ;
849 *
850 * lines : line
851 * | lines line
852 * ;
853 *
854 * line : <keyword> '=' <value> ';'
855 * | <keyword> '=' expr ';'
856 * | ';'
857 *
858 * expr : '[' <kw> <op> <value> expr expr ']'
859 * | <value>
860 * ;
861 **************************************************************************
862 */
863 static int skip_to_semicolon(int state)
864 {
865 int token;
866 while(1)
867 {
868 token = config_lex();
869 if(token == ';')
870 return 0;
871 else if(token == EOF)
872 return state;
873 }
874 }
875
876 static node_t *new_node(char *val)
877 {
878 node_t *n = xmalloc(sizeof(*n));
879 if(!strcmp(val, "state"))
880 n->key = KEY_STATE;
881 else if(!strcmp(val, "author"))
882 n->key = KEY_AUTHOR;
883 else if(!strcmp(val, "tag"))
884 n->key = KEY_TAG;
885 else if(!strcmp(val, "date"))
886 n->key = KEY_DATE;
887 else if(!strcmp(val, "rev"))
888 n->key = KEY_REV;
889 else
890 {
891 n->key = KEY_UNKNOWN;
892 stack_msg(MSG_ERR, "config: %d: Unknown key '%s'", line_number, val);
893 }
894 return n;
895 }
896
897 typedef struct __statestack_t
898 {
899 int state;
900 node_t *node;
901 } statestack_t;
902
903 static int nstatestack = 0;
904 static int nastatestack;
905 static statestack_t *statestack = NULL;
906
907 static void push_state(node_t *node, int state)
908 {
909 if(!statestack)
910 {
911 statestack = xmalloc(4 * sizeof(*statestack));
912 nastatestack = 4;
913 nstatestack = 0;
914 }
915 else if(nstatestack >= nastatestack)
916 {
917 nastatestack *= 2;
918 statestack = xrealloc(statestack, nastatestack * sizeof(*statestack));
919 }
920 statestack[nstatestack].node = node;
921 statestack[nstatestack].state = state;
922 nstatestack++;
923 }
924
925 static int pop_state(node_t **node, int *state)
926 {
927 if(nstatestack <= 0)
928 {
929 *state = 3;
930 return 0;
931 }
932 assert(*node != NULL);
933 if(statestack[nstatestack-1].state == 8)
934 statestack[nstatestack-1].node->tcase = *node;
935 else if(statestack[nstatestack-1].state == 9)
936 statestack[nstatestack-1].node->fcase = *node;
937 *state = statestack[nstatestack-1].state;
938 *node = statestack[nstatestack-1].node;
939 return nstatestack--;
940 }
941
942 /* YYYY.MM.DD.hh.mm.ss */
943 /* 0123456789012345678 */
944 /* 111111111 */
945 static char *fixup_date(const char *str)
946 {
947 int i;
948 int y=1970, m=1, d=1, h=0, mi=0, s=0;
949 int l = strlen(str);
950 char date[6*16]; /* Should be wildly enough to hold 19 chars from 6 numbers with all possible errors */
951 if(l < 4 || l > 19)
952 {
953 date_err:
954 stack_msg(MSG_ERR, "config: %d: Invalid date string '%s'", line_number, str);
955 return "1970.01.01.00.00.00";
956 }
957 for(i = 0; i < l; i++)
958 {
959 if(!strchr("0123456789.", str[i]))
960 goto date_err;
961 }
962 i = sscanf(str, "%d.%d.%d.%d.%d.%d", &y, &m, &d, &h, &mi, &s);
963 if(i == EOF || i < 0 || i > 6)
964 goto date_err;
965 if(i >= 1 && (y < 1970 || y > 2037))
966 goto date_err;
967 if(i >= 2 && (m < 1 || m > 12))
968 goto date_err;
969 if(i >= 3 && (d < 1 || d > 31))
970 goto date_err;
971 if(i >= 4 && (h < 0 || h > 23))
972 goto date_err;
973 if(i >= 5 && (mi < 0 || mi > 59))
974 goto date_err;
975 if(i >= 6 && (s < 0 || s > 59))
976 goto date_err;
977 sprintf(date, "%04d.%02d.%02d.%02d.%02d.%02d", y, m, d, h, mi, s);
978 return strdup(date);
979 }
980
981 static void config_parse(void)
982 {
983 int state = 0;
984 int token;
985 int t;
986 keyword_t *kw = NULL;
987 node_t *node = NULL;
988
989 while(1)
990 {
991 token = config_lex();
992 if(token == EOF)
993 {
994 if(state)
995 stack_msg(MSG_ERR, "config: %d: Unexpected EOF", line_number);
996 break;
997 }
998
999 switch(state)
1000 {
1001 case 0:
1002 if(token == TYPE_KEYWORD)
1003 {
1004 kw = yylval.kw;
1005 state = 1;
1006 }
1007 else if(token != ';')
1008 stack_msg(MSG_ERR, "config: %d: Keyword expected", line_number);
1009 break;
1010 case 1:
1011 if(token != '=')
1012 {
1013 stack_msg(MSG_ERR, "config: %d: '=' expected", line_number);
1014 state = skip_to_semicolon(state);
1015 break;
1016 }
1017 else
1018 state = 2;
1019 break;
1020 case 2:
1021 if(!kw)
1022 {
1023 /* Error recovery of failed keyword */
1024 state = 3;
1025 break;
1026 }
1027 if(token == '[')
1028 {
1029 if(kw->type != TYPE_COLOR && kw->type != TYPE_CSTRING)
1030 {
1031 stack_msg(MSG_ERR, "config: %d: Conditional expression not allowed for keyword", line_number);
1032 state = skip_to_semicolon(state);
1033 break;
1034 }
1035 state = 4;
1036 break;
1037 }
1038 if(kw->type == TYPE_FONT || kw->type == TYPE_BOOLEAN)
1039 t = TYPE_NUMBER;
1040 else if(kw->type == TYPE_COLOR || kw->type == TYPE_COLORLIST || kw->type == TYPE_STRINGLIST || kw->type == TYPE_CSTRING)
1041 t = TYPE_STRING;
1042 else
1043 t = kw->type;
1044
1045 if(token == TYPE_NUMBER && kw->type == TYPE_DOUBLE)
1046 {
1047 /* Auto promote numbers to doubles if required */
1048 yylval.d = (double)yylval.i;
1049 token = TYPE_DOUBLE;
1050 }
1051
1052 if(token != t)
1053 {
1054 char *e;
1055 switch(kw->type)
1056 {
1057 case TYPE_STRING: e = "String"; yylval.str = xstrdup("error recovery"); break;
1058 case TYPE_STRINGLIST: e = "StringL"; yylval.str = xstrdup("error recovery"); break;
1059 case TYPE_CSTRING: e = "CString"; yylval.str = xstrdup("error recovery"); break;
1060 case TYPE_NUMBER: e = "Number"; yylval.i = 0; break;
1061 case TYPE_COLOR: e = "Color"; yylval.str = xstrdup("#123456"); break;
1062 case TYPE_COLORLIST: e = "ColorL"; yylval.str = xstrdup("#123456"); break;
1063 case TYPE_FONT: e = "Font"; yylval.i = 0; break;
1064 case TYPE_BOOLEAN: e = "Boolean"; yylval.i = 0; break;
1065 case TYPE_DOUBLE: e = "Double"; yylval.d = 0.0; break;
1066 default: e = "Internal error: Unknown type"; yylval.i = 0; break;
1067 }
1068 stack_msg(MSG_ERR, "config: %d: %s expected", line_number, e);
1069 }
1070 #ifdef DEBUG
1071 printf("processing: '%s'\n", kw->keyword);
1072 #endif
1073 switch(kw->type)
1074 {
1075 case TYPE_STRING:
1076 *kw->confref.s = yylval.str;
1077 break;
1078 case TYPE_STRINGLIST:
1079 kw->confref.sl->strs = xrealloc(kw->confref.sl->strs, sizeof(*kw->confref.sl->strs) * (kw->confref.sl->n + 1));
1080 kw->confref.sl->strs[kw->confref.sl->n] = yylval.str;
1081 kw->confref.sl->n++;
1082 break;
1083 case TYPE_CSTRING:
1084 kw->confref.cs->str = yylval.str;
1085 break;
1086 case TYPE_NUMBER:
1087 *kw->confref.i = yylval.i;
1088 break;
1089 case TYPE_BOOLEAN:
1090 if(yylval.i == -1)
1091 *kw->confref.i = !*kw->confref.i;
1092 else
1093 *kw->confref.i = yylval.i != 0;
1094 break;
1095 case TYPE_COLOR:
1096 set_color(kw->confref.c, yylval.str);
1097 break;
1098 case TYPE_COLORLIST:
1099 kw->confref.cl->clrs = xrealloc(kw->confref.cl->clrs, sizeof(*kw->confref.cl->clrs) * (kw->confref.cl->n + 1));
1100 set_color(&kw->confref.cl->clrs[kw->confref.cl->n], yylval.str);
1101 kw->confref.cl->n++;
1102 break;
1103 case TYPE_FONT:
1104 kw->confref.f->gdfont = get_font(yylval.i);
1105 break;
1106 case TYPE_DOUBLE:
1107 *kw->confref.d = yylval.d;
1108 break;
1109 default:
1110 yyerror("Internal error: Unknown type passed %d", kw->type);
1111 break;
1112 }
1113 kw = NULL;
1114 state = 3;
1115 break;
1116 case 3:
1117 if(token != ';')
1118 stack_msg(MSG_ERR, "config: %d: ';' expected", line_number);
1119 state = 0;
1120 break;
1121 case 4:
1122 if(token != TYPE_STRING)
1123 {
1124 stack_msg(MSG_ERR, "config: %d: String expected (condition key)", line_number);
1125 state = skip_to_semicolon(state);
1126 break;
1127 }
1128 node = new_node(yylval.str);
1129 state = 5;
1130 break;
1131 case 5:
1132 if(token <= OP_FIRST || token >= OP_LAST)
1133 {
1134 stack_msg(MSG_ERR, "config: %d: Operator expected", line_number);
1135 state = skip_to_semicolon(state);
1136 break;
1137 }
1138 node->op = token;
1139 state = 6;
1140 break;
1141 case 6:
1142 if(token != TYPE_STRING)
1143 {
1144 stack_msg(MSG_ERR, "config: %d: String expected (condition)", line_number);
1145 state = skip_to_semicolon(state);
1146 break;
1147 }
1148 if(node->key == KEY_DATE)
1149 node->content = fixup_date(yylval.str);
1150 else
1151 node->content = yylval.str;
1152 state = 7;
1153 break;
1154 case 7:
1155 if(token == '[')
1156 {
1157 push_state(node, 8);
1158 node = NULL;
1159 state = 4;
1160 break;
1161 }
1162 if(token != TYPE_STRING)
1163 {
1164 stack_msg(MSG_ERR, "config: %d: String or '[' expected (true case)", line_number);
1165 state = skip_to_semicolon(state);
1166 break;
1167 }
1168 node->tcase = xmalloc(sizeof(*node->tcase));
1169 if(kw->type == TYPE_COLOR || kw->type == TYPE_COLORLIST)
1170 {
1171 node->tcase->key = TYPE_COLOR;
1172 set_color(&node->tcase->value.clr, yylval.str);
1173 }
1174 else
1175 {
1176 node->tcase->key = TYPE_STRING;
1177 node->tcase->value.str = yylval.str;
1178 }
1179 state = 8;
1180 break;
1181 case 8:
1182 if(token == '[')
1183 {
1184 push_state(node, 9);
1185 node = NULL;
1186 state = 4;
1187 break;
1188 }
1189 if(token != TYPE_STRING)
1190 {
1191 stack_msg(MSG_ERR, "config: %d: String or '[' expected (false case)", line_number);
1192 state = skip_to_semicolon(state);
1193 break;
1194 }
1195 node->fcase = xmalloc(sizeof(*node->fcase));
1196 if(kw->type == TYPE_COLOR || kw->type == TYPE_COLORLIST)
1197 {
1198 node->fcase->key = TYPE_COLOR;
1199 set_color(&node->fcase->value.clr, yylval.str);
1200 }
1201 else
1202 {
1203 node->fcase->key = TYPE_STRING;
1204 node->fcase->value.str = yylval.str;
1205 }
1206 state = 9;
1207 break;
1208 case 9:
1209 if(token != ']')
1210 {
1211 stack_msg(MSG_ERR, "config: %d: ']' expected", line_number);
1212 state = skip_to_semicolon(state);
1213 break;
1214 }
1215 if(!pop_state(&node, &state))
1216 {
1217 if(kw->type == TYPE_COLOR)
1218 kw->confref.c->node = node;
1219 else if(kw->type == TYPE_CSTRING)
1220 kw->confref.cs->node = node;
1221 else
1222 stack_msg(MSG_ERR, "config: %d: Color or conditional string keyword expected", line_number);
1223 node = NULL;
1224 kw = NULL;
1225 }
1226 break;
1227 default:
1228 yyerror("Internal error: invalid state %d", state);
1229 break;
1230 }
1231 }
1232 }
1233
1234 /*
1235 **************************************************************************
1236 * Configuration
1237 **************************************************************************
1238 */
1239 void stack_option(const char *opt)
1240 {
1241 stacked_opts = xrealloc(stacked_opts, sizeof(*stacked_opts) * (nstacked_opts + 1));
1242 stacked_opts[nstacked_opts] = xmalloc(strlen(opt) + 2);
1243 strcpy(stacked_opts[nstacked_opts], opt);
1244 strcat(stacked_opts[nstacked_opts], ";");
1245 nstacked_opts++;
1246 #ifdef DEBUG
1247 printf("stacking option: '%s'\n", stacked_opts[nstacked_opts-1]);
1248 #endif
1249 }
1250
1251 void read_config(const char *path)
1252 {
1253 FILE *fp;
1254
1255 /* Make sure we have them sorted for bsearch */
1256 qsort(keywords, NKEYWORDS, sizeof(keywords[0]), cmp_kw);
1257
1258 if(path)
1259 {
1260 if((fp = fopen(path, "r")) != NULL)
1261 input_file = path;
1262 }
1263 else
1264 {
1265 if((fp = fopen("./" CONFFILENAME, "r")) == NULL)
1266 {
1267 if((fp = fopen(ETCDIR "/" CONFFILENAME, "r")) != NULL)
1268 input_file = ETCDIR "/" CONFFILENAME;
1269 }
1270 else
1271 input_file = "./" CONFFILENAME;
1272 }
1273
1274 if(fp)
1275 {
1276 line_number = 1;
1277 set_input(fp, NULL);
1278 config_parse();
1279 fclose(fp);
1280 input_file = NULL;
1281 }
1282
1283 if(nstacked_opts)
1284 {
1285 int i;
1286 for(i = 0; i < nstacked_opts; i++)
1287 {
1288 line_number = 0;
1289 set_input(NULL, stacked_opts[i]);
1290 input_file = stacked_opts[i];
1291 #ifdef DEBUG
1292 printf("parsing stacked option: '%s'\n", stacked_opts[i]);
1293 #endif
1294 config_parse();
1295 }
1296 input_file = NULL;
1297 }
1298
1299 if(conf.merge_from.n != conf.merge_to.n)
1300 {
1301 int x = conf.merge_from.n < conf.merge_to.n ? conf.merge_from.n : conf.merge_to.n;
1302 stack_msg(MSG_ERR, "config: merge_from(n=%d) does not match merge_to(n=%d)", conf.merge_from.n, conf.merge_to.n);
1303 conf.merge_from.n = x;
1304 conf.merge_to.n = x;
1305 }
1306 if(conf.merge_color.n < conf.merge_from.n)
1307 {
1308 /* Silently extend missing merge_color statements with black */
1309 int x;
1310 char c[] = "#000000";
1311 for(x = conf.merge_color.n; x < conf.merge_from.n; x++)
1312 {
1313 conf.merge_color.clrs = xrealloc(conf.merge_color.clrs, sizeof(*conf.merge_color.clrs) * (conf.merge_color.n + 1));
1314 set_color(&conf.merge_color.clrs[conf.merge_color.n], c);
1315 conf.merge_color.n++;
1316 }
1317 }
1318
1319 #ifdef DEBUG
1320 dump_config();
1321 #endif
1322 }
1323
1324 /*
1325 **************************************************************************
1326 * Color reference by name for late-binding color allocation
1327 **************************************************************************
1328 */
1329 color_t *get_colorref(const char *confcolor, int idx)
1330 {
1331 keyword_t skw;
1332 keyword_t *kw;
1333
1334 if(!confcolor)
1335 return NULL;
1336
1337 skw.keyword = confcolor;
1338 kw = bsearch(&skw, keywords, NKEYWORDS, sizeof(keywords[0]), cmp_kw);
1339 if(!kw || (kw->type != TYPE_COLOR && kw->type != TYPE_COLORLIST))
1340 return NULL;
1341 if(kw->type == TYPE_COLORLIST)
1342 {
1343 if(idx >= kw->confref.cl->n)
1344 return NULL;
1345 return &kw->confref.cl->clrs[idx];
1346 }
1347 return kw->confref.c;
1348 }

  ViewVC Help
Powered by ViewVC 1.1.0 with CvsGraph 1.7.0