gimpscanner.c 16.1 KB
Newer Older
1
/* LIBGIMP - The GIMP Library
2 3 4 5 6 7
 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
 *
 * gimpscanner.c
 * Copyright (C) 2002  Sven Neumann <sven@gimp.org>
 *                     Michael Natterer <mitch@gimp.org>
 *
8
 * This library is free software: you can redistribute it and/or
9 10
 * modify it under the terms of the GNU Lesser General Public
 * 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 16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU Lesser General Public
19 20
 * License along with this library.  If not, see
 * <http://www.gnu.org/licenses/>.
21 22 23 24
 */

#include "config.h"

Hans Breuer's avatar
Hans Breuer committed
25
#include <string.h>
26
#include <errno.h>
27

28
#include <cairo.h>
29 30
#include <gegl.h>
#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 38
#include "gimpscanner.h"

39
#include "libgimp/libgimp-intl.h"
40 41


42 43 44 45 46 47 48 49 50
/**
 * SECTION: gimpscanner
 * @title: GimpScanner
 * @short_description: A wrapper around #GScanner with some convenience API.
 *
 * A wrapper around #GScanner with some convenience API.
 **/


51 52
typedef struct
{
53 54 55
  gchar        *name;
  GMappedFile  *file;
  GError      **error;
56 57 58
} GimpScannerData;


59 60
/*  local function prototypes  */

61
static GScanner * gimp_scanner_new     (const gchar  *name,
62
                                        GMappedFile  *file,
63 64 65 66
                                        GError      **error);
static void       gimp_scanner_message (GScanner     *scanner,
                                        gchar        *message,
                                        gboolean      is_error);
67 68 69 70


/*  public functions  */

71 72 73 74 75 76 77 78 79
/**
 * gimp_scanner_new_file:
 * @filename:
 * @error:
 *
 * Return value:
 *
 * Since: GIMP 2.4
 **/
80
GScanner *
81
gimp_scanner_new_file (const gchar  *filename,
Sven Neumann's avatar
Sven Neumann committed
82
                       GError      **error)
83
{
84 85
  GScanner    *scanner;
  GMappedFile *file;
86 87

  g_return_val_if_fail (filename != NULL, NULL);
88
  g_return_val_if_fail (error == NULL || *error == NULL, NULL);
89

90
  file = g_mapped_file_new (filename, FALSE, error);
91

92
  if (! file)
93
    {
94 95 96 97 98 99 100
      if (error)
        {
          (*error)->domain = GIMP_CONFIG_ERROR;
          (*error)->code   = ((*error)->code == G_FILE_ERROR_NOENT ?
                              GIMP_CONFIG_ERROR_OPEN_ENOENT :
                              GIMP_CONFIG_ERROR_OPEN);
        }
101

102 103
      return NULL;
    }
104

105 106
  /*  gimp_scanner_new() takes a "name" for the scanner, not a filename  */
  scanner = gimp_scanner_new (gimp_filename_to_utf8 (filename), file, error);
107

108 109 110
  g_scanner_input_text (scanner,
                        g_mapped_file_get_contents (file),
                        g_mapped_file_get_length (file));
111 112 113 114

  return scanner;
}

115 116 117 118 119 120 121 122 123 124
/**
 * gimp_scanner_new_string:
 * @text:
 * @text_len:
 * @error:
 *
 * Return value:
 *
 * Since: GIMP 2.4
 **/
125 126
GScanner *
gimp_scanner_new_string (const gchar  *text,
Sven Neumann's avatar
Sven Neumann committed
127 128
                         gint          text_len,
                         GError      **error)
129 130 131 132 133 134 135 136 137
{
  GScanner *scanner;

  g_return_val_if_fail (text != NULL || text_len == 0, NULL);
  g_return_val_if_fail (error == NULL || *error == NULL, NULL);

  if (text_len < 0)
    text_len = strlen (text);

138
  scanner = gimp_scanner_new (NULL, NULL, error);
139 140 141 142 143 144 145

  g_scanner_input_text (scanner, text, text_len);

  return scanner;
}

