]> glassweightruler.freedombox.rocks Git - Ventoy.git/blob - GRUB2/MOD_SRC/grub-2.04/grub-core/gfxmenu/theme_loader.c
cc905ced5b140f1f2c9cd1b9f10fe410c8876639
[Ventoy.git] / GRUB2 / MOD_SRC / grub-2.04 / grub-core / gfxmenu / theme_loader.c
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 #include <grub/color.h>
34 #include <grub/env.h>
35
36 static grub_err_t
37 parse_proportional_spec (const char *value, signed *abs, grub_fixed_signed_t *prop);
38
39 /* Construct a new box widget using ABSPATTERN to find the pixmap files for
40 it, storing the new box instance at *BOXPTR.
41 PATTERN should be of the form: "(hd0,0)/somewhere/style*.png".
42 The '*' then gets substituted with the various pixmap names that the
43 box uses. */
44 static grub_err_t
45 recreate_box_absolute (grub_gfxmenu_box_t *boxptr, const char *abspattern)
46 {
47 char *prefix;
48 char *suffix;
49 char *star;
50 grub_gfxmenu_box_t box;
51
52 star = grub_strchr (abspattern, '*');
53 if (! star)
54 return grub_error (GRUB_ERR_BAD_ARGUMENT,
55 "missing `*' in box pixmap pattern `%s'", abspattern);
56
57 /* Prefix: Get the part before the '*'. */
58 prefix = grub_malloc (star - abspattern + 1);
59 if (! prefix)
60 return grub_errno;
61
62 grub_memcpy (prefix, abspattern, star - abspattern);
63 prefix[star - abspattern] = '\0';
64
65 /* Suffix: Everything after the '*' is the suffix. */
66 suffix = star + 1;
67
68 box = grub_gfxmenu_create_box (prefix, suffix);
69 grub_free (prefix);
70 if (! box)
71 return grub_errno;
72
73 if (*boxptr)
74 (*boxptr)->destroy (*boxptr);
75 *boxptr = box;
76 return grub_errno;
77 }
78
79
80 /* Construct a new box widget using PATTERN to find the pixmap files for it,
81 storing the new widget at *BOXPTR. PATTERN should be of the form:
82 "somewhere/style*.png". The '*' then gets substituted with the various
83 pixmap names that the widget uses.
84
85 Important! The value of *BOXPTR must be initialized! It must either
86 (1) Be 0 (a NULL pointer), or
87 (2) Be a pointer to a valid 'grub_gfxmenu_box_t' instance.
88 In this case, the previous instance is destroyed. */
89 grub_err_t
90 grub_gui_recreate_box (grub_gfxmenu_box_t *boxptr,
91 const char *pattern, const char *theme_dir)
92 {
93 char *abspattern;
94
95 /* Check arguments. */
96 if (! pattern)
97 {
98 /* If no pixmap pattern is given, then just create an empty box. */
99 if (*boxptr)
100 (*boxptr)->destroy (*boxptr);
101 *boxptr = grub_gfxmenu_create_box (0, 0);
102 return grub_errno;
103 }
104
105 if (! theme_dir)
106 return grub_error (GRUB_ERR_BAD_ARGUMENT,
107 "styled box missing theme directory");
108
109 /* Resolve to an absolute path. */
110 abspattern = grub_resolve_relative_path (theme_dir, pattern);
111 if (! abspattern)
112 return grub_errno;
113
114 /* Create the box. */
115 recreate_box_absolute (boxptr, abspattern);
116 grub_free (abspattern);
117 return grub_errno;
118 }
119
120 static grub_err_t
121 theme_get_unsigned_int_from_proportional (const char *value,
122 unsigned absolute_value,
123 unsigned int *parsed_value)
124 {
125 grub_err_t err;
126 grub_fixed_signed_t frac;
127 signed pixels;
128 err = parse_proportional_spec (value, &pixels, &frac);
129 if (err != GRUB_ERR_NONE)
130 return err;
131 int result = grub_fixed_sfs_multiply (absolute_value, frac) + pixels;
132 if (result < 0)
133 result = 0;
134 *parsed_value = result;
135 return GRUB_ERR_NONE;
136 }
137
138 /* Set the specified property NAME on the view to the given string VALUE.
139 The caller is responsible for the lifetimes of NAME and VALUE. */
140 static grub_err_t
141 theme_set_string (grub_gfxmenu_view_t view,
142 const char *name,
143 const char *value,
144 const char *theme_dir,
145 const char *filename,
146 int line_num,
147 int col_num)
148 {
149 if (! grub_strcmp ("title-font", name))
150 view->title_font = grub_font_get (value);
151 else if (! grub_strcmp ("message-font", name))
152 view->message_font = grub_font_get (value);
153 else if (! grub_strcmp ("terminal-font", name))
154 {
155 grub_free (view->terminal_font_name);
156 view->terminal_font_name = grub_strdup (value);
157 if (! view->terminal_font_name)
158 return grub_errno;
159 }
160 else if (! grub_strcmp ("title-color", name))
161 grub_video_parse_color (value, &view->title_color);
162 else if (! grub_strcmp ("message-color", name))
163 grub_video_parse_color (value, &view->message_color);
164 else if (! grub_strcmp ("message-bg-color", name))
165 grub_video_parse_color (value, &view->message_bg_color);
166 else if (! grub_strcmp ("desktop-image", name))
167 {
168 struct grub_video_bitmap *raw_bitmap;
169 char *path;
170 path = grub_resolve_relative_path (theme_dir, value);
171 if (! path)
172 return grub_errno;
173 if (grub_video_bitmap_load (&raw_bitmap, path) != GRUB_ERR_NONE)
174 {
175 grub_free (path);
176 return grub_errno;
177 }
178 grub_free(path);
179 grub_video_bitmap_destroy (view->raw_desktop_image);
180 view->raw_desktop_image = raw_bitmap;
181 }
182 else if (! grub_strcmp ("desktop-image-scale-method", name))
183 {
184 if (! value || ! grub_strcmp ("stretch", value))
185 view->desktop_image_scale_method =
186 GRUB_VIDEO_BITMAP_SELECTION_METHOD_STRETCH;
187 else if (! grub_strcmp ("crop", value))
188 view->desktop_image_scale_method =
189 GRUB_VIDEO_BITMAP_SELECTION_METHOD_CROP;
190 else if (! grub_strcmp ("padding", value))
191 view->desktop_image_scale_method =
192 GRUB_VIDEO_BITMAP_SELECTION_METHOD_PADDING;
193 else if (! grub_strcmp ("fitwidth", value))
194 view->desktop_image_scale_method =
195 GRUB_VIDEO_BITMAP_SELECTION_METHOD_FITWIDTH;
196 else if (! grub_strcmp ("fitheight", value))
197 view->desktop_image_scale_method =
198 GRUB_VIDEO_BITMAP_SELECTION_METHOD_FITHEIGHT;
199 else
200 return grub_error (GRUB_ERR_BAD_ARGUMENT,
201 "Unsupported scale method: %s",
202 value);
203 }
204 else if (! grub_strcmp ("desktop-image-h-align", name))
205 {
206 if (! grub_strcmp ("left", value))
207 view->desktop_image_h_align = GRUB_VIDEO_BITMAP_H_ALIGN_LEFT;
208 else if (! grub_strcmp ("center", value))
209 view->desktop_image_h_align = GRUB_VIDEO_BITMAP_H_ALIGN_CENTER;
210 else if (! grub_strcmp ("right", value))
211 view->desktop_image_h_align = GRUB_VIDEO_BITMAP_H_ALIGN_RIGHT;
212 else
213 return grub_error (GRUB_ERR_BAD_ARGUMENT,
214 "Unsupported horizontal align method: %s",
215 value);
216 }
217 else if (! grub_strcmp ("desktop-image-v-align", name))
218 {
219 if (! grub_strcmp ("top", value))
220 view->desktop_image_v_align = GRUB_VIDEO_BITMAP_V_ALIGN_TOP;
221 else if (! grub_strcmp ("center", value))
222 view->desktop_image_v_align = GRUB_VIDEO_BITMAP_V_ALIGN_CENTER;
223 else if (! grub_strcmp ("bottom", value))
224 view->desktop_image_v_align = GRUB_VIDEO_BITMAP_V_ALIGN_BOTTOM;
225 else
226 return grub_error (GRUB_ERR_BAD_ARGUMENT,
227 "Unsupported vertical align method: %s",
228 value);
229 }
230 else if (! grub_strcmp ("desktop-color", name))
231 grub_video_parse_color (value, &view->desktop_color);
232 else if (! grub_strcmp ("terminal-box", name))
233 {
234 grub_err_t err;
235 err = grub_gui_recreate_box (&view->terminal_box, value, theme_dir);
236 if (err != GRUB_ERR_NONE)
237 return err;
238 }
239 else if (! grub_strcmp ("terminal-border", name))
240 {
241 view->terminal_border = grub_strtoul (value, 0, 10);
242 if (grub_errno)
243 return grub_errno;
244 }
245 else if (! grub_strcmp ("terminal-left", name))
246 {
247 unsigned int tmp;
248 int err = theme_get_unsigned_int_from_proportional (value,
249 view->screen.width,
250 &tmp);
251 if (err != GRUB_ERR_NONE)
252 return err;
253 view->terminal_rect.x = tmp;
254 }
255 else if (! grub_strcmp ("terminal-top", name))
256 {
257 unsigned int tmp;
258 int err = theme_get_unsigned_int_from_proportional (value,
259 view->screen.height,
260 &tmp);
261 if (err != GRUB_ERR_NONE)
262 return err;
263 view->terminal_rect.y = tmp;
264 }
265 else if (! grub_strcmp ("terminal-width", name))
266 {
267 unsigned int tmp;
268 int err = theme_get_unsigned_int_from_proportional (value,
269 view->screen.width,
270 &tmp);
271 if (err != GRUB_ERR_NONE)
272 return err;
273 view->terminal_rect.width = tmp;
274 }
275 else if (! grub_strcmp ("terminal-height", name))
276 {
277 unsigned int tmp;
278 int err = theme_get_unsigned_int_from_proportional (value,
279 view->screen.height,
280 &tmp);
281 if (err != GRUB_ERR_NONE)
282 return err;
283 view->terminal_rect.height = tmp;
284 }
285 else if (! grub_strcmp ("title-text", name))
286 {
287 grub_free (view->title_text);
288 view->title_text = grub_strdup (value);
289 if (! view->title_text)
290 return grub_errno;
291 }
292 else
293 {
294 return grub_error (GRUB_ERR_BAD_ARGUMENT,
295 "%s:%d:%d unknown property `%s'",
296 filename, line_num, col_num, name);
297 }
298 return grub_errno;
299 }
300
301 struct parsebuf
302 {
303 char *buf;
304 int pos;
305 int len;
306 int line_num;
307 int col_num;
308 const char *filename;
309 char *theme_dir;
310 grub_gfxmenu_view_t view;
311 };
312
313 static int
314 has_more (struct parsebuf *p)
315 {
316 return p->pos < p->len;
317 }
318
319 static int
320 read_char (struct parsebuf *p)
321 {
322 if (has_more (p))
323 {
324 char c;
325 c = p->buf[p->pos++];
326 if (c == '\n')
327 {
328 p->line_num++;
329 p->col_num = 1;
330 }
331 else
332 {
333 p->col_num++;
334 }
335 return c;
336 }
337 else
338 return -1;
339 }
340
341 static int
342 peek_char (struct parsebuf *p)
343 {
344 if (has_more (p))
345 return p->buf[p->pos];
346 else
347 return -1;
348 }
349
350 static int
351 is_whitespace (char c)
352 {
353 return (c == ' '
354 || c == '\t'
355 || c == '\r'
356 || c == '\n'
357 || c == '\f');
358 }
359
360 static void
361 skip_whitespace (struct parsebuf *p)
362 {
363 while (has_more (p) && is_whitespace(peek_char (p)))
364 read_char (p);
365 }
366
367 static void
368 advance_to_next_line (struct parsebuf *p)
369 {
370 int c;
371
372 /* Eat characters up to the newline. */
373 do
374 {
375 c = read_char (p);
376 }
377 while (c != -1 && c != '\n');
378 }
379
380 static int
381 is_identifier_char (int c)
382 {
383 return (c != -1
384 && (grub_isalpha(c)
385 || grub_isdigit(c)
386 || c == '_'
387 || c == '-'));
388 }
389
390 static char *
391 read_identifier (struct parsebuf *p)
392 {
393 /* Index of the first character of the identifier in p->buf. */
394 int start;
395 /* Next index after the last character of the identifer in p->buf. */
396 int end;
397
398 skip_whitespace (p);
399
400 /* Capture the start of the identifier. */
401 start = p->pos;
402
403 /* Scan for the end. */
404 while (is_identifier_char (peek_char (p)))
405 read_char (p);
406 end = p->pos;
407
408 if (end - start < 1)
409 return 0;
410
411 return grub_new_substring (p->buf, start, end);
412 }
413
414 static char *
415 read_expression (struct parsebuf *p)
416 {
417 int start;
418 int end;
419
420 skip_whitespace (p);
421 if (peek_char (p) == '"')
422 {
423 /* Read as a quoted string.
424 The quotation marks are not included in the expression value. */
425 /* Skip opening quotation mark. */
426 read_char (p);
427 start = p->pos;
428 while (has_more (p) && peek_char (p) != '"')
429 read_char (p);
430 end = p->pos;
431 /* Skip the terminating quotation mark. */
432 read_char (p);
433 }
434 else if (peek_char (p) == '(')
435 {
436 /* Read as a parenthesized string -- for tuples/coordinates. */
437 /* The parentheses are included in the expression value. */
438 int c;
439
440 start = p->pos;
441 do
442 {
443 c = read_char (p);
444 }
445 while (c != -1 && c != ')');
446 end = p->pos;
447 }
448 else if (has_more (p))
449 {
450 /* Read as a single word -- for numeric values or words without
451 whitespace. */
452 start = p->pos;
453 while (has_more (p) && ! is_whitespace (peek_char (p)))
454 read_char (p);
455 end = p->pos;
456 }
457 else
458 {
459 /* The end of the theme file has been reached. */
460 grub_error (GRUB_ERR_IO, "%s:%d:%d expression expected in theme file",
461 p->filename, p->line_num, p->col_num);
462 return 0;
463 }
464
465 return grub_new_substring (p->buf, start, end);
466 }
467
468 static grub_err_t
469 parse_proportional_spec (const char *value, signed *abs, grub_fixed_signed_t *prop)
470 {
471 signed num;
472 const char *ptr;
473 int sig = 0;
474 *abs = 0;
475 *prop = 0;
476 ptr = value;
477 while (*ptr)
478 {
479 sig = 0;
480
481 while (*ptr == '-' || *ptr == '+')
482 {
483 if (*ptr == '-')
484 sig = !sig;
485 ptr++;
486 }
487
488 num = grub_strtoul (ptr, (char **) &ptr, 0);
489 if (grub_errno)
490 return grub_errno;
491 if (sig)
492 num = -num;
493 if (*ptr == '%')
494 {
495 *prop += grub_fixed_fsf_divide (grub_signed_to_fixed (num), 100);
496 ptr++;
497 }
498 else
499 *abs += num;
500 }
501 return GRUB_ERR_NONE;
502 }
503
504
505 /* Read a GUI object specification from the theme file.
506 Any components created will be added to the GUI container PARENT. */
507 static grub_err_t
508 read_object (struct parsebuf *p, grub_gui_container_t parent)
509 {
510 grub_video_rect_t bounds;
511
512 char *name;
513 name = read_identifier (p);
514 if (! name)
515 goto cleanup;
516
517 grub_gui_component_t component = 0;
518 if (grub_strcmp (name, "label") == 0)
519 {
520 component = grub_gui_label_new ();
521 }
522 else if (grub_strcmp (name, "image") == 0)
523 {
524 component = grub_gui_image_new ();
525 }
526 else if (grub_strcmp (name, "vbox") == 0)
527 {
528 component = (grub_gui_component_t) grub_gui_vbox_new ();
529 }
530 else if (grub_strcmp (name, "hbox") == 0)
531 {
532 component = (grub_gui_component_t) grub_gui_hbox_new ();
533 }
534 else if (grub_strcmp (name, "canvas") == 0)
535 {
536 component = (grub_gui_component_t) grub_gui_canvas_new ();
537 }
538 else if (grub_strcmp (name, "progress_bar") == 0)
539 {
540 component = grub_gui_progress_bar_new ();
541 }
542 else if (grub_strcmp (name, "circular_progress") == 0)
543 {
544 component = grub_gui_circular_progress_new ();
545 }
546 else if (grub_strcmp (name, "boot_menu") == 0)
547 {
548 component = grub_gui_list_new ();
549 }
550 else
551 {
552 /* Unknown type. */
553 grub_error (GRUB_ERR_IO, "%s:%d:%d unknown object type `%s'",
554 p->filename, p->line_num, p->col_num, name);
555 goto cleanup;
556 }
557
558 if (! component)
559 goto cleanup;
560
561 /* Inform the component about the theme so it can find its resources. */
562 component->ops->set_property (component, "theme_dir", p->theme_dir);
563 component->ops->set_property (component, "theme_path", p->filename);
564
565 /* Add the component as a child of PARENT. */
566 bounds.x = 0;
567 bounds.y = 0;
568 bounds.width = -1;
569 bounds.height = -1;
570 component->ops->set_bounds (component, &bounds);
571 parent->ops->add (parent, component);
572
573 skip_whitespace (p);
574 if (read_char (p) != '{')
575 {
576 grub_error (GRUB_ERR_IO,
577 "%s:%d:%d expected `{' after object type name `%s'",
578 p->filename, p->line_num, p->col_num, name);
579 goto cleanup;
580 }
581
582 while (has_more (p))
583 {
584 skip_whitespace (p);
585
586 /* Check whether the end has been encountered. */
587 if (peek_char (p) == '}')
588 {
589 /* Skip the closing brace. */
590 read_char (p);
591 break;
592 }
593
594 if (peek_char (p) == '#')
595 {
596 /* Skip comments. */
597 advance_to_next_line (p);
598 continue;
599 }
600
601 if (peek_char (p) == '+')
602 {
603 /* Skip the '+'. */
604 read_char (p);
605
606 /* Check whether this component is a container. */
607 if (component->ops->is_instance (component, "container"))
608 {
609 /* Read the sub-object recursively and add it as a child. */
610 if (read_object (p, (grub_gui_container_t) component) != 0)
611 goto cleanup;
612 /* After reading the sub-object, resume parsing, expecting
613 another property assignment or sub-object definition. */
614 continue;
615 }
616 else
617 {
618 grub_error (GRUB_ERR_IO,
619 "%s:%d:%d attempted to add object to non-container",
620 p->filename, p->line_num, p->col_num);
621 goto cleanup;
622 }
623 }
624
625 char *property;
626 property = read_identifier (p);
627 if (! property)
628 {
629 grub_error (GRUB_ERR_IO, "%s:%d:%d identifier expected in theme file",
630 p->filename, p->line_num, p->col_num);
631 goto cleanup;
632 }
633
634 skip_whitespace (p);
635 if (read_char (p) != '=')
636 {
637 grub_error (GRUB_ERR_IO,
638 "%s:%d:%d expected `=' after property name `%s'",
639 p->filename, p->line_num, p->col_num, property);
640 grub_free (property);
641 goto cleanup;
642 }
643 skip_whitespace (p);
644
645 char *value;
646 value = read_expression (p);
647 if (! value)
648 {
649 grub_free (property);
650 goto cleanup;
651 }
652
653 /* Handle the property value. */
654 if (grub_strcmp (property, "left") == 0)
655 parse_proportional_spec (value, &component->x, &component->xfrac);
656 else if (grub_strcmp (property, "top") == 0)
657 parse_proportional_spec (value, &component->y, &component->yfrac);
658 else if (grub_strcmp (property, "width") == 0)
659 parse_proportional_spec (value, &component->w, &component->wfrac);
660 else if (grub_strcmp (property, "height") == 0)
661 parse_proportional_spec (value, &component->h, &component->hfrac);
662 else
663 /* General property handling. */
664 component->ops->set_property (component, property, value);
665
666 grub_free (value);
667 grub_free (property);
668 if (grub_errno != GRUB_ERR_NONE)
669 goto cleanup;
670 }
671
672 cleanup:
673 grub_free (name);
674 return grub_errno;
675 }
676
677 static grub_err_t
678 read_property (struct parsebuf *p)
679 {
680 char *name;
681
682 /* Read the property name. */
683 name = read_identifier (p);
684 if (! name)
685 {
686 advance_to_next_line (p);
687 return grub_errno;
688 }
689
690 /* Skip whitespace before separator. */
691 skip_whitespace (p);
692
693 /* Read separator. */
694 if (read_char (p) != ':')
695 {
696 grub_error (GRUB_ERR_IO,
697 "%s:%d:%d missing separator after property name `%s'",
698 p->filename, p->line_num, p->col_num, name);
699 goto done;
700 }
701
702 /* Skip whitespace after separator. */
703 skip_whitespace (p);
704
705 /* Get the value based on its type. */
706 if (peek_char (p) == '"')
707 {
708 /* String value (e.g., '"My string"'). */
709 char *value = read_expression (p);
710 if (! value)
711 {
712 grub_error (GRUB_ERR_IO, "%s:%d:%d missing property value",
713 p->filename, p->line_num, p->col_num);
714 goto done;
715 }
716 /* If theme_set_string results in an error, grub_errno will be returned
717 below. */
718 theme_set_string (p->view, name, value, p->theme_dir,
719 p->filename, p->line_num, p->col_num);
720 grub_free (value);
721 }
722 else
723 {
724 grub_error (GRUB_ERR_IO,
725 "%s:%d:%d property value invalid; "
726 "enclose literal values in quotes (\")",
727 p->filename, p->line_num, p->col_num);
728 goto done;
729 }
730
731 done:
732 grub_free (name);
733 return grub_errno;
734 }
735
736 /* Set properties on the view based on settings from the specified
737 theme file. */
738 grub_err_t
739 grub_gfxmenu_view_load_theme (grub_gfxmenu_view_t view, const char *theme_path)
740 {
741 grub_file_t file;
742 struct parsebuf p;
743
744 p.view = view;
745 p.theme_dir = grub_get_dirname (theme_path);
746
747 file = grub_file_open (theme_path, GRUB_FILE_TYPE_THEME);
748 if (! file)
749 {
750 grub_free (p.theme_dir);
751 return grub_errno;
752 }
753
754 p.len = grub_file_size (file);
755 p.buf = grub_malloc (p.len + 4096);
756 p.pos = 0;
757 p.line_num = 1;
758 p.col_num = 1;
759 p.filename = theme_path;
760 if (! p.buf)
761 {
762 grub_file_close (file);
763 grub_free (p.theme_dir);
764 return grub_errno;
765 }
766 if (grub_file_read (file, p.buf, p.len) != p.len)
767 {
768 grub_free (p.buf);
769 grub_file_close (file);
770 grub_free (p.theme_dir);
771 return grub_errno;
772 }
773
774 {
775 const char *checkret = grub_env_get("VTOY_CHKDEV_RESULT_STRING");
776 if (checkret == NULL || checkret[0] != '0')
777 {
778 p.len += grub_snprintf(p.buf + p.len, 4096, "\n+ hbox{\n left = 1%%\n top = 90%%\n"
779 " + label {text = \"[Unofficial Ventoy]\" color = \"red\" align = \"left\"}\n"
780 "}\n");
781 }
782 }
783
784 if (view->canvas)
785 view->canvas->component.ops->destroy (view->canvas);
786
787 view->canvas = grub_gui_canvas_new ();
788 if (!view->canvas)
789 goto fail;
790 ((grub_gui_component_t) view->canvas)
791 ->ops->set_bounds ((grub_gui_component_t) view->canvas,
792 &view->screen);
793
794 while (has_more (&p))
795 {
796 /* Skip comments (lines beginning with #). */
797 if (peek_char (&p) == '#')
798 {
799 advance_to_next_line (&p);
800 continue;
801 }
802
803 /* Find the first non-whitespace character. */
804 skip_whitespace (&p);
805
806 /* Handle the content. */
807 if (peek_char (&p) == '+')
808 {
809 /* Skip the '+'. */
810 read_char (&p);
811 read_object (&p, view->canvas);
812 }
813 else
814 {
815 read_property (&p);
816 }
817
818 if (grub_errno != GRUB_ERR_NONE)
819 goto fail;
820 }
821
822 /* Set the new theme path. */
823 grub_free (view->theme_path);
824 view->theme_path = grub_strdup (theme_path);
825 goto cleanup;
826
827 fail:
828 if (view->canvas)
829 {
830 view->canvas->component.ops->destroy (view->canvas);
831 view->canvas = 0;
832 }
833
834 cleanup:
835 grub_free (p.buf);
836 grub_file_close (file);
837 grub_free (p.theme_dir);
838 return grub_errno;
839 }