]>
Commit | Line | Data |
---|---|---|
d920a32a CB |
1 | /* theme_loader.c - Theme file loader for gfxmenu. */ |
2 | /* | |
3 | * GRUB -- GRand Unified Bootloader | |
4 | * Copyright (C) 2008 Free Software Foundation, Inc. | |
5 | * | |
6 | * GRUB is free software: you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License as published by | |
8 | * the Free Software Foundation, either version 3 of the License, or | |
9 | * (at your option) any later version. | |
10 | * | |
11 | * GRUB is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
17 | * along with GRUB. If not, see <http://www.gnu.org/licenses/>. | |
18 | */ | |
19 | ||
20 | #include <grub/types.h> | |
21 | #include <grub/file.h> | |
22 | #include <grub/misc.h> | |
23 | #include <grub/mm.h> | |
24 | #include <grub/err.h> | |
25 | #include <grub/dl.h> | |
26 | #include <grub/video.h> | |
27 | #include <grub/gui_string_util.h> | |
28 | #include <grub/bitmap.h> | |
29 | #include <grub/bitmap_scale.h> | |
30 | #include <grub/gfxwidgets.h> | |
31 | #include <grub/gfxmenu_view.h> | |
32 | #include <grub/gui.h> | |
33 | ||
34 | /* Construct a new box widget using ABSPATTERN to find the pixmap files for | |
35 | it, storing the new box instance at *BOXPTR. | |
36 | PATTERN should be of the form: "(hd0,0)/somewhere/style*.png". | |
37 | The '*' then gets substituted with the various pixmap names that the | |
38 | box uses. */ | |
39 | static grub_err_t | |
40 | recreate_box_absolute (grub_gfxmenu_box_t *boxptr, const char *abspattern) | |
41 | { | |
42 | char *prefix; | |
43 | char *suffix; | |
44 | char *star; | |
45 | grub_gfxmenu_box_t box; | |
46 | ||
47 | star = grub_strchr (abspattern, '*'); | |
48 | if (! star) | |
49 | return grub_error (GRUB_ERR_BAD_ARGUMENT, | |
50 | "missing `*' in box pixmap pattern `%s'", abspattern); | |
51 | ||
52 | /* Prefix: Get the part before the '*'. */ | |
53 | prefix = grub_malloc (star - abspattern + 1); | |
54 | if (! prefix) | |
55 | return grub_errno; | |
56 | ||
57 | grub_memcpy (prefix, abspattern, star - abspattern); | |
58 | prefix[star - abspattern] = '\0'; | |
59 | ||
60 | /* Suffix: Everything after the '*' is the suffix. */ | |
61 | suffix = star + 1; | |
62 | ||
63 | box = grub_gfxmenu_create_box (prefix, suffix); | |
64 | grub_free (prefix); | |
65 | if (! box) | |
66 | return grub_errno; | |
67 | ||
68 | if (*boxptr) | |
69 | (*boxptr)->destroy (*boxptr); | |
70 | *boxptr = box; | |
71 | return grub_errno; | |
72 | } | |
73 | ||
74 | ||
75 | /* Construct a new box widget using PATTERN to find the pixmap files for it, | |
76 | storing the new widget at *BOXPTR. PATTERN should be of the form: | |
77 | "somewhere/style*.png". The '*' then gets substituted with the various | |
78 | pixmap names that the widget uses. | |
79 | ||
80 | Important! The value of *BOXPTR must be initialized! It must either | |
81 | (1) Be 0 (a NULL pointer), or | |
82 | (2) Be a pointer to a valid 'grub_gfxmenu_box_t' instance. | |
83 | In this case, the previous instance is destroyed. */ | |
84 | grub_err_t | |
85 | grub_gui_recreate_box (grub_gfxmenu_box_t *boxptr, | |
86 | const char *pattern, const char *theme_dir) | |
87 | { | |
88 | char *abspattern; | |
89 | ||
90 | /* Check arguments. */ | |
91 | if (! pattern) | |
92 | { | |
93 | /* If no pixmap pattern is given, then just create an empty box. */ | |
94 | if (*boxptr) | |
95 | (*boxptr)->destroy (*boxptr); | |
96 | *boxptr = grub_gfxmenu_create_box (0, 0); | |
97 | return grub_errno; | |
98 | } | |
99 | ||
100 | if (! theme_dir) | |
101 | return grub_error (GRUB_ERR_BAD_ARGUMENT, | |
102 | "styled box missing theme directory"); | |
103 | ||
104 | /* Resolve to an absolute path. */ | |
105 | abspattern = grub_resolve_relative_path (theme_dir, pattern); | |
106 | if (! abspattern) | |
107 | return grub_errno; | |
108 | ||
109 | /* Create the box. */ | |
110 | recreate_box_absolute (boxptr, abspattern); | |
111 | grub_free (abspattern); | |
112 | return grub_errno; | |
113 | } | |
114 | ||
115 | /* Set the specified property NAME on the view to the given string VALUE. | |
116 | The caller is responsible for the lifetimes of NAME and VALUE. */ | |
117 | static grub_err_t | |
118 | theme_set_string (grub_gfxmenu_view_t view, | |
119 | const char *name, | |
120 | const char *value, | |
121 | const char *theme_dir, | |
122 | const char *filename, | |
123 | int line_num, | |
124 | int col_num) | |
125 | { | |
126 | if (! grub_strcmp ("title-font", name)) | |
127 | view->title_font = grub_font_get (value); | |
128 | else if (! grub_strcmp ("message-font", name)) | |
129 | view->message_font = grub_font_get (value); | |
130 | else if (! grub_strcmp ("terminal-font", name)) | |
131 | { | |
132 | grub_free (view->terminal_font_name); | |
133 | view->terminal_font_name = grub_strdup (value); | |
134 | if (! view->terminal_font_name) | |
135 | return grub_errno; | |
136 | } | |
137 | else if (! grub_strcmp ("title-color", name)) | |
138 | grub_gui_parse_color (value, &view->title_color); | |
139 | else if (! grub_strcmp ("message-color", name)) | |
140 | grub_gui_parse_color (value, &view->message_color); | |
141 | else if (! grub_strcmp ("message-bg-color", name)) | |
142 | grub_gui_parse_color (value, &view->message_bg_color); | |
143 | else if (! grub_strcmp ("desktop-image", name)) | |
144 | { | |
145 | struct grub_video_bitmap *raw_bitmap; | |
146 | struct grub_video_bitmap *scaled_bitmap; | |
147 | char *path; | |
148 | path = grub_resolve_relative_path (theme_dir, value); | |
149 | if (! path) | |
150 | return grub_errno; | |
151 | if (grub_video_bitmap_load (&raw_bitmap, path) != GRUB_ERR_NONE) | |
152 | { | |
153 | grub_free (path); | |
154 | return grub_errno; | |
155 | } | |
156 | grub_free(path); | |
157 | grub_video_bitmap_create_scaled (&scaled_bitmap, | |
158 | view->screen.width, | |
159 | view->screen.height, | |
160 | raw_bitmap, | |
161 | GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST); | |
162 | grub_video_bitmap_destroy (raw_bitmap); | |
163 | if (! scaled_bitmap) | |
164 | { | |
165 | grub_error_push (); | |
166 | return grub_error (grub_errno, "error scaling desktop image"); | |
167 | } | |
168 | ||
169 | grub_video_bitmap_destroy (view->desktop_image); | |
170 | view->desktop_image = scaled_bitmap; | |
171 | } | |
172 | else if (! grub_strcmp ("desktop-color", name)) | |
173 | grub_gui_parse_color (value, &view->desktop_color); | |
174 | else if (! grub_strcmp ("terminal-box", name)) | |
175 | { | |
176 | grub_err_t err; | |
177 | err = grub_gui_recreate_box (&view->terminal_box, value, theme_dir); | |
178 | if (err != GRUB_ERR_NONE) | |
179 | return err; | |
180 | } | |
181 | else if (! grub_strcmp ("title-text", name)) | |
182 | { | |
183 | grub_free (view->title_text); | |
184 | view->title_text = grub_strdup (value); | |
185 | if (! view->title_text) | |
186 | return grub_errno; | |
187 | } | |
188 | else | |
189 | { | |
190 | return grub_error (GRUB_ERR_BAD_ARGUMENT, | |
191 | "%s:%d:%d unknown property `%s'", | |
192 | filename, line_num, col_num, name); | |
193 | } | |
194 | return grub_errno; | |
195 | } | |
196 | ||
197 | struct parsebuf | |
198 | { | |
199 | char *buf; | |
200 | int pos; | |
201 | int len; | |
202 | int line_num; | |
203 | int col_num; | |
204 | const char *filename; | |
205 | char *theme_dir; | |
206 | grub_gfxmenu_view_t view; | |
207 | }; | |
208 | ||
209 | static int | |
210 | has_more (struct parsebuf *p) | |
211 | { | |
212 | return p->pos < p->len; | |
213 | } | |
214 | ||
215 | static int | |
216 | read_char (struct parsebuf *p) | |
217 | { | |
218 | if (has_more (p)) | |
219 | { | |
220 | char c; | |
221 | c = p->buf[p->pos++]; | |
222 | if (c == '\n') | |
223 | { | |
224 | p->line_num++; | |
225 | p->col_num = 1; | |
226 | } | |
227 | else | |
228 | { | |
229 | p->col_num++; | |
230 | } | |
231 | return c; | |
232 | } | |
233 | else | |
234 | return -1; | |
235 | } | |
236 | ||
237 | static int | |
238 | peek_char (struct parsebuf *p) | |
239 | { | |
240 | if (has_more (p)) | |
241 | return p->buf[p->pos]; | |
242 | else | |
243 | return -1; | |
244 | } | |
245 | ||
246 | static int | |
247 | is_whitespace (char c) | |
248 | { | |
249 | return (c == ' ' | |
250 | || c == '\t' | |
251 | || c == '\r' | |
252 | || c == '\n' | |
253 | || c == '\f'); | |
254 | } | |
255 | ||
256 | static void | |
257 | skip_whitespace (struct parsebuf *p) | |
258 | { | |
259 | while (has_more (p) && is_whitespace(peek_char (p))) | |
260 | read_char (p); | |
261 | } | |
262 | ||
263 | static void | |
264 | advance_to_next_line (struct parsebuf *p) | |
265 | { | |
266 | int c; | |
267 | ||
268 | /* Eat characters up to the newline. */ | |
269 | do | |
270 | { | |
271 | c = read_char (p); | |
272 | } | |
273 | while (c != -1 && c != '\n'); | |
274 | } | |
275 | ||
276 | static int | |
277 | is_identifier_char (int c) | |
278 | { | |
279 | return (c != -1 | |
280 | && (grub_isalpha(c) | |
281 | || grub_isdigit(c) | |
282 | || c == '_' | |
283 | || c == '-')); | |
284 | } | |
285 | ||
286 | static char * | |
287 | read_identifier (struct parsebuf *p) | |
288 | { | |
289 | /* Index of the first character of the identifier in p->buf. */ | |
290 | int start; | |
291 | /* Next index after the last character of the identifer in p->buf. */ | |
292 | int end; | |
293 | ||
294 | skip_whitespace (p); | |
295 | ||
296 | /* Capture the start of the identifier. */ | |
297 | start = p->pos; | |
298 | ||
299 | /* Scan for the end. */ | |
300 | while (is_identifier_char (peek_char (p))) | |
301 | read_char (p); | |
302 | end = p->pos; | |
303 | ||
304 | if (end - start < 1) | |
305 | return 0; | |
306 | ||
307 | return grub_new_substring (p->buf, start, end); | |
308 | } | |
309 | ||
310 | static char * | |
311 | read_expression (struct parsebuf *p) | |
312 | { | |
313 | int start; | |
314 | int end; | |
315 | ||
316 | skip_whitespace (p); | |
317 | if (peek_char (p) == '"') | |
318 | { | |
319 | /* Read as a quoted string. | |
320 | The quotation marks are not included in the expression value. */ | |
321 | /* Skip opening quotation mark. */ | |
322 | read_char (p); | |
323 | start = p->pos; | |
324 | while (has_more (p) && peek_char (p) != '"') | |
325 | read_char (p); | |
326 | end = p->pos; | |
327 | /* Skip the terminating quotation mark. */ | |
328 | read_char (p); | |
329 | } | |
330 | else if (peek_char (p) == '(') | |
331 | { | |
332 | /* Read as a parenthesized string -- for tuples/coordinates. */ | |
333 | /* The parentheses are included in the expression value. */ | |
334 | int c; | |
335 | ||
336 | start = p->pos; | |
337 | do | |
338 | { | |
339 | c = read_char (p); | |
340 | } | |
341 | while (c != -1 && c != ')'); | |
342 | end = p->pos; | |
343 | } | |
344 | else if (has_more (p)) | |
345 | { | |
346 | /* Read as a single word -- for numeric values or words without | |
347 | whitespace. */ | |
348 | start = p->pos; | |
349 | while (has_more (p) && ! is_whitespace (peek_char (p))) | |
350 | read_char (p); | |
351 | end = p->pos; | |
352 | } | |
353 | else | |
354 | { | |
355 | /* The end of the theme file has been reached. */ | |
356 | grub_error (GRUB_ERR_IO, "%s:%d:%d expression expected in theme file", | |
357 | p->filename, p->line_num, p->col_num); | |
358 | return 0; | |
359 | } | |
360 | ||
361 | return grub_new_substring (p->buf, start, end); | |
362 | } | |
363 | ||
b9da1700 VS |
364 | static grub_err_t |
365 | parse_proportional_spec (char *value, signed *abs, grub_fixed_signed_t *prop) | |
366 | { | |
367 | signed num; | |
368 | char *ptr; | |
369 | int sig = 0; | |
370 | *abs = 0; | |
371 | *prop = 0; | |
372 | ptr = value; | |
373 | while (*ptr) | |
374 | { | |
375 | sig = 0; | |
376 | ||
377 | while (*ptr == '-' || *ptr == '+') | |
378 | { | |
379 | if (*ptr == '-') | |
380 | sig = !sig; | |
381 | ptr++; | |
382 | } | |
383 | ||
384 | num = grub_strtoul (ptr, &ptr, 0); | |
385 | if (grub_errno) | |
386 | return grub_errno; | |
387 | if (sig) | |
388 | num = -num; | |
389 | if (*ptr == '%') | |
6812d2e7 VS |
390 | { |
391 | *prop += grub_fixed_fsf_divide (grub_signed_to_fixed (num), 100); | |
392 | ptr++; | |
393 | } | |
b9da1700 VS |
394 | else |
395 | *abs += num; | |
396 | } | |
397 | return GRUB_ERR_NONE; | |
398 | } | |
399 | ||
400 | ||
d920a32a CB |
401 | /* Read a GUI object specification from the theme file. |
402 | Any components created will be added to the GUI container PARENT. */ | |
403 | static grub_err_t | |
404 | read_object (struct parsebuf *p, grub_gui_container_t parent) | |
405 | { | |
406 | grub_video_rect_t bounds; | |
407 | ||
408 | char *name; | |
409 | name = read_identifier (p); | |
410 | if (! name) | |
411 | goto cleanup; | |
412 | ||
413 | grub_gui_component_t component = 0; | |
414 | if (grub_strcmp (name, "label") == 0) | |
415 | { | |
416 | component = grub_gui_label_new (); | |
417 | } | |
418 | else if (grub_strcmp (name, "image") == 0) | |
419 | { | |
420 | component = grub_gui_image_new (); | |
421 | } | |
422 | else if (grub_strcmp (name, "vbox") == 0) | |
423 | { | |
424 | component = (grub_gui_component_t) grub_gui_vbox_new (); | |
425 | } | |
426 | else if (grub_strcmp (name, "hbox") == 0) | |
427 | { | |
428 | component = (grub_gui_component_t) grub_gui_hbox_new (); | |
429 | } | |
430 | else if (grub_strcmp (name, "canvas") == 0) | |
431 | { | |
432 | component = (grub_gui_component_t) grub_gui_canvas_new (); | |
433 | } | |
434 | else if (grub_strcmp (name, "progress_bar") == 0) | |
435 | { | |
436 | component = grub_gui_progress_bar_new (); | |
437 | } | |
438 | else if (grub_strcmp (name, "circular_progress") == 0) | |
439 | { | |
440 | component = grub_gui_circular_progress_new (); | |
441 | } | |
442 | else if (grub_strcmp (name, "boot_menu") == 0) | |
443 | { | |
444 | component = grub_gui_list_new (); | |
445 | } | |
446 | else | |
447 | { | |
448 | /* Unknown type. */ | |
449 | grub_error (GRUB_ERR_IO, "%s:%d:%d unknown object type `%s'", | |
450 | p->filename, p->line_num, p->col_num, name); | |
451 | goto cleanup; | |
452 | } | |
453 | ||
454 | if (! component) | |
455 | goto cleanup; | |
456 | ||
457 | /* Inform the component about the theme so it can find its resources. */ | |
458 | component->ops->set_property (component, "theme_dir", p->theme_dir); | |
459 | component->ops->set_property (component, "theme_path", p->filename); | |
460 | ||
461 | /* Add the component as a child of PARENT. */ | |
462 | bounds.x = 0; | |
463 | bounds.y = 0; | |
464 | bounds.width = -1; | |
465 | bounds.height = -1; | |
466 | component->ops->set_bounds (component, &bounds); | |
467 | parent->ops->add (parent, component); | |
468 | ||
469 | skip_whitespace (p); | |
470 | if (read_char (p) != '{') | |
471 | { | |
472 | grub_error (GRUB_ERR_IO, | |
473 | "%s:%d:%d expected `{' after object type name `%s'", | |
474 | p->filename, p->line_num, p->col_num, name); | |
475 | goto cleanup; | |
476 | } | |
477 | ||
478 | while (has_more (p)) | |
479 | { | |
480 | skip_whitespace (p); | |
481 | ||
482 | /* Check whether the end has been encountered. */ | |
483 | if (peek_char (p) == '}') | |
484 | { | |
485 | /* Skip the closing brace. */ | |
486 | read_char (p); | |
487 | break; | |
488 | } | |
489 | ||
490 | if (peek_char (p) == '#') | |
491 | { | |
492 | /* Skip comments. */ | |
493 | advance_to_next_line (p); | |
494 | continue; | |
495 | } | |
496 | ||
497 | if (peek_char (p) == '+') | |
498 | { | |
499 | /* Skip the '+'. */ | |
500 | read_char (p); | |
501 | ||
502 | /* Check whether this component is a container. */ | |
503 | if (component->ops->is_instance (component, "container")) | |
504 | { | |
505 | /* Read the sub-object recursively and add it as a child. */ | |
506 | if (read_object (p, (grub_gui_container_t) component) != 0) | |
507 | goto cleanup; | |
508 | /* After reading the sub-object, resume parsing, expecting | |
509 | another property assignment or sub-object definition. */ | |
510 | continue; | |
511 | } | |
512 | else | |
513 | { | |
514 | grub_error (GRUB_ERR_IO, | |
515 | "%s:%d:%d attempted to add object to non-container", | |
516 | p->filename, p->line_num, p->col_num); | |
517 | goto cleanup; | |
518 | } | |
519 | } | |
520 | ||
521 | char *property; | |
522 | property = read_identifier (p); | |
523 | if (! property) | |
524 | { | |
525 | grub_error (GRUB_ERR_IO, "%s:%d:%d identifier expected in theme file", | |
526 | p->filename, p->line_num, p->col_num); | |
527 | goto cleanup; | |
528 | } | |
529 | ||
530 | skip_whitespace (p); | |
531 | if (read_char (p) != '=') | |
532 | { | |
533 | grub_error (GRUB_ERR_IO, | |
534 | "%s:%d:%d expected `=' after property name `%s'", | |
535 | p->filename, p->line_num, p->col_num, property); | |
536 | grub_free (property); | |
537 | goto cleanup; | |
538 | } | |
539 | skip_whitespace (p); | |
540 | ||
541 | char *value; | |
542 | value = read_expression (p); | |
543 | if (! value) | |
544 | { | |
545 | grub_free (property); | |
546 | goto cleanup; | |
547 | } | |
548 | ||
549 | /* Handle the property value. */ | |
9a175884 | 550 | if (grub_strcmp (property, "left") == 0) |
b9da1700 | 551 | parse_proportional_spec (value, &component->x, &component->xfrac); |
9a175884 | 552 | else if (grub_strcmp (property, "top") == 0) |
b9da1700 | 553 | parse_proportional_spec (value, &component->y, &component->yfrac); |
9a175884 | 554 | else if (grub_strcmp (property, "width") == 0) |
b9da1700 | 555 | parse_proportional_spec (value, &component->w, &component->wfrac); |
9a175884 | 556 | else if (grub_strcmp (property, "height") == 0) |
b9da1700 | 557 | parse_proportional_spec (value, &component->h, &component->hfrac); |
d920a32a | 558 | else |
b9da1700 VS |
559 | /* General property handling. */ |
560 | component->ops->set_property (component, property, value); | |
d920a32a CB |
561 | |
562 | grub_free (value); | |
563 | grub_free (property); | |
564 | if (grub_errno != GRUB_ERR_NONE) | |
565 | goto cleanup; | |
566 | } | |
567 | ||
d920a32a CB |
568 | cleanup: |
569 | grub_free (name); | |
570 | return grub_errno; | |
571 | } | |
572 | ||
573 | static grub_err_t | |
574 | read_property (struct parsebuf *p) | |
575 | { | |
576 | char *name; | |
577 | ||
578 | /* Read the property name. */ | |
579 | name = read_identifier (p); | |
580 | if (! name) | |
581 | { | |
582 | advance_to_next_line (p); | |
583 | return grub_errno; | |
584 | } | |
585 | ||
586 | /* Skip whitespace before separator. */ | |
587 | skip_whitespace (p); | |
588 | ||
589 | /* Read separator. */ | |
590 | if (read_char (p) != ':') | |
591 | { | |
592 | grub_error (GRUB_ERR_IO, | |
593 | "%s:%d:%d missing separator after property name `%s'", | |
594 | p->filename, p->line_num, p->col_num, name); | |
595 | goto done; | |
596 | } | |
597 | ||
598 | /* Skip whitespace after separator. */ | |
599 | skip_whitespace (p); | |
600 | ||
601 | /* Get the value based on its type. */ | |
602 | if (peek_char (p) == '"') | |
603 | { | |
604 | /* String value (e.g., '"My string"'). */ | |
605 | char *value = read_expression (p); | |
606 | if (! value) | |
607 | { | |
608 | grub_error (GRUB_ERR_IO, "%s:%d:%d missing property value", | |
609 | p->filename, p->line_num, p->col_num); | |
610 | goto done; | |
611 | } | |
612 | /* If theme_set_string results in an error, grub_errno will be returned | |
613 | below. */ | |
614 | theme_set_string (p->view, name, value, p->theme_dir, | |
615 | p->filename, p->line_num, p->col_num); | |
616 | grub_free (value); | |
617 | } | |
618 | else | |
619 | { | |
620 | grub_error (GRUB_ERR_IO, | |
621 | "%s:%d:%d property value invalid; " | |
622 | "enclose literal values in quotes (\")", | |
623 | p->filename, p->line_num, p->col_num); | |
624 | goto done; | |
625 | } | |
626 | ||
627 | done: | |
628 | grub_free (name); | |
629 | return grub_errno; | |
630 | } | |
631 | ||
632 | /* Set properties on the view based on settings from the specified | |
633 | theme file. */ | |
634 | grub_err_t | |
635 | grub_gfxmenu_view_load_theme (grub_gfxmenu_view_t view, const char *theme_path) | |
636 | { | |
637 | grub_file_t file; | |
638 | struct parsebuf p; | |
639 | ||
640 | p.view = view; | |
641 | p.theme_dir = grub_get_dirname (theme_path); | |
642 | ||
643 | file = grub_file_open (theme_path); | |
644 | if (! file) | |
645 | { | |
646 | grub_free (p.theme_dir); | |
647 | return grub_errno; | |
648 | } | |
649 | ||
650 | p.len = grub_file_size (file); | |
651 | p.buf = grub_malloc (p.len); | |
652 | p.pos = 0; | |
653 | p.line_num = 1; | |
654 | p.col_num = 1; | |
655 | p.filename = theme_path; | |
656 | if (! p.buf) | |
657 | { | |
658 | grub_file_close (file); | |
659 | grub_free (p.theme_dir); | |
660 | return grub_errno; | |
661 | } | |
662 | if (grub_file_read (file, p.buf, p.len) != p.len) | |
663 | { | |
664 | grub_free (p.buf); | |
665 | grub_file_close (file); | |
666 | grub_free (p.theme_dir); | |
667 | return grub_errno; | |
668 | } | |
669 | ||
670 | if (view->canvas) | |
9a175884 | 671 | view->canvas->component.ops->destroy (view->canvas); |
d920a32a CB |
672 | |
673 | view->canvas = grub_gui_canvas_new (); | |
674 | ((grub_gui_component_t) view->canvas) | |
675 | ->ops->set_bounds ((grub_gui_component_t) view->canvas, | |
676 | &view->screen); | |
677 | ||
678 | while (has_more (&p)) | |
679 | { | |
680 | /* Skip comments (lines beginning with #). */ | |
681 | if (peek_char (&p) == '#') | |
682 | { | |
683 | advance_to_next_line (&p); | |
684 | continue; | |
685 | } | |
686 | ||
687 | /* Find the first non-whitespace character. */ | |
688 | skip_whitespace (&p); | |
689 | ||
690 | /* Handle the content. */ | |
691 | if (peek_char (&p) == '+') | |
692 | { | |
693 | /* Skip the '+'. */ | |
694 | read_char (&p); | |
695 | read_object (&p, view->canvas); | |
696 | } | |
697 | else | |
698 | { | |
699 | read_property (&p); | |
700 | } | |
701 | ||
702 | if (grub_errno != GRUB_ERR_NONE) | |
703 | goto fail; | |
704 | } | |
705 | ||
706 | /* Set the new theme path. */ | |
707 | grub_free (view->theme_path); | |
708 | view->theme_path = grub_strdup (theme_path); | |
709 | goto cleanup; | |
710 | ||
711 | fail: | |
712 | if (view->canvas) | |
713 | { | |
9a175884 | 714 | view->canvas->component.ops->destroy (view->canvas); |
d920a32a CB |
715 | view->canvas = 0; |
716 | } | |
717 | ||
718 | cleanup: | |
719 | grub_free (p.buf); | |
720 | grub_file_close (file); | |
721 | grub_free (p.theme_dir); | |
722 | return grub_errno; | |
723 | } |