static GScanner *
146
gimp_scanner_new (const gchar  *name,
147
                  GMappedFile  *file,
148
                  GError      **error)
149
{
150 151
  GScanner        *scanner;
  GimpScannerData *data;
152 153

  scanner = g_scanner_new (NULL);
154

155
  data = g_slice_new0 (GimpScannerData);
156 157

  data->name  = g_strdup (name);
158
  data->file  = file;
159 160 161
  data->error = error;

  scanner->user_data   = data;
162 163 164
  scanner->msg_handler = gimp_scanner_message;

  scanner->config->cset_identifier_first = ( G_CSET_a_2_z G_CSET_A_2_Z );
165 166
  scanner->config->cset_identifier_nth   = ( G_CSET_a_2_z G_CSET_A_2_Z
                                             G_CSET_DIGITS "-_" );
167
  scanner->config->scan_identifier_1char = TRUE;
168

169 170
  scanner->config->store_int64           = TRUE;

171 172 173
  return scanner;
}

174 175
/**
 * gimp_scanner_destroy:
176 177
 * @scanner: A #GScanner created by gimp_scanner_new_file() or
 *           gimp_scanner_new_string()
178 179 180
 *
 * Since: GIMP 2.4
 **/
181 182 183
void
gimp_scanner_destroy (GScanner *scanner)
{
184 185
  GimpScannerData *data;

186 187
  g_return_if_fail (scanner != NULL);

188 189
  data = scanner->user_data;

190
  if (data->file)
191
    g_mapped_file_unref (data->file);
192 193

  g_free (data->name);
194
  g_slice_free (GimpScannerData, data);
195

196 197 198
  g_scanner_destroy (scanner);
}

199 200
/**
 * gimp_scanner_parse_token:
201 202 203
 * @scanner: A #GScanner created by gimp_scanner_new_file() or
 *           gimp_scanner_new_string()
 * @token: Return location for the parsed token
204
 *
205
 * Return value: %TRUE on success
206 207 208
 *
 * Since: GIMP 2.4
 **/
209 210 211 212 213 214 215 216 217 218 219 220
gboolean
gimp_scanner_parse_token (GScanner   *scanner,
                          GTokenType  token)
{
  if (g_scanner_peek_next_token (scanner) != token)
    return FALSE;

  g_scanner_get_next_token (scanner);

  return TRUE;
}

221 222
/**
 * gimp_scanner_parse_identifier:
223 224 225
 * @scanner: A #GScanner created by gimp_scanner_new_file() or
 *           gimp_scanner_new_string()
 * @identifier: Return location for the parsed identifier
226
 *
227
 * Return value: %TRUE on success
228 229 230
 *
 * Since: GIMP 2.4
 **/
231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
gboolean
gimp_scanner_parse_identifier (GScanner    *scanner,
                               const gchar *identifier)
{
  if (g_scanner_peek_next_token (scanner) != G_TOKEN_IDENTIFIER)
    return FALSE;

  g_scanner_get_next_token (scanner);

  if (strcmp (scanner->value.v_identifier, identifier))
    return FALSE;

  return TRUE;
}

246 247
/**
 * gimp_scanner_parse_string:
248 249 250
 * @scanner: A #GScanner created by gimp_scanner_new_file() or
 *           gimp_scanner_new_string()
 * @dest: Return location for the parsed string
251
 *
252
 * Return value: %TRUE on success
253 254 255
 *
 * Since: GIMP 2.4
 **/
256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271
gboolean
gimp_scanner_parse_string (GScanner  *scanner,
                           gchar    **dest)
{
  if (g_scanner_peek_next_token (scanner) != G_TOKEN_STRING)
    return FALSE;

  g_scanner_get_next_token (scanner);

  if (*scanner->value.v_string)
    {
      if (! g_utf8_validate (scanner->value.v_string, -1, NULL))
        {
          g_scanner_warn (scanner, _("invalid UTF-8 string"));
          return FALSE;
        }
272

273 274
      *dest = g_strdup (scanner->value.v_string);
    }
275 276 277 278
  else
    {
      *dest = NULL;
    }
279 280 281 282

  return TRUE;
}

