]> Pileus Git - ~andy/gtk/blob - gtk/gtkcssparser.c
css: Add _gtk_css_parser_try_enum
[~andy/gtk] / gtk / gtkcssparser.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 2011 Benjamin Otte <otte@gnome.org>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #include "config.h"
21
22 #include "gtkcssparserprivate.h"
23 #include "gtkwin32themeprivate.h"
24
25 #include <errno.h>
26 #include <string.h>
27
28 /* just for the errors, yay! */
29 #include "gtkcssprovider.h"
30
31 #define NEWLINE_CHARS "\r\n"
32 #define WHITESPACE_CHARS "\f \t"
33 #define NMSTART "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
34 #define NMCHAR NMSTART "01234567890-_"
35 #define URLCHAR NMCHAR "!#$%&*~"
36
37 #define GTK_IS_CSS_PARSER(parser) ((parser) != NULL)
38
39 struct _GtkCssParser
40 {
41   const char *data;
42   GtkCssParserErrorFunc error_func;
43   gpointer              user_data;
44
45   const char *line_start;
46   guint line;
47 };
48
49 GtkCssParser *
50 _gtk_css_parser_new (const char            *data,
51                      GtkCssParserErrorFunc  error_func,
52                      gpointer               user_data)
53 {
54   GtkCssParser *parser;
55
56   g_return_val_if_fail (data != NULL, NULL);
57
58   parser = g_slice_new0 (GtkCssParser);
59
60   parser->data = data;
61   parser->error_func = error_func;
62   parser->user_data = user_data;
63
64   parser->line_start = data;
65   parser->line = 0;
66
67   return parser;
68 }
69
70 void
71 _gtk_css_parser_free (GtkCssParser *parser)
72 {
73   g_return_if_fail (GTK_IS_CSS_PARSER (parser));
74
75   g_slice_free (GtkCssParser, parser);
76 }
77
78 gboolean
79 _gtk_css_parser_is_eof (GtkCssParser *parser)
80 {
81   g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), TRUE);
82
83   return *parser->data == 0;
84 }
85
86 gboolean
87 _gtk_css_parser_begins_with (GtkCssParser *parser,
88                              char          c)
89 {
90   g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), TRUE);
91
92   return *parser->data == c;
93 }
94
95 guint
96 _gtk_css_parser_get_line (GtkCssParser *parser)
97 {
98   g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), 1);
99
100   return parser->line;
101 }
102
103 guint
104 _gtk_css_parser_get_position (GtkCssParser *parser)
105 {
106   g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), 1);
107
108   return parser->data - parser->line_start;
109 }
110
111 void
112 _gtk_css_parser_take_error (GtkCssParser *parser,
113                             GError       *error)
114 {
115   parser->error_func (parser, error, parser->user_data);
116
117   g_error_free (error);
118 }
119
120 void
121 _gtk_css_parser_error (GtkCssParser *parser,
122                        const char   *format,
123                        ...)
124 {
125   GError *error;
126
127   va_list args;
128
129   va_start (args, format);
130   error = g_error_new_valist (GTK_CSS_PROVIDER_ERROR,
131                               GTK_CSS_PROVIDER_ERROR_SYNTAX,
132                               format, args);
133   va_end (args);
134
135   _gtk_css_parser_take_error (parser, error);
136 }
137
138 static gboolean
139 gtk_css_parser_new_line (GtkCssParser *parser)
140 {
141   gboolean result = FALSE;
142
143   if (*parser->data == '\r')
144     {
145       result = TRUE;
146       parser->data++;
147     }
148   if (*parser->data == '\n')
149     {
150       result = TRUE;
151       parser->data++;
152     }
153
154   if (result)
155     {
156       parser->line++;
157       parser->line_start = parser->data;
158     }
159
160   return result;
161 }
162
163 static gboolean
164 gtk_css_parser_skip_comment (GtkCssParser *parser)
165 {
166   if (parser->data[0] != '/' ||
167       parser->data[1] != '*')
168     return FALSE;
169
170   parser->data += 2;
171
172   while (*parser->data)
173     {
174       gsize len = strcspn (parser->data, NEWLINE_CHARS "/");
175
176       parser->data += len;
177   
178       if (gtk_css_parser_new_line (parser))
179         continue;
180
181       parser->data++;
182
183       if (parser->data[-2] == '*')
184         return TRUE;
185       if (parser->data[0] == '*')
186         _gtk_css_parser_error (parser, "'/*' in comment block");
187     }
188
189   /* FIXME: position */
190   _gtk_css_parser_error (parser, "Unterminated comment");
191   return TRUE;
192 }
193
194 void
195 _gtk_css_parser_skip_whitespace (GtkCssParser *parser)
196 {
197   size_t len;
198
199   while (*parser->data)
200     {
201       if (gtk_css_parser_new_line (parser))
202         continue;
203
204       len = strspn (parser->data, WHITESPACE_CHARS);
205       if (len)
206         {
207           parser->data += len;
208           continue;
209         }
210       
211       if (!gtk_css_parser_skip_comment (parser))
212         break;
213     }
214 }
215
216 gboolean
217 _gtk_css_parser_try (GtkCssParser *parser,
218                      const char   *string,
219                      gboolean      skip_whitespace)
220 {
221   g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), FALSE);
222   g_return_val_if_fail (string != NULL, FALSE);
223
224   if (g_ascii_strncasecmp (parser->data, string, strlen (string)) != 0)
225     return FALSE;
226
227   parser->data += strlen (string);
228
229   if (skip_whitespace)
230     _gtk_css_parser_skip_whitespace (parser);
231   return TRUE;
232 }
233
234 static guint
235 get_xdigit (char c)
236 {
237   if (c >= 'a')
238     return c - 'a' + 10;
239   else if (c >= 'A')
240     return c - 'A' + 10;
241   else
242     return c - '0';
243 }
244
245 static void
246 _gtk_css_parser_unescape (GtkCssParser *parser,
247                           GString      *str)
248 {
249   guint i;
250   gunichar result = 0;
251
252   g_assert (*parser->data == '\\');
253
254   parser->data++;
255
256   for (i = 0; i < 6; i++)
257     {
258       if (!g_ascii_isxdigit (parser->data[i]))
259         break;
260
261       result = (result << 4) + get_xdigit (parser->data[i]);
262     }
263
264   if (i != 0)
265     {
266       g_string_append_unichar (str, result);
267       parser->data += i;
268
269       /* NB: gtk_css_parser_new_line() forward data pointer itself */
270       if (!gtk_css_parser_new_line (parser) &&
271           *parser->data &&
272           strchr (WHITESPACE_CHARS, *parser->data))
273         parser->data++;
274       return;
275     }
276
277   if (gtk_css_parser_new_line (parser))
278     return;
279
280   g_string_append_c (str, *parser->data);
281   parser->data++;
282
283   return;
284 }
285
286 static gboolean
287 _gtk_css_parser_read_char (GtkCssParser *parser,
288                            GString *     str,
289                            const char *  allowed)
290 {
291   if (*parser->data == 0)
292     return FALSE;
293
294   if (strchr (allowed, *parser->data))
295     {
296       g_string_append_c (str, *parser->data);
297       parser->data++;
298       return TRUE;
299     }
300   if (*parser->data >= 127)
301     {
302       gsize len = g_utf8_skip[(guint) *(guchar *) parser->data];
303
304       g_string_append_len (str, parser->data, len);
305       parser->data += len;
306       return TRUE;
307     }
308   if (*parser->data == '\\')
309     {
310       _gtk_css_parser_unescape (parser, str);
311       return TRUE;
312     }
313
314   return FALSE;
315 }
316
317 char *
318 _gtk_css_parser_try_name (GtkCssParser *parser,
319                           gboolean      skip_whitespace)
320 {
321   GString *name;
322
323   g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), NULL);
324
325   name = g_string_new (NULL);
326
327   while (_gtk_css_parser_read_char (parser, name, NMCHAR))
328     ;
329
330   if (skip_whitespace)
331     _gtk_css_parser_skip_whitespace (parser);
332
333   return g_string_free (name, FALSE);
334 }
335
336 char *
337 _gtk_css_parser_try_ident (GtkCssParser *parser,
338                            gboolean      skip_whitespace)
339 {
340   const char *start;
341   GString *ident;
342
343   g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), NULL);
344
345   start = parser->data;
346   
347   ident = g_string_new (NULL);
348
349   if (*parser->data == '-')
350     {
351       g_string_append_c (ident, '-');
352       parser->data++;
353     }
354
355   if (!_gtk_css_parser_read_char (parser, ident, NMSTART))
356     {
357       parser->data = start;
358       g_string_free (ident, TRUE);
359       return NULL;
360     }
361
362   while (_gtk_css_parser_read_char (parser, ident, NMCHAR))
363     ;
364
365   if (skip_whitespace)
366     _gtk_css_parser_skip_whitespace (parser);
367
368   return g_string_free (ident, FALSE);
369 }
370
371 gboolean
372 _gtk_css_parser_is_string (GtkCssParser *parser)
373 {
374   g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), FALSE);
375
376   return *parser->data == '"' || *parser->data == '\'';
377 }
378
379 char *
380 _gtk_css_parser_read_string (GtkCssParser *parser)
381 {
382   GString *str;
383   char quote;
384
385   g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), NULL);
386
387   quote = *parser->data;
388   
389   if (quote != '"' && quote != '\'')
390     {
391       _gtk_css_parser_error (parser, "Expected a string.");
392       return NULL;
393     }
394   
395   parser->data++;
396   str = g_string_new (NULL);
397
398   while (TRUE)
399     {
400       gsize len = strcspn (parser->data, "\\'\"\n\r\f");
401
402       g_string_append_len (str, parser->data, len);
403
404       parser->data += len;
405
406       switch (*parser->data)
407         {
408         case '\\':
409           _gtk_css_parser_unescape (parser, str);
410           break;
411         case '"':
412         case '\'':
413           if (*parser->data == quote)
414             {
415               parser->data++;
416               _gtk_css_parser_skip_whitespace (parser);
417               return g_string_free (str, FALSE);
418             }
419           
420           g_string_append_c (str, *parser->data);
421           parser->data++;
422           break;
423         case '\0':
424           /* FIXME: position */
425           _gtk_css_parser_error (parser, "Missing end quote in string.");
426           g_string_free (str, TRUE);
427           return NULL;
428         default:
429           _gtk_css_parser_error (parser, 
430                                  "Invalid character in string. Must be escaped.");
431           g_string_free (str, TRUE);
432           return NULL;
433         }
434     }
435
436   g_assert_not_reached ();
437   return NULL;
438 }
439
440 char *
441 _gtk_css_parser_read_uri (GtkCssParser *parser)
442 {
443   char *result;
444
445   g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), NULL);
446
447   if (!_gtk_css_parser_try (parser, "url(", TRUE))
448     {
449       _gtk_css_parser_error (parser, "expected 'url('");
450       return NULL;
451     }
452
453   _gtk_css_parser_skip_whitespace (parser);
454
455   if (_gtk_css_parser_is_string (parser))
456     {
457       result = _gtk_css_parser_read_string (parser);
458     }
459   else
460     {
461       GString *str = g_string_new (NULL);
462
463       while (_gtk_css_parser_read_char (parser, str, URLCHAR))
464         ;
465       result = g_string_free (str, FALSE);
466       if (result == NULL)
467         _gtk_css_parser_error (parser, "not a url");
468     }
469   
470   if (result == NULL)
471     return NULL;
472
473   _gtk_css_parser_skip_whitespace (parser);
474
475   if (*parser->data != ')')
476     {
477       _gtk_css_parser_error (parser, "missing ')' for url");
478       g_free (result);
479       return NULL;
480     }
481
482   parser->data++;
483
484   _gtk_css_parser_skip_whitespace (parser);
485
486   return result;
487 }
488
489 gboolean
490 _gtk_css_parser_try_int (GtkCssParser *parser,
491                          int          *value)
492 {
493   gint64 result;
494   char *end;
495
496   g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), FALSE);
497   g_return_val_if_fail (value != NULL, FALSE);
498
499   /* strtoll parses a plus, but we are not allowed to */
500   if (*parser->data == '+')
501     return FALSE;
502
503   errno = 0;
504   result = g_ascii_strtoll (parser->data, &end, 10);
505   if (errno)
506     return FALSE;
507   if (result > G_MAXINT || result < G_MININT)
508     return FALSE;
509   if (parser->data == end)
510     return FALSE;
511
512   parser->data = end;
513   *value = result;
514
515   _gtk_css_parser_skip_whitespace (parser);
516
517   return TRUE;
518 }
519
520 gboolean
521 _gtk_css_parser_try_uint (GtkCssParser *parser,
522                           guint        *value)
523 {
524   guint64 result;
525   char *end;
526
527   g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), FALSE);
528   g_return_val_if_fail (value != NULL, FALSE);
529
530   errno = 0;
531   result = g_ascii_strtoull (parser->data, &end, 10);
532   if (errno)
533     return FALSE;
534   if (result > G_MAXUINT)
535     return FALSE;
536   if (parser->data == end)
537     return FALSE;
538
539   parser->data = end;
540   *value = result;
541
542   _gtk_css_parser_skip_whitespace (parser);
543
544   return TRUE;
545 }
546
547 gboolean
548 _gtk_css_parser_try_double (GtkCssParser *parser,
549                             gdouble      *value)
550 {
551   gdouble result;
552   char *end;
553
554   g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), FALSE);
555   g_return_val_if_fail (value != NULL, FALSE);
556
557   errno = 0;
558   result = g_ascii_strtod (parser->data, &end);
559   if (errno)
560     return FALSE;
561   if (parser->data == end)
562     return FALSE;
563
564   parser->data = end;
565   *value = result;
566
567   _gtk_css_parser_skip_whitespace (parser);
568
569   return TRUE;
570 }
571
572 gboolean
573 _gtk_css_parser_try_enum (GtkCssParser *parser,
574                           GType         enum_type,
575                           int          *value)
576 {
577   GEnumClass *enum_class;
578   gboolean result;
579   const char *start;
580   char *str;
581
582   g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), FALSE);
583   g_return_val_if_fail (value != NULL, FALSE);
584
585   result = FALSE;
586
587   enum_class = g_type_class_ref (enum_type);
588
589   start = parser->data;
590
591   str = _gtk_css_parser_try_ident (parser, TRUE);
592   if (str == NULL)
593     return FALSE;
594
595   if (enum_class->n_values)
596     {
597       GEnumValue *enum_value;
598
599       for (enum_value = enum_class->values; enum_value->value_name; enum_value++)
600         {
601           if (enum_value->value_nick &&
602               g_ascii_strcasecmp (str, enum_value->value_nick) == 0)
603             {
604               *value = enum_value->value;
605               result = TRUE;
606               break;
607             }
608         }
609     }
610
611   g_type_class_unref (enum_class);
612
613   if (!result)
614     parser->data = start;
615
616   return result;
617 }
618
619 typedef enum {
620   COLOR_RGBA,
621   COLOR_RGB,
622   COLOR_LIGHTER,
623   COLOR_DARKER,
624   COLOR_SHADE,
625   COLOR_ALPHA,
626   COLOR_MIX,
627   COLOR_WIN32
628 } ColorType;
629
630 static GtkSymbolicColor *
631 gtk_css_parser_read_symbolic_color_function (GtkCssParser *parser,
632                                              ColorType     color)
633 {
634   GtkSymbolicColor *symbolic;
635   GtkSymbolicColor *child1, *child2;
636   double value;
637
638   if (!_gtk_css_parser_try (parser, "(", TRUE))
639     {
640       _gtk_css_parser_error (parser, "Missing opening bracket in color definition");
641       return NULL;
642     }
643
644   if (color == COLOR_RGB || color == COLOR_RGBA)
645     {
646       GdkRGBA rgba;
647       double tmp;
648       guint i;
649
650       for (i = 0; i < 3; i++)
651         {
652           if (i > 0 && !_gtk_css_parser_try (parser, ",", TRUE))
653             {
654               _gtk_css_parser_error (parser, "Expected ',' in color definition");
655               return NULL;
656             }
657
658           if (!_gtk_css_parser_try_double (parser, &tmp))
659             {
660               _gtk_css_parser_error (parser, "Invalid number for color value");
661               return NULL;
662             }
663           if (_gtk_css_parser_try (parser, "%", TRUE))
664             tmp /= 100.0;
665           else
666             tmp /= 255.0;
667           if (i == 0)
668             rgba.red = tmp;
669           else if (i == 1)
670             rgba.green = tmp;
671           else if (i == 2)
672             rgba.blue = tmp;
673           else
674             g_assert_not_reached ();
675         }
676
677       if (color == COLOR_RGBA)
678         {
679           if (i > 0 && !_gtk_css_parser_try (parser, ",", TRUE))
680             {
681               _gtk_css_parser_error (parser, "Expected ',' in color definition");
682               return NULL;
683             }
684
685           if (!_gtk_css_parser_try_double (parser, &rgba.alpha))
686             {
687               _gtk_css_parser_error (parser, "Invalid number for alpha value");
688               return NULL;
689             }
690         }
691       else
692         rgba.alpha = 1.0;
693       
694       symbolic = gtk_symbolic_color_new_literal (&rgba);
695     }
696   else if (color == COLOR_WIN32)
697     {
698       symbolic = _gtk_win32_theme_color_parse (parser);
699       if (symbolic == NULL)
700         return NULL;
701     }
702   else
703     {
704       child1 = _gtk_css_parser_read_symbolic_color (parser);
705       if (child1 == NULL)
706         return NULL;
707
708       if (color == COLOR_MIX)
709         {
710           if (!_gtk_css_parser_try (parser, ",", TRUE))
711             {
712               _gtk_css_parser_error (parser, "Expected ',' in color definition");
713               gtk_symbolic_color_unref (child1);
714               return NULL;
715             }
716
717           child2 = _gtk_css_parser_read_symbolic_color (parser);
718           if (child2 == NULL)
719             {
720               gtk_symbolic_color_unref (child1);
721               return NULL;
722             }
723         }
724       else
725         child2 = NULL;
726
727       if (color == COLOR_LIGHTER)
728         value = 1.3;
729       else if (color == COLOR_DARKER)
730         value = 0.7;
731       else
732         {
733           if (!_gtk_css_parser_try (parser, ",", TRUE))
734             {
735               _gtk_css_parser_error (parser, "Expected ',' in color definition");
736               gtk_symbolic_color_unref (child1);
737               if (child2)
738                 gtk_symbolic_color_unref (child2);
739               return NULL;
740             }
741
742           if (!_gtk_css_parser_try_double (parser, &value))
743             {
744               _gtk_css_parser_error (parser, "Expected number in color definition");
745               gtk_symbolic_color_unref (child1);
746               if (child2)
747                 gtk_symbolic_color_unref (child2);
748               return NULL;
749             }
750         }
751       
752       switch (color)
753         {
754         case COLOR_LIGHTER:
755         case COLOR_DARKER:
756         case COLOR_SHADE:
757           symbolic = gtk_symbolic_color_new_shade (child1, value);
758           break;
759         case COLOR_ALPHA:
760           symbolic = gtk_symbolic_color_new_alpha (child1, value);
761           break;
762         case COLOR_MIX:
763           symbolic = gtk_symbolic_color_new_mix (child1, child2, value);
764           break;
765         default:
766           g_assert_not_reached ();
767           symbolic = NULL;
768         }
769
770       gtk_symbolic_color_unref (child1);
771       if (child2)
772         gtk_symbolic_color_unref (child2);
773     }
774
775   if (!_gtk_css_parser_try (parser, ")", TRUE))
776     {
777       _gtk_css_parser_error (parser, "Expected ')' in color definition");
778       gtk_symbolic_color_unref (symbolic);
779       return NULL;
780     }
781
782   return symbolic;
783 }
784
785 static GtkSymbolicColor *
786 gtk_css_parser_try_hash_color (GtkCssParser *parser)
787 {
788   if (parser->data[0] == '#' &&
789       g_ascii_isxdigit (parser->data[1]) &&
790       g_ascii_isxdigit (parser->data[2]) &&
791       g_ascii_isxdigit (parser->data[3]))
792     {
793       GdkRGBA rgba;
794       
795       if (g_ascii_isxdigit (parser->data[4]) &&
796           g_ascii_isxdigit (parser->data[5]) &&
797           g_ascii_isxdigit (parser->data[6]))
798         {
799           rgba.red   = ((get_xdigit (parser->data[1]) << 4) + get_xdigit (parser->data[2])) / 255.0;
800           rgba.green = ((get_xdigit (parser->data[3]) << 4) + get_xdigit (parser->data[4])) / 255.0;
801           rgba.blue  = ((get_xdigit (parser->data[5]) << 4) + get_xdigit (parser->data[6])) / 255.0;
802           rgba.alpha = 1.0;
803           parser->data += 7;
804         }
805       else
806         {
807           rgba.red   = get_xdigit (parser->data[1]) / 15.0;
808           rgba.green = get_xdigit (parser->data[2]) / 15.0;
809           rgba.blue  = get_xdigit (parser->data[3]) / 15.0;
810           rgba.alpha = 1.0;
811           parser->data += 4;
812         }
813
814       _gtk_css_parser_skip_whitespace (parser);
815
816       return gtk_symbolic_color_new_literal (&rgba);
817     }
818
819   return NULL;
820 }
821
822 GtkSymbolicColor *
823 _gtk_css_parser_read_symbolic_color (GtkCssParser *parser)
824 {
825   GtkSymbolicColor *symbolic;
826   guint color;
827   const char *names[] = {"rgba", "rgb",  "lighter", "darker", "shade", "alpha", "mix",
828                          GTK_WIN32_THEME_SYMBOLIC_COLOR_NAME};
829   char *name;
830
831   g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), NULL);
832
833   if (_gtk_css_parser_try (parser, "@", FALSE))
834     {
835       name = _gtk_css_parser_try_name (parser, TRUE);
836
837       if (name)
838         {
839           symbolic = gtk_symbolic_color_new_name (name);
840         }
841       else
842         {
843           _gtk_css_parser_error (parser, "'%s' is not a valid symbolic color name", name);
844           symbolic = NULL;
845         }
846
847       g_free (name);
848       return symbolic;
849     }
850
851   for (color = 0; color < G_N_ELEMENTS (names); color++)
852     {
853       if (_gtk_css_parser_try (parser, names[color], TRUE))
854         break;
855     }
856
857   if (color < G_N_ELEMENTS (names))
858     return gtk_css_parser_read_symbolic_color_function (parser, color);
859
860   symbolic = gtk_css_parser_try_hash_color (parser);
861   if (symbolic)
862     return symbolic;
863
864   name = _gtk_css_parser_try_name (parser, TRUE);
865   if (name)
866     {
867       GdkRGBA rgba;
868
869       if (gdk_rgba_parse (&rgba, name))
870         {
871           symbolic = gtk_symbolic_color_new_literal (&rgba);
872         }
873       else
874         {
875           _gtk_css_parser_error (parser, "'%s' is not a valid color name", name);
876           symbolic = NULL;
877         }
878       g_free (name);
879       return symbolic;
880     }
881
882   _gtk_css_parser_error (parser, "Not a color definition");
883   return NULL;
884 }
885
886 void
887 _gtk_css_parser_resync_internal (GtkCssParser *parser,
888                                  gboolean      sync_at_semicolon,
889                                  gboolean      read_sync_token,
890                                  char          terminator)
891 {
892   gsize len;
893
894   do {
895     len = strcspn (parser->data, "\\\"'/()[]{};" NEWLINE_CHARS);
896     parser->data += len;
897
898     if (gtk_css_parser_new_line (parser))
899       continue;
900
901     if (_gtk_css_parser_is_string (parser))
902       {
903         /* Hrm, this emits errors, and i suspect it shouldn't... */
904         char *free_me = _gtk_css_parser_read_string (parser);
905         g_free (free_me);
906         continue;
907       }
908
909     if (gtk_css_parser_skip_comment (parser))
910       continue;
911
912     switch (*parser->data)
913       {
914       case '\\':
915         {
916           GString *ignore = g_string_new (NULL);
917           _gtk_css_parser_unescape (parser, ignore);
918           g_string_free (ignore, TRUE);
919         }
920         break;
921       case ';':
922         if (sync_at_semicolon && !read_sync_token)
923           return;
924         parser->data++;
925         if (sync_at_semicolon)
926           {
927             _gtk_css_parser_skip_whitespace (parser);
928             return;
929           }
930         break;
931       case '(':
932         parser->data++;
933         _gtk_css_parser_resync (parser, FALSE, ')');
934         if (*parser->data)
935           parser->data++;
936         break;
937       case '[':
938         parser->data++;
939         _gtk_css_parser_resync (parser, FALSE, ']');
940         if (*parser->data)
941           parser->data++;
942         break;
943       case '{':
944         parser->data++;
945         _gtk_css_parser_resync (parser, FALSE, '}');
946         if (*parser->data)
947           parser->data++;
948         if (sync_at_semicolon || !terminator)
949           {
950             _gtk_css_parser_skip_whitespace (parser);
951             return;
952           }
953         break;
954       case '}':
955       case ')':
956       case ']':
957         if (terminator == *parser->data)
958           {
959             _gtk_css_parser_skip_whitespace (parser);
960             return;
961           }
962         parser->data++;
963         continue;
964       case '\0':
965         break;
966       case '/':
967       default:
968         parser->data++;
969         break;
970       }
971   } while (*parser->data);
972 }
973
974 char *
975 _gtk_css_parser_read_value (GtkCssParser *parser)
976 {
977   const char *start;
978   char *result;
979
980   g_return_val_if_fail (GTK_IS_CSS_PARSER (parser), NULL);
981
982   start = parser->data;
983
984   /* This needs to be done better */
985   _gtk_css_parser_resync_internal (parser, TRUE, FALSE, '}');
986
987   result = g_strndup (start, parser->data - start);
988   if (result)
989     {
990       g_strchomp (result);
991       if (result[0] == 0)
992         {
993           g_free (result);
994           result = NULL;
995         }
996     }
997
998   if (result == NULL)
999     _gtk_css_parser_error (parser, "Expected a property value");
1000
1001   return result;
1002 }
1003
1004 void
1005 _gtk_css_parser_resync (GtkCssParser *parser,
1006                         gboolean      sync_at_semicolon,
1007                         char          terminator)
1008 {
1009   g_return_if_fail (GTK_IS_CSS_PARSER (parser));
1010
1011   _gtk_css_parser_resync_internal (parser, sync_at_semicolon, TRUE, terminator);
1012 }