/* theme_loader.c - Theme file loader for gfxmenu. */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2008 Free Software Foundation, Inc.
*
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/gui_string_util.h>
#include <grub/bitmap_scale.h>
#include <grub/gfxwidgets.h>
#include <grub/gfxmenu_view.h>
/* Construct a new box widget using ABSPATTERN to find the pixmap files for
it, storing the new box instance at *BOXPTR.
The '*' then gets substituted with the various pixmap names that the
box uses. */
static grub_err_t
{
char *prefix;
char *suffix;
char *star;
if (! star)
return grub_error (GRUB_ERR_BAD_ARGUMENT,
"missing `*' in box pixmap pattern `%s'", abspattern);
/* Prefix: Get the part before the '*'. */
if (! prefix)
return grub_errno;
/* Suffix: Everything after the '*' is the suffix. */
if (! box)
return grub_errno;
if (*boxptr)
return grub_errno;
}
/* Construct a new box widget using PATTERN to find the pixmap files for it,
storing the new widget at *BOXPTR. PATTERN should be of the form:
pixmap names that the widget uses.
Important! The value of *BOXPTR must be initialized! It must either
(1) Be 0 (a NULL pointer), or
(2) Be a pointer to a valid 'grub_gfxmenu_box_t' instance.
In this case, the previous instance is destroyed. */
{
char *abspattern;
/* Check arguments. */
if (! pattern)
{
/* If no pixmap pattern is given, then just create an empty box. */
if (*boxptr)
*boxptr = grub_gfxmenu_create_box (0, 0);
return grub_errno;
}
if (! theme_dir)
return grub_error (GRUB_ERR_BAD_ARGUMENT,
"styled box missing theme directory");
/* Resolve to an absolute path. */
if (! abspattern)
return grub_errno;
/* Create the box. */
return grub_errno;
}
/* Set the specified property NAME on the view to the given string VALUE.
The caller is responsible for the lifetimes of NAME and VALUE. */
static grub_err_t
const char *name,
const char *value,
const char *theme_dir,
const char *filename,
int line_num,
int col_num)
{
{
if (! view->terminal_font_name)
return grub_errno;
}
{
char *path;
if (! path)
return grub_errno;
{
return grub_errno;
}
if (! scaled_bitmap)
{
grub_error_push ();
}
}
{
if (err != GRUB_ERR_NONE)
return err;
}
{
if (! view->title_text)
return grub_errno;
}
else
{
return grub_error (GRUB_ERR_BAD_ARGUMENT,
"%s:%d:%d unknown property `%s'",
}
return grub_errno;
}
struct parsebuf
{
char *buf;
int pos;
int len;
int line_num;
int col_num;
const char *filename;
char *theme_dir;
};
static int
{
}
static int
{
if (has_more (p))
{
char c;
if (c == '\n')
{
p->line_num++;
p->col_num = 1;
}
else
{
p->col_num++;
}
return c;
}
else
return -1;
}
static int
{
if (has_more (p))
else
return -1;
}
static int
is_whitespace (char c)
{
return (c == ' '
|| c == '\t'
|| c == '\r'
|| c == '\n'
|| c == '\f');
}
static void
{
read_char (p);
}
static void
{
int c;
/* Eat characters up to the newline. */
do
{
c = read_char (p);
}
while (c != -1 && c != '\n');
}
static int
is_identifier_char (int c)
{
return (c != -1
&& (grub_isalpha(c)
|| grub_isdigit(c)
|| c == '_'
|| c == '-'));
}
static char *
{
/* Index of the first character of the identifier in p->buf. */
int start;
/* Next index after the last character of the identifer in p->buf. */
int end;
skip_whitespace (p);
/* Capture the start of the identifier. */
/* Scan for the end. */
while (is_identifier_char (peek_char (p)))
read_char (p);
return 0;
}
static char *
{
int start;
int end;
skip_whitespace (p);
if (peek_char (p) == '"')
{
/* Read as a quoted string.
The quotation marks are not included in the expression value. */
/* Skip opening quotation mark. */
read_char (p);
read_char (p);
/* Skip the terminating quotation mark. */
read_char (p);
}
else if (peek_char (p) == '(')
{
/* Read as a parenthesized string -- for tuples/coordinates. */
/* The parentheses are included in the expression value. */
int c;
do
{
c = read_char (p);
}
while (c != -1 && c != ')');
}
else if (has_more (p))
{
/* Read as a single word -- for numeric values or words without
whitespace. */
read_char (p);
}
else
{
/* The end of the theme file has been reached. */
return 0;
}
}
static grub_err_t
{
signed num;
char *ptr;
int sig = 0;
*abs = 0;
*prop = 0;
while (*ptr)
{
sig = 0;
{
if (*ptr == '-')
ptr++;
}
if (grub_errno)
return grub_errno;
if (sig)
if (*ptr == '%')
{
ptr++;
}
else
}
return GRUB_ERR_NONE;
}
/* Read a GUI object specification from the theme file.
Any components created will be added to the GUI container PARENT. */
static grub_err_t
{
char *name;
name = read_identifier (p);
if (! name)
goto cleanup;
{
component = grub_gui_label_new ();
}
{
component = grub_gui_image_new ();
}
{
}
{
}
{
}
{
}
{
}
{
component = grub_gui_list_new ();
}
else
{
/* Unknown type. */
goto cleanup;
}
if (! component)
goto cleanup;
/* Inform the component about the theme so it can find its resources. */
/* Add the component as a child of PARENT. */
bounds.x = 0;
bounds.y = 0;
skip_whitespace (p);
if (read_char (p) != '{')
{
"%s:%d:%d expected `{' after object type name `%s'",
goto cleanup;
}
while (has_more (p))
{
skip_whitespace (p);
/* Check whether the end has been encountered. */
if (peek_char (p) == '}')
{
/* Skip the closing brace. */
read_char (p);
break;
}
if (peek_char (p) == '#')
{
/* Skip comments. */
advance_to_next_line (p);
continue;
}
if (peek_char (p) == '+')
{
/* Skip the '+'. */
read_char (p);
/* Check whether this component is a container. */
{
/* Read the sub-object recursively and add it as a child. */
goto cleanup;
/* After reading the sub-object, resume parsing, expecting
another property assignment or sub-object definition. */
continue;
}
else
{
"%s:%d:%d attempted to add object to non-container",
goto cleanup;
}
}
char *property;
property = read_identifier (p);
if (! property)
{
goto cleanup;
}
skip_whitespace (p);
if (read_char (p) != '=')
{
"%s:%d:%d expected `=' after property name `%s'",
goto cleanup;
}
skip_whitespace (p);
char *value;
value = read_expression (p);
if (! value)
{
goto cleanup;
}
/* Handle the property value. */
else
/* General property handling. */
if (grub_errno != GRUB_ERR_NONE)
goto cleanup;
}
return grub_errno;
}
static grub_err_t
{
char *name;
/* Read the property name. */
name = read_identifier (p);
if (! name)
{
advance_to_next_line (p);
return grub_errno;
}
/* Skip whitespace before separator. */
skip_whitespace (p);
/* Read separator. */
if (read_char (p) != ':')
{
"%s:%d:%d missing separator after property name `%s'",
goto done;
}
/* Skip whitespace after separator. */
skip_whitespace (p);
/* Get the value based on its type. */
if (peek_char (p) == '"')
{
/* String value (e.g., '"My string"'). */
if (! value)
{
goto done;
}
/* If theme_set_string results in an error, grub_errno will be returned
below. */
}
else
{
"%s:%d:%d property value invalid; "
"enclose literal values in quotes (\")",
goto done;
}
done:
return grub_errno;
}
/* Set properties on the view based on settings from the specified
theme file. */
{
struct parsebuf p;
if (! file)
{
return grub_errno;
}
p.pos = 0;
p.line_num = 1;
p.col_num = 1;
p.filename = theme_path;
if (! p.buf)
{
return grub_errno;
}
{
return grub_errno;
}
goto fail;
while (has_more (&p))
{
/* Skip comments (lines beginning with #). */
if (peek_char (&p) == '#')
{
advance_to_next_line (&p);
continue;
}
/* Find the first non-whitespace character. */
skip_whitespace (&p);
/* Handle the content. */
if (peek_char (&p) == '+')
{
/* Skip the '+'. */
read_char (&p);
}
else
{
read_property (&p);
}
if (grub_errno != GRUB_ERR_NONE)
goto fail;
}
/* Set the new theme path. */
goto cleanup;
fail:
{
}
return grub_errno;
}