theme_loader.c 22 KB

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