2009-11-20 15:02:58 +00:00
|
|
|
/* theme_loader.c - Theme file loader for gfxmenu. */
|
|
|
|
/*
|
|
|
|
* GRUB -- GRand Unified Bootloader
|
|
|
|
* Copyright (C) 2008 Free Software Foundation, Inc.
|
|
|
|
*
|
|
|
|
* GRUB is free software: you can redistribute it and/or modify
|
|
|
|
* 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/types.h>
|
|
|
|
#include <grub/file.h>
|
|
|
|
#include <grub/misc.h>
|
|
|
|
#include <grub/mm.h>
|
|
|
|
#include <grub/err.h>
|
|
|
|
#include <grub/dl.h>
|
|
|
|
#include <grub/video.h>
|
|
|
|
#include <grub/gui_string_util.h>
|
|
|
|
#include <grub/bitmap.h>
|
|
|
|
#include <grub/bitmap_scale.h>
|
|
|
|
#include <grub/gfxwidgets.h>
|
|
|
|
#include <grub/gfxmenu_view.h>
|
|
|
|
#include <grub/gui.h>
|
2013-05-30 22:42:33 +00:00
|
|
|
#include <grub/color.h>
|
2009-11-20 15:02:58 +00:00
|
|
|
|
2013-08-08 08:55:24 +00:00
|
|
|
static grub_err_t
|
|
|
|
parse_proportional_spec (const char *value, signed *abs, grub_fixed_signed_t *prop);
|
|
|
|
|
2009-11-20 15:02:58 +00:00
|
|
|
/* Construct a new box widget using ABSPATTERN to find the pixmap files for
|
|
|
|
it, storing the new box instance at *BOXPTR.
|
|
|
|
PATTERN should be of the form: "(hd0,0)/somewhere/style*.png".
|
|
|
|
The '*' then gets substituted with the various pixmap names that the
|
|
|
|
box uses. */
|
|
|
|
static grub_err_t
|
|
|
|
recreate_box_absolute (grub_gfxmenu_box_t *boxptr, const char *abspattern)
|
|
|
|
{
|
|
|
|
char *prefix;
|
|
|
|
char *suffix;
|
|
|
|
char *star;
|
|
|
|
grub_gfxmenu_box_t box;
|
|
|
|
|
|
|
|
star = grub_strchr (abspattern, '*');
|
|
|
|
if (! star)
|
|
|
|
return grub_error (GRUB_ERR_BAD_ARGUMENT,
|
|
|
|
"missing `*' in box pixmap pattern `%s'", abspattern);
|
|
|
|
|
|
|
|
/* Prefix: Get the part before the '*'. */
|
|
|
|
prefix = grub_malloc (star - abspattern + 1);
|
|
|
|
if (! prefix)
|
|
|
|
return grub_errno;
|
|
|
|
|
|
|
|
grub_memcpy (prefix, abspattern, star - abspattern);
|
|
|
|
prefix[star - abspattern] = '\0';
|
|
|
|
|
|
|
|
/* Suffix: Everything after the '*' is the suffix. */
|
|
|
|
suffix = star + 1;
|
|
|
|
|
|
|
|
box = grub_gfxmenu_create_box (prefix, suffix);
|
|
|
|
grub_free (prefix);
|
|
|
|
if (! box)
|
|
|
|
return grub_errno;
|
|
|
|
|
|
|
|
if (*boxptr)
|
|
|
|
(*boxptr)->destroy (*boxptr);
|
|
|
|
*boxptr = box;
|
|
|
|
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:
|
|
|
|
"somewhere/style*.png". The '*' then gets substituted with the various
|
|
|
|
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. */
|
|
|
|
grub_err_t
|
|
|
|
grub_gui_recreate_box (grub_gfxmenu_box_t *boxptr,
|
|
|
|
const char *pattern, const char *theme_dir)
|
|
|
|
{
|
|
|
|
char *abspattern;
|
|
|
|
|
|
|
|
/* Check arguments. */
|
|
|
|
if (! pattern)
|
|
|
|
{
|
|
|
|
/* If no pixmap pattern is given, then just create an empty box. */
|
|
|
|
if (*boxptr)
|
|
|
|
(*boxptr)->destroy (*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. */
|
|
|
|
abspattern = grub_resolve_relative_path (theme_dir, pattern);
|
|
|
|
if (! abspattern)
|
|
|
|
return grub_errno;
|
|
|
|
|
|
|
|
/* Create the box. */
|
|
|
|
recreate_box_absolute (boxptr, abspattern);
|
|
|
|
grub_free (abspattern);
|
|
|
|
return grub_errno;
|
|
|
|
}
|
|
|
|
|
2013-08-08 08:55:24 +00:00
|
|
|
static grub_err_t
|
|
|
|
theme_get_unsigned_int_from_proportional (const char *value,
|
|
|
|
unsigned absolute_value,
|
|
|
|
unsigned int *parsed_value)
|
|
|
|
{
|
|
|
|
grub_err_t err;
|
|
|
|
grub_fixed_signed_t frac;
|
|
|
|
signed pixels;
|
|
|
|
err = parse_proportional_spec (value, &pixels, &frac);
|
|
|
|
if (err != GRUB_ERR_NONE)
|
|
|
|
return err;
|
|
|
|
int result = grub_fixed_sfs_multiply (absolute_value, frac) + pixels;
|
|
|
|
if (result < 0)
|
|
|
|
result = 0;
|
|
|
|
*parsed_value = result;
|
|
|
|
return GRUB_ERR_NONE;
|
|
|
|
}
|
|
|
|
|
2009-11-20 15:02:58 +00:00
|
|
|
/* 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
|
|
|
|
theme_set_string (grub_gfxmenu_view_t view,
|
|
|
|
const char *name,
|
|
|
|
const char *value,
|
|
|
|
const char *theme_dir,
|
|
|
|
const char *filename,
|
|
|
|
int line_num,
|
|
|
|
int col_num)
|
|
|
|
{
|
|
|
|
if (! grub_strcmp ("title-font", name))
|
|
|
|
view->title_font = grub_font_get (value);
|
|
|
|
else if (! grub_strcmp ("message-font", name))
|
|
|
|
view->message_font = grub_font_get (value);
|
|
|
|
else if (! grub_strcmp ("terminal-font", name))
|
|
|
|
{
|
|
|
|
grub_free (view->terminal_font_name);
|
|
|
|
view->terminal_font_name = grub_strdup (value);
|
|
|
|
if (! view->terminal_font_name)
|
|
|
|
return grub_errno;
|
|
|
|
}
|
|
|
|
else if (! grub_strcmp ("title-color", name))
|
2010-12-10 16:45:58 +00:00
|
|
|
grub_video_parse_color (value, &view->title_color);
|
2009-11-20 15:02:58 +00:00
|
|
|
else if (! grub_strcmp ("message-color", name))
|
2010-12-10 16:45:58 +00:00
|
|
|
grub_video_parse_color (value, &view->message_color);
|
2009-11-20 15:02:58 +00:00
|
|
|
else if (! grub_strcmp ("message-bg-color", name))
|
2010-12-10 16:45:58 +00:00
|
|
|
grub_video_parse_color (value, &view->message_bg_color);
|
2009-11-20 15:02:58 +00:00
|
|
|
else if (! grub_strcmp ("desktop-image", name))
|
|
|
|
{
|
|
|
|
struct grub_video_bitmap *raw_bitmap;
|
|
|
|
char *path;
|
|
|
|
path = grub_resolve_relative_path (theme_dir, value);
|
|
|
|
if (! path)
|
|
|
|
return grub_errno;
|
|
|
|
if (grub_video_bitmap_load (&raw_bitmap, path) != GRUB_ERR_NONE)
|
|
|
|
{
|
|
|
|
grub_free (path);
|
|
|
|
return grub_errno;
|
|
|
|
}
|
|
|
|
grub_free(path);
|
2013-10-02 14:17:33 +00:00
|
|
|
grub_video_bitmap_destroy (view->raw_desktop_image);
|
|
|
|
view->raw_desktop_image = raw_bitmap;
|
|
|
|
}
|
|
|
|
else if (! grub_strcmp ("desktop-image-scale-method", name))
|
|
|
|
{
|
|
|
|
if (! value || ! grub_strcmp ("stretch", value))
|
|
|
|
view->desktop_image_scale_method =
|
|
|
|
GRUB_VIDEO_BITMAP_SELECTION_METHOD_STRETCH;
|
|
|
|
else if (! grub_strcmp ("crop", value))
|
|
|
|
view->desktop_image_scale_method =
|
|
|
|
GRUB_VIDEO_BITMAP_SELECTION_METHOD_CROP;
|
|
|
|
else if (! grub_strcmp ("padding", value))
|
|
|
|
view->desktop_image_scale_method =
|
|
|
|
GRUB_VIDEO_BITMAP_SELECTION_METHOD_PADDING;
|
|
|
|
else if (! grub_strcmp ("fitwidth", value))
|
|
|
|
view->desktop_image_scale_method =
|
|
|
|
GRUB_VIDEO_BITMAP_SELECTION_METHOD_FITWIDTH;
|
|
|
|
else if (! grub_strcmp ("fitheight", value))
|
|
|
|
view->desktop_image_scale_method =
|
|
|
|
GRUB_VIDEO_BITMAP_SELECTION_METHOD_FITHEIGHT;
|
|
|
|
else
|
|
|
|
return grub_error (GRUB_ERR_BAD_ARGUMENT,
|
2013-10-03 21:23:00 +00:00
|
|
|
"Unsupported scale method: %s",
|
|
|
|
value);
|
2013-10-02 14:17:33 +00:00
|
|
|
}
|
|
|
|
else if (! grub_strcmp ("desktop-image-h-align", name))
|
|
|
|
{
|
|
|
|
if (! grub_strcmp ("left", value))
|
|
|
|
view->desktop_image_h_align = GRUB_VIDEO_BITMAP_H_ALIGN_LEFT;
|
|
|
|
else if (! grub_strcmp ("center", value))
|
|
|
|
view->desktop_image_h_align = GRUB_VIDEO_BITMAP_H_ALIGN_CENTER;
|
|
|
|
else if (! grub_strcmp ("right", value))
|
|
|
|
view->desktop_image_h_align = GRUB_VIDEO_BITMAP_H_ALIGN_RIGHT;
|
|
|
|
else
|
|
|
|
return grub_error (GRUB_ERR_BAD_ARGUMENT,
|
2013-10-03 21:23:00 +00:00
|
|
|
"Unsupported horizontal align method: %s",
|
|
|
|
value);
|
2013-10-02 14:17:33 +00:00
|
|
|
}
|
|
|
|
else if (! grub_strcmp ("desktop-image-v-align", name))
|
|
|
|
{
|
|
|
|
if (! grub_strcmp ("top", value))
|
|
|
|
view->desktop_image_v_align = GRUB_VIDEO_BITMAP_V_ALIGN_TOP;
|
|
|
|
else if (! grub_strcmp ("center", value))
|
|
|
|
view->desktop_image_v_align = GRUB_VIDEO_BITMAP_V_ALIGN_CENTER;
|
|
|
|
else if (! grub_strcmp ("bottom", value))
|
|
|
|
view->desktop_image_v_align = GRUB_VIDEO_BITMAP_V_ALIGN_BOTTOM;
|
|
|
|
else
|
|
|
|
return grub_error (GRUB_ERR_BAD_ARGUMENT,
|
2013-10-03 21:23:00 +00:00
|
|
|
"Unsupported vertical align method: %s",
|
|
|
|
value);
|
2009-11-20 15:02:58 +00:00
|
|
|
}
|
|
|
|
else if (! grub_strcmp ("desktop-color", name))
|
2010-12-10 16:45:58 +00:00
|
|
|
grub_video_parse_color (value, &view->desktop_color);
|
2009-11-20 15:02:58 +00:00
|
|
|
else if (! grub_strcmp ("terminal-box", name))
|
|
|
|
{
|
|
|
|
grub_err_t err;
|
|
|
|
err = grub_gui_recreate_box (&view->terminal_box, value, theme_dir);
|
|
|
|
if (err != GRUB_ERR_NONE)
|
|
|
|
return err;
|
|
|
|
}
|
2013-08-08 08:55:24 +00:00
|
|
|
else if (! grub_strcmp ("terminal-border", name))
|
|
|
|
{
|
|
|
|
view->terminal_border = grub_strtoul (value, 0, 10);
|
|
|
|
if (grub_errno)
|
|
|
|
return grub_errno;
|
|
|
|
}
|
|
|
|
else if (! grub_strcmp ("terminal-left", name))
|
|
|
|
{
|
|
|
|
unsigned int tmp;
|
|
|
|
int err = theme_get_unsigned_int_from_proportional (value,
|
|
|
|
view->screen.width,
|
|
|
|
&tmp);
|
|
|
|
if (err != GRUB_ERR_NONE)
|
|
|
|
return err;
|
|
|
|
view->terminal_rect.x = tmp;
|
|
|
|
}
|
|
|
|
else if (! grub_strcmp ("terminal-top", name))
|
|
|
|
{
|
|
|
|
unsigned int tmp;
|
|
|
|
int err = theme_get_unsigned_int_from_proportional (value,
|
2015-11-13 18:54:19 +00:00
|
|
|
view->screen.height,
|
2013-08-08 08:55:24 +00:00
|
|
|
&tmp);
|
|
|
|
if (err != GRUB_ERR_NONE)
|
|
|
|
return err;
|
|
|
|
view->terminal_rect.y = tmp;
|
|
|
|
}
|
|
|
|
else if (! grub_strcmp ("terminal-width", name))
|
|
|
|
{
|
|
|
|
unsigned int tmp;
|
|
|
|
int err = theme_get_unsigned_int_from_proportional (value,
|
|
|
|
view->screen.width,
|
|
|
|
&tmp);
|
|
|
|
if (err != GRUB_ERR_NONE)
|
|
|
|
return err;
|
|
|
|
view->terminal_rect.width = tmp;
|
|
|
|
}
|
|
|
|
else if (! grub_strcmp ("terminal-height", name))
|
|
|
|
{
|
|
|
|
unsigned int tmp;
|
|
|
|
int err = theme_get_unsigned_int_from_proportional (value,
|
2015-11-13 18:54:19 +00:00
|
|
|
view->screen.height,
|
2013-08-08 08:55:24 +00:00
|
|
|
&tmp);
|
|
|
|
if (err != GRUB_ERR_NONE)
|
|
|
|
return err;
|
|
|
|
view->terminal_rect.height = tmp;
|
|
|
|
}
|
2009-11-20 15:02:58 +00:00
|
|
|
else if (! grub_strcmp ("title-text", name))
|
|
|
|
{
|
|
|
|
grub_free (view->title_text);
|
|
|
|
view->title_text = grub_strdup (value);
|
|
|
|
if (! view->title_text)
|
|
|
|
return grub_errno;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return grub_error (GRUB_ERR_BAD_ARGUMENT,
|
|
|
|
"%s:%d:%d unknown property `%s'",
|
|
|
|
filename, line_num, col_num, name);
|
|
|
|
}
|
|
|
|
return grub_errno;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct parsebuf
|
|
|
|
{
|
|
|
|
char *buf;
|
|
|
|
int pos;
|
|
|
|
int len;
|
|
|
|
int line_num;
|
|
|
|
int col_num;
|
|
|
|
const char *filename;
|
|
|
|
char *theme_dir;
|
|
|
|
grub_gfxmenu_view_t view;
|
|
|
|
};
|
|
|
|
|
|
|
|
static int
|
|
|
|
has_more (struct parsebuf *p)
|
|
|
|
{
|
|
|
|
return p->pos < p->len;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
read_char (struct parsebuf *p)
|
|
|
|
{
|
|
|
|
if (has_more (p))
|
|
|
|
{
|
|
|
|
char c;
|
|
|
|
c = p->buf[p->pos++];
|
|
|
|
if (c == '\n')
|
|
|
|
{
|
|
|
|
p->line_num++;
|
|
|
|
p->col_num = 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
p->col_num++;
|
|
|
|
}
|
|
|
|
return c;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
peek_char (struct parsebuf *p)
|
|
|
|
{
|
|
|
|
if (has_more (p))
|
|
|
|
return p->buf[p->pos];
|
|
|
|
else
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
is_whitespace (char c)
|
|
|
|
{
|
|
|
|
return (c == ' '
|
|
|
|
|| c == '\t'
|
|
|
|
|| c == '\r'
|
|
|
|
|| c == '\n'
|
|
|
|
|| c == '\f');
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
skip_whitespace (struct parsebuf *p)
|
|
|
|
{
|
|
|
|
while (has_more (p) && is_whitespace(peek_char (p)))
|
|
|
|
read_char (p);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
advance_to_next_line (struct parsebuf *p)
|
|
|
|
{
|
|
|
|
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 *
|
|
|
|
read_identifier (struct parsebuf *p)
|
|
|
|
{
|
|
|
|
/* 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. */
|
|
|
|
start = p->pos;
|
|
|
|
|
|
|
|
/* Scan for the end. */
|
|
|
|
while (is_identifier_char (peek_char (p)))
|
|
|
|
read_char (p);
|
|
|
|
end = p->pos;
|
|
|
|
|
|
|
|
if (end - start < 1)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return grub_new_substring (p->buf, start, end);
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *
|
|
|
|
read_expression (struct parsebuf *p)
|
|
|
|
{
|
|
|
|
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);
|
|
|
|
start = p->pos;
|
|
|
|
while (has_more (p) && peek_char (p) != '"')
|
|
|
|
read_char (p);
|
|
|
|
end = p->pos;
|
|
|
|
/* 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;
|
|
|
|
|
|
|
|
start = p->pos;
|
|
|
|
do
|
|
|
|
{
|
|
|
|
c = read_char (p);
|
|
|
|
}
|
|
|
|
while (c != -1 && c != ')');
|
|
|
|
end = p->pos;
|
|
|
|
}
|
|
|
|
else if (has_more (p))
|
|
|
|
{
|
|
|
|
/* Read as a single word -- for numeric values or words without
|
|
|
|
whitespace. */
|
|
|
|
start = p->pos;
|
|
|
|
while (has_more (p) && ! is_whitespace (peek_char (p)))
|
|
|
|
read_char (p);
|
|
|
|
end = p->pos;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* The end of the theme file has been reached. */
|
|
|
|
grub_error (GRUB_ERR_IO, "%s:%d:%d expression expected in theme file",
|
|
|
|
p->filename, p->line_num, p->col_num);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return grub_new_substring (p->buf, start, end);
|
|
|
|
}
|
|
|
|
|
2010-01-05 18:38:11 +00:00
|
|
|
static grub_err_t
|
2013-08-08 08:55:24 +00:00
|
|
|
parse_proportional_spec (const char *value, signed *abs, grub_fixed_signed_t *prop)
|
2010-01-05 18:38:11 +00:00
|
|
|
{
|
|
|
|
signed num;
|
2013-08-08 08:55:24 +00:00
|
|
|
const char *ptr;
|
2010-01-05 18:38:11 +00:00
|
|
|
int sig = 0;
|
|
|
|
*abs = 0;
|
|
|
|
*prop = 0;
|
|
|
|
ptr = value;
|
|
|
|
while (*ptr)
|
|
|
|
{
|
|
|
|
sig = 0;
|
|
|
|
|
|
|
|
while (*ptr == '-' || *ptr == '+')
|
|
|
|
{
|
|
|
|
if (*ptr == '-')
|
|
|
|
sig = !sig;
|
|
|
|
ptr++;
|
|
|
|
}
|
|
|
|
|
misc: Make grub_strtol() "end" pointers have safer const qualifiers
Currently the string functions grub_strtol(), grub_strtoul(), and
grub_strtoull() don't declare the "end" pointer in such a way as to
require the pointer itself or the character array to be immutable to the
implementation, nor does the C standard do so in its similar functions,
though it does require us not to change any of it.
The typical declarations of these functions follow this pattern:
long
strtol(const char * restrict nptr, char ** restrict endptr, int base);
Much of the reason for this is historic, and a discussion of that
follows below, after the explanation of this change. (GRUB currently
does not include the "restrict" qualifiers, and we name the arguments a
bit differently.)
The implementation is semantically required to treat the character array
as immutable, but such accidental modifications aren't stopped by the
compiler, and the semantics for both the callers and the implementation
of these functions are sometimes also helped by adding that requirement.
This patch changes these declarations to follow this pattern instead:
long
strtol(const char * restrict nptr,
const char ** const restrict endptr,
int base);
This means that if any modification to these functions accidentally
introduces either an errant modification to the underlying character
array, or an accidental assignment to endptr rather than *endptr, the
compiler should generate an error. (The two uses of "restrict" in this
case basically mean strtol() isn't allowed to modify the character array
by going through *endptr, and endptr isn't allowed to point inside the
array.)
It also means the typical use case changes to:
char *s = ...;
const char *end;
long l;
l = strtol(s, &end, 10);
Or even:
const char *p = str;
while (p && *p) {
long l = strtol(p, &p, 10);
...
}
This fixes 26 places where we discard our attempts at treating the data
safely by doing:
const char *p = str;
long l;
l = strtol(p, (char **)&ptr, 10);
It also adds 5 places where we do:
char *p = str;
while (p && *p) {
long l = strtol(p, (const char ** const)&p, 10);
...
/* more calls that need p not to be pointer-to-const */
}
While moderately distasteful, this is a better problem to have.
With one minor exception, I have tested that all of this compiles
without relevant warnings or errors, and that /much/ of it behaves
correctly, with gcc 9 using 'gcc -W -Wall -Wextra'. The one exception
is the changes in grub-core/osdep/aros/hostdisk.c , which I have no idea
how to build.
Because the C standard defined type-qualifiers in a way that can be
confusing, in the past there's been a slow but fairly regular stream of
churn within our patches, which add and remove the const qualifier in many
of the users of these functions. This change should help avoid that in
the future, and in order to help ensure this, I've added an explanation
in misc.h so that when someone does get a compiler warning about a type
error, they have the fix at hand.
The reason we don't have "const" in these calls in the standard is
purely anachronistic: C78 (de facto) did not have type qualifiers in the
syntax, and the "const" type qualifier was added for C89 (I think; it
may have been later). strtol() appears to date from 4.3BSD in 1986,
which means it could not be added to those functions in the standard
without breaking compatibility, which is usually avoided.
The syntax chosen for type qualifiers is what has led to the churn
regarding usage of const, and is especially confusing on string
functions due to the lack of a string type. Quoting from C99, the
syntax is:
declarator:
pointer[opt] direct-declarator
direct-declarator:
identifier
( declarator )
direct-declarator [ type-qualifier-list[opt] assignment-expression[opt] ]
...
direct-declarator [ type-qualifier-list[opt] * ]
...
pointer:
* type-qualifier-list[opt]
* type-qualifier-list[opt] pointer
type-qualifier-list:
type-qualifier
type-qualifier-list type-qualifier
...
type-qualifier:
const
restrict
volatile
So the examples go like:
const char foo; // immutable object
const char *foo; // mutable pointer to object
char * const foo; // immutable pointer to mutable object
const char * const foo; // immutable pointer to immutable object
const char const * const foo; // XXX extra const keyword in the middle
const char * const * const foo; // immutable pointer to immutable
// pointer to immutable object
const char ** const foo; // immutable pointer to mutable pointer
// to immutable object
Making const left-associative for * and right-associative for everything
else may not have been the best choice ever, but here we are, and the
inevitable result is people using trying to use const (as they should!),
putting it at the wrong place, fighting with the compiler for a bit, and
then either removing it or typecasting something in a bad way. I won't
go into describing restrict, but its syntax has exactly the same issue
as with const.
Anyway, the last example above actually represents the *behavior* that's
required of strtol()-like functions, so that's our choice for the "end"
pointer.
Signed-off-by: Peter Jones <pjones@redhat.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
2020-02-21 21:39:33 +00:00
|
|
|
num = grub_strtoul (ptr, &ptr, 0);
|
2010-01-05 18:38:11 +00:00
|
|
|
if (grub_errno)
|
|
|
|
return grub_errno;
|
|
|
|
if (sig)
|
|
|
|
num = -num;
|
|
|
|
if (*ptr == '%')
|
2010-01-05 20:06:27 +00:00
|
|
|
{
|
|
|
|
*prop += grub_fixed_fsf_divide (grub_signed_to_fixed (num), 100);
|
|
|
|
ptr++;
|
|
|
|
}
|
2010-01-05 18:38:11 +00:00
|
|
|
else
|
|
|
|
*abs += num;
|
|
|
|
}
|
|
|
|
return GRUB_ERR_NONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-11-20 15:02:58 +00:00
|
|
|
/* Read a GUI object specification from the theme file.
|
|
|
|
Any components created will be added to the GUI container PARENT. */
|
|
|
|
static grub_err_t
|
|
|
|
read_object (struct parsebuf *p, grub_gui_container_t parent)
|
|
|
|
{
|
|
|
|
grub_video_rect_t bounds;
|
|
|
|
|
|
|
|
char *name;
|
|
|
|
name = read_identifier (p);
|
|
|
|
if (! name)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
grub_gui_component_t component = 0;
|
|
|
|
if (grub_strcmp (name, "label") == 0)
|
|
|
|
{
|
|
|
|
component = grub_gui_label_new ();
|
|
|
|
}
|
|
|
|
else if (grub_strcmp (name, "image") == 0)
|
|
|
|
{
|
|
|
|
component = grub_gui_image_new ();
|
|
|
|
}
|
|
|
|
else if (grub_strcmp (name, "vbox") == 0)
|
|
|
|
{
|
|
|
|
component = (grub_gui_component_t) grub_gui_vbox_new ();
|
|
|
|
}
|
|
|
|
else if (grub_strcmp (name, "hbox") == 0)
|
|
|
|
{
|
|
|
|
component = (grub_gui_component_t) grub_gui_hbox_new ();
|
|
|
|
}
|
|
|
|
else if (grub_strcmp (name, "canvas") == 0)
|
|
|
|
{
|
|
|
|
component = (grub_gui_component_t) grub_gui_canvas_new ();
|
|
|
|
}
|
|
|
|
else if (grub_strcmp (name, "progress_bar") == 0)
|
|
|
|
{
|
|
|
|
component = grub_gui_progress_bar_new ();
|
|
|
|
}
|
|
|
|
else if (grub_strcmp (name, "circular_progress") == 0)
|
|
|
|
{
|
|
|
|
component = grub_gui_circular_progress_new ();
|
|
|
|
}
|
|
|
|
else if (grub_strcmp (name, "boot_menu") == 0)
|
|
|
|
{
|
|
|
|
component = grub_gui_list_new ();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Unknown type. */
|
|
|
|
grub_error (GRUB_ERR_IO, "%s:%d:%d unknown object type `%s'",
|
|
|
|
p->filename, p->line_num, p->col_num, name);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (! component)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
/* Inform the component about the theme so it can find its resources. */
|
|
|
|
component->ops->set_property (component, "theme_dir", p->theme_dir);
|
|
|
|
component->ops->set_property (component, "theme_path", p->filename);
|
|
|
|
|
|
|
|
/* Add the component as a child of PARENT. */
|
|
|
|
bounds.x = 0;
|
|
|
|
bounds.y = 0;
|
|
|
|
bounds.width = -1;
|
|
|
|
bounds.height = -1;
|
|
|
|
component->ops->set_bounds (component, &bounds);
|
|
|
|
parent->ops->add (parent, component);
|
|
|
|
|
|
|
|
skip_whitespace (p);
|
|
|
|
if (read_char (p) != '{')
|
|
|
|
{
|
|
|
|
grub_error (GRUB_ERR_IO,
|
|
|
|
"%s:%d:%d expected `{' after object type name `%s'",
|
|
|
|
p->filename, p->line_num, p->col_num, name);
|
|
|
|
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. */
|
|
|
|
if (component->ops->is_instance (component, "container"))
|
|
|
|
{
|
|
|
|
/* Read the sub-object recursively and add it as a child. */
|
|
|
|
if (read_object (p, (grub_gui_container_t) component) != 0)
|
|
|
|
goto cleanup;
|
|
|
|
/* After reading the sub-object, resume parsing, expecting
|
|
|
|
another property assignment or sub-object definition. */
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
grub_error (GRUB_ERR_IO,
|
|
|
|
"%s:%d:%d attempted to add object to non-container",
|
|
|
|
p->filename, p->line_num, p->col_num);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
char *property;
|
|
|
|
property = read_identifier (p);
|
|
|
|
if (! property)
|
|
|
|
{
|
|
|
|
grub_error (GRUB_ERR_IO, "%s:%d:%d identifier expected in theme file",
|
|
|
|
p->filename, p->line_num, p->col_num);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
skip_whitespace (p);
|
|
|
|
if (read_char (p) != '=')
|
|
|
|
{
|
|
|
|
grub_error (GRUB_ERR_IO,
|
|
|
|
"%s:%d:%d expected `=' after property name `%s'",
|
|
|
|
p->filename, p->line_num, p->col_num, property);
|
|
|
|
grub_free (property);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
skip_whitespace (p);
|
|
|
|
|
|
|
|
char *value;
|
|
|
|
value = read_expression (p);
|
|
|
|
if (! value)
|
|
|
|
{
|
|
|
|
grub_free (property);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Handle the property value. */
|
2009-12-29 16:31:02 +00:00
|
|
|
if (grub_strcmp (property, "left") == 0)
|
2010-01-05 18:38:11 +00:00
|
|
|
parse_proportional_spec (value, &component->x, &component->xfrac);
|
2009-12-29 16:31:02 +00:00
|
|
|
else if (grub_strcmp (property, "top") == 0)
|
2010-01-05 18:38:11 +00:00
|
|
|
parse_proportional_spec (value, &component->y, &component->yfrac);
|
2009-12-29 16:31:02 +00:00
|
|
|
else if (grub_strcmp (property, "width") == 0)
|
2010-01-05 18:38:11 +00:00
|
|
|
parse_proportional_spec (value, &component->w, &component->wfrac);
|
2009-12-29 16:31:02 +00:00
|
|
|
else if (grub_strcmp (property, "height") == 0)
|
2010-01-05 18:38:11 +00:00
|
|
|
parse_proportional_spec (value, &component->h, &component->hfrac);
|
2009-11-20 15:02:58 +00:00
|
|
|
else
|
2010-01-05 18:38:11 +00:00
|
|
|
/* General property handling. */
|
|
|
|
component->ops->set_property (component, property, value);
|
2009-11-20 15:02:58 +00:00
|
|
|
|
|
|
|
grub_free (value);
|
|
|
|
grub_free (property);
|
|
|
|
if (grub_errno != GRUB_ERR_NONE)
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
grub_free (name);
|
|
|
|
return grub_errno;
|
|
|
|
}
|
|
|
|
|
|
|
|
static grub_err_t
|
|
|
|
read_property (struct parsebuf *p)
|
|
|
|
{
|
|
|
|
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) != ':')
|
|
|
|
{
|
|
|
|
grub_error (GRUB_ERR_IO,
|
|
|
|
"%s:%d:%d missing separator after property name `%s'",
|
|
|
|
p->filename, p->line_num, p->col_num, name);
|
|
|
|
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"'). */
|
|
|
|
char *value = read_expression (p);
|
|
|
|
if (! value)
|
|
|
|
{
|
|
|
|
grub_error (GRUB_ERR_IO, "%s:%d:%d missing property value",
|
|
|
|
p->filename, p->line_num, p->col_num);
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
/* If theme_set_string results in an error, grub_errno will be returned
|
|
|
|
below. */
|
|
|
|
theme_set_string (p->view, name, value, p->theme_dir,
|
|
|
|
p->filename, p->line_num, p->col_num);
|
|
|
|
grub_free (value);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
grub_error (GRUB_ERR_IO,
|
|
|
|
"%s:%d:%d property value invalid; "
|
|
|
|
"enclose literal values in quotes (\")",
|
|
|
|
p->filename, p->line_num, p->col_num);
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
done:
|
|
|
|
grub_free (name);
|
|
|
|
return grub_errno;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set properties on the view based on settings from the specified
|
|
|
|
theme file. */
|
|
|
|
grub_err_t
|
|
|
|
grub_gfxmenu_view_load_theme (grub_gfxmenu_view_t view, const char *theme_path)
|
|
|
|
{
|
|
|
|
grub_file_t file;
|
|
|
|
struct parsebuf p;
|
|
|
|
|
|
|
|
p.view = view;
|
|
|
|
p.theme_dir = grub_get_dirname (theme_path);
|
|
|
|
|
2013-11-20 01:28:29 +00:00
|
|
|
file = grub_file_open (theme_path, GRUB_FILE_TYPE_THEME);
|
2009-11-20 15:02:58 +00:00
|
|
|
if (! file)
|
|
|
|
{
|
|
|
|
grub_free (p.theme_dir);
|
|
|
|
return grub_errno;
|
|
|
|
}
|
|
|
|
|
|
|
|
p.len = grub_file_size (file);
|
|
|
|
p.buf = grub_malloc (p.len);
|
|
|
|
p.pos = 0;
|
|
|
|
p.line_num = 1;
|
|
|
|
p.col_num = 1;
|
|
|
|
p.filename = theme_path;
|
|
|
|
if (! p.buf)
|
|
|
|
{
|
|
|
|
grub_file_close (file);
|
|
|
|
grub_free (p.theme_dir);
|
|
|
|
return grub_errno;
|
|
|
|
}
|
|
|
|
if (grub_file_read (file, p.buf, p.len) != p.len)
|
|
|
|
{
|
|
|
|
grub_free (p.buf);
|
|
|
|
grub_file_close (file);
|
|
|
|
grub_free (p.theme_dir);
|
|
|
|
return grub_errno;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (view->canvas)
|
2009-12-29 16:31:02 +00:00
|
|
|
view->canvas->component.ops->destroy (view->canvas);
|
2009-11-20 15:02:58 +00:00
|
|
|
|
|
|
|
view->canvas = grub_gui_canvas_new ();
|
2015-01-26 08:37:39 +00:00
|
|
|
if (!view->canvas)
|
|
|
|
goto fail;
|
2009-11-20 15:02:58 +00:00
|
|
|
((grub_gui_component_t) view->canvas)
|
|
|
|
->ops->set_bounds ((grub_gui_component_t) view->canvas,
|
|
|
|
&view->screen);
|
|
|
|
|
|
|
|
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);
|
|
|
|
read_object (&p, view->canvas);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
read_property (&p);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (grub_errno != GRUB_ERR_NONE)
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set the new theme path. */
|
|
|
|
grub_free (view->theme_path);
|
|
|
|
view->theme_path = grub_strdup (theme_path);
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
if (view->canvas)
|
|
|
|
{
|
2009-12-29 16:31:02 +00:00
|
|
|
view->canvas->component.ops->destroy (view->canvas);
|
2009-11-20 15:02:58 +00:00
|
|
|
view->canvas = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
grub_free (p.buf);
|
|
|
|
grub_file_close (file);
|
|
|
|
grub_free (p.theme_dir);
|
|
|
|
return grub_errno;
|
|
|
|
}
|