summaryrefslogtreecommitdiff
path: root/gtk/gtkcssparser.c
diff options
context:
space:
mode:
authorBenjamin Otte <otte@redhat.com>2011-04-14 04:47:18 +0200
committerBenjamin Otte <otte@redhat.com>2011-05-18 22:17:55 +0200
commit7ccb9db79e702e507dedf211ed25787be2f32721 (patch)
treeea1f4e802eeeb04c74bbbc08f82705f12db1586a /gtk/gtkcssparser.c
parent058bbb2aec58a8c4c5184d63d7eddfa52ab91289 (diff)
downloadgtk+-7ccb9db79e702e507dedf211ed25787be2f32721.tar.gz
css: Rewrite the parser
Instead of relying on GScanner and its idea of syntax, code up a parser that obeys the CSS spec. This also has the great side effect of reporting correct line numbers and positions. Also included is a reorganization of the returned error values. Instead of error values describing what type of syntax error was returned, the code just returns SYNTAX_ERROR. Other messages exist for when actual values don't work or when errors shouldn't be fatal due to backwards compatibility.
Diffstat (limited to 'gtk/gtkcssparser.c')
-rw-r--r--gtk/gtkcssparser.c938
1 files changed, 938 insertions, 0 deletions
diff --git a/gtk/gtkcssparser.c b/gtk/gtkcssparser.c
new file mode 100644
index 0000000000..de798e781a
--- /dev/null
+++ b/gtk/gtkcssparser.c
@@ -0,0 +1,938 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2011 Benjamin Otte <otte@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "gtkcssparserprivate.h"
+
+#include <errno.h>
+#include <string.h>
+
+/* just for the errors, yay! */
+#include "gtkcssprovider.h"
+
+#define NEWLINE_CHARS "\r\n"
+#define WHITESPACE_CHARS "\f \t"
+#define NMSTART "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
+#define NMCHAR NMSTART "01234567890-_"
+#define URLCHAR NMCHAR "!#$%&*~"
+
+#define GTK_IS_CSS_PARSER(parser) ((parser) != NULL)
+
+struct _GtkCssParser
+{
+ const char *data;
+ GtkCssParserErrorFunc error_func;
+ gpointer user_data;
+
+ const char *line_start;
+ guint line;
+};
+
+GtkCssParser *
+_gtk_css_parser_new (const char *data,
+ GtkCssParserErrorFunc error_func,
+ gpointer user_data)
+{
+ GtkCssParser *parser;
+
+ g_return_val_if_fail (data != NULL, NULL);
+
+ parser = g_slice_new0 (GtkCssParser);
+
+ parser->data = data;
+ parser->error_func = error_func;
+ parser->user_data = user_data;
+
+ parser->line_start = data;
+ parser->line = 1;
+
+ return parser;
+}
+
+void
+_gtk_css_parser_free (GtkCssParser *parser)
+{
+ g_return_if_fail (GTK_IS_CSS_PARSER (parser));
+
+ g_slice_free (GtkCssParser, parser);
+}
+
+gboolean
+_gtk_css_parser_is_eof (GtkCssParser *parser)
+{
+ g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), TRUE);
+
+ return *parser->data == 0;
+}
+
+gboolean
+_gtk_css_parser_begins_with (GtkCssParser *parser,
+ char c)
+{
+ g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), TRUE);
+
+ return *parser->data == c;
+}
+
+guint
+_gtk_css_parser_get_line (GtkCssParser *parser)
+{
+ g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), 1);
+
+ return parser->line;
+}
+
+guint
+_gtk_css_parser_get_position (GtkCssParser *parser)
+{
+ g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), 1);
+
+ return parser->data - parser->line_start;
+}
+
+void
+_gtk_css_parser_error (GtkCssParser *parser,
+ const char *format,
+ ...)
+{
+ GError *error;
+
+ va_list args;
+
+ va_start (args, format);
+ error = g_error_new_valist (GTK_CSS_PROVIDER_ERROR,
+ GTK_CSS_PROVIDER_ERROR_SYNTAX,
+ format, args);
+ va_end (args);
+
+ parser->error_func (parser, error, parser->user_data);
+
+ g_error_free (error);
+}
+
+static gboolean
+gtk_css_parser_new_line (GtkCssParser *parser)
+{
+ gboolean result = FALSE;
+
+ if (*parser->data == '\r')
+ {
+ result = TRUE;
+ parser->data++;
+ }
+ if (*parser->data == '\n')
+ {
+ result = TRUE;
+ parser->data++;
+ }
+
+ if (result)
+ {
+ parser->line++;
+ parser->line_start = parser->data;
+ }
+
+ return result;
+}
+
+static gboolean
+gtk_css_parser_skip_comment (GtkCssParser *parser)
+{
+ if (parser->data[0] != '/' ||
+ parser->data[1] != '*')
+ return FALSE;
+
+ parser->data += 2;
+
+ while (*parser->data)
+ {
+ gsize len = strcspn (parser->data, NEWLINE_CHARS "/");
+
+ parser->data += len;
+
+ if (gtk_css_parser_new_line (parser))
+ continue;
+
+ parser->data++;
+
+ if (parser->data[-2] == '*')
+ return TRUE;
+ if (parser->data[0] == '*')
+ _gtk_css_parser_error (parser, "'/*' in comment block");
+ }
+
+ /* FIXME: position */
+ _gtk_css_parser_error (parser, "Unterminated comment");
+ return TRUE;
+}
+
+void
+_gtk_css_parser_skip_whitespace (GtkCssParser *parser)
+{
+ size_t len;
+
+ while (*parser->data)
+ {
+ if (gtk_css_parser_new_line (parser))
+ continue;
+
+ len = strspn (parser->data, WHITESPACE_CHARS);
+ if (len)
+ {
+ parser->data += len;
+ continue;
+ }
+
+ if (!gtk_css_parser_skip_comment (parser))
+ break;
+ }
+}
+
+gboolean
+_gtk_css_parser_try (GtkCssParser *parser,
+ const char *string,
+ gboolean skip_whitespace)
+{
+ g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), FALSE);
+ g_return_val_if_fail (string != NULL, FALSE);
+
+ if (g_ascii_strncasecmp (parser->data, string, strlen (string)) != 0)
+ return FALSE;
+
+ parser->data += strlen (string);
+
+ if (skip_whitespace)
+ _gtk_css_parser_skip_whitespace (parser);
+ return TRUE;
+}
+
+static guint
+get_xdigit (char c)
+{
+ if (c >= 'a')
+ return c - 'a' + 10;
+ else if (c >= 'A')
+ return c - 'A' + 10;
+ else
+ return c - '0';
+}
+
+static void
+_gtk_css_parser_unescape (GtkCssParser *parser,
+ GString *str)
+{
+ guint i;
+ gunichar result = 0;
+
+ g_assert (*parser->data == '\\');
+
+ parser->data++;
+
+ for (i = 0; i < 6; i++)
+ {
+ if (!g_ascii_isxdigit (parser->data[i]))
+ break;
+
+ result = (result << 4) + get_xdigit (parser->data[i]);
+ }
+
+ if (i != 0)
+ {
+ g_string_append_unichar (str, result);
+ parser->data += i;
+
+ /* NB: gtk_css_parser_new_line() forward data pointer itself */
+ if (!gtk_css_parser_new_line (parser) &&
+ *parser->data &&
+ strchr (WHITESPACE_CHARS, *parser->data))
+ parser->data++;
+ return;
+ }
+
+ if (gtk_css_parser_new_line (parser))
+ return;
+
+ g_string_append_c (str, *parser->data);
+ parser->data++;
+
+ return;
+}
+
+static gboolean
+_gtk_css_parser_read_char (GtkCssParser *parser,
+ GString * str,
+ const char * allowed)
+{
+ if (*parser->data == 0)
+ return FALSE;
+
+ if (strchr (allowed, *parser->data))
+ {
+ g_string_append_c (str, *parser->data);
+ parser->data++;
+ return TRUE;
+ }
+ if (*parser->data >= 127)
+ {
+ gsize len = g_utf8_skip[(guint) *(guchar *) parser->data];
+
+ g_string_append_len (str, parser->data, len);
+ parser->data += len;
+ return TRUE;
+ }
+ if (*parser->data == '\\')
+ {
+ _gtk_css_parser_unescape (parser, str);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+char *
+_gtk_css_parser_try_name (GtkCssParser *parser,
+ gboolean skip_whitespace)
+{
+ GString *name;
+
+ g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), NULL);
+
+ name = g_string_new (NULL);
+
+ while (_gtk_css_parser_read_char (parser, name, NMCHAR))
+ ;
+
+ if (skip_whitespace)
+ _gtk_css_parser_skip_whitespace (parser);
+
+ return g_string_free (name, FALSE);
+}
+
+char *
+_gtk_css_parser_try_ident (GtkCssParser *parser,
+ gboolean skip_whitespace)
+{
+ const char *start;
+ GString *ident;
+
+ g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), NULL);
+
+ start = parser->data;
+
+ ident = g_string_new (NULL);
+
+ if (*parser->data == '-')
+ {
+ g_string_append_c (ident, '-');
+ parser->data++;
+ }
+
+ if (!_gtk_css_parser_read_char (parser, ident, NMSTART))
+ {
+ parser->data = start;
+ g_string_free (ident, TRUE);
+ return NULL;
+ }
+
+ while (_gtk_css_parser_read_char (parser, ident, NMCHAR))
+ ;
+
+ if (skip_whitespace)
+ _gtk_css_parser_skip_whitespace (parser);
+
+ return g_string_free (ident, FALSE);
+}
+
+gboolean
+_gtk_css_parser_is_string (GtkCssParser *parser)
+{
+ g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), FALSE);
+
+ return *parser->data == '"' || *parser->data == '\'';
+}
+
+char *
+_gtk_css_parser_read_string (GtkCssParser *parser)
+{
+ GString *str;
+ char quote;
+
+ g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), NULL);
+
+ quote = *parser->data;
+
+ if (quote != '"' && quote != '\'')
+ return NULL;
+
+ parser->data++;
+ str = g_string_new (NULL);
+
+ while (TRUE)
+ {
+ gsize len = strcspn (parser->data, "\\'\"\n\r\f");
+
+ g_string_append_len (str, parser->data, len);
+
+ parser->data += len;
+
+ switch (*parser->data)
+ {
+ case '\\':
+ _gtk_css_parser_unescape (parser, str);
+ break;
+ case '"':
+ case '\'':
+ if (*parser->data == quote)
+ {
+ parser->data++;
+ _gtk_css_parser_skip_whitespace (parser);
+ return g_string_free (str, FALSE);
+ }
+
+ g_string_append_c (str, *parser->data);
+ parser->data++;
+ break;
+ case '\0':
+ /* FIXME: position */
+ _gtk_css_parser_error (parser, "Missing end quote in string.");
+ g_string_free (str, TRUE);
+ return NULL;
+ default:
+ _gtk_css_parser_error (parser,
+ "Invalid character in string. Must be escaped.");
+ g_string_free (str, TRUE);
+ return NULL;
+ }
+ }
+
+ g_assert_not_reached ();
+ return NULL;
+}
+
+char *
+_gtk_css_parser_read_uri (GtkCssParser *parser)
+{
+ char *result;
+
+ g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), NULL);
+
+ if (!_gtk_css_parser_try (parser, "url(", TRUE))
+ {
+ _gtk_css_parser_error (parser, "expected 'url('");
+ return NULL;
+ }
+
+ _gtk_css_parser_skip_whitespace (parser);
+
+ if (_gtk_css_parser_is_string (parser))
+ {
+ result = _gtk_css_parser_read_string (parser);
+ }
+ else
+ {
+ GString *str = g_string_new (NULL);
+
+ while (_gtk_css_parser_read_char (parser, str, URLCHAR))
+ ;
+ result = g_string_free (str, FALSE);
+ if (result == NULL)
+ _gtk_css_parser_error (parser, "not a url");
+ }
+
+ if (result == NULL)
+ return NULL;
+
+ _gtk_css_parser_skip_whitespace (parser);
+
+ if (*parser->data != ')')
+ {
+ _gtk_css_parser_error (parser, "missing ')' for url");
+ g_free (result);
+ return NULL;
+ }
+
+ parser->data++;
+
+ _gtk_css_parser_skip_whitespace (parser);
+
+ return result;
+}
+
+gboolean
+_gtk_css_parser_try_int (GtkCssParser *parser,
+ int *value)
+{
+ gint64 result;
+ char *end;
+
+ g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), FALSE);
+ g_return_val_if_fail (value != NULL, FALSE);
+
+ /* strtoll parses a plus, but we are not allowed to */
+ if (*parser->data == '+')
+ return FALSE;
+
+ errno = 0;
+ result = g_ascii_strtoll (parser->data, &end, 10);
+ if (errno)
+ return FALSE;
+ if (result > G_MAXINT || result < G_MININT)
+ return FALSE;
+ if (parser->data == end)
+ return FALSE;
+
+ parser->data = end;
+ *value = result;
+
+ _gtk_css_parser_skip_whitespace (parser);
+
+ return TRUE;
+}
+
+gboolean
+_gtk_css_parser_try_uint (GtkCssParser *parser,
+ uint *value)
+{
+ guint64 result;
+ char *end;
+
+ g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), FALSE);
+ g_return_val_if_fail (value != NULL, FALSE);
+
+ errno = 0;
+ result = g_ascii_strtoull (parser->data, &end, 10);
+ if (errno)
+ return FALSE;
+ if (result > G_MAXUINT)
+ return FALSE;
+ if (parser->data == end)
+ return FALSE;
+
+ parser->data = end;
+ *value = result;
+
+ _gtk_css_parser_skip_whitespace (parser);
+
+ return TRUE;
+}
+
+gboolean
+_gtk_css_parser_try_double (GtkCssParser *parser,
+ gdouble *value)
+{
+ gdouble result;
+ char *end;
+
+ g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), FALSE);
+ g_return_val_if_fail (value != NULL, FALSE);
+
+ errno = 0;
+ result = g_ascii_strtod (parser->data, &end);
+ if (errno)
+ return FALSE;
+ if (parser->data == end)
+ return FALSE;
+
+ parser->data = end;
+ *value = result;
+
+ _gtk_css_parser_skip_whitespace (parser);
+
+ return TRUE;
+}
+
+typedef enum {
+ COLOR_RGBA,
+ COLOR_RGB,
+ COLOR_LIGHTER,
+ COLOR_DARKER,
+ COLOR_SHADE,
+ COLOR_ALPHA,
+ COLOR_MIX
+} ColorType;
+
+static GtkSymbolicColor *
+gtk_css_parser_read_symbolic_color_function (GtkCssParser *parser,
+ ColorType color)
+{
+ GtkSymbolicColor *symbolic;
+ GtkSymbolicColor *child1, *child2;
+ double value;
+
+ if (!_gtk_css_parser_try (parser, "(", TRUE))
+ {
+ _gtk_css_parser_error (parser, "Missing opening bracket in color definition");
+ return NULL;
+ }
+
+ if (color == COLOR_RGB || color == COLOR_RGBA)
+ {
+ GdkRGBA rgba;
+ double tmp;
+ guint i;
+
+ for (i = 0; i < 3; i++)
+ {
+ if (i > 0 && !_gtk_css_parser_try (parser, ",", TRUE))
+ {
+ _gtk_css_parser_error (parser, "Expected ',' in color definition");
+ return NULL;
+ }
+
+ if (!_gtk_css_parser_try_double (parser, &tmp))
+ {
+ _gtk_css_parser_error (parser, "Invalid number for color value");
+ return NULL;
+ }
+ if (_gtk_css_parser_try (parser, "%", TRUE))
+ tmp /= 100.0;
+ else
+ tmp /= 255.0;
+ if (i == 0)
+ rgba.red = tmp;
+ else if (i == 1)
+ rgba.green = tmp;
+ else if (i == 2)
+ rgba.blue = tmp;
+ else
+ g_assert_not_reached ();
+ }
+
+ if (color == COLOR_RGBA)
+ {
+ if (i > 0 && !_gtk_css_parser_try (parser, ",", TRUE))
+ {
+ _gtk_css_parser_error (parser, "Expected ',' in color definition");
+ return NULL;
+ }
+
+ if (!_gtk_css_parser_try_double (parser, &rgba.alpha))
+ {
+ _gtk_css_parser_error (parser, "Invalid number for alpha value");
+ return NULL;
+ }
+ }
+ else
+ rgba.alpha = 1.0;
+
+ symbolic = gtk_symbolic_color_new_literal (&rgba);
+ }
+ else
+ {
+ child1 = _gtk_css_parser_read_symbolic_color (parser);
+ if (child1 == NULL)
+ return NULL;
+
+ if (color == COLOR_MIX)
+ {
+ if (!_gtk_css_parser_try (parser, ",", TRUE))
+ {
+ _gtk_css_parser_error (parser, "Expected ',' in color definition");
+ gtk_symbolic_color_unref (child1);
+ return NULL;
+ }
+
+ child2 = _gtk_css_parser_read_symbolic_color (parser);
+ if (child2 == NULL)
+ {
+ g_object_unref (child1);
+ return NULL;
+ }
+ }
+ else
+ child2 = NULL;
+
+ if (color == COLOR_LIGHTER)
+ value = 1.3;
+ else if (color == COLOR_DARKER)
+ value = 0.7;
+ else
+ {
+ if (!_gtk_css_parser_try (parser, ",", TRUE))
+ {
+ _gtk_css_parser_error (parser, "Expected ',' in color definition");
+ gtk_symbolic_color_unref (child1);
+ if (child2)
+ gtk_symbolic_color_unref (child2);
+ return NULL;
+ }
+
+ if (!_gtk_css_parser_try_double (parser, &value))
+ {
+ _gtk_css_parser_error (parser, "Expected number in color definition");
+ gtk_symbolic_color_unref (child1);
+ if (child2)
+ gtk_symbolic_color_unref (child2);
+ return NULL;
+ }
+ }
+
+ switch (color)
+ {
+ case COLOR_LIGHTER:
+ case COLOR_DARKER:
+ case COLOR_SHADE:
+ symbolic = gtk_symbolic_color_new_shade (child1, value);
+ break;
+ case COLOR_ALPHA:
+ symbolic = gtk_symbolic_color_new_alpha (child1, value);
+ break;
+ case COLOR_MIX:
+ symbolic = gtk_symbolic_color_new_mix (child1, child2, value);
+ break;
+ default:
+ g_assert_not_reached ();
+ symbolic = NULL;
+ }
+
+ gtk_symbolic_color_unref (child1);
+ if (child2)
+ gtk_symbolic_color_unref (child2);
+ }
+
+ if (!_gtk_css_parser_try (parser, ")", TRUE))
+ {
+ gtk_symbolic_color_unref (symbolic);
+ return NULL;
+ }
+
+ return symbolic;
+}
+
+static GtkSymbolicColor *
+gtk_css_parser_try_hash_color (GtkCssParser *parser)
+{
+ if (parser->data[0] == '#' &&
+ g_ascii_isxdigit (parser->data[1]) &&
+ g_ascii_isxdigit (parser->data[2]) &&
+ g_ascii_isxdigit (parser->data[3]))
+ {
+ GdkRGBA rgba;
+
+ if (g_ascii_isxdigit (parser->data[4]) &&
+ g_ascii_isxdigit (parser->data[5]) &&
+ g_ascii_isxdigit (parser->data[6]))
+ {
+ rgba.red = ((get_xdigit (parser->data[1]) << 4) + get_xdigit (parser->data[2])) / 255.0;
+ rgba.green = ((get_xdigit (parser->data[3]) << 4) + get_xdigit (parser->data[4])) / 255.0;
+ rgba.blue = ((get_xdigit (parser->data[5]) << 4) + get_xdigit (parser->data[6])) / 255.0;
+ rgba.alpha = 1.0;
+ parser->data += 7;
+ }
+ else
+ {
+ rgba.red = get_xdigit (parser->data[1]) / 15.0;
+ rgba.green = get_xdigit (parser->data[2]) / 15.0;
+ rgba.blue = get_xdigit (parser->data[3]) / 15.0;
+ rgba.alpha = 1.0;
+ parser->data += 4;
+ }
+
+ _gtk_css_parser_skip_whitespace (parser);
+
+ return gtk_symbolic_color_new_literal (&rgba);
+ }
+
+ return NULL;
+}
+
+GtkSymbolicColor *
+_gtk_css_parser_read_symbolic_color (GtkCssParser *parser)
+{
+ GtkSymbolicColor *symbolic;
+ guint color;
+ const char *names[] = {"rgba", "rgb", "lighter", "darker", "shade", "alpha", "mix" };
+ char *name;
+
+ g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), NULL);
+
+ if (_gtk_css_parser_try (parser, "@", FALSE))
+ {
+ name = _gtk_css_parser_try_name (parser, TRUE);
+
+ if (name)
+ {
+ symbolic = gtk_symbolic_color_new_name (name);
+ }
+ else
+ {
+ _gtk_css_parser_error (parser, "'%s' is not a valid symbolic color name", name);
+ symbolic = NULL;
+ }
+
+ g_free (name);
+ return symbolic;
+ }
+
+ for (color = 0; color < G_N_ELEMENTS (names); color++)
+ {
+ if (_gtk_css_parser_try (parser, names[color], TRUE))
+ break;
+ }
+
+ if (color < G_N_ELEMENTS (names))
+ return gtk_css_parser_read_symbolic_color_function (parser, color);
+
+ symbolic = gtk_css_parser_try_hash_color (parser);
+ if (symbolic)
+ return symbolic;
+
+ name = _gtk_css_parser_try_name (parser, TRUE);
+ if (name)
+ {
+ GdkRGBA rgba;
+
+ if (gdk_rgba_parse (&rgba, name))
+ {
+ symbolic = gtk_symbolic_color_new_literal (&rgba);
+ }
+ else
+ {
+ _gtk_css_parser_error (parser, "'%s' is not a valid color name", name);
+ symbolic = NULL;
+ }
+ g_free (name);
+ return symbolic;
+ }
+
+ _gtk_css_parser_error (parser, "Not a color definition");
+ return NULL;
+}
+
+void
+_gtk_css_parser_resync_internal (GtkCssParser *parser,
+ gboolean sync_at_semicolon,
+ gboolean read_sync_token,
+ char terminator)
+{
+ gsize len;
+
+ do {
+ len = strcspn (parser->data, "\\\"'/()[]{};" NEWLINE_CHARS);
+ parser->data += len;
+
+ if (gtk_css_parser_new_line (parser))
+ continue;
+
+ if (_gtk_css_parser_is_string (parser))
+ {
+ /* Hrm, this emits errors, and i suspect it shouldn't... */
+ char *free_me = _gtk_css_parser_read_string (parser);
+ g_free (free_me);
+ continue;
+ }
+
+ if (gtk_css_parser_skip_comment (parser))
+ continue;
+
+ switch (*parser->data)
+ {
+ case '/':
+ {
+ GString *ignore = g_string_new (NULL);
+ _gtk_css_parser_unescape (parser, ignore);
+ g_string_free (ignore, TRUE);
+ }
+ break;
+ case ';':
+ if (sync_at_semicolon && !read_sync_token)
+ return;
+ parser->data++;
+ if (sync_at_semicolon)
+ {
+ _gtk_css_parser_skip_whitespace (parser);
+ return;
+ }
+ break;
+ case '(':
+ parser->data++;
+ _gtk_css_parser_resync (parser, FALSE, ')');
+ parser->data++;
+ break;
+ case '[':
+ parser->data++;
+ _gtk_css_parser_resync (parser, FALSE, ']');
+ parser->data++;
+ break;
+ case '{':
+ parser->data++;
+ _gtk_css_parser_resync (parser, FALSE, '}');
+ parser->data++;
+ if (sync_at_semicolon || !terminator)
+ {
+ _gtk_css_parser_skip_whitespace (parser);
+ return;
+ }
+ break;
+ case '}':
+ case ')':
+ case ']':
+ if (terminator == *parser->data)
+ {
+ _gtk_css_parser_skip_whitespace (parser);
+ return;
+ }
+ parser->data++;
+ continue;
+ default:
+ break;
+ }
+ } while (*parser->data);
+}
+
+char *
+_gtk_css_parser_read_value (GtkCssParser *parser)
+{
+ const char *start;
+ char *result;
+
+ g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), NULL);
+
+ start = parser->data;
+
+ /* This needs to be done better */
+ _gtk_css_parser_resync_internal (parser, TRUE, FALSE, '}');
+
+ result = g_strndup (start, parser->data - start);
+ if (result)
+ {
+ g_strchomp (result);
+ if (result[0] == 0)
+ {
+ g_free (result);
+ result = NULL;
+ }
+ }
+
+ if (result == NULL)
+ _gtk_css_parser_error (parser, "Expected a property value");
+
+ return result;
+}
+
+void
+_gtk_css_parser_resync (GtkCssParser *parser,
+ gboolean sync_at_semicolon,
+ char terminator)
+{
+ g_return_if_fail (GTK_IS_CSS_PARSER (parser));
+
+ _gtk_css_parser_resync_internal (parser, sync_at_semicolon, TRUE, terminator);
+}