aboutsummaryrefslogtreecommitdiffstats
path: root/grub-core/gfxmenu/gui_string_util.c
diff options
context:
space:
mode:
Diffstat (limited to 'grub-core/gfxmenu/gui_string_util.c')
-rw-r--r--grub-core/gfxmenu/gui_string_util.c327
1 files changed, 327 insertions, 0 deletions
diff --git a/grub-core/gfxmenu/gui_string_util.c b/grub-core/gfxmenu/gui_string_util.c
new file mode 100644
index 0000000..8c51e39
--- /dev/null
+++ b/grub-core/gfxmenu/gui_string_util.c
@@ -0,0 +1,327 @@
+/* gui_string_util.c - String utilities used by the GUI system. */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2008,2009 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/gui_string_util.h>
+#include <grub/types.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+
+/* Create a new NUL-terminated string on the heap as a substring of BUF.
+ The range of buf included is the half-open interval [START,END).
+ The index START is inclusive, END is exclusive. */
+char *
+grub_new_substring (const char *buf,
+ grub_size_t start, grub_size_t end)
+{
+ if (end < start)
+ return 0;
+ grub_size_t len = end - start;
+ char *s = grub_malloc (len + 1);
+ if (! s)
+ return 0;
+ grub_memcpy (s, buf + start, len);
+ s[len] = '\0';
+ return s;
+}
+
+/* Eliminate "." and ".." path elements from PATH. A new heap-allocated
+ string is returned. */
+static char *
+canonicalize_path (const char *path)
+{
+ int i;
+ const char *p;
+ char *newpath = 0;
+
+ /* Count the path components in path. */
+ int components = 1;
+ for (p = path; *p; p++)
+ if (*p == '/')
+ components++;
+
+ char **path_array = grub_malloc (components * sizeof (*path_array));
+ if (! path_array)
+ return 0;
+
+ /* Initialize array elements to NULL pointers; in case once of the
+ allocations fails, the cleanup code can just call grub_free() for all
+ pointers in the array. */
+ for (i = 0; i < components; i++)
+ path_array[i] = 0;
+
+ /* Parse the path into path_array. */
+ p = path;
+ for (i = 0; i < components && p; i++)
+ {
+ /* Find the end of the path element. */
+ const char *end = grub_strchr (p, '/');
+ if (!end)
+ end = p + grub_strlen (p);
+
+ /* Copy the element. */
+ path_array[i] = grub_new_substring (p, 0, end - p);
+ if (! path_array[i])
+ goto cleanup;
+
+ /* Advance p to point to the start of the next element, or NULL. */
+ if (*end)
+ p = end + 1;
+ else
+ p = 0;
+ }
+
+ /* Eliminate '.' and '..' elements from the path array. */
+ int newpath_length = 0;
+ for (i = components - 1; i >= 0; --i)
+ {
+ if (! grub_strcmp (path_array[i], "."))
+ {
+ grub_free (path_array[i]);
+ path_array[i] = 0;
+ }
+ else if (! grub_strcmp (path_array[i], "..")
+ && i > 0)
+ {
+ /* Delete the '..' and the prior path element. */
+ grub_free (path_array[i]);
+ path_array[i] = 0;
+ --i;
+ grub_free (path_array[i]);
+ path_array[i] = 0;
+ }
+ else
+ {
+ newpath_length += grub_strlen (path_array[i]) + 1;
+ }
+ }
+
+ /* Construct a new path string. */
+ newpath = grub_malloc (newpath_length + 1);
+ if (! newpath)
+ goto cleanup;
+
+ newpath[0] = '\0';
+ char *newpath_end = newpath;
+ int first = 1;
+ for (i = 0; i < components; i++)
+ {
+ char *element = path_array[i];
+ if (element)
+ {
+ /* For all components but the first, prefix with a slash. */
+ if (! first)
+ newpath_end = grub_stpcpy (newpath_end, "/");
+ newpath_end = grub_stpcpy (newpath_end, element);
+ first = 0;
+ }
+ }
+
+cleanup:
+ for (i = 0; i < components; i++)
+ grub_free (path_array[i]);
+ grub_free (path_array);
+
+ return newpath;
+}
+
+/* Return a new heap-allocated string representing to absolute path
+ to the file referred to by PATH. If PATH is an absolute path, then
+ the returned path is a copy of PATH. If PATH is a relative path, then
+ BASE is with PATH used to construct the absolute path. */
+char *
+grub_resolve_relative_path (const char *base, const char *path)
+{
+ char *abspath;
+ char *canonpath;
+ char *p;
+ grub_size_t l;
+
+ /* If PATH is an absolute path, then just use it as is. */
+ if (path[0] == '/' || path[0] == '(')
+ return canonicalize_path (path);
+
+ abspath = grub_malloc (grub_strlen (base) + grub_strlen (path) + 3);
+ if (! abspath)
+ return 0;
+
+ /* Concatenate BASE and PATH. */
+ p = grub_stpcpy (abspath, base);
+ l = grub_strlen (abspath);
+ if (l == 0 || abspath[l-1] != '/')
+ {
+ *p = '/';
+ p++;
+ *p = 0;
+ }
+ grub_stpcpy (p, path);
+
+ canonpath = canonicalize_path (abspath);
+ if (! canonpath)
+ return abspath;
+
+ grub_free (abspath);
+ return canonpath;
+}
+
+/* Get the path of the directory where the file at FILE_PATH is located.
+ FILE_PATH should refer to a file, not a directory. The returned path
+ includes a trailing slash.
+ This does not handle GRUB "(hd0,0)" paths properly yet since it only
+ looks at slashes. */
+char *
+grub_get_dirname (const char *file_path)
+{
+ int i;
+ int last_slash;
+
+ last_slash = -1;
+ for (i = grub_strlen (file_path) - 1; i >= 0; --i)
+ {
+ if (file_path[i] == '/')
+ {
+ last_slash = i;
+ break;
+ }
+ }
+ if (last_slash == -1)
+ return grub_strdup ("/");
+
+ return grub_new_substring (file_path, 0, last_slash + 1);
+}
+
+static __inline int
+my_isxdigit (char c)
+{
+ return ((c >= '0' && c <= '9')
+ || (c >= 'a' && c <= 'f')
+ || (c >= 'A' && c <= 'F'));
+}
+
+static int
+parse_hex_color_component (const char *s, unsigned start, unsigned end)
+{
+ unsigned len;
+ char buf[3];
+
+ len = end - start;
+ /* Check the limits so we don't overrun the buffer. */
+ if (len < 1 || len > 2)
+ return 0;
+
+ if (len == 1)
+ {
+ buf[0] = s[start]; /* Get the first and only hex digit. */
+ buf[1] = buf[0]; /* Duplicate the hex digit. */
+ }
+ else if (len == 2)
+ {
+ buf[0] = s[start];
+ buf[1] = s[start + 1];
+ }
+
+ buf[2] = '\0';
+
+ return grub_strtoul (buf, 0, 16);
+}
+
+/* Parse a color string of the form "r, g, b", "#RGB", "#RGBA",
+ "#RRGGBB", or "#RRGGBBAA". */
+grub_err_t
+grub_gui_parse_color (const char *s, grub_gui_color_t *color)
+{
+ grub_gui_color_t c;
+
+ /* Skip whitespace. */
+ while (*s && grub_isspace (*s))
+ s++;
+
+ if (*s == '#')
+ {
+ /* HTML-style. Number if hex digits:
+ [6] #RRGGBB [3] #RGB
+ [8] #RRGGBBAA [4] #RGBA */
+
+ s++; /* Skip the '#'. */
+ /* Count the hexits to determine the format. */
+ int hexits = 0;
+ const char *end = s;
+ while (my_isxdigit (*end))
+ {
+ end++;
+ hexits++;
+ }
+
+ /* Parse the color components based on the format. */
+ if (hexits == 3 || hexits == 4)
+ {
+ c.red = parse_hex_color_component (s, 0, 1);
+ c.green = parse_hex_color_component (s, 1, 2);
+ c.blue = parse_hex_color_component (s, 2, 3);
+ if (hexits == 4)
+ c.alpha = parse_hex_color_component (s, 3, 4);
+ else
+ c.alpha = 255;
+ }
+ else if (hexits == 6 || hexits == 8)
+ {
+ c.red = parse_hex_color_component (s, 0, 2);
+ c.green = parse_hex_color_component (s, 2, 4);
+ c.blue = parse_hex_color_component (s, 4, 6);
+ if (hexits == 8)
+ c.alpha = parse_hex_color_component (s, 6, 8);
+ else
+ c.alpha = 255;
+ }
+ else
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
+ "invalid HTML-type color string `%s'", s);
+ }
+ else if (grub_isdigit (*s))
+ {
+ /* Comma separated decimal values. */
+ c.red = grub_strtoul (s, 0, 0);
+ if ((s = grub_strchr (s, ',')) == 0)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
+ "missing 1st comma separator in color `%s'", s);
+ s++;
+ c.green = grub_strtoul (s, 0, 0);
+ if ((s = grub_strchr (s, ',')) == 0)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
+ "missing 2nd comma separator in color `%s'", s);
+ s++;
+ c.blue = grub_strtoul (s, 0, 0);
+ if ((s = grub_strchr (s, ',')) == 0)
+ c.alpha = 255;
+ else
+ {
+ s++;
+ c.alpha = grub_strtoul (s, 0, 0);
+ }
+ }
+ else
+ {
+ if (! grub_gui_get_named_color (s, &c))
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
+ "invalid named color `%s'", s);
+ }
+
+ if (grub_errno == GRUB_ERR_NONE)
+ *color = c;
+ return grub_errno;
+}