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; }
GFile * _gtk_css_parser_read_url (GtkCssParser *parser) { gchar *path; char *scheme; GFile *file; if (_gtk_css_parser_try (parser, "url", FALSE)) { if (!_gtk_css_parser_try (parser, "(", TRUE)) { _gtk_css_parser_skip_whitespace (parser); if (_gtk_css_parser_try (parser, "(", TRUE)) { _gtk_css_parser_error_full (parser, GTK_CSS_PROVIDER_ERROR_DEPRECATED, "Whitespace between 'url' and '(' is deprecated"); } else { _gtk_css_parser_error (parser, "Expected '(' after 'url'"); return NULL; } } path = _gtk_css_parser_read_string (parser); if (path == NULL) return NULL; if (!_gtk_css_parser_try (parser, ")", TRUE)) { _gtk_css_parser_error (parser, "No closing ')' found for 'url'"); g_free (path); return NULL; } scheme = g_uri_parse_scheme (path); if (scheme != NULL) { file = g_file_new_for_uri (path); g_free (path); g_free (scheme); return file; } } else { path = _gtk_css_parser_try_name (parser, TRUE); if (path == NULL) { _gtk_css_parser_error (parser, "Not a valid url"); return NULL; } } file = _gtk_css_parser_get_file_for_path (parser, path); g_free (path); return file; }
gboolean _gtk_css_parser_try_hash_color (GtkCssParser *parser, GdkRGBA *rgba) { if (parser->data[0] == '#' && g_ascii_isxdigit (parser->data[1]) && g_ascii_isxdigit (parser->data[2]) && g_ascii_isxdigit (parser->data[3])) { 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 TRUE; } return FALSE; }
gboolean _gtk_css_parser_try_uint (GtkCssParser *parser, guint *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_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; }
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 != '\'') { _gtk_css_parser_error (parser, "Expected a string."); 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_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); }
GtkCssValue * _gtk_css_ease_value_parse (GtkCssParser *parser) { guint i; g_return_val_if_fail (parser != NULL, NULL); for (i = 0; i < G_N_ELEMENTS (parser_values); i++) { if (_gtk_css_parser_try (parser, parser_values[i].name, FALSE)) { if (parser_values[i].needs_custom) { if (parser_values[i].is_bezier) return gtk_css_ease_value_parse_cubic_bezier (parser); else return gtk_css_ease_value_parse_steps (parser); } _gtk_css_parser_skip_whitespace (parser); if (parser_values[i].is_bezier) return _gtk_css_ease_value_new_cubic_bezier (parser_values[i].values[0], parser_values[i].values[1], parser_values[i].values[2], parser_values[i].values[3]); else return _gtk_css_ease_value_new_steps (parser_values[i].values[0], parser_values[i].values[1] != 0.0); } } _gtk_css_parser_error (parser, "Unknown value"); return NULL; }
GFile * _gtk_css_parse_url (GtkCssParser *parser, GFile *base) { gchar *path; GFile *file; if (_gtk_css_parser_try (parser, "url", FALSE)) { if (!_gtk_css_parser_try (parser, "(", TRUE)) { _gtk_css_parser_skip_whitespace (parser); if (_gtk_css_parser_try (parser, "(", TRUE)) { GError *error; error = g_error_new_literal (GTK_CSS_PROVIDER_ERROR, GTK_CSS_PROVIDER_ERROR_DEPRECATED, "Whitespace between 'url' and '(' is not allowed"); _gtk_css_parser_take_error (parser, error); } else { _gtk_css_parser_error (parser, "Expected '(' after 'url'"); return NULL; } } path = _gtk_css_parser_read_string (parser); if (path == NULL) return NULL; if (!_gtk_css_parser_try (parser, ")", TRUE)) { _gtk_css_parser_error (parser, "No closing ')' found for 'url'"); g_free (path); return NULL; } } else { path = _gtk_css_parser_try_name (parser, TRUE); if (path == NULL) { _gtk_css_parser_error (parser, "Not a valid url"); return NULL; } } file = g_file_resolve_relative_path (base, path); g_free (path); return file; }
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; }
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); }
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; }
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; }
static 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, ')'); if (*parser->data) parser->data++; break; case '[': parser->data++; _gtk_css_parser_resync (parser, FALSE, ']'); if (*parser->data) parser->data++; break; case '{': parser->data++; _gtk_css_parser_resync (parser, FALSE, '}'); if (*parser->data) 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; case '\0': break; case '/': default: parser->data++; break; } } while (*parser->data); }
GtkCssValue * _gtk_css_number_value_parse (GtkCssParser *parser, GtkCssNumberParseFlags flags) { static const struct { const char *name; GtkCssUnit unit; GtkCssNumberParseFlags required_flags; } units[] = { { "px", GTK_CSS_PX, GTK_CSS_PARSE_LENGTH }, { "pt", GTK_CSS_PT, GTK_CSS_PARSE_LENGTH }, { "em", GTK_CSS_EM, GTK_CSS_PARSE_LENGTH }, { "ex", GTK_CSS_EX, GTK_CSS_PARSE_LENGTH }, { "pc", GTK_CSS_PC, GTK_CSS_PARSE_LENGTH }, { "in", GTK_CSS_IN, GTK_CSS_PARSE_LENGTH }, { "cm", GTK_CSS_CM, GTK_CSS_PARSE_LENGTH }, { "mm", GTK_CSS_MM, GTK_CSS_PARSE_LENGTH }, { "rad", GTK_CSS_RAD, GTK_CSS_PARSE_ANGLE }, { "deg", GTK_CSS_DEG, GTK_CSS_PARSE_ANGLE }, { "grad", GTK_CSS_GRAD, GTK_CSS_PARSE_ANGLE }, { "turn", GTK_CSS_TURN, GTK_CSS_PARSE_ANGLE }, { "s", GTK_CSS_S, GTK_CSS_PARSE_TIME }, { "ms", GTK_CSS_MS, GTK_CSS_PARSE_TIME } }; char *end, *unit_name; double value; GtkCssUnit unit; g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), NULL); errno = 0; value = g_ascii_strtod (parser->data, &end); if (errno) { _gtk_css_parser_error (parser, "not a number: %s", g_strerror (errno)); return NULL; } if (parser->data == end) { _gtk_css_parser_error (parser, "not a number"); return NULL; } parser->data = end; if (flags & GTK_CSS_POSITIVE_ONLY && value < 0) { _gtk_css_parser_error (parser, "negative values are not allowed."); return NULL; } unit_name = _gtk_css_parser_try_ident (parser, FALSE); if (unit_name) { guint i; for (i = 0; i < G_N_ELEMENTS (units); i++) { if (flags & units[i].required_flags && g_ascii_strcasecmp (unit_name, units[i].name) == 0) break; } if (i >= G_N_ELEMENTS (units)) { _gtk_css_parser_error (parser, "`%s' is not a valid unit.", unit_name); g_free (unit_name); return NULL; } unit = units[i].unit; g_free (unit_name); } else { if ((flags & GTK_CSS_PARSE_PERCENT) && _gtk_css_parser_try (parser, "%", FALSE)) { unit = GTK_CSS_PERCENT; } else if (value == 0.0) { if (flags & GTK_CSS_PARSE_NUMBER) unit = GTK_CSS_NUMBER; else if (flags & GTK_CSS_PARSE_LENGTH) unit = GTK_CSS_PX; else if (flags & GTK_CSS_PARSE_ANGLE) unit = GTK_CSS_DEG; else if (flags & GTK_CSS_PARSE_TIME) unit = GTK_CSS_S; else unit = GTK_CSS_PERCENT; } else if (flags & GTK_CSS_NUMBER_AS_PIXELS) { _gtk_css_parser_error_full (parser, GTK_CSS_PROVIDER_ERROR_DEPRECATED, "Not using units is deprecated. Assuming 'px'."); unit = GTK_CSS_PX; } else if (flags & GTK_CSS_PARSE_NUMBER) { unit = GTK_CSS_NUMBER; } else { _gtk_css_parser_error (parser, "Unit is missing."); return NULL; } } _gtk_css_parser_skip_whitespace (parser); return _gtk_css_number_value_new (value, unit); }