283 284
/**
 * gimp_scanner_parse_string_no_validate:
285 286 287
 * @scanner: A #GScanner created by gimp_scanner_new_file() or
 *           gimp_scanner_new_string()
 * @dest: Return location for the parsed string
288
 *
289
 * Return value: %TRUE on success
290 291 292
 *
 * Since: GIMP 2.4
 **/
293 294 295 296 297 298 299 300 301 302 303
gboolean
gimp_scanner_parse_string_no_validate (GScanner  *scanner,
                                       gchar    **dest)
{
  if (g_scanner_peek_next_token (scanner) != G_TOKEN_STRING)
    return FALSE;

  g_scanner_get_next_token (scanner);

  if (*scanner->value.v_string)
    *dest = g_strdup (scanner->value.v_string);
304 305
  else
    *dest = NULL;
306 307 308 309

  return TRUE;
}

310 311
/**
 * gimp_scanner_parse_data:
312 313 314 315
 * @scanner: A #GScanner created by gimp_scanner_new_file() or
 *           gimp_scanner_new_string()
 * @length: Length of tha data to parse
 * @dest: Return location for the parsed data
316
 *
317
 * Return value: %TRUE on success
318 319 320
 *
 * Since: GIMP 2.4
 **/
321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338
gboolean
gimp_scanner_parse_data (GScanner  *scanner,
                         gint       length,
                         guint8   **dest)
{
  if (g_scanner_peek_next_token (scanner) != G_TOKEN_STRING)
    return FALSE;

  g_scanner_get_next_token (scanner);

  if (scanner->value.v_string)
    *dest = g_memdup (scanner->value.v_string, length);
  else
    *dest = NULL;

  return TRUE;
}

339 340
/**
 * gimp_scanner_parse_int:
341 342 343
 * @scanner: A #GScanner created by gimp_scanner_new_file() or
 *           gimp_scanner_new_string()
 * @dest: Return location for the parsed integer
344
 *
345
 * Return value: %TRUE on success
346 347 348
 *
 * Since: GIMP 2.4
 **/
349 350 351 352
gboolean
gimp_scanner_parse_int (GScanner *scanner,
                        gint     *dest)
{
353 354 355 356 357 358 359 360
  gboolean negate = FALSE;

  if (g_scanner_peek_next_token (scanner) == '-')
    {
      negate = TRUE;
      g_scanner_get_next_token (scanner);
    }

361 362 363 364 365
  if (g_scanner_peek_next_token (scanner) != G_TOKEN_INT)
    return FALSE;

  g_scanner_get_next_token (scanner);

366
  if (negate)
367
    *dest = -scanner->value.v_int64;
368
  else
369
    *dest = scanner->value.v_int64;
370 371 372 373

  return TRUE;
}

374 375
/**
 * gimp_scanner_parse_float:
376 377 378
 * @scanner: A #GScanner created by gimp_scanner_new_file() or
 *           gimp_scanner_new_string()
 * @dest: Return location for the parsed float
379
 *
380
 * Return value: %TRUE on success
381 382 383
 *
 * Since: GIMP 2.4
 **/
384 385 386 387 388 389 390 391 392 393 394 395 396
gboolean
gimp_scanner_parse_float (GScanner *scanner,
                          gdouble  *dest)
{
  if (g_scanner_peek_next_token (scanner) != G_TOKEN_FLOAT)
    return FALSE;

  g_scanner_get_next_token (scanner);

  *dest = scanner->value.v_float;

  return TRUE;
}
397

398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440
/**
 * gimp_scanner_parse_boolean:
 * @scanner: A #GScanner created by gimp_scanner_new_file() or
 *           gimp_scanner_new_string()
 * @dest: Return location for the parsed boolean
 *
 * Return value: %TRUE on success
 *
 * Since: GIMP 2.4
 **/
gboolean
gimp_scanner_parse_boolean (GScanner *scanner,
                            gboolean *dest)
{
  if (g_scanner_peek_next_token (scanner) != G_TOKEN_IDENTIFIER)
    return FALSE;

  g_scanner_get_next_token (scanner);

  if (! g_ascii_strcasecmp (scanner->value.v_identifier, "yes") ||
      ! g_ascii_strcasecmp (scanner->value.v_identifier, "true"))
    {
      *dest = TRUE;
    }
  else if (! g_ascii_strcasecmp (scanner->value.v_identifier, "no") ||
           ! g_ascii_strcasecmp (scanner->value.v_identifier, "false"))
    {
      *dest = FALSE;
    }
  else
    {
      g_scanner_error
        (scanner,
         /* please don't translate 'yes' and 'no' */
         _("expected 'yes' or 'no' for boolean token, got '%s'"),
         scanner->value.v_identifier);

      return FALSE;
    }

  return TRUE;
}

441 442 443 444 445 446 447 448
enum
{
  COLOR_RGB  = 1,
  COLOR_RGBA,
  COLOR_HSV,
  COLOR_HSVA
};

449 450
/**
 * gimp_scanner_parse_color:
451 452 453
 * @scanner: A #GScanner created by gimp_scanner_new_file() or
 *           gimp_scanner_new_string()
 * @dest: Pointer to a color to store the result
454
 *
455
 * Return value: %TRUE on success
456 457 458
 *
 * Since: GIMP 2.4
 **/
459 460 461 462 463 464 465
gboolean
gimp_scanner_parse_color (GScanner *scanner,
                          GimpRGB  *dest)
{
  guint      scope_id;
  guint      old_scope_id;
  GTokenType token;
466
  GimpRGB    color = { 0.0, 0.0, 0.0, 1.0 };
467 468 469 470 471 472

  scope_id = g_quark_from_static_string ("gimp_scanner_parse_color");
  old_scope_id = g_scanner_set_scope (scanner, scope_id);

  if (! g_scanner_scope_lookup_symbol (scanner, scope_id, "color-rgb"))
    {
473
      g_scanner_scope_add_symbol (scanner, scope_id,
474
                                  "color-rgb", GINT_TO_POINTER (COLOR_RGB));
475
      g_scanner_scope_add_symbol (scanner, scope_id,
476
                                  "color-rgba", GINT_TO_POINTER (COLOR_RGBA));
477
      g_scanner_scope_add_symbol (scanner, scope_id,
478
                                  "color-hsv", GINT_TO_POINTER (COLOR_HSV));
479
      g_scanner_scope_add_symbol (scanner, scope_id,
480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496
                                  "color-hsva", GINT_TO_POINTER (COLOR_HSVA));
    }

  token = G_TOKEN_LEFT_PAREN;

  while (g_scanner_peek_next_token (scanner) == token)
    {
      token = g_scanner_get_next_token (scanner);

      switch (token)
        {
        case G_TOKEN_LEFT_PAREN:
          token = G_TOKEN_SYMBOL;
          break;

        case G_TOKEN_SYMBOL:
          {
497
            gdouble  col[4]     = { 0.0, 0.0, 0.0, 1.0 };
498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539
            gint     n_channels = 4;
            gboolean is_hsv     = FALSE;
            gint     i;

            switch (GPOINTER_TO_INT (scanner->value.v_symbol))
              {
              case COLOR_RGB:
                n_channels = 3;
                /* fallthrough */
              case COLOR_RGBA:
                break;

              case COLOR_HSV:
                n_channels = 3;
                /* fallthrough */
              case COLOR_HSVA:
                is_hsv = TRUE;
                break;
              }

            token = G_TOKEN_FLOAT;

            for (i = 0; i < n_channels; i++)
              {
                if (! gimp_scanner_parse_float (scanner, &col[i]))
                  goto finish;
              }

            if (is_hsv)
              {
                GimpHSV hsv;

                gimp_hsva_set (&hsv, col[0], col[1], col[2], col[3]);
                gimp_hsv_clamp (&hsv);

                gimp_hsv_to_rgb (&hsv, &color);
              }
            else
              {
                gimp_rgba_set (&color, col[0], col[1], col[2], col[3]);
                gimp_rgb_clamp (&color);
              }
540 541

            token = G_TOKEN_RIGHT_PAREN;
542 543 544 545
          }
          break;

        case G_TOKEN_RIGHT_PAREN:
546
          token = G_TOKEN_NONE; /* indicates success */
547 548 549 550 551 552 553 554 555
          goto finish;

        default: /* do nothing */
          break;
        }
    }

 finish:

556
  if (token != G_TOKEN_NONE)
557 558 559 560 561
    {
      g_scanner_get_next_token (scanner);
      g_scanner_unexp_token (scanner, token, NULL, NULL, NULL,
                             _("fatal parse error"), TRUE);
    }
562 563 564 565
  else
    {
      *dest = color;
    }
566 567 568

  g_scanner_set_scope (scanner, old_scope_id);

569 570 571
  return (token == G_TOKEN_NONE);
}

572 573
/**
 * gimp_scanner_parse_matrix2:
574 575 576
 * @scanner: A #GScanner created by gimp_scanner_new_file() or
 *           gimp_scanner_new_string()
 * @dest: Pointer to a matrix to store the result
577
 *
578
 * Return value: %TRUE on success
579 580 581
 *
 * Since: GIMP 2.4
 **/
582 583 584 585 586 587 588 589 590 591 592 593 594
gboolean
gimp_scanner_parse_matrix2 (GScanner    *scanner,
                            GimpMatrix2 *dest)
{
  guint        scope_id;
  guint        old_scope_id;
  GTokenType   token;
  GimpMatrix2  matrix;

  scope_id = g_quark_from_static_string ("gimp_scanner_parse_matrix");
  old_scope_id = g_scanner_set_scope (scanner, scope_id);

  if (! g_scanner_scope_lookup_symbol (scanner, scope_id, "matrix"))
595 596
    g_scanner_scope_add_symbol (scanner, scope_id,
                                "matrix", GINT_TO_POINTER (0));
597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653

  token = G_TOKEN_LEFT_PAREN;

  while (g_scanner_peek_next_token (scanner) == token)
    {
      token = g_scanner_get_next_token (scanner);

      switch (token)
        {
        case G_TOKEN_LEFT_PAREN:
          token = G_TOKEN_SYMBOL;
          break;

        case G_TOKEN_SYMBOL:
          {
            token = G_TOKEN_FLOAT;

            if (! gimp_scanner_parse_float (scanner, &matrix.coeff[0][0]))
              goto finish;
            if (! gimp_scanner_parse_float (scanner, &matrix.coeff[0][1]))
              goto finish;
            if (! gimp_scanner_parse_float (scanner, &matrix.coeff[1][0]))
              goto finish;
            if (! gimp_scanner_parse_float (scanner, &matrix.coeff[1][1]))
              goto finish;

            token = G_TOKEN_RIGHT_PAREN;
          }
          break;

        case G_TOKEN_RIGHT_PAREN:
          token = G_TOKEN_NONE; /* indicates success */
          goto finish;

        default: /* do nothing */
          break;
        }
    }

 finish:

  if (token != G_TOKEN_NONE)
    {
      g_scanner_get_next_token (scanner);
      g_scanner_unexp_token (scanner, token, NULL, NULL, NULL,
                             _("fatal parse error"), TRUE);
    }
  else
    {
      *dest = matrix;
    }

  g_scanner_set_scope (scanner, old_scope_id);

  return (token == G_TOKEN_NONE);
}

654 655 656 657 658 659 660 661

/*  private functions  */

static void
gimp_scanner_message (GScanner *scanner,
                      gchar    *message,
                      gboolean  is_error)
{
662
  GimpScannerData *data = scanner->user_data;
663 664 665 666

  /* we don't expect warnings */
  g_return_if_fail (is_error);

667
  if (data->name)
668 669
    g_set_error (data->error, GIMP_CONFIG_ERROR, GIMP_CONFIG_ERROR_PARSE,
                 _("Error while parsing '%s' in line %d: %s"),
670
                 data->name, scanner->line, message);
671
  else
672
    /*  should never happen, thus not marked for translation  */
673 674
    g_set_error (data->error, GIMP_CONFIG_ERROR, GIMP_CONFIG_ERROR_PARSE,
                 "Error parsing internal buffer: %s", message);
675
}