1 /* LIBGIMP - The GIMP Library
2  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3  *
4  * gimpscanner.c
5  * Copyright (C) 2002  Sven Neumann <sven@gimp.org>
6  *                     Michael Natterer <mitch@gimp.org>
7  *
8  * This library is free software: you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 3 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library.  If not, see
20  * <https://www.gnu.org/licenses/>.
21  */
22 
23 #include "config.h"
24 
25 #include <string.h>
26 #include <errno.h>
27 
28 #include <cairo.h>
29 #include <gegl.h>
30 #include <gdk-pixbuf/gdk-pixbuf.h>
31 
32 #include "libgimpbase/gimpbase.h"
33 #include "libgimpcolor/gimpcolor.h"
34 #include "libgimpmath/gimpmath.h"
35 
36 #include "gimpconfig-error.h"
37 #include "gimpscanner.h"
38 
39 #include "libgimp/libgimp-intl.h"
40 
41 
42 /**
43  * SECTION: gimpscanner
44  * @title: GimpScanner
45  * @short_description: A wrapper around #GScanner with some convenience API.
46  *
47  * A wrapper around #GScanner with some convenience API.
48  **/
49 
50 
51 typedef struct
52 {
53   gchar        *name;
54   GMappedFile  *mapped;
55   gchar        *text;
56   GError      **error;
57 } GimpScannerData;
58 
59 
60 /*  local function prototypes  */
61 
62 static GScanner * gimp_scanner_new     (const gchar  *name,
63                                         GMappedFile  *mapped,
64                                         gchar        *text,
65                                         GError      **error);
66 static void       gimp_scanner_message (GScanner     *scanner,
67                                         gchar        *message,
68                                         gboolean      is_error);
69 
70 
71 /*  public functions  */
72 
73 /**
74  * gimp_scanner_new_file:
75  * @filename:
76  * @error:
77  *
78  * Return value:
79  *
80  * Since: 2.4
81  **/
82 GScanner *
gimp_scanner_new_file(const gchar * filename,GError ** error)83 gimp_scanner_new_file (const gchar  *filename,
84                        GError      **error)
85 {
86   GScanner *scanner;
87   GFile    *file;
88 
89   g_return_val_if_fail (filename != NULL, NULL);
90   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
91 
92   file = g_file_new_for_path (filename);
93   scanner = gimp_scanner_new_gfile (file, error);
94   g_object_unref (file);
95 
96   return scanner;
97 }
98 
99 /**
100  * gimp_scanner_new_gfile:
101  * @file: a #GFile
102  * @error: return location for #GError, or %NULL
103  *
104  * Return value: The new #GScanner.
105  *
106  * Since: 2.10
107  **/
108 GScanner *
gimp_scanner_new_gfile(GFile * file,GError ** error)109 gimp_scanner_new_gfile (GFile   *file,
110                         GError **error)
111 {
112   GScanner *scanner;
113   gchar    *path;
114 
115   g_return_val_if_fail (G_IS_FILE (file), NULL);
116   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
117 
118   path = g_file_get_path (file);
119 
120   if (path)
121     {
122       GMappedFile *mapped;
123 
124       mapped = g_mapped_file_new (path, FALSE, error);
125       g_free (path);
126 
127       if (! mapped)
128         {
129           if (error)
130             {
131               (*error)->domain = GIMP_CONFIG_ERROR;
132               (*error)->code   = ((*error)->code == G_FILE_ERROR_NOENT ?
133                                   GIMP_CONFIG_ERROR_OPEN_ENOENT :
134                                   GIMP_CONFIG_ERROR_OPEN);
135             }
136 
137           return NULL;
138         }
139 
140       /*  gimp_scanner_new() takes a "name" for the scanner, not a filename  */
141       scanner = gimp_scanner_new (gimp_file_get_utf8_name (file),
142                                   mapped, NULL, error);
143 
144       g_scanner_input_text (scanner,
145                             g_mapped_file_get_contents (mapped),
146                             g_mapped_file_get_length (mapped));
147     }
148   else
149     {
150       GInputStream *input;
151 
152       input = G_INPUT_STREAM (g_file_read (file, NULL, error));
153 
154       if (! input)
155         {
156           if (error)
157             {
158               (*error)->domain = GIMP_CONFIG_ERROR;
159               (*error)->code   = ((*error)->code == G_IO_ERROR_NOT_FOUND ?
160                                   GIMP_CONFIG_ERROR_OPEN_ENOENT :
161                                   GIMP_CONFIG_ERROR_OPEN);
162             }
163 
164           return NULL;
165         }
166 
167       g_object_set_data (G_OBJECT (input), "gimp-data", file);
168 
169       scanner = gimp_scanner_new_stream (input, error);
170 
171       g_object_unref (input);
172     }
173 
174   return scanner;
175 }
176 
177 /**
178  * gimp_scanner_new_stream:
179  * @input: a #GInputStream
180  * @error: return location for #GError, or %NULL
181  *
182  * Return value: The new #GScanner.
183  *
184  * Since: 2.10
185  **/
186 GScanner *
gimp_scanner_new_stream(GInputStream * input,GError ** error)187 gimp_scanner_new_stream (GInputStream  *input,
188                          GError       **error)
189 {
190   GScanner    *scanner;
191   GFile       *file;
192   const gchar *path;
193   GString     *string;
194   gchar        buffer[4096];
195   gsize        bytes_read;
196 
197   g_return_val_if_fail (G_IS_INPUT_STREAM (input), NULL);
198   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
199 
200   file = g_object_get_data (G_OBJECT (input), "gimp-file");
201   if (file)
202     path = gimp_file_get_utf8_name (file);
203   else
204     path = "stream";
205 
206   string = g_string_new (NULL);
207 
208   do
209     {
210       GError   *my_error = NULL;
211       gboolean  success;
212 
213       success = g_input_stream_read_all (input, buffer, sizeof (buffer),
214                                          &bytes_read, NULL, &my_error);
215 
216       if (bytes_read > 0)
217         g_string_append_len (string, buffer, bytes_read);
218 
219       if (! success)
220         {
221           if (string->len > 0)
222             {
223               g_printerr ("%s: read error in '%s', trying to scan "
224                           "partial content: %s",
225                           G_STRFUNC, path, my_error->message);
226               g_clear_error (&my_error);
227               break;
228             }
229 
230           g_string_free (string, TRUE);
231 
232           g_propagate_error (error, my_error);
233 
234           return NULL;
235         }
236     }
237   while (bytes_read == sizeof (buffer));
238 
239   /*  gimp_scanner_new() takes a "name" for the scanner, not a filename  */
240   scanner = gimp_scanner_new (path, NULL, string->str, error);
241 
242   bytes_read = string->len;
243 
244   g_scanner_input_text (scanner, g_string_free (string, FALSE), bytes_read);
245 
246   return scanner;
247 }
248 
249 /**
250  * gimp_scanner_new_string:
251  * @text:
252  * @text_len:
253  * @error:
254  *
255  * Return value:
256  *
257  * Since: 2.4
258  **/
259 GScanner *
gimp_scanner_new_string(const gchar * text,gint text_len,GError ** error)260 gimp_scanner_new_string (const gchar  *text,
261                          gint          text_len,
262                          GError      **error)
263 {
264   GScanner *scanner;
265 
266   g_return_val_if_fail (text != NULL || text_len <= 0, NULL);
267   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
268 
269   if (text_len < 0)
270     text_len = text ? strlen (text) : 0;
271 
272   scanner = gimp_scanner_new (NULL, NULL, NULL, error);
273 
274   g_scanner_input_text (scanner, text, text_len);
275 
276   return scanner;
277 }
278 
279 static GScanner *
gimp_scanner_new(const gchar * name,GMappedFile * mapped,gchar * text,GError ** error)280 gimp_scanner_new (const gchar  *name,
281                   GMappedFile  *mapped,
282                   gchar        *text,
283                   GError      **error)
284 {
285   GScanner        *scanner;
286   GimpScannerData *data;
287 
288   scanner = g_scanner_new (NULL);
289 
290   data = g_slice_new0 (GimpScannerData);
291 
292   data->name   = g_strdup (name);
293   data->mapped = mapped;
294   data->text   = text;
295   data->error  = error;
296 
297   scanner->user_data   = data;
298   scanner->msg_handler = gimp_scanner_message;
299 
300   scanner->config->cset_identifier_first = ( G_CSET_a_2_z G_CSET_A_2_Z );
301   scanner->config->cset_identifier_nth   = ( G_CSET_a_2_z G_CSET_A_2_Z
302                                              G_CSET_DIGITS "-_" );
303   scanner->config->scan_identifier_1char = TRUE;
304 
305   scanner->config->store_int64           = TRUE;
306 
307   return scanner;
308 }
309 
310 /**
311  * gimp_scanner_destroy:
312  * @scanner: A #GScanner created by gimp_scanner_new_file() or
313  *           gimp_scanner_new_string()
314  *
315  * Since: 2.4
316  **/
317 void
gimp_scanner_destroy(GScanner * scanner)318 gimp_scanner_destroy (GScanner *scanner)
319 {
320   GimpScannerData *data;
321 
322   g_return_if_fail (scanner != NULL);
323 
324   data = scanner->user_data;
325 
326   if (data->mapped)
327     g_mapped_file_unref (data->mapped);
328 
329   if (data->text)
330     g_free (data->text);
331 
332   g_free (data->name);
333   g_slice_free (GimpScannerData, data);
334 
335   g_scanner_destroy (scanner);
336 }
337 
338 /**
339  * gimp_scanner_parse_token:
340  * @scanner: A #GScanner created by gimp_scanner_new_file() or
341  *           gimp_scanner_new_string()
342  * @token: the #GTokenType expected as next token.
343  *
344  * Return value: %TRUE if the next token is @token, %FALSE otherwise.
345  *
346  * Since: 2.4
347  **/
348 gboolean
gimp_scanner_parse_token(GScanner * scanner,GTokenType token)349 gimp_scanner_parse_token (GScanner   *scanner,
350                           GTokenType  token)
351 {
352   if (g_scanner_peek_next_token (scanner) != token)
353     return FALSE;
354 
355   g_scanner_get_next_token (scanner);
356 
357   return TRUE;
358 }
359 
360 /**
361  * gimp_scanner_parse_identifier:
362  * @scanner: A #GScanner created by gimp_scanner_new_file() or
363  *           gimp_scanner_new_string()
364  * @identifier: the expected identifier.
365  *
366  * Return value: %TRUE if the next token is an identifier and if its
367  * value matches @identifier.
368  *
369  * Since: 2.4
370  **/
371 gboolean
gimp_scanner_parse_identifier(GScanner * scanner,const gchar * identifier)372 gimp_scanner_parse_identifier (GScanner    *scanner,
373                                const gchar *identifier)
374 {
375   if (g_scanner_peek_next_token (scanner) != G_TOKEN_IDENTIFIER)
376     return FALSE;
377 
378   g_scanner_get_next_token (scanner);
379 
380   if (strcmp (scanner->value.v_identifier, identifier))
381     return FALSE;
382 
383   return TRUE;
384 }
385 
386 /**
387  * gimp_scanner_parse_string:
388  * @scanner: A #GScanner created by gimp_scanner_new_file() or
389  *           gimp_scanner_new_string()
390  * @dest: Return location for the parsed string
391  *
392  * Return value: %TRUE on success
393  *
394  * Since: 2.4
395  **/
396 gboolean
gimp_scanner_parse_string(GScanner * scanner,gchar ** dest)397 gimp_scanner_parse_string (GScanner  *scanner,
398                            gchar    **dest)
399 {
400   if (g_scanner_peek_next_token (scanner) != G_TOKEN_STRING)
401     return FALSE;
402 
403   g_scanner_get_next_token (scanner);
404 
405   if (*scanner->value.v_string)
406     {
407       if (! g_utf8_validate (scanner->value.v_string, -1, NULL))
408         {
409           g_scanner_warn (scanner, _("invalid UTF-8 string"));
410           return FALSE;
411         }
412 
413       *dest = g_strdup (scanner->value.v_string);
414     }
415   else
416     {
417       *dest = NULL;
418     }
419 
420   return TRUE;
421 }
422 
423 /**
424  * gimp_scanner_parse_string_no_validate:
425  * @scanner: A #GScanner created by gimp_scanner_new_file() or
426  *           gimp_scanner_new_string()
427  * @dest: Return location for the parsed string
428  *
429  * Return value: %TRUE on success
430  *
431  * Since: 2.4
432  **/
433 gboolean
gimp_scanner_parse_string_no_validate(GScanner * scanner,gchar ** dest)434 gimp_scanner_parse_string_no_validate (GScanner  *scanner,
435                                        gchar    **dest)
436 {
437   if (g_scanner_peek_next_token (scanner) != G_TOKEN_STRING)
438     return FALSE;
439 
440   g_scanner_get_next_token (scanner);
441 
442   if (*scanner->value.v_string)
443     *dest = g_strdup (scanner->value.v_string);
444   else
445     *dest = NULL;
446 
447   return TRUE;
448 }
449 
450 /**
451  * gimp_scanner_parse_data:
452  * @scanner: A #GScanner created by gimp_scanner_new_file() or
453  *           gimp_scanner_new_string()
454  * @length: Length of the data to parse
455  * @dest: Return location for the parsed data
456  *
457  * Return value: %TRUE on success
458  *
459  * Since: 2.4
460  **/
461 gboolean
gimp_scanner_parse_data(GScanner * scanner,gint length,guint8 ** dest)462 gimp_scanner_parse_data (GScanner  *scanner,
463                          gint       length,
464                          guint8   **dest)
465 {
466   if (g_scanner_peek_next_token (scanner) != G_TOKEN_STRING)
467     return FALSE;
468 
469   g_scanner_get_next_token (scanner);
470 
471   if (scanner->value.v_string)
472     *dest = g_memdup (scanner->value.v_string, length);
473   else
474     *dest = NULL;
475 
476   return TRUE;
477 }
478 
479 /**
480  * gimp_scanner_parse_int:
481  * @scanner: A #GScanner created by gimp_scanner_new_file() or
482  *           gimp_scanner_new_string()
483  * @dest: Return location for the parsed integer
484  *
485  * Return value: %TRUE on success
486  *
487  * Since: 2.4
488  **/
489 gboolean
gimp_scanner_parse_int(GScanner * scanner,gint * dest)490 gimp_scanner_parse_int (GScanner *scanner,
491                         gint     *dest)
492 {
493   gboolean negate = FALSE;
494 
495   if (g_scanner_peek_next_token (scanner) == '-')
496     {
497       negate = TRUE;
498       g_scanner_get_next_token (scanner);
499     }
500 
501   if (g_scanner_peek_next_token (scanner) != G_TOKEN_INT)
502     return FALSE;
503 
504   g_scanner_get_next_token (scanner);
505 
506   if (negate)
507     *dest = -scanner->value.v_int64;
508   else
509     *dest = scanner->value.v_int64;
510 
511   return TRUE;
512 }
513 
514 /**
515  * gimp_scanner_parse_int64:
516  * @scanner: A #GScanner created by gimp_scanner_new_file() or
517  *           gimp_scanner_new_string()
518  * @dest: Return location for the parsed integer
519  *
520  * Return value: %TRUE on success
521  *
522  * Since: 2.8
523  **/
524 gboolean
gimp_scanner_parse_int64(GScanner * scanner,gint64 * dest)525 gimp_scanner_parse_int64 (GScanner *scanner,
526                           gint64   *dest)
527 {
528   gboolean negate = FALSE;
529 
530   if (g_scanner_peek_next_token (scanner) == '-')
531     {
532       negate = TRUE;
533       g_scanner_get_next_token (scanner);
534     }
535 
536   if (g_scanner_peek_next_token (scanner) != G_TOKEN_INT)
537     return FALSE;
538 
539   g_scanner_get_next_token (scanner);
540 
541   if (negate)
542     *dest = -scanner->value.v_int64;
543   else
544     *dest = scanner->value.v_int64;
545 
546   return TRUE;
547 }
548 
549 /**
550  * gimp_scanner_parse_float:
551  * @scanner: A #GScanner created by gimp_scanner_new_file() or
552  *           gimp_scanner_new_string()
553  * @dest: Return location for the parsed float
554  *
555  * Return value: %TRUE on success
556  *
557  * Since: 2.4
558  **/
559 gboolean
gimp_scanner_parse_float(GScanner * scanner,gdouble * dest)560 gimp_scanner_parse_float (GScanner *scanner,
561                           gdouble  *dest)
562 {
563   gboolean negate = FALSE;
564 
565   if (g_scanner_peek_next_token (scanner) == '-')
566     {
567       negate = TRUE;
568       g_scanner_get_next_token (scanner);
569     }
570 
571   if (g_scanner_peek_next_token (scanner) == G_TOKEN_FLOAT)
572     {
573       g_scanner_get_next_token (scanner);
574 
575       if (negate)
576         *dest = -scanner->value.v_float;
577       else
578         *dest = scanner->value.v_float;
579 
580       return TRUE;
581     }
582   else if (g_scanner_peek_next_token (scanner) == G_TOKEN_INT)
583     {
584       g_scanner_get_next_token (scanner);
585 
586       if (negate)
587         *dest = -scanner->value.v_int;
588       else
589         *dest = scanner->value.v_int;
590 
591       return TRUE;
592     }
593 
594   return FALSE;
595 }
596 
597 /**
598  * gimp_scanner_parse_boolean:
599  * @scanner: A #GScanner created by gimp_scanner_new_file() or
600  *           gimp_scanner_new_string()
601  * @dest: Return location for the parsed boolean
602  *
603  * Return value: %TRUE on success
604  *
605  * Since: 2.4
606  **/
607 gboolean
gimp_scanner_parse_boolean(GScanner * scanner,gboolean * dest)608 gimp_scanner_parse_boolean (GScanner *scanner,
609                             gboolean *dest)
610 {
611   if (g_scanner_peek_next_token (scanner) != G_TOKEN_IDENTIFIER)
612     return FALSE;
613 
614   g_scanner_get_next_token (scanner);
615 
616   if (! g_ascii_strcasecmp (scanner->value.v_identifier, "yes") ||
617       ! g_ascii_strcasecmp (scanner->value.v_identifier, "true"))
618     {
619       *dest = TRUE;
620     }
621   else if (! g_ascii_strcasecmp (scanner->value.v_identifier, "no") ||
622            ! g_ascii_strcasecmp (scanner->value.v_identifier, "false"))
623     {
624       *dest = FALSE;
625     }
626   else
627     {
628       g_scanner_error
629         (scanner,
630          /* please don't translate 'yes' and 'no' */
631          _("expected 'yes' or 'no' for boolean token, got '%s'"),
632          scanner->value.v_identifier);
633 
634       return FALSE;
635     }
636 
637   return TRUE;
638 }
639 
640 enum
641 {
642   COLOR_RGB  = 1,
643   COLOR_RGBA,
644   COLOR_HSV,
645   COLOR_HSVA
646 };
647 
648 /**
649  * gimp_scanner_parse_color:
650  * @scanner: A #GScanner created by gimp_scanner_new_file() or
651  *           gimp_scanner_new_string()
652  * @dest: Pointer to a color to store the result
653  *
654  * Return value: %TRUE on success
655  *
656  * Since: 2.4
657  **/
658 gboolean
gimp_scanner_parse_color(GScanner * scanner,GimpRGB * dest)659 gimp_scanner_parse_color (GScanner *scanner,
660                           GimpRGB  *dest)
661 {
662   guint      scope_id;
663   guint      old_scope_id;
664   GTokenType token;
665   GimpRGB    color = { 0.0, 0.0, 0.0, 1.0 };
666 
667   scope_id = g_quark_from_static_string ("gimp_scanner_parse_color");
668   old_scope_id = g_scanner_set_scope (scanner, scope_id);
669 
670   if (! g_scanner_scope_lookup_symbol (scanner, scope_id, "color-rgb"))
671     {
672       g_scanner_scope_add_symbol (scanner, scope_id,
673                                   "color-rgb", GINT_TO_POINTER (COLOR_RGB));
674       g_scanner_scope_add_symbol (scanner, scope_id,
675                                   "color-rgba", GINT_TO_POINTER (COLOR_RGBA));
676       g_scanner_scope_add_symbol (scanner, scope_id,
677                                   "color-hsv", GINT_TO_POINTER (COLOR_HSV));
678       g_scanner_scope_add_symbol (scanner, scope_id,
679                                   "color-hsva", GINT_TO_POINTER (COLOR_HSVA));
680     }
681 
682   token = G_TOKEN_LEFT_PAREN;
683 
684   while (g_scanner_peek_next_token (scanner) == token)
685     {
686       token = g_scanner_get_next_token (scanner);
687 
688       switch (token)
689         {
690         case G_TOKEN_LEFT_PAREN:
691           token = G_TOKEN_SYMBOL;
692           break;
693 
694         case G_TOKEN_SYMBOL:
695           {
696             gdouble  col[4]     = { 0.0, 0.0, 0.0, 1.0 };
697             gint     n_channels = 4;
698             gboolean is_hsv     = FALSE;
699             gint     i;
700 
701             switch (GPOINTER_TO_INT (scanner->value.v_symbol))
702               {
703               case COLOR_RGB:
704                 n_channels = 3;
705                 /* fallthrough */
706               case COLOR_RGBA:
707                 break;
708 
709               case COLOR_HSV:
710                 n_channels = 3;
711                 /* fallthrough */
712               case COLOR_HSVA:
713                 is_hsv = TRUE;
714                 break;
715               }
716 
717             token = G_TOKEN_FLOAT;
718 
719             for (i = 0; i < n_channels; i++)
720               {
721                 if (! gimp_scanner_parse_float (scanner, &col[i]))
722                   goto finish;
723               }
724 
725             if (is_hsv)
726               {
727                 GimpHSV hsv;
728 
729                 gimp_hsva_set (&hsv, col[0], col[1], col[2], col[3]);
730                 gimp_hsv_to_rgb (&hsv, &color);
731               }
732             else
733               {
734                 gimp_rgba_set (&color, col[0], col[1], col[2], col[3]);
735               }
736 
737             token = G_TOKEN_RIGHT_PAREN;
738           }
739           break;
740 
741         case G_TOKEN_RIGHT_PAREN:
742           token = G_TOKEN_NONE; /* indicates success */
743           goto finish;
744 
745         default: /* do nothing */
746           break;
747         }
748     }
749 
750  finish:
751 
752   if (token != G_TOKEN_NONE)
753     {
754       g_scanner_get_next_token (scanner);
755       g_scanner_unexp_token (scanner, token, NULL, NULL, NULL,
756                              _("fatal parse error"), TRUE);
757     }
758   else
759     {
760       *dest = color;
761     }
762 
763   g_scanner_set_scope (scanner, old_scope_id);
764 
765   return (token == G_TOKEN_NONE);
766 }
767 
768 /**
769  * gimp_scanner_parse_matrix2:
770  * @scanner: A #GScanner created by gimp_scanner_new_file() or
771  *           gimp_scanner_new_string()
772  * @dest: Pointer to a matrix to store the result
773  *
774  * Return value: %TRUE on success
775  *
776  * Since: 2.4
777  **/
778 gboolean
gimp_scanner_parse_matrix2(GScanner * scanner,GimpMatrix2 * dest)779 gimp_scanner_parse_matrix2 (GScanner    *scanner,
780                             GimpMatrix2 *dest)
781 {
782   guint        scope_id;
783   guint        old_scope_id;
784   GTokenType   token;
785   GimpMatrix2  matrix;
786 
787   scope_id = g_quark_from_static_string ("gimp_scanner_parse_matrix");
788   old_scope_id = g_scanner_set_scope (scanner, scope_id);
789 
790   if (! g_scanner_scope_lookup_symbol (scanner, scope_id, "matrix"))
791     g_scanner_scope_add_symbol (scanner, scope_id,
792                                 "matrix", GINT_TO_POINTER (0));
793 
794   token = G_TOKEN_LEFT_PAREN;
795 
796   while (g_scanner_peek_next_token (scanner) == token)
797     {
798       token = g_scanner_get_next_token (scanner);
799 
800       switch (token)
801         {
802         case G_TOKEN_LEFT_PAREN:
803           token = G_TOKEN_SYMBOL;
804           break;
805 
806         case G_TOKEN_SYMBOL:
807           {
808             token = G_TOKEN_FLOAT;
809 
810             if (! gimp_scanner_parse_float (scanner, &matrix.coeff[0][0]))
811               goto finish;
812             if (! gimp_scanner_parse_float (scanner, &matrix.coeff[0][1]))
813               goto finish;
814             if (! gimp_scanner_parse_float (scanner, &matrix.coeff[1][0]))
815               goto finish;
816             if (! gimp_scanner_parse_float (scanner, &matrix.coeff[1][1]))
817               goto finish;
818 
819             token = G_TOKEN_RIGHT_PAREN;
820           }
821           break;
822 
823         case G_TOKEN_RIGHT_PAREN:
824           token = G_TOKEN_NONE; /* indicates success */
825           goto finish;
826 
827         default: /* do nothing */
828           break;
829         }
830     }
831 
832  finish:
833 
834   if (token != G_TOKEN_NONE)
835     {
836       g_scanner_get_next_token (scanner);
837       g_scanner_unexp_token (scanner, token, NULL, NULL, NULL,
838                              _("fatal parse error"), TRUE);
839     }
840   else
841     {
842       *dest = matrix;
843     }
844 
845   g_scanner_set_scope (scanner, old_scope_id);
846 
847   return (token == G_TOKEN_NONE);
848 }
849 
850 
851 /*  private functions  */
852 
853 static void
gimp_scanner_message(GScanner * scanner,gchar * message,gboolean is_error)854 gimp_scanner_message (GScanner *scanner,
855                       gchar    *message,
856                       gboolean  is_error)
857 {
858   GimpScannerData *data = scanner->user_data;
859 
860   /* we don't expect warnings */
861   g_return_if_fail (is_error);
862 
863   if (data->name)
864     g_set_error (data->error, GIMP_CONFIG_ERROR, GIMP_CONFIG_ERROR_PARSE,
865                  _("Error while parsing '%s' in line %d: %s"),
866                  data->name, scanner->line, message);
867   else
868     /*  should never happen, thus not marked for translation  */
869     g_set_error (data->error, GIMP_CONFIG_ERROR, GIMP_CONFIG_ERROR_PARSE,
870                  "Error parsing internal buffer: %s", message);
871 }
872