Also remove the starting underscore from function names where appropriate, as those functions are static now and not exported anymore. This is part of a bunch of fixes for gcc complaining about -Wmissing-declarations.
		
			
				
	
	
		
			1015 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1015 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* 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, see <http://www.gnu.org/licenses/>.
 | 
						|
 */
 | 
						|
 | 
						|
#include "config.h"
 | 
						|
 | 
						|
#include "gtkcssparserprivate.h"
 | 
						|
 | 
						|
#include "gtkcssnumbervalueprivate.h"
 | 
						|
#include "gtkwin32themeprivate.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;
 | 
						|
  GFile                 *file;
 | 
						|
  GtkCssParserErrorFunc  error_func;
 | 
						|
  gpointer               user_data;
 | 
						|
 | 
						|
  const char            *line_start;
 | 
						|
  guint                  line;
 | 
						|
};
 | 
						|
 | 
						|
GtkCssParser *
 | 
						|
_gtk_css_parser_new (const char            *data,
 | 
						|
                     GFile                 *file,
 | 
						|
                     GtkCssParserErrorFunc  error_func,
 | 
						|
                     gpointer               user_data)
 | 
						|
{
 | 
						|
  GtkCssParser *parser;
 | 
						|
 | 
						|
  g_return_val_if_fail (data != NULL, NULL);
 | 
						|
  g_return_val_if_fail (file == NULL || G_IS_FILE (file), NULL);
 | 
						|
 | 
						|
  parser = g_slice_new0 (GtkCssParser);
 | 
						|
 | 
						|
  parser->data = data;
 | 
						|
  if (file)
 | 
						|
    parser->file = g_object_ref (file);
 | 
						|
  parser->error_func = error_func;
 | 
						|
  parser->user_data = user_data;
 | 
						|
 | 
						|
  parser->line_start = data;
 | 
						|
  parser->line = 0;
 | 
						|
 | 
						|
  return parser;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
_gtk_css_parser_free (GtkCssParser *parser)
 | 
						|
{
 | 
						|
  g_return_if_fail (GTK_IS_CSS_PARSER (parser));
 | 
						|
 | 
						|
  if (parser->file)
 | 
						|
    g_object_unref (parser->file);
 | 
						|
 | 
						|
  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;
 | 
						|
}
 | 
						|
 | 
						|
gboolean
 | 
						|
_gtk_css_parser_has_prefix (GtkCssParser *parser,
 | 
						|
                            const char   *prefix)
 | 
						|
{
 | 
						|
  g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), FALSE);
 | 
						|
 | 
						|
  return g_ascii_strncasecmp (parser->data, prefix, strlen (prefix)) == 0;
 | 
						|
}
 | 
						|
 | 
						|
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;
 | 
						|
}
 | 
						|
 | 
						|
static GFile *
 | 
						|
gtk_css_parser_get_base_file (GtkCssParser *parser)
 | 
						|
{
 | 
						|
  GFile *base;
 | 
						|
 | 
						|
  if (parser->file)
 | 
						|
    {
 | 
						|
      base = g_file_get_parent (parser->file);
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      char *dir = g_get_current_dir ();
 | 
						|
      base = g_file_new_for_path (dir);
 | 
						|
      g_free (dir);
 | 
						|
    }
 | 
						|
 | 
						|
  return base;
 | 
						|
}
 | 
						|
 | 
						|
GFile *
 | 
						|
_gtk_css_parser_get_file_for_path (GtkCssParser *parser,
 | 
						|
                                   const char   *path)
 | 
						|
{
 | 
						|
  GFile *base, *file;
 | 
						|
 | 
						|
  g_return_val_if_fail (parser != NULL, NULL);
 | 
						|
  g_return_val_if_fail (path != NULL, NULL);
 | 
						|
 | 
						|
  base = gtk_css_parser_get_base_file (parser);
 | 
						|
  file = g_file_resolve_relative_path (base, path);
 | 
						|
  g_object_unref (base);
 | 
						|
 | 
						|
  return file;
 | 
						|
}
 | 
						|
 | 
						|
GFile *
 | 
						|
_gtk_css_parser_get_file (GtkCssParser *parser)
 | 
						|
{
 | 
						|
  g_return_val_if_fail (parser != NULL, NULL);
 | 
						|
 | 
						|
  return parser->file;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
_gtk_css_parser_take_error (GtkCssParser *parser,
 | 
						|
                            GError       *error)
 | 
						|
{
 | 
						|
  parser->error_func (parser, error, parser->user_data);
 | 
						|
 | 
						|
  g_error_free (error);
 | 
						|
}
 | 
						|
 | 
						|
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);
 | 
						|
 | 
						|
  _gtk_css_parser_take_error (parser, error);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
_gtk_css_parser_error_full (GtkCssParser        *parser,
 | 
						|
                            GtkCssProviderError  code,
 | 
						|
                            const char          *format,
 | 
						|
                            ...)
 | 
						|
{
 | 
						|
  GError *error;
 | 
						|
 | 
						|
  va_list args;
 | 
						|
 | 
						|
  va_start (args, format);
 | 
						|
  error = g_error_new_valist (GTK_CSS_PROVIDER_ERROR,
 | 
						|
                              code, format, args);
 | 
						|
  va_end (args);
 | 
						|
 | 
						|
  _gtk_css_parser_take_error (parser, 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 != '\'')
 | 
						|
    {
 | 
						|
      _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;
 | 
						|
}
 | 
						|
 | 
						|
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,
 | 
						|
                          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_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;
 | 
						|
}
 | 
						|
 | 
						|
gboolean
 | 
						|
_gtk_css_parser_has_number (GtkCssParser *parser)
 | 
						|
{
 | 
						|
  /* ahem */
 | 
						|
  return strchr ("+-0123456789.", parser->data[0]) != NULL;
 | 
						|
}
 | 
						|
 | 
						|
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);
 | 
						|
}
 | 
						|
 | 
						|
/* XXX: we should introduce GtkCssLenght that deals with
 | 
						|
 * different kind of units */
 | 
						|
gboolean
 | 
						|
_gtk_css_parser_try_length (GtkCssParser *parser,
 | 
						|
                            int          *value)
 | 
						|
{
 | 
						|
  if (!_gtk_css_parser_try_int (parser, value))
 | 
						|
    return FALSE;
 | 
						|
 | 
						|
  /* FIXME: _try_uint skips spaces while the
 | 
						|
   * spec forbids them
 | 
						|
   */
 | 
						|
  _gtk_css_parser_try (parser, "px", TRUE);
 | 
						|
 | 
						|
  return TRUE;
 | 
						|
}
 | 
						|
 | 
						|
gboolean
 | 
						|
_gtk_css_parser_try_enum (GtkCssParser *parser,
 | 
						|
			  GType         enum_type,
 | 
						|
			  int          *value)
 | 
						|
{
 | 
						|
  GEnumClass *enum_class;
 | 
						|
  gboolean result;
 | 
						|
  const char *start;
 | 
						|
  char *str;
 | 
						|
 | 
						|
  g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), FALSE);
 | 
						|
  g_return_val_if_fail (value != NULL, FALSE);
 | 
						|
 | 
						|
  result = FALSE;
 | 
						|
 | 
						|
  enum_class = g_type_class_ref (enum_type);
 | 
						|
 | 
						|
  start = parser->data;
 | 
						|
 | 
						|
  str = _gtk_css_parser_try_ident (parser, TRUE);
 | 
						|
  if (str == NULL)
 | 
						|
    return FALSE;
 | 
						|
 | 
						|
  if (enum_class->n_values)
 | 
						|
    {
 | 
						|
      GEnumValue *enum_value;
 | 
						|
 | 
						|
      for (enum_value = enum_class->values; enum_value->value_name; enum_value++)
 | 
						|
	{
 | 
						|
	  if (enum_value->value_nick &&
 | 
						|
	      g_ascii_strcasecmp (str, enum_value->value_nick) == 0)
 | 
						|
	    {
 | 
						|
	      *value = enum_value->value;
 | 
						|
	      result = TRUE;
 | 
						|
	      break;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
  g_free (str);
 | 
						|
  g_type_class_unref (enum_class);
 | 
						|
 | 
						|
  if (!result)
 | 
						|
    parser->data = start;
 | 
						|
 | 
						|
  return result;
 | 
						|
}
 | 
						|
 | 
						|
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;
 | 
						|
}
 | 
						|
 | 
						|
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;
 | 
						|
}
 | 
						|
 | 
						|
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);
 | 
						|
}
 | 
						|
 | 
						|
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);
 | 
						|
}
 |