core: add support for the autoverbose feature
[fbsplash.git] / core / src / parse.c
blobe9a1b2fdda29d8565aaa379fcc4780a3bb8c87d5
1 /*
2 * parse.c -- Functions for parsing splashutils cfg files
4 * Copyright (C) 2004-2008, Michal Januszewski <spock@gentoo.org>
6 * This file is subject to the terms and conditions of the GNU General Public
7 * License v2. See the file COPYING in the main directory of this archive for
8 * more details.
12 #include <stdlib.h>
13 #include <string.h>
14 #include <stdio.h>
15 #include <sys/stat.h>
16 #include <ctype.h>
17 #include "common.h"
18 #include "render.h"
20 struct cfg_opt {
21 char *name;
22 enum {
23 t_int, t_path, t_box, t_icon, t_rect, t_color, t_fontpath,
24 t_type_open, t_type_close, t_anim, t_text, t_textbox_open, t_textbox_close,
25 } type;
26 void *val;
29 int line = 0;
30 u16 text_x, text_y;
31 u16 text_size;
32 color text_color;
33 char *text_font;
35 char *curr_cfgfile;
36 stheme_t tmptheme;
38 static bool is_textbox = false;
40 /* Note that pic256 and silentpic256 have to be located before pic and
41 * silentpic or we are gonna get a parse error @ pic256/silentpic256. */
42 struct cfg_opt opts[] =
44 { .name = "jpeg",
45 .type = t_path,
46 .val = &tmptheme.pic },
48 { .name = "pic256",
49 .type = t_path,
50 .val = &tmptheme.pic256 },
52 { .name = "silentpic256",
53 .type = t_path,
54 .val = &tmptheme.silentpic256 },
56 { .name = "silentjpeg",
57 .type = t_path,
58 .val = &tmptheme.silentpic },
60 { .name = "pic",
61 .type = t_path,
62 .val = &tmptheme.pic },
64 { .name = "silentpic",
65 .type = t_path,
66 .val = &tmptheme.silentpic },
68 { .name = "bg_color",
69 .type = t_int,
70 .val = &tmptheme.bg_color },
72 { .name = "tx",
73 .type = t_int,
74 .val = &tmptheme.tx },
76 { .name = "ty",
77 .type = t_int,
78 .val = &tmptheme.ty },
80 { .name = "tw",
81 .type = t_int,
82 .val = &tmptheme.tw },
84 { .name = "th",
85 .type = t_int,
86 .val = &tmptheme.th },
88 { .name = "box",
89 .type = t_box,
90 .val = NULL },
92 { .name = "icon",
93 .type = t_icon,
94 .val = NULL },
96 { .name = "rect",
97 .type = t_rect,
98 .val = NULL },
100 { .name = "<type",
101 .type = t_type_open,
102 .val = NULL },
104 { .name = "</type>",
105 .type = t_type_close,
106 .val = NULL },
108 { .name = "<textbox>",
109 .type = t_textbox_open,
110 .val = NULL },
112 { .name = "</textbox>",
113 .type = t_textbox_close,
114 .val = NULL },
116 #if WANT_MNG
117 { .name = "anim",
118 .type = t_anim,
119 .val = NULL },
120 #endif
121 #if WANT_TTF
122 { .name = "log_lines",
123 .type = t_int,
124 .val = &tmptheme.log_lines },
126 { .name = "log_cols",
127 .type = t_int,
128 .val = &tmptheme.log_cols },
130 { .name = "text_x",
131 .type = t_int,
132 .val = &text_x },
134 { .name = "text_y",
135 .type = t_int,
136 .val = &text_y },
138 { .name = "text_size",
139 .type = t_int,
140 .val = &text_size },
142 { .name = "text_color",
143 .type = t_color,
144 .val = &text_color },
146 { .name = "text_font",
147 .type = t_fontpath,
148 .val = &text_font },
150 { .name = "text",
151 .type = t_text,
152 .val = NULL },
153 #endif /* TTF */
157 #define parse_error(msg, args...) \
158 do { \
159 iprint(MSG_ERROR, "Parse error at '%s', line %d: " msg "\n", curr_cfgfile, line, ## args); \
160 } while (0)
162 #define checknskip(label, req, msg, args...) \
163 do { \
164 if (t == p) { \
165 iprint(MSG_ERROR, "Parse error at '%s', line %d: " msg "\n", curr_cfgfile, line, ## args); \
166 goto label; \
168 t = p; \
169 if (!skip_whitespace(&t, req)) \
170 goto label; \
171 } while (0)
173 static char *get_filepath(char *path)
175 char buf[512];
177 if (path[0] == '/')
178 return strdup(path);
180 snprintf(buf, 512, FBSPL_THEME_DIR "/%s/%s", config.theme, path);
181 return strdup(buf);
184 static int ishexdigit(char c)
186 return (isdigit(c) || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f')) ? 1 : 0;
189 static bool skip_whitespace(char **buf, bool req)
191 if (req && (**buf != ' ' && **buf != '\t' && **buf)) {
192 parse_error("expected a non-zero amount of whitespace instead of '%s'", *buf);
193 while (**buf)
194 (*buf)++;
195 return false;
198 while ((**buf == ' ' || **buf == '\t') && **buf)
199 (*buf)++;
201 return true;
204 static bool skip_nonwhitespace(char **buf, bool req)
206 if (req && ((**buf == ' ' || **buf == '\t') && **buf)) {
207 parse_error("expected a non-zero amount of non-whitespace instead of '%s'", *buf);
208 while (**buf)
209 (*buf)++;
210 return false;
213 while ((**buf != ' ' && **buf != '\t') && **buf)
214 (*buf)++;
216 return true;
219 static void parse_int(char *t, struct cfg_opt opt)
221 if (*t != '=') {
222 parse_error("expected '=' instead of '%c'", *t);
223 return;
226 t++; skip_whitespace(&t, false);
227 *(u16*)opt.val = strtol(t,NULL,0);
230 static void parse_path(char *t, struct cfg_opt opt)
232 if (*t != '=') {
233 parse_error("expected '=' instead of'%c'", *t);
234 return;
237 t++; skip_whitespace(&t, false);
238 *(char**)opt.val = get_filepath(t);
241 static char *get_fontpath(char *t)
243 char buf[512];
244 char buf2[512];
245 struct stat st1, st2;
247 if (t[0] == '/') {
248 return strdup(t);
251 snprintf(buf, 512, "%s/%s/%s", FBSPL_THEME_DIR, config.theme, t);
252 snprintf(buf2, 512, "%s/%s", FBSPL_THEME_DIR, t);
254 stat(buf, &st1);
255 stat(buf2, &st2);
257 if (S_ISREG(st1.st_mode) || S_ISLNK(st1.st_mode)) {
258 return strdup(buf);
259 } else if (S_ISREG(st2.st_mode) || S_ISLNK(st2.st_mode)) {
260 return strdup(buf2);
261 } else {
262 return strdup(buf);
265 return NULL;
268 static void parse_fontpath(char *t, struct cfg_opt opt)
270 if (*t != '=') {
271 parse_error("expected '=' instead of '%c'", *t);
272 return;
275 t++; skip_whitespace(&t, false);
276 *(char**)opt.val = get_fontpath(t);
279 static int parse_color(char **t, struct color *cl)
281 u32 h, len = 0;
282 char *p;
284 if (**t != '#') {
285 if (strncmp(*t, "0x", 2))
286 return -1;
287 else
288 (*t) += 2;
289 } else {
290 (*t)++;
293 for (p = *t; ishexdigit(*p); p++, len++);
295 if (len != 6 && len != 8)
296 return -1;
298 p = *t;
299 h = strtoul(*t, &p, 16);
300 if (*t == p)
301 return -2;
303 if (len > 6) {
304 cl->a = h & 0xff;
305 cl->r = (h >> 24) & 0xff;
306 cl->g = (h >> 16) & 0xff;
307 cl->b = (h >> 8) & 0xff;
308 } else {
309 cl->a = 0xff;
310 cl->r = (h >> 16) & 0xff;
311 cl->g = (h >> 8 ) & 0xff;
312 cl->b = h & 0xff;
315 *t = p;
317 return 0;
320 static int parse_4vec(char **t, rect *r)
322 char *p;
324 if (**t != '<')
325 return -1;
326 (*t)++;
328 skip_whitespace(t, false);
330 p = *t;
331 r->x1 = strtol(p, t, 0);
332 if (p == *t)
333 return -1;
334 skip_whitespace(t, false);
335 if (**t != ',')
336 return -1;
337 (*t)++;
339 p = *t;
340 r->y1 = strtol(p, t, 0);
341 if (p == *t)
342 return -1;
343 skip_whitespace(t, false);
344 if (**t != ',')
345 return -1;
346 (*t)++;
348 p = *t;
349 r->x2 = strtol(p, t, 0);
350 if (p == *t)
351 return -1;
352 skip_whitespace(t, false);
353 if (**t != ',')
354 return -1;
355 (*t)++;
357 p = *t;
358 r->y2 = strtol(p, t, 0);
359 if (p == *t)
360 return -1;
362 skip_whitespace(t, false);
363 if (**t != '>')
364 return -1;
366 (*t)++;
367 return 0;
370 int parse_svc_state(char *t, enum ESVC *state)
372 if (!strncmp(t, "svc_inactive_start", 18)) {
373 *state = e_svc_inact_start; return 18;
374 } else if (!strncmp(t, "svc_inactive_stop", 17)) {
375 *state = e_svc_inact_stop; return 17;
376 } else if (!strncmp(t, "svc_started", 11)) {
377 *state = e_svc_started; return 11;
378 } else if (!strncmp(t, "svc_stopped", 11)) {
379 *state = e_svc_stopped; return 11;
380 } else if (!strncmp(t, "svc_start_failed", 16)) {
381 *state = e_svc_start_failed; return 16;
382 } else if (!strncmp(t, "svc_stop_failed", 15)) {
383 *state = e_svc_stop_failed; return 15;
384 } else if (!strncmp(t, "svc_stop", 8)) {
385 *state = e_svc_stop; return 8;
386 } else if (!strncmp(t, "svc_start", 9)) {
387 *state = e_svc_start; return 9;
390 return 0;
393 void obj_add(void *x) {
394 obj *o = container_of(x);
396 if (tmptheme.objs.tail)
397 o->id = ((obj*)(tmptheme.objs.tail->p))->id + 1;
398 else
399 o->id = 0;
401 list_add(&tmptheme.objs, o);
403 if (is_textbox)
404 list_add(&tmptheme.textbox, o);
407 bool parse_effects(char *t, obj *o)
409 char *p;
410 skip_whitespace(&t, false);
412 while (isalpha(*t)) {
413 if (!strncmp(t, "blendin(", 8)) {
414 t += 8;
415 o->blendin = strtol(t, &p, 0);
416 checknskip(pe_err, false, "expected a number instead of '%s'", t);
417 if (*t != ')') {
418 parse_error("expected ')' instead of '%s'", t);
419 goto pe_err;
421 t++;
422 } else if (!strncmp(t, "blendout(", 9)) {
423 t += 9;
424 o->blendout = strtol(t, &p, 0);
425 checknskip(pe_err, false, "expected a number instead of '%s'", t);
426 if (*t != ')') {
427 parse_error("expected ')' instead of '%s'", t);
428 goto pe_err;
430 t++;
431 } else {
432 parse_error("expected 'blendin(..)' or 'blendout(..)' instead of '%s'", t);
433 pe_err:
434 return false;
437 skip_whitespace(&t, false);
440 return true;
443 bool parse_icon(char *t)
445 char *filename = NULL;
446 char *p;
447 obj *o;
448 icon *cic = obj_alloc(icon, FBSPL_MODE_SILENT);
449 icon_img *cim;
450 item *ti;
451 int i;
453 if (!cic) {
454 iprint(MSG_ERROR, "%s: failed to allocate memory.\n", __func__);
455 return false;
458 o = container_of(cic);
459 cic->svc = NULL;
461 if (!skip_whitespace(&t, true))
462 goto pi_err;
464 for (i = 0; t[i] != ' ' && t[i] != '\t' && t[i] != '\0'; i++);
465 t[i] = 0;
467 filename = get_filepath(t);
468 t += (i+1);
470 skip_whitespace(&t, false);
471 cic->x = strtol(t, &p, 0);
472 checknskip(pi_err, true, "expected a number instead of '%s'", t);
474 cic->y = strtol(t,&p,0);
475 checknskip(pi_err, true, "expected a number instead of '%s'", t);
477 /* Do we need to crop this icon? */
478 if (!strncmp(t, "crop", 4)) {
479 t += 4;
481 if (!skip_whitespace(&t, true))
482 goto pi_err;
484 if (parse_4vec(&t, &cic->crop_from)) {
485 parse_error("expected a 4-tuple instead of '%s'", t);
486 goto pi_err;
489 if (!skip_whitespace(&t, true))
490 goto pi_err;
492 if (parse_4vec(&t, &cic->crop_to)) {
493 parse_error("expected a 4-tuple instead of '%s'", t);
494 goto pi_err;
497 if (!skip_whitespace(&t, true))
498 goto pi_err;
500 cic->crop = true;
501 rect_interpolate(&cic->crop_from, &cic->crop_to, &cic->crop_curr);
502 } else {
503 cic->crop = false;
506 i = parse_svc_state(t, &cic->type);
508 if (!i) {
509 cic->type = e_display;
510 } else {
511 t += i;
513 /* Find the service name */
514 if (!skip_whitespace(&t, true))
515 goto pi_err;
516 for (i = 0; t[i] != ' ' && t[i] != '\t' && t[i] != '\0'; i++);
517 t[i] = 0;
519 o->visible = false;
521 if (*t == '\0') {
522 parse_error("expected service name");
523 goto pi_err;
526 cic->svc = strdup(t);
527 t += (i+1);
530 if (!parse_effects(t, o))
531 goto pi_err;
533 for (ti = tmptheme.icons.head ; ti != NULL; ti = ti->next) {
534 icon_img *ii = (icon_img*) ti->p;
536 if (!strcmp(ii->filename, filename)) {
537 cic->img = ii;
538 goto pi_end;
542 /* Allocate a new entry in the icons list */
543 cim = malloc(sizeof(icon_img));
544 if (!cim)
545 goto pi_outm;
546 cim->filename = filename;
547 cim->w = cim->h = 0;
548 cim->picbuf = NULL;
549 list_add(&tmptheme.icons, cim);
550 cic->img = cim;
552 pi_end:
553 obj_add(cic);
554 return true;
556 pi_err:
557 pi_out:
558 if (filename)
559 free(filename);
560 if (cic->svc)
561 free(cic->svc);
562 free(container_of(cic));
563 return false;
564 pi_outm:
565 iprint(MSG_ERROR, "%s: failed to allocate memory\n", __func__);
566 goto pi_out;
569 void parse_rect(char *t)
571 char *p;
572 rect *crect = malloc(sizeof(rect));
574 if (!crect) {
575 iprint(MSG_ERROR, "%s: failed to allocate memory\n", __func__);
576 return;
579 skip_whitespace(&t, true);
581 while (!isdigit(*t)) {
582 t++;
585 crect->x1 = strtol(t, &p, 0);
586 checknskip(pr_err, true, "expected a number instead of '%s'", t);
588 crect->y1 = strtol(t, &p, 0);
589 checknskip(pr_err, true, "expected a number instead of '%s'", t);
591 crect->x2 = strtol(t,&p,0);
592 checknskip(pr_err, true, "expected a number instead of '%s'", t);
594 crect->y2 = strtol(t,&p,0);
595 checknskip(pr_err, true, "expected a number instead of '%s'", t);
597 /* sanity checks */
598 if (crect->x1 >= tmptheme.xres)
599 crect->x1 = tmptheme.xres-1;
600 if (crect->x2 >= tmptheme.xres)
601 crect->x2 = tmptheme.xres-1;
602 if (crect->y1 >= tmptheme.yres)
603 crect->y1 = tmptheme.yres-1;
604 if (crect->y2 >= tmptheme.yres)
605 crect->y2 = tmptheme.yres-1;
607 list_add(&tmptheme.rects, crect);
608 return;
609 pr_err:
610 free(crect);
611 return;
614 #if WANT_MNG
615 bool parse_anim(char *t)
617 char *p;
618 char *filename;
619 anim *canim = obj_alloc(anim, FBSPL_MODE_SILENT);
620 obj *o;
621 int i;
623 if (!canim) {
624 iprint(MSG_ERROR, "%s: failed to allocate memory\n", __func__);
625 return false;
628 o = container_of(canim);
629 obj_setmode(container_of(canim), FBSPL_MODE_SILENT);
631 if (!skip_whitespace(&t, true))
632 goto pa_err;
634 canim->flags = 0;
636 if (!strncmp(t, "once", 4)) {
637 canim->flags |= F_ANIM_ONCE;
638 t += 4;
639 } else if (!strncmp(t, "loop", 4)) {
640 canim->flags |= F_ANIM_LOOP;
641 t += 4;
642 } else if (!strncmp(t, "proportional", 12)) {
643 canim->flags |= F_ANIM_PROPORTIONAL;
644 t += 12;
645 } else {
646 parse_error("an anim has to be flagged with one of the following:\n"
647 "'once', 'loop' or 'proportional' (got '%s' instead)", t);
648 goto pa_err;
651 if (!skip_whitespace(&t, true))
652 goto pa_err;
654 filename = t;
656 if (!skip_nonwhitespace(&t, true))
657 goto pa_err;
658 *t = '\0';
659 t++;
661 /* We don't require whitespace, as we already had one which
662 * we have just replaced with 0. */
663 skip_whitespace(&t, false);
665 canim->x = strtol(t, &p, 0);
666 checknskip(pa_err, true, "expected a number instead of '%s'", t);
668 canim->y = strtol(t, &p, 0);
669 checknskip(pa_err, true, "expected a number instead of '%s'", t);
671 /* Sanity checks */
672 if (canim->x >= tmptheme.xres)
673 canim->x = tmptheme.xres-1;
674 if (canim->y >= tmptheme.yres)
675 canim->y = tmptheme.yres-1;
677 canim->status = 0;
679 i = parse_svc_state(t, &canim->type);
680 if (!i) {
681 canim->type = e_display;
682 } else {
683 t += i;
685 /* Find the service name */
686 skip_whitespace(&t, true);
687 for (i = 0; t[i] != ' ' && t[i] != '\t' && t[i] != '\0'; i++);
688 t[i] = 0;
689 i = 0;
691 o->visible = false;
693 if (*t == '\0') {
694 parse_error("expected service name");
695 goto pa_err;
698 canim->svc = strdup(t);
699 if (!skip_nonwhitespace(&t, true))
700 goto pa_err;
703 if (!parse_effects(t, o))
704 goto pa_err;
706 canim->filename = get_filepath(filename);
707 list_add(&tmptheme.anims, canim);
708 obj_add(canim);
709 return true;
710 pa_err:
711 free(container_of(canim));
712 return false;
714 #endif /* WANT_MNG */
716 box* parse_box(char *t)
718 char *p;
719 int ret;
720 box *cbox = obj_alloc(box, FBSPL_MODE_VERBOSE);
722 if (!cbox)
723 return NULL;
725 if (!skip_whitespace(&t, true))
726 goto pb_err;
728 cbox->attr = 0;
729 cbox->curr = NULL;
731 while (!isdigit(*t)) {
732 if (!strncmp(t,"noover",6)) {
733 cbox->attr |= BOX_NOOVER;
734 t += 6;
735 } else if (!strncmp(t, "inter", 5)) {
736 cbox->attr |= BOX_INTER;
737 t += 5;
738 } else if (!strncmp(t, "silent", 6)) {
739 obj_setmode(container_of(cbox), FBSPL_MODE_SILENT);
740 t += 6;
741 } else {
742 parse_error("expected 'noover', 'inter' or 'silent' instead of '%s'", t);
743 goto pb_err;
746 skip_whitespace(&t, false);
749 cbox->re.x1 = strtol(t, &p, 0);
750 checknskip(pb_err, true, "expected a number instead of '%s'", t);
752 cbox->re.y1 = strtol(t, &p, 0);
753 checknskip(pb_err, true, "expected a number instead of '%s'", t);
755 cbox->re.x2 = strtol(t, &p, 0);
756 checknskip(pb_err, true, "expected a number instead of '%s'", t);
758 cbox->re.y2 = strtol(t, &p, 0);
759 checknskip(pb_err, true, "expected a number instead of '%s'", t);
761 /* Sanity checks */
762 if (cbox->re.x1 >= tmptheme.xres)
763 cbox->re.x1 = tmptheme.xres-1;
764 if (cbox->re.x2 >= tmptheme.xres)
765 cbox->re.x2 = tmptheme.xres-1;
766 if (cbox->re.y1 >= tmptheme.yres)
767 cbox->re.y1 = tmptheme.yres-1;
768 if (cbox->re.y2 >= tmptheme.yres)
769 cbox->re.y2 = tmptheme.yres-1;
771 if (cbox->re.x2 < cbox->re.x1) {
772 parse_error("x2 has to be larger or equal to x1");
773 goto pb_err;
776 if (cbox->re.y2 < cbox->re.y1) {
777 parse_error("y2 has to be larger or equal to y1");
778 goto pb_err;
781 #define zero_color(cl) *(u32*)(&cl) = 0;
782 #define is_zero_color(cl) (*(u32*)(&cl) == 0)
783 #define assign_color(c1, c2) *(u32*)(&c1) = *(u32*)(&c2);
785 zero_color(cbox->c_ul);
786 zero_color(cbox->c_ur);
787 zero_color(cbox->c_ll);
788 zero_color(cbox->c_lr);
790 if (parse_color(&t, &cbox->c_ul)) {
791 parse_error("expected a color instead of '%s'", t);
792 goto pb_err;
795 skip_whitespace(&t, false);
797 ret = parse_color(&t, &cbox->c_ur);
799 if (ret == -1) {
800 assign_color(cbox->c_ur, cbox->c_ul);
801 assign_color(cbox->c_lr, cbox->c_ul);
802 assign_color(cbox->c_ll, cbox->c_ul);
803 goto pb_end;
804 } else if (ret == -2) {
805 parse_error("failed to parse color");
806 goto pb_err;
809 if (!skip_whitespace(&t, true))
810 goto pb_err;
812 if (parse_color(&t, &cbox->c_ll)) {
813 parse_error("expected a color instead of '%s'", t);
814 goto pb_err;
817 if (!skip_whitespace(&t, true))
818 goto pb_err;
820 if (parse_color(&t, &cbox->c_lr)) {
821 parse_error("expected a color instead of '%s'", t);
822 goto pb_err;
825 pb_end:
826 if (!parse_effects(t, container_of(cbox)))
827 goto pb_err;
829 return cbox;
831 pb_err:
832 free(container_of(cbox));
833 return NULL;
836 static char *parse_quoted_string(char *t, u8 keepvar)
838 char *p, *out;
839 int cnt = 0;
840 int len = 0;
841 int i;
843 if (*t != '"')
844 return NULL;
846 t++;
847 p = t;
849 while ((*p != '"' || *(p-1) == '\\') && *p != 0) {
850 if (*p == '\\') {
851 if (!keepvar)
852 cnt++;
853 p++;
854 if (*p == 0)
855 break;
857 p++;
860 if (*p != '"')
861 return NULL;
863 len = p-t;
864 out = malloc(len - cnt + 1);
865 if (!out) {
866 iprint(MSG_ERROR, "Failed to allocate memory for a quoted string.\n");
867 return NULL;
870 for (i = 0; i < len; i++, t++) {
871 if (*t == '\\' && i < len-1) {
872 if (!keepvar) {
873 t++;
876 out[i] = t[0];
879 out[len-cnt] = 0;
880 return out;
883 #if WANT_TTF
884 bool parse_text(char *t)
886 char *p, *fontname = NULL, *fpath = NULL;
887 int ret, fontsize;
888 text *ct = obj_alloc(text, 0);
889 item *ti;
890 font_e *fe;
891 int mode = 0;
893 if (!ct) {
894 iprint(MSG_ERROR, "%s: failed to allocate memory\n", __func__);
895 return false;
898 if (!skip_whitespace(&t, true))
899 goto pt_err;
901 ct->log_last = -1;
902 ct->flags = 0;
903 ct->hotspot = 0;
904 ct->style = TTF_STYLE_NORMAL;
905 ret = 1;
907 while (!isdigit(*t) && ret) {
908 if (!strncmp(t, "silent", 6)) {
909 mode |= FBSPL_MODE_SILENT;
910 t += 6;
911 ret = 1;
912 } else if (!strncmp(t, "verbose", 7)) {
913 mode |= FBSPL_MODE_VERBOSE;
914 t += 7;
915 ret = 1;
916 } else {
917 ret = 0;
920 skip_whitespace(&t, false);
923 if (!mode)
924 mode = FBSPL_MODE_SILENT | FBSPL_MODE_VERBOSE;
926 obj_setmode(container_of(ct), mode);
928 if (!isdigit(*t)) {
929 p = t;
930 if (!skip_nonwhitespace(&p, true))
931 goto pt_err;
932 fontname = t;
933 *p = 0;
934 t = p+1;
937 /* We have already skipped at least one whitespace (replaced with 0)
938 * -- no need to enforce any more here. */
939 skip_whitespace(&t, false);
941 /* Parse the style selector */
942 while (!isdigit(*t)) {
943 if (*t == 'b') {
944 ct->style |= TTF_STYLE_BOLD;
945 } else if (*t == 'i') {
946 ct->style |= TTF_STYLE_ITALIC;
947 } else if (*t == 'u') {
948 ct->style |= TTF_STYLE_UNDERLINE;
949 } else if (*t != ' ' && *t != '\t') {
950 parse_error("expected a style specifier instead of '%s'", t);
951 goto pt_err;
953 t++;
956 skip_whitespace(&t, false);
958 /* Parse font size */
959 fontsize = strtol(t, &p, 0);
960 checknskip(pt_err, true, "expected font size (a number) instead of '%s'", t);
962 /* Parse x position */
963 ct->x = strtol(t,&p,0);
964 checknskip(pt_err, true, "expected x position (a number) instead of '%s'", t);
966 if (!isdigit(*t)) {
967 if (!strncmp(t, "left", 4)) {
968 ct->hotspot |= F_HS_LEFT;
969 t += 4;
970 } else if (!strncmp(t, "right", 5)) {
971 ct->hotspot |= F_HS_RIGHT;
972 t += 5;
973 } else if (!strncmp(t, "middle", 6)) {
974 ct->hotspot |= F_HS_HMIDDLE;
975 t += 6;
976 } else {
977 parse_error("expected 'left', 'right' or 'middle' instead of '%s'", t);
978 goto pt_err;
981 if (!skip_whitespace(&t, true))
982 goto pt_err;
983 } else {
984 ct->hotspot |= F_HS_LEFT;
987 /* Parse y position */
988 ct->y = strtol(t,&p,0);
989 checknskip(pt_err, true, "expected y position (a number) instead of '%s'", t);
991 if (!strncmp(t, "top", 3)) {
992 ct->hotspot |= F_HS_TOP;
993 t += 3;
994 } else if (!strncmp(t, "bottom", 6)) {
995 ct->hotspot |= F_HS_BOTTOM;
996 t += 6;
997 } else if (!strncmp(t, "middle", 6)) {
998 ct->hotspot |= F_HS_VMIDDLE;
999 t += 6;
1000 } else {
1001 ct->hotspot |= F_HS_TOP;
1004 skip_whitespace(&t, false);
1006 /* Sanity checks */
1007 if (ct->x >= tmptheme.xres) {
1008 parse_error("the x position is invalid (larger than x resolution)");
1009 goto pt_err;
1012 if (ct->x < 0)
1013 ct->x = 0;
1015 if (ct->y >= tmptheme.yres) {
1016 parse_error("the y position is invalid (larger than y resolution)");
1017 goto pt_err;
1020 if (ct->y < 0)
1021 ct->y = 0;
1023 zero_color(ct->col);
1025 if (parse_color(&t, &ct->col)) {
1026 parse_error("expected a color instead of '%s'", t);
1027 goto pt_err;
1030 ct->curr_progress = -1;
1031 again:
1032 if (!skip_whitespace(&t, true))
1033 goto pt_err;
1036 if (!strncmp(t, "exec", 4)) {
1037 ct->flags |= F_TXT_EXEC;
1038 t += 4;
1039 goto again;
1040 } else if (!strncmp(t, "eval", 4)) {
1041 ct->flags |= F_TXT_EVAL;
1042 t += 4;
1043 goto again;
1044 } else if (!strncmp(t, "msglog", 6)) {
1045 ct->flags |= F_TXT_MSGLOG;
1046 ct->val = NULL;
1047 t += 6;
1048 goto endparse;
1051 skip_whitespace(&t, false);
1053 ct->val = parse_quoted_string(t, (ct->flags & F_TXT_EVAL) ? 1 : 0);
1054 if (!ct->val) {
1055 parse_error("failed to parse a quoted string: '%s'", t);
1056 goto pt_err;
1059 if (strstr(ct->val, "$progress")) {
1060 ct->curr_progress = config.progress;
1063 endparse:
1064 if (!parse_effects(t, container_of(ct)))
1065 goto pt_err;
1067 if (!fontname)
1068 fontname = DEFAULT_FONT;
1070 fpath = get_fontpath(fontname);
1072 for (ti = tmptheme.fonts.head ; ti != NULL; ti = ti->next) {
1073 fe = (font_e*) ti->p;
1075 if (!strcmp(fe->file, fpath) && fe->size == fontsize) {
1076 ct->font = fe;
1077 goto pt_end;
1081 /* Allocate a new entry in the fonts list */
1082 fe = malloc(sizeof(font_e));
1083 if (!fe) {
1084 iprint(MSG_ERROR, "%s: failed to allocate memory\n", __func__);
1085 goto pt_err;
1087 fe->file = fpath;
1088 fe->size = fontsize;
1089 fe->font = NULL;
1091 list_add(&tmptheme.fonts, fe);
1092 ct->font = fe;
1094 pt_end:
1095 obj_add(ct);
1096 return true;
1098 pt_err:
1099 free(container_of(ct));
1100 if (fpath)
1101 free(fpath);
1102 return false;
1105 void add_main_msg()
1107 char *fpath;
1108 text *ct = obj_alloc(text, FBSPL_MODE_SILENT);
1109 item *ti;
1110 font_e *fe;
1112 if (!ct) {
1113 iprint(MSG_ERROR, "%s: failed to allocate memory\n", __func__);
1114 return;
1117 ct->hotspot = F_HS_LEFT | F_HS_TOP;
1118 ct->style = TTF_STYLE_NORMAL;
1119 ct->x = text_x;
1120 ct->y = text_y;
1121 ct->col = text_color;
1122 ct->val = strdup(config.message);
1123 ct->curr_progress = config.progress;
1124 ct->flags = F_TXT_EVAL;
1126 fpath = text_font;
1127 if (!fpath)
1128 return;
1130 for (ti = tmptheme.fonts.head ; ti != NULL; ti = ti->next) {
1131 fe = (font_e*) ti->p;
1133 if (!strcmp(fe->file, fpath) && fe->size == text_size) {
1134 ct->font = fe;
1135 goto mm_end;
1139 /* Allocate a new entry in the fonts list */
1140 fe = malloc(sizeof(font_e));
1141 if (!fe) {
1142 iprint(MSG_ERROR, "%s: failed to allocate memory\n", __func__);
1143 goto mm_err;
1145 fe->file = fpath;
1146 fe->size = text_size;
1147 fe->font = NULL;
1149 list_add(&tmptheme.fonts, fe);
1150 ct->font = fe;
1152 mm_end:
1153 obj_add(ct);
1154 return;
1156 mm_err:
1157 free(container_of(ct));
1158 return;
1163 #endif /* TTF */
1165 int parse_cfg(char *cfgfile, stheme_t *theme)
1167 FILE* cfgfp;
1168 char buf[1024];
1169 char *t;
1170 int len, i;
1171 bool ignore = false;
1172 box *bprev = NULL;
1174 if ((cfgfp = fopen(cfgfile,"r")) == NULL) {
1175 iprint(MSG_ERROR, "Can't open cfg file %s.\n", cfgfile);
1176 return 1;
1179 /* Save the path of the file that is currently being parsed, so that
1180 * it can be used when printing error messages. */
1181 curr_cfgfile = cfgfile;
1183 memcpy(&tmptheme, theme, sizeof(tmptheme));
1185 while (fgets(buf, sizeof(buf), cfgfp)) {
1187 line++;
1189 len = strlen(buf);
1191 if (len == 0 || len == sizeof(buf)-1)
1192 continue;
1194 buf[len-1] = 0; /* get rid of \n */
1196 t = buf;
1197 skip_whitespace(&t, false);
1199 /* skip comments */
1200 if (*t == '#')
1201 continue;
1203 for (i = 0; i < sizeof(opts) / sizeof(struct cfg_opt); i++)
1205 if (!strncmp(opts[i].name, t, strlen(opts[i].name))) {
1207 if (ignore && opts[i].type != t_type_close)
1208 continue;
1210 t += strlen(opts[i].name);
1212 switch (opts[i].type) {
1214 case t_path:
1215 skip_whitespace(&t, false);
1216 parse_path(t, opts[i]);
1217 break;
1219 case t_fontpath:
1220 skip_whitespace(&t, false);
1221 parse_fontpath(t, opts[i]);
1222 break;
1224 case t_color:
1226 if (*t != '=') {
1227 parse_error("expected '=' instead of '%c'", *t);
1228 break;
1231 t++;
1232 skip_whitespace(&t, false);
1233 parse_color(&t, opts[i].val);
1234 break;
1237 case t_int:
1238 skip_whitespace(&t, false);
1239 parse_int(t, opts[i]);
1240 break;
1242 case t_box:
1244 box *tbox = parse_box(t);
1245 if (!tbox)
1246 break;
1248 if (tbox->attr & BOX_INTER) {
1249 bprev = tbox;
1250 goto box_post;
1251 } else if (bprev != NULL) {
1252 bprev->inter = tbox;
1253 bprev->curr = malloc(sizeof(box));
1254 box_interpolate(bprev, tbox, bprev->curr);
1255 if (!bprev->curr) {
1256 free(container_of(tbox));
1257 free(container_of(bprev));
1258 iprint(MSG_ERROR, "Failed to allocate cache for an interpolated box.\n");
1259 } else {
1260 obj_add(bprev);
1262 if (!memcmp(&bprev->c_ul, &tbox->c_ul, sizeof(color)) &&
1263 !memcmp(&bprev->c_ll, &tbox->c_ll, sizeof(color)) &&
1264 !memcmp(&bprev->c_ur, &tbox->c_ur, sizeof(color)) &&
1265 !memcmp(&bprev->c_lr, &tbox->c_lr, sizeof(color)))
1267 if (!memcmp(&bprev->c_ul, &bprev->c_ur, sizeof(color))) {
1268 if (!memcmp(&bprev->c_ll, &bprev->c_lr, sizeof(color))) {
1269 if (!memcmp(&bprev->c_ll, &bprev->c_ul, sizeof(color)))
1270 bprev->attr |= BOX_SOLID;
1271 else
1272 bprev->attr |= BOX_VGRAD;
1274 } else if (!memcmp(&bprev->c_ul, &bprev->c_ll, sizeof(color)) &&
1275 !memcmp(&bprev->c_ur, &bprev->c_lr, sizeof(color))) {
1276 bprev->attr |= BOX_HGRAD;
1280 bprev = NULL;
1281 /* Non-interpolated box */
1282 } else {
1283 if (!memcmp(&tbox->c_ul, &tbox->c_ur, sizeof(color)) &&
1284 !memcmp(&tbox->c_ll, &tbox->c_lr, sizeof(color)) &&
1285 !memcmp(&tbox->c_ll, &tbox->c_ul, sizeof(color))) {
1286 tbox->attr |= BOX_SOLID;
1288 obj_add(tbox);
1290 break;
1293 case t_icon:
1294 parse_icon(t);
1295 break;
1297 case t_rect:
1298 parse_rect(t);
1299 break;
1301 case t_type_open:
1302 ignore = true;
1303 while (*t != '>') {
1304 skip_whitespace(&t, true);
1305 if (!strncmp(t, "bootup", 6)) {
1306 if (config.type == fbspl_bootup)
1307 ignore = false;
1308 t += 6;
1309 } else if (!strncmp(t, "reboot", 6)) {
1310 if (config.type == fbspl_reboot)
1311 ignore = false;
1312 t += 6;
1313 } else if (!strncmp(t, "shutdown", 8)) {
1314 if (config.type == fbspl_shutdown)
1315 ignore = false;
1316 t += 8;
1317 } else if (!strncmp(t, "suspend", 7)) {
1318 if (config.type == fbspl_suspend)
1319 ignore = false;
1320 t += 7;
1321 } else if (!strncmp(t, "resume", 6)) {
1322 if (config.type == fbspl_resume)
1323 ignore = false;
1324 t += 6;
1325 } else if (!strncmp(t, "other", 5)) {
1326 if (config.type == fbspl_undef)
1327 ignore = false;
1328 t += 5;
1329 } else {
1330 parse_error("expected 'other', 'bootup', 'reboot' or 'shutdown' instead of '%s'", t);
1331 break;
1334 break;
1336 case t_type_close:
1337 ignore = false;
1338 break;
1340 case t_textbox_open:
1341 is_textbox = true;
1342 break;
1344 case t_textbox_close:
1345 is_textbox = false;
1346 break;
1348 #if WANT_MNG
1349 case t_anim:
1350 parse_anim(t);
1351 break;
1352 #endif
1353 #if WANT_TTF
1354 case t_text:
1355 parse_text(t);
1356 break;
1357 #endif
1362 if (bprev) {
1363 parse_error("an 'inter' box must be directly followed by another box");
1364 free(container_of(bprev));
1365 bprev = NULL;
1368 box_post:
1372 if (is_textbox)
1373 parse_error("unclosed <textbox> section");
1375 #if WANT_TTF
1376 add_main_msg();
1377 #endif
1378 memcpy(theme, &tmptheme, sizeof(tmptheme));
1380 fclose(cfgfp);
1381 return 0;