Logo Search packages:      
Sourcecode: cairo version File versions  Download package

cairo-script-scanner.c

/*
 * Copyright © 2008 Chris Wilson <chris@chris-wilson.co.uk>
 *
 * This library is free software; you can redistribute it and/or
 * modify it either under the terms of the GNU Lesser General Public
 * License version 2.1 as published by the Free Software Foundation
 * (the "LGPL") or, at your option, under the terms of the Mozilla
 * Public License Version 1.1 (the "MPL"). If you do not alter this
 * notice, a recipient may use your version of this file under either
 * the MPL or the LGPL.
 *
 * You should have received a copy of the LGPL along with this library
 * in the file COPYING-LGPL-2.1; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
 * You should have received a copy of the MPL along with this library
 * in the file COPYING-MPL-1.1
 *
 * The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
 * OF ANY KIND, either express or implied. See the LGPL or the MPL for
 * the specific language governing rights and limitations.
 *
 * The Original Code is the cairo graphics library.
 *
 * The Initial Developer of the Original Code is Chris Wilson.
 *
 * Contributor(s):
 *    Chris Wilson <chris@chris-wilson.co.uk>
 */

#include "cairo-script-private.h"

#include <limits.h> /* INT_MAX */
#include <math.h> /* pow */
#include <stdio.h> /* EOF */
#include <string.h> /* memset */
#include <assert.h>
#include <zlib.h>

#define DEBUG_SCAN 0

#if WORDS_BIGENDIAN
#define le16(x) bswap_16 (x)
#define le32(x) bswap_32 (x)
#define be16(x) x
#define be32(x) x
#define to_be32(x) x
#else
#define le16(x) x
#define le32(x) x
#define be16(x) bswap_16 (x)
#define be32(x) bswap_32 (x)
#define to_be32(x) bswap_32 (x)
#endif

/*
 * whitespace:
 * 0 - nul
 * 9 - tab
 * A - LF
 * C - FF
 * D - CR
 *
 * syntax delimiters
 * ( = 28, ) = 29 - literal strings
 * < = 3C, > = 3E - hex/base85 strings, dictionary name
 * [ = 5B, ] = 5D - array
 * { = 7B, } = 7C - procedure
 * / = 5C - literal marker
 * % = 25 - comment
 */

static void
fprintf_obj (FILE *stream, csi_t *ctx, const csi_object_t *obj)
{
    switch (csi_object_get_type (obj)) {
      case CSI_OBJECT_TYPE_NULL:
          fprintf (stream, "NULL\n");
          break;

          /* atomics */
      case CSI_OBJECT_TYPE_BOOLEAN:
          fprintf (stream, "boolean: %s\n",
                obj->datum.boolean ? "true" : "false");
          break;
      case CSI_OBJECT_TYPE_INTEGER:
          fprintf (stream, "integer: %ld\n", obj->datum.integer);
          break;
      case CSI_OBJECT_TYPE_MARK:
          fprintf (stream, "mark\n");
          break;
      case CSI_OBJECT_TYPE_NAME:
          fprintf (stream, "name: %s\n", (char *) obj->datum.name);
          break;
      case CSI_OBJECT_TYPE_OPERATOR:
          fprintf (stream, "operator: %p\n", obj->datum.ptr);
          break;
      case CSI_OBJECT_TYPE_REAL:
          fprintf (stream, "real: %g\n", obj->datum.real);
          break;

          /* compound */
      case CSI_OBJECT_TYPE_ARRAY:
          fprintf (stream, "array\n");
          break;
      case CSI_OBJECT_TYPE_DICTIONARY:
          fprintf (stream, "dictionary\n");
          break;
      case CSI_OBJECT_TYPE_FILE:
          fprintf (stream, "file\n");
          break;
      case CSI_OBJECT_TYPE_MATRIX:
          fprintf (stream, "matrix: [%g %g %g %g %g %g]\n",
                obj->datum.matrix->matrix.xx,
                obj->datum.matrix->matrix.yx,
                obj->datum.matrix->matrix.xy,
                obj->datum.matrix->matrix.yy,
                obj->datum.matrix->matrix.x0,
                obj->datum.matrix->matrix.y0);
          break;
      case CSI_OBJECT_TYPE_STRING:
          fprintf (stream, "string: len=%ld\n", obj->datum.string->len);
          break;

          /* cairo */
      case CSI_OBJECT_TYPE_CONTEXT:
          fprintf (stream, "context\n");
          break;
      case CSI_OBJECT_TYPE_FONT:
          fprintf (stream, "font\n");
          break;
      case CSI_OBJECT_TYPE_PATTERN:
          fprintf (stream, "pattern\n");
          break;
      case CSI_OBJECT_TYPE_SCALED_FONT:
          fprintf (stream, "scaled-font\n");
          break;
      case CSI_OBJECT_TYPE_SURFACE:
          fprintf (stream, "surface\n");
          break;
    }
}

/* takes ownership of obj */
static inline csi_status_t
scan_push (csi_t *ctx, csi_object_t *obj)
{
    return ctx->scanner.push (ctx, obj);
}

static inline csi_status_t
scan_execute (csi_t *ctx, csi_object_t *obj)
{
    return ctx->scanner.execute (ctx, obj);
}

static cairo_status_t
buffer_init (csi_t *ctx, csi_buffer_t *buffer)
{
    cairo_status_t status = CSI_STATUS_SUCCESS;

    buffer->size = 16384;
    buffer->base = _csi_alloc (ctx, buffer->size);
    if (_csi_unlikely (buffer->base == NULL)) {
      status = _csi_error (CSI_STATUS_NO_MEMORY);
      buffer->size = 0;
    }

    buffer->ptr = buffer->base;
    buffer->end = buffer->base + buffer->size;

    return status;
}

static void
buffer_fini (csi_t *ctx, csi_buffer_t *buffer)
{
    _csi_free (ctx, buffer->base);
}

static void
_buffer_grow (csi_t *ctx, csi_scanner_t *scan)
{
    int newsize;
    int offset;
    char *base;

    if (_csi_unlikely (scan->buffer.size > INT_MAX / 2))
      longjmp (scan->jmpbuf,  _csi_error (CSI_STATUS_NO_MEMORY));

    offset = scan->buffer.ptr - scan->buffer.base;
    newsize = scan->buffer.size * 2;
    base = _csi_realloc (ctx, scan->buffer.base, newsize);
    if (_csi_unlikely (base == NULL))
      longjmp (scan->jmpbuf,  _csi_error (CSI_STATUS_NO_MEMORY));

    scan->buffer.base = base;
    scan->buffer.ptr  = base + offset;
    scan->buffer.end  = base + newsize;
    scan->buffer.size = newsize;
}

static inline void
buffer_check (csi_t *ctx, csi_scanner_t *scan, int count)
{
    if (_csi_unlikely (scan->buffer.ptr + count > scan->buffer.end))
      _buffer_grow (ctx, scan);
}

static inline void
buffer_add (csi_buffer_t *buffer, int c)
{
    *buffer->ptr++ = c;
}

static inline void
buffer_reset (csi_buffer_t *buffer)
{
    buffer->ptr = buffer->base;
}

static void
token_start (csi_scanner_t *scan)
{
    buffer_reset (&scan->buffer);
}

static void
token_add (csi_t *ctx, csi_scanner_t *scan, int c)
{
    buffer_check (ctx, scan, 1);
    buffer_add (&scan->buffer, c);
}

static void
token_add_unchecked (csi_scanner_t *scan, int c)
{
    buffer_add (&scan->buffer, c);
}

csi_boolean_t
_csi_parse_number (csi_object_t *obj, const char *s, int len)
{
    int radix = 0;
    long long mantissa = 0;
    int exponent = 0;
    int sign = 1;
    int decimal = -1;
    int exponent_sign = 0;
    const char * const end = s + len;

    switch (*s) {
    case '0':
    case '1':
    case '2':
    case '3':
    case '4':
    case '5':
    case '6':
    case '7':
    case '8':
    case '9':
      mantissa = *s - '0';
    case '+':
      break;
    case '-':
      sign = -1;
      break;
    case '.':
      decimal = 0;
      break;
    default:
      return FALSE;
    }

    while (++s < end) {
      if (*s < '0') {
          if (*s == '.') {
            if (_csi_unlikely (radix))
                return FALSE;
            if (_csi_unlikely (decimal != -1))
                return FALSE;
            if (_csi_unlikely (exponent_sign))
                return FALSE;

            decimal = 0;
          } else if (*s == '!') {
            if (_csi_unlikely (radix))
                return FALSE;
            if (_csi_unlikely (decimal != -1))
                return FALSE;
            if (_csi_unlikely (exponent_sign))
                return FALSE;

            radix = mantissa;
            mantissa = 0;

            if (_csi_unlikely (radix < 2 || radix > 36))
                return FALSE;
          } else
            return FALSE;
      } else if (*s <= '9') {
          int v = *s - '0';
          if (_csi_unlikely (radix && v >= radix))
            return FALSE;

          if (exponent_sign) {
            exponent = 10 * exponent + v;
          } else {
            if (radix)
                mantissa = radix * mantissa + v;
            else
                mantissa = 10 * mantissa + v;
            if (decimal != -1)
                decimal++;
          }
      } else if (*s == 'E' || * s== 'e') {
          if (radix == 0) {
            if (_csi_unlikely (s + 1 == end))
                return FALSE;

            exponent_sign = 1;
            if (s[1] == '-') {
                exponent_sign = -1;
                s++;
            } else if (s[1] == '+')
                s++;
          } else {
            int v = 0xe;

            if (_csi_unlikely (v >= radix))
                return FALSE;

            mantissa = radix * mantissa + v;
          }
      } else if (*s < 'A') {
          return FALSE;
      } else if (*s <= 'Z') {
          int v = *s - 'A' + 0xA;

          if (_csi_unlikely (v >= radix))
            return FALSE;

          mantissa = radix * mantissa + v;
      } else if (*s < 'a') {
          return FALSE;
      } else if (*s <= 'z') {
          int v = *s - 'a' + 0xa;

          if (_csi_unlikely (v >= radix))
            return FALSE;

          mantissa = radix * mantissa + v;
      } else
          return FALSE;
    }

    if (exponent_sign || decimal != -1) {
      if (mantissa == 0) {
          obj->type = CSI_OBJECT_TYPE_REAL;
          obj->datum.real = 0.;
          return TRUE;
      } else {
          int e;
          double v;

          v = mantissa;
          e = exponent * exponent_sign;
          if (decimal != -1)
            e -= decimal;
          switch (e) {
          case -7: v *= 0.0000001; break;
          case -6: v *= 0.000001; break;
          case -5: v *= 0.00001; break;
          case -4: v *= 0.0001; break;
          case -3: v *= 0.001; break;
          case -2: v *= 0.01; break;
          case -1: v *= 0.1; break;
          case  0: break;
          case  1: v *= 10; break;
          case  2: v *= 100; break;
          case  3: v *= 1000; break;
          case  4: v *= 10000; break;
          case  5: v *= 100000; break;
          case  6: v *= 1000000; break;
          default:
                v *= pow (10, e); /* XXX */
                break;
          }

          obj->type = CSI_OBJECT_TYPE_REAL;
          obj->datum.real = sign * v;
          return TRUE;
      }
    } else {
      obj->type = CSI_OBJECT_TYPE_INTEGER;
      obj->datum.integer = sign * mantissa;
      return TRUE;
    }
}

static void
token_end (csi_t *ctx, csi_scanner_t *scan, csi_file_t *src)
{
    cairo_status_t status;
    char *s;
    csi_object_t obj;
    int len;

    /*
     * Any token that consists entirely of regular characters and
     * cannot be interpreted as a number is treated as a name object
     * (more precisely, an executable name). All characters except
     * delimiters and white-space characters can appear in names,
     * including characters ordinarily considered to be punctuation.
     */

    if (_csi_unlikely (scan->buffer.ptr == scan->buffer.base))
      return;

    s = scan->buffer.base;
    len = scan->buffer.ptr - scan->buffer.base;

    if (_csi_likely (! scan->bind)) {
      if (s[0] == '{') { /* special case procedures */
          if (scan->build_procedure.type != CSI_OBJECT_TYPE_NULL) {
            status = _csi_stack_push (ctx,
                                &scan->procedure_stack,
                                &scan->build_procedure);
            if (_csi_unlikely (status))
                longjmp (scan->jmpbuf, status);
          }

          status = csi_array_new (ctx, 0, &scan->build_procedure);
          if (_csi_unlikely (status))
            longjmp (scan->jmpbuf, status);

          scan->build_procedure.type |= CSI_OBJECT_ATTR_EXECUTABLE;
          return;
      } else if (s[0] == '}') {
          if (_csi_unlikely
            (scan->build_procedure.type == CSI_OBJECT_TYPE_NULL))
          {
            longjmp (scan->jmpbuf, _csi_error (CSI_STATUS_INVALID_SCRIPT));
          }

          if (scan->procedure_stack.len) {
            csi_object_t *next;

            next = _csi_stack_peek (&scan->procedure_stack, 0);
            status = csi_array_append (ctx, next->datum.array,
                                 &scan->build_procedure);
            scan->build_procedure = *next;
            scan->procedure_stack.len--;
          } else {
            status = scan_push (ctx, &scan->build_procedure);
            scan->build_procedure.type = CSI_OBJECT_TYPE_NULL;
          }
          if (_csi_unlikely (status))
            longjmp (scan->jmpbuf, status);

          return;
      }
    }

    if (s[0] == '/') {
      if (len >= 2 && s[1] == '/') { /* substituted name */
          status = csi_name_new (ctx, &obj, s + 2, len - 2);
          if (_csi_unlikely (status))
            longjmp (scan->jmpbuf, status);

          status = _csi_name_lookup (ctx, obj.datum.name, &obj);
      } else { /* literal name */
          status = csi_name_new (ctx, &obj, s + 1, len - 1);
      }
      if (_csi_unlikely (status))
          longjmp (scan->jmpbuf, status);
    } else {
      if (! _csi_parse_number (&obj, s, len)) {
          status = csi_name_new (ctx, &obj, s, len);
          if (_csi_unlikely (status))
            longjmp (scan->jmpbuf, status);

          obj.type |= CSI_OBJECT_ATTR_EXECUTABLE;
      }
    }

    /* consume whitespace after token, before calling the interpreter */
    if (scan->build_procedure.type != CSI_OBJECT_TYPE_NULL) {
      status = csi_array_append (ctx,
                           scan->build_procedure.datum.array,
                           &obj);
    } else if (obj.type & CSI_OBJECT_ATTR_EXECUTABLE) {
      status = scan_execute (ctx, &obj);
      csi_object_free (ctx, &obj);
    } else {
      status = scan_push (ctx, &obj);
    }
    if (_csi_unlikely (status))
      longjmp (scan->jmpbuf, status);
}

static void
string_add (csi_t *ctx, csi_scanner_t *scan, int c)
{
    buffer_check (ctx, scan, 1);
    buffer_add (&scan->buffer, c);
}

static void
string_end (csi_t *ctx, csi_scanner_t *scan)
{
    csi_object_t obj;
    cairo_status_t status;

    status = csi_string_new (ctx,
                       &obj,
                       scan->buffer.base,
                       scan->buffer.ptr - scan->buffer.base);
    if (_csi_unlikely (status))
      longjmp (scan->jmpbuf, status);

    if (scan->build_procedure.type != CSI_OBJECT_TYPE_NULL)
      status = csi_array_append (ctx,
                           scan->build_procedure.datum.array,
                           &obj);
    else
      status = scan_push (ctx, &obj);
    if (_csi_unlikely (status))
      longjmp (scan->jmpbuf, status);
}

static int
hex_value (int c)
{
    if (c < '0')
      return EOF;
    if (c <= '9')
      return c - '0';
    c |= 32;
    if (c < 'a')
      return EOF;
    if (c <= 'f')
      return c - 'a' + 0xa;
    return EOF;
}

static void
hex_add (csi_t *ctx, csi_scanner_t *scan, int c)
{
    if (scan->accumulator_count == 0) {
      scan->accumulator |= hex_value (c) << 4;
      scan->accumulator_count = 1;
    } else {
      scan->accumulator |= hex_value (c) << 0;
      buffer_check (ctx, scan, 1);
      buffer_add (&scan->buffer, scan->accumulator);

      scan->accumulator = 0;
      scan->accumulator_count = 0;
    }
}

static void
hex_end (csi_t *ctx, csi_scanner_t *scan)
{
    csi_object_t obj;
    cairo_status_t status;

    if (scan->accumulator_count)
      hex_add (ctx, scan, '0');

    status = csi_string_new (ctx,
                       &obj,
                       scan->buffer.base,
                       scan->buffer.ptr - scan->buffer.base);
    if (_csi_unlikely (status))
      longjmp (scan->jmpbuf, status);

    if (scan->build_procedure.type != CSI_OBJECT_TYPE_NULL)
      status = csi_array_append (ctx,
                           scan->build_procedure.datum.array,
                           &obj);
    else
      status = scan_push (ctx, &obj);
    if (_csi_unlikely (status))
      longjmp (scan->jmpbuf, status);
}

static void
base85_add (csi_t *ctx, csi_scanner_t *scan, int c)
{
    if (c == 'z') {
      if (_csi_unlikely (scan->accumulator_count != 0))
          longjmp (scan->jmpbuf, _csi_error (CSI_STATUS_INVALID_SCRIPT));

      buffer_check (ctx, scan, 4);
      buffer_add (&scan->buffer, 0);
      buffer_add (&scan->buffer, 0);
      buffer_add (&scan->buffer, 0);
      buffer_add (&scan->buffer, 0);
    } else if (_csi_unlikely (c < '!' || c > 'u')) {
      longjmp (scan->jmpbuf, _csi_error (CSI_STATUS_INVALID_SCRIPT));
    } else {
      scan->accumulator = scan->accumulator*85 + c - '!';
      if (++scan->accumulator_count == 5) {
          buffer_check (ctx, scan, 4);
          buffer_add (&scan->buffer, (scan->accumulator >> 24) & 0xff);
          buffer_add (&scan->buffer, (scan->accumulator >> 16) & 0xff);
          buffer_add (&scan->buffer, (scan->accumulator >>  8) & 0xff);
          buffer_add (&scan->buffer, (scan->accumulator >>  0) & 0xff);

          scan->accumulator = 0;
          scan->accumulator_count = 0;
      }
    }
}

static void
base85_end (csi_t *ctx, csi_scanner_t *scan, cairo_bool_t deflate)
{
    csi_object_t obj;
    cairo_status_t status;

    buffer_check (ctx, scan, 4);

    switch (scan->accumulator_count) {
    case 0:
      break;
    case 1:
      longjmp (scan->jmpbuf, _csi_error (CSI_STATUS_INVALID_SCRIPT));
      break;

    case 2:
      scan->accumulator = scan->accumulator * (85*85*85) + 85*85*85 -1;
      buffer_add (&scan->buffer, (scan->accumulator >> 24) & 0xff);
      break;
    case 3:
      scan->accumulator = scan->accumulator * (85*85) + 85*85 -1;
      buffer_add (&scan->buffer, (scan->accumulator >> 24) & 0xff);
      buffer_add (&scan->buffer, (scan->accumulator >> 16) & 0xff);
      break;
    case 4:
      scan->accumulator = scan->accumulator * 85 + 84;
      buffer_add (&scan->buffer, (scan->accumulator >> 24) & 0xff);
      buffer_add (&scan->buffer, (scan->accumulator >> 16) & 0xff);
      buffer_add (&scan->buffer, (scan->accumulator >>  8) & 0xff);
      break;
    }

    if (deflate) {
        uLongf len = be32 (*(uint32_t *) scan->buffer.base);
      Bytef *source = (Bytef *) (scan->buffer.base + sizeof (uint32_t));

      status = csi_string_deflate_new (ctx, &obj,
                               source,
                               (Bytef *) scan->buffer.ptr - source,
                               len);
      if (_csi_unlikely (status))
          longjmp (scan->jmpbuf, status);
    } else {
      status = csi_string_new (ctx,
                         &obj,
                         scan->buffer.base,
                         scan->buffer.ptr - scan->buffer.base);
      if (_csi_unlikely (status))
          longjmp (scan->jmpbuf, status);
    }

    if (scan->build_procedure.type != CSI_OBJECT_TYPE_NULL)
      status = csi_array_append (ctx,
                           scan->build_procedure.datum.array,
                           &obj);
    else
      status = scan_push (ctx, &obj);
    if (_csi_unlikely (status))
      longjmp (scan->jmpbuf, status);
}

static void
base64_add (csi_t *ctx, csi_scanner_t *scan, int c)
{
    int val;

    /* Convert Base64 character to its 6 bit nibble */
    val = scan->accumulator;
    if (c =='/') {
      val = (val << 6) | 63;
    } else if (c =='+') {
      val = (val << 6) | 62;
    } else if (c >='A' && c <='Z') {
      val = (val << 6) | (c -'A');
    } else if (c >='a' && c <='z') {
      val = (val << 6) | (c -'a' + 26);
    } else if (c >='0' && c <='9') {
      val = (val << 6) | (c -'0' + 52);
    }

    buffer_check (ctx, scan, 1);
    switch (scan->accumulator_count++) {
    case 0:
      break;

    case 1:
      buffer_add (&scan->buffer, (val >> 4) & 0xFF);
      val &= 0xF;
      break;

    case 2:
      buffer_add (&scan->buffer, (val >> 2) & 0xFF);
      val &= 0x3;
      break;

    case 3:
      buffer_add (&scan->buffer, (val >> 0) & 0xFF);
      scan->accumulator_count = 0;
      val = 0;
      break;
    }

     if (c == '=') {
      scan->accumulator_count = 0;
      scan->accumulator = 0;
     } else {
       scan->accumulator = val;
     }
}

static void
base64_end (csi_t *ctx, csi_scanner_t *scan)
{
    csi_object_t obj;
    cairo_status_t status;

    switch (scan->accumulator_count) {
    case 0:
      break;
    case 2:
      base64_add (ctx, scan, (scan->accumulator << 2) & 0x3f);
      base64_add (ctx, scan, '=');
      break;
    case 1:
      base64_add (ctx, scan, (scan->accumulator << 4) & 0x3f);
      base64_add (ctx, scan, '=');
      base64_add (ctx, scan, '=');
      break;
    }

    status = csi_string_new (ctx,
                       &obj,
                       scan->buffer.base,
                       scan->buffer.ptr - scan->buffer.base);
    if (_csi_unlikely (status))
      longjmp (scan->jmpbuf, status);

    if (scan->build_procedure.type != CSI_OBJECT_TYPE_NULL)
      status = csi_array_append (ctx,
                           scan->build_procedure.datum.array,
                           &obj);
    else
      status = scan_push (ctx, &obj);
    if (_csi_unlikely (status))
      longjmp (scan->jmpbuf, status);
}

static inline void
scan_read (csi_scanner_t *scan, csi_file_t *src, void *ptr, int len)
{
    uint8_t *data = ptr;
    do {
      int ret = csi_file_read (src, data, len);
      if (_csi_unlikely (ret == 0))
          longjmp (scan->jmpbuf, _csi_error (CSI_STATUS_READ_ERROR));
      data += ret;
      len -= ret;
    } while (_csi_unlikely (len));
}

static void
string_read (csi_t *ctx,
           csi_scanner_t *scan,
           csi_file_t *src,
           int len,
           int compressed,
           csi_object_t *obj)
{
    csi_status_t status;

    status = csi_string_new (ctx, obj, NULL, len);
    if (_csi_unlikely (status))
      longjmp (scan->jmpbuf, status);

    if (compressed) {
      uint32_t u32;
      scan_read (scan, src, &u32, 4);
      obj->datum.string->deflate = be32 (u32);
    }

    if (_csi_likely (len))
      scan_read (scan, src, obj->datum.string->string, len);
    obj->datum.string->string[len] = '\0';
}

static void
_scan_file (csi_t *ctx, csi_file_t *src)
{
    csi_scanner_t *scan = &ctx->scanner;
    int c, next;
    union {
      int8_t i8;
      uint8_t u8;
      int16_t i16;
      uint16_t u16;
      int32_t i32;
      uint32_t u32;
      float f;
    } u;
    int deflate = 0;
    int string_p;

scan_none:
    while ((c = csi_file_getc (src)) != EOF) {
      csi_object_t obj = { CSI_OBJECT_TYPE_NULL };

      switch (c) {
      case 0xa:
          scan->line_number++;
      case 0x0:
      case 0x9:
      case 0xc:
      case 0xd:
      case 0x20: /* ignore whitespace */
          break;

      case '%':
          goto scan_comment;

      case '(':
          goto scan_string;

      case '[': /* needs special case */
      case ']':
      case '{':
      case '}':
          token_start (scan);
          token_add_unchecked (scan, c);
          token_end (ctx, scan, src);
          goto scan_none;

      case '<':
          next = csi_file_getc (src);
          switch (next) {
          case EOF:
            csi_file_putc (src, '<');
            return;
          case '<':
            /* dictionary name */
            token_start (scan);
            token_add_unchecked (scan, '<');
            token_add_unchecked (scan, '<');
            token_end (ctx, scan, src);
            goto scan_none;
          case '|':
            deflate = 1;
          case '~':
            goto scan_base85;
          case '{':
            goto scan_base64;
          default:
            csi_file_putc (src, next);
            goto scan_hex;
          }
          break;

          /* binary token */
#define MSB_INT8 128
#define MSB_UINT8 129
#define MSB_INT16 130
#define MSB_UINT16 131
#define MSB_INT32 132
#define LSB_INT8 MSB_INT8
#define LSB_UINT8 MSB_UINT8
#define LSB_INT16 133
#define LSB_UINT16 134
#define LSB_INT32 135
#define MSB_FLOAT32 140
#define LSB_FLOAT32 141
      case MSB_INT8:
          scan_read (scan, src, &u.i8, 1);
          csi_integer_new (&obj, u.i8);
          break;
      case MSB_UINT8:
          scan_read (scan, src, &u.u8, 1);
          csi_integer_new (&obj, u.u8);
          break;
      case MSB_INT16:
          scan_read (scan, src, &u.i16, 2);
          csi_integer_new (&obj, be16 (u.i16));
          break;
      case LSB_INT16:
          scan_read (scan, src, &u.i16, 2);
          csi_integer_new (&obj, le16 (u.i16));
          break;
      case MSB_UINT16:
          scan_read (scan, src, &u.u16, 2);
          csi_integer_new (&obj, be16 (u.u16));
          break;
      case LSB_UINT16:
          scan_read (scan, src, &u.u16, 2);
          csi_integer_new (&obj, le16 (u.u16));
          break;
      case MSB_INT32:
          scan_read (scan, src, &u.i32, 4);
          csi_integer_new (&obj, be32 (u.i32));
          break;
      case LSB_INT32:
          scan_read (scan, src, &u.i32, 4);
          csi_integer_new (&obj, le32 (u.i32));
          break;

      case 136: /* 16.16 msb */
          scan_read (scan, src, &u.i32, 4);
          csi_real_new (&obj, be32 (u.i32) / 65536.);
          break;
      case 137: /* 16.16 lsb */
          scan_read (scan, src, &u.i32, 4);
          csi_real_new (&obj, le32 (u.i32) / 65536.);
          break;
      case 138: /* 24.8 msb */
          scan_read (scan, src, &u.i32, 4);
          csi_real_new (&obj, be32 (u.i32) / 256.);
          break;
      case 139: /* 24.8 lsb */
          scan_read (scan, src, &u.i32, 4);
          csi_real_new (&obj, le32 (u.i32) / 256.);
          break;
      case MSB_FLOAT32:
          scan_read (scan, src, &u.i32, 4);
          u.i32 = be32 (u.i32);
          csi_real_new (&obj, u.f);
          break;
      case LSB_FLOAT32:
          scan_read (scan, src, &u.i32, 4);
          u.i32 = le32 (u.i32);
          csi_real_new (&obj, u.f);
          break;

#define STRING_1 142
#define STRING_2_MSB 144
#define STRING_2_LSB 146
#define STRING_4_MSB 148
#define STRING_4_LSB 150
#define STRING_DEFLATE 1
      case STRING_1:
      case STRING_1 | STRING_DEFLATE:
          scan_read (scan, src, &u.u8, 1);
          string_read (ctx, scan, src, u.u8, c & STRING_DEFLATE, &obj);
          break;
      case STRING_2_MSB:
      case STRING_2_MSB | STRING_DEFLATE:
          scan_read (scan, src, &u.u16, 2);
          string_read (ctx, scan, src, be16 (u.u16),  c & STRING_DEFLATE, &obj);
          break;
      case STRING_2_LSB:
      case STRING_2_LSB | STRING_DEFLATE:
          scan_read (scan, src, &u.u16, 2);
          string_read (ctx, scan, src, le16 (u.u16), c & STRING_DEFLATE, &obj);
          break;
      case STRING_4_MSB:
      case STRING_4_MSB | STRING_DEFLATE:
          scan_read (scan, src, &u.u32, 4);
          string_read (ctx, scan, src, be32 (u.u32), c & STRING_DEFLATE, &obj);
          break;
      case STRING_4_LSB:
      case STRING_4_LSB | STRING_DEFLATE:
          scan_read (scan, src, &u.u32, 4);
          string_read (ctx, scan, src, le32 (u.u32), c & STRING_DEFLATE, &obj);
          break;

#define OPCODE 152
      case OPCODE:
          scan_read (scan, src, &u.u8, 1);
          csi_operator_new (&obj, ctx->opcode[u.u8]);
          break;

      case OPCODE | 1:
          scan_read (scan, src, &u.u8, 1);
          csi_operator_new (&obj, ctx->opcode[u.u8]);
          obj.type &= ~CSI_OBJECT_ATTR_EXECUTABLE;
          break;

          /* unassigned */
      case 154:
      case 155:
      case 156:
      case 157:
      case 158:
      case 159:
          longjmp (scan->jmpbuf, _csi_error (CSI_STATUS_INVALID_SCRIPT));

      case '#': /* PDF 1.2 escape code */
          {
            int c_hi = csi_file_getc (src);
            int c_lo = csi_file_getc (src);
            c = (hex_value (c_hi) << 4) | hex_value (c_lo);
          }
          /* fall-through */
      default:
          token_start (scan);
          token_add_unchecked (scan, c);
          goto scan_token;
      }

      if (obj.type != CSI_OBJECT_TYPE_NULL) {
          cairo_status_t status;

          if (scan->build_procedure.type != CSI_OBJECT_TYPE_NULL) {
            status = csi_array_append (ctx,
                                 scan->build_procedure.datum.array,
                                 &obj);
          } else if (obj.type & CSI_OBJECT_ATTR_EXECUTABLE) {
            status = scan_execute (ctx, &obj);
            csi_object_free (ctx, &obj);
          } else {
            status = scan_push (ctx, &obj);
          }
          if (_csi_unlikely (status))
            longjmp (scan->jmpbuf, status);
      }
    }
    return;

scan_token:
    while ((c = csi_file_getc (src)) != EOF) {
      switch (c) {
      case 0xa:
          scan->line_number++;
      case 0x0:
      case 0x9:
      case 0xc:
      case 0xd:
      case 0x20:
          token_end (ctx, scan, src);
          goto scan_none;

          /* syntax delimiters */
      case '%':
          token_end (ctx, scan, src);
          goto scan_comment;
          /* syntax error? */
      case '(':
          token_end (ctx, scan, src);
          goto scan_string;
          /* XXX syntax error? */
      case ')':
          token_end (ctx, scan, src);
          goto scan_none;
      case '/':
          /* need to special case '^//?' */
          if (scan->buffer.ptr > scan->buffer.base+1 ||
            scan->buffer.base[0] != '/')
          {
            token_end (ctx, scan, src);
            token_start (scan);
          }
          token_add_unchecked (scan, '/');
          goto scan_token;

      case '{':
      case '}':
      case ']':
          token_end (ctx, scan, src);
          token_start (scan);
          token_add_unchecked (scan, c);
          token_end (ctx, scan, src);
          goto scan_none;

      case '<':
          csi_file_putc (src, '<');
          token_end (ctx, scan, src);
          goto scan_none;

      case '#': /* PDF 1.2 escape code */
          {
            int c_hi = csi_file_getc (src);
            int c_lo = csi_file_getc (src);
            c = (hex_value (c_hi) << 4) | hex_value (c_lo);
          }
          /* fall-through */
      default:
          token_add (ctx, scan, c);
          break;
      }
    }
    token_end (ctx, scan, src);
    return;

scan_comment:
    /* discard until newline */
    while ((c = csi_file_getc (src)) != EOF) {
      switch (c) {
      case 0xa:
          scan->line_number++;
      case 0xc:
          goto scan_none;
      }
    }
    return;

scan_string:
    buffer_reset (&scan->buffer);
    string_p = 1;
    while ((c = csi_file_getc (src)) != EOF) {
      switch (c) {
      case '\\': /* escape */
          next = csi_file_getc (src);
          switch (next) {
          case EOF:
            longjmp (scan->jmpbuf, _csi_error (CSI_STATUS_INVALID_SCRIPT));

          case 'n':
            string_add (ctx, scan, '\n');
            break;
          case 'r':
            string_add (ctx, scan, '\r');
            break;
          case 't':
            string_add (ctx, scan, '\t');
            break;
          case 'b':
            string_add (ctx, scan, '\b');
            break;
          case 'f':
            string_add (ctx, scan, '\f');
            break;
          case '\\':
            string_add (ctx, scan, '\\');
            break;
          case '(':
            string_add (ctx, scan, '(');
            break;
          case ')':
            string_add (ctx, scan, ')');
            break;

          case '0': case '1': case '2': case '3':
          case '4': case '5': case '6': case '7':
            { /* octal code: \d{1,3} */
                int i;

                c = next - '0';

                for (i = 0; i < 2; i++) {
                  next = csi_file_getc (src);
                  switch (next) {
                  case EOF:
                      return;

                  case '0': case '1': case '2': case '3':
                  case '4': case '5': case '6': case '7':
                      c = 8*c + next-'0';
                      break;

                  default:
                      csi_file_putc (src, next);
                      goto octal_code_done;
                  }
                }
  octal_code_done:
                string_add (ctx, scan, c);
            }
            break;

          case 0xa:
            /* skip the newline */
            next = csi_file_getc (src); /* might be compound LFCR */
            switch (next) {
            case EOF:
                return;
            case 0xc:
                break;
            default:
                csi_file_putc (src, next);
                break;
            }
            scan->line_number++;
            break;
          case 0xc:
            break;

          default:
            /* ignore the '\' */
            break;
          }
          break;

      case '(':
          string_p++;
          string_add (ctx, scan, c);
          break;

      case ')':
          if (--string_p == 0) {
            string_end (ctx, scan);
            goto scan_none;
          }
          /* fall through */
      default:
          string_add (ctx, scan, c);
          break;
      }
    }
    longjmp (scan->jmpbuf, _csi_error (CSI_STATUS_INVALID_SCRIPT));

scan_hex:
    buffer_reset (&scan->buffer);
    scan->accumulator_count = 0;
    scan->accumulator = 0;
    while ((c = csi_file_getc (src)) != EOF) {
      switch (c) {
      case 0xa:
          scan->line_number++;
      case 0x0:
      case 0x9:
      case 0xc:
      case 0xd:
      case 0x20: /* ignore whitespace */
          break;

      case '>':
          hex_end (ctx, scan); /* fixup odd digit with '0' */
          goto scan_none;

      case '0':
      case '1':
      case '2':
      case '3':
      case '4':
      case '5':
      case '6':
      case '7':
      case '8':
      case '9':
      case 'a':
      case 'b':
      case 'c':
      case 'd':
      case 'e':
      case 'f':
      case 'A':
      case 'B':
      case 'C':
      case 'D':
      case 'E':
      case 'F':
          hex_add (ctx, scan, c);
          break;

      default:
          longjmp (scan->jmpbuf, _csi_error (CSI_STATUS_INVALID_SCRIPT));
      }
    }
    longjmp (scan->jmpbuf, _csi_error (CSI_STATUS_INVALID_SCRIPT));

scan_base85:
    buffer_reset (&scan->buffer);
    scan->accumulator = 0;
    scan->accumulator_count = 0;
    while ((c = csi_file_getc (src)) != EOF) {
      switch (c) {
      case '~':
          next = csi_file_getc (src);
          switch (next) {
          case EOF:
            return;

          case '>':
            base85_end (ctx, scan, deflate);
            deflate = 0;
            goto scan_none;
          }
          csi_file_putc (src, next);

          /* fall-through */
      default:
          base85_add (ctx, scan, c);
          break;
      }
    }
    longjmp (scan->jmpbuf, _csi_error (CSI_STATUS_INVALID_SCRIPT));

scan_base64:
    buffer_reset (&scan->buffer);
    scan->accumulator = 0;
    scan->accumulator_count = 0;
    while ((c = csi_file_getc (src)) != EOF) {
      switch (c) {
      case '}':
          next = csi_file_getc (src);
          switch (next) {
          case EOF:
            return;

          case '>':
            base64_end (ctx, scan);
            goto scan_none;
          }
          longjmp (scan->jmpbuf, _csi_error (CSI_STATUS_INVALID_SCRIPT));

      default:
          base64_add (ctx, scan, c);
          break;
      }
    }
    longjmp (scan->jmpbuf, _csi_error (CSI_STATUS_INVALID_SCRIPT));
}

static csi_status_t
_scan_push (csi_t *ctx, csi_object_t *obj)
{
    if (DEBUG_SCAN) {
      fprintf (stderr, "push ");
      fprintf_obj (stderr, ctx, obj);
    }
    return _csi_push_ostack (ctx, obj);
}

static csi_status_t
_scan_execute (csi_t *ctx, csi_object_t *obj)
{
    if (DEBUG_SCAN) {
      fprintf (stderr, "exec ");
      fprintf_obj (stderr, ctx, obj);
    }
    return csi_object_execute (ctx, obj);
}

csi_status_t
_csi_scanner_init (csi_t *ctx, csi_scanner_t *scanner)
{
    csi_status_t status;

    memset (scanner, 0, sizeof (csi_scanner_t));

    status = buffer_init (ctx, &scanner->buffer);
    if (status)
      return status;

    status = _csi_stack_init (ctx, &scanner->procedure_stack, 4);
    if (status)
      return status;

    scanner->bind = 0;
    scanner->push = _scan_push;
    scanner->execute = _scan_execute;

    return CSI_STATUS_SUCCESS;
}

void
_csi_scanner_fini (csi_t *ctx, csi_scanner_t *scanner)
{
    buffer_fini (ctx, &scanner->buffer);
    _csi_stack_fini (ctx, &scanner->procedure_stack);
    if (scanner->build_procedure.type != CSI_OBJECT_TYPE_NULL)
      csi_object_free (ctx, &scanner->build_procedure);
}

csi_status_t
_csi_scan_file (csi_t *ctx, csi_file_t *src)
{
    csi_status_t status;
    int old_line_number;

    /* This function needs to be reentrant to handle recursive scanners.
     * i.e. one script executes a second.
     */

    if (ctx->scanner.depth++ == 0) {
      if ((status = setjmp (ctx->scanner.jmpbuf))) {
          ctx->scanner.depth = 0;
          return status;
      }
    }

    old_line_number = ctx->scanner.line_number;
    ctx->scanner.line_number = 0;

    _scan_file (ctx, src);

    ctx->scanner.line_number = old_line_number;

    --ctx->scanner.depth;
    return CSI_STATUS_SUCCESS;
}

01401 struct _translate_closure {
    csi_dictionary_t *opcodes;
    cairo_write_func_t write_func;
    void *closure;
};

static csi_status_t
_translate_name (csi_t *ctx,
               csi_name_t name,
             csi_boolean_t executable,
             struct _translate_closure *closure)
{
    if (executable) {
      csi_dictionary_entry_t *entry;
      uint16_t u16;

      /* Bind executable names.
       * XXX This may break some scripts that overload system operators.
       */
      entry = _csi_hash_table_lookup (&closure->opcodes->hash_table,
                                    (csi_hash_entry_t *) &name);
      if (entry == NULL)
          goto STRING;

      u16 = entry->value.datum.integer;
      u16 = be16 (u16);
      closure->write_func (closure->closure, (unsigned char *) &u16, 2);
    } else {
      closure->write_func (closure->closure, (unsigned char *) "/", 1);
STRING:
      closure->write_func (closure->closure,
                         (unsigned char *) name,
                       strlen ((char *) name));
      closure->write_func (closure->closure, (unsigned char *) "\n", 1);
    }

    return CSI_STATUS_SUCCESS;
}

static csi_status_t
_translate_operator (csi_t *ctx,
                   csi_operator_t op,
                 csi_boolean_t executable,
                 struct _translate_closure *closure)
{
    csi_dictionary_entry_t *entry;
    uint16_t u16;

    entry = _csi_hash_table_lookup (&closure->opcodes->hash_table,
                                  (csi_hash_entry_t *) &op);
    if (entry == NULL)
      return _csi_error (CSI_STATUS_INVALID_SCRIPT);

    u16 = entry->value.datum.integer;
    if (! executable)
      u16 += 1 << 8;
    u16 = be16 (u16);
    closure->write_func (closure->closure, (unsigned char *) &u16, 2);

    return CSI_STATUS_SUCCESS;
}

static csi_status_t
_translate_integer (csi_t *ctx,
                  csi_integer_t i,
                struct _translate_closure *closure)
{
    uint8_t hdr;
    union {
      int8_t i8;
      uint8_t u8;
      int16_t i16;
      uint16_t u16;
      int32_t i32;
      uint32_t u32;
    } u;
    int len;

#if WORDS_BIGENDIAN
    if (i < INT16_MIN) {
      hdr = MSB_INT32;
      len = 4;
      u.i32 = i;
    } else if (i < INT8_MIN) {
      hdr = MSB_INT16;
      len = 2;
      u.i16 = i;
    } else if (i < 0) {
      hdr = MSB_INT8;
      len = 1;
      u.i8 = i;
    } else if (i <= UINT8_MAX) {
      hdr = MSB_UINT8;
      len = 1;
      u.u8 = i;
    } else if (i <= UINT16_MAX) {
      hdr = MSB_UINT16;
      len = 2;
      u.u16 = i;
    } else {
      hdr = MSB_INT32;
      len = 4;
      u.u32 = i;
    }
#else
    if (i < INT16_MIN) {
      hdr = LSB_INT32;
      len = 4;
      u.i32 = i;
    } else if (i < INT8_MIN) {
      hdr = LSB_INT16;
      len = 2;
      u.i16 = i;
    } else if (i < 0) {
      hdr = LSB_INT8;
      len = 1;
      u.i8 = i;
    } else if (i <= UINT8_MAX) {
      hdr = LSB_UINT8;
      len = 1;
      u.u8 = i;
    } else if (i <= UINT16_MAX) {
      hdr = LSB_UINT16;
      len = 2;
      u.u16 = i;
    } else {
      hdr = LSB_INT32;
      len = 4;
      u.u32 = i;
    }
#endif

    closure->write_func (closure->closure, (unsigned char *) &hdr, 1);
    closure->write_func (closure->closure, (unsigned char *) &u, len);

    return CSI_STATUS_SUCCESS;
}

static csi_status_t
_translate_real (csi_t *ctx,
               csi_real_t real,
             struct _translate_closure *closure)
{
    uint8_t hdr;

    if (real >= INT32_MIN && real <= INT32_MAX && (int) real == real)
      return _translate_integer (ctx, real, closure);

#if WORDS_BIGENDIAN
    hdr = MSB_FLOAT32;
#else
    hdr = LSB_FLOAT32;
#endif
    closure->write_func (closure->closure, (unsigned char *) &hdr, 1);
    closure->write_func (closure->closure, (unsigned char *) &real, 4);

    return CSI_STATUS_SUCCESS;
}

static csi_status_t
_translate_string (csi_t *ctx,
                 csi_string_t *string,
               struct _translate_closure *closure)
{
    uint8_t hdr;
    union {
      uint8_t u8;
      uint16_t u16;
      uint32_t u32;
    } u;
    int len;

#if WORDS_BIGENDIAN
    if (string->len <= UINT8_MAX) {
      hdr = STRING_1;
      u.u8 = string->len;
      len = 1;
    } else if (string->len <= UINT16_MAX) {
      hdr = STRING_2_MSB;
      u.u16 = string->len;
      len = 2;
    } else {
      hdr = STRING_4_MSB;
      u.u32 = string->len;
      len = 4;
    }
#else
    if (string->len <= UINT8_MAX) {
      hdr = STRING_1;
      u.u8 = string->len;
      len = 1;
    } else if (string->len <= UINT16_MAX) {
      hdr = STRING_2_LSB;
      u.u16 = string->len;
      len = 2;
    } else {
      hdr = STRING_4_LSB;
      u.u32 = string->len;
      len = 4;
    }
#endif
    if (string->deflate)
      hdr |= STRING_DEFLATE;

    closure->write_func (closure->closure,
                       (unsigned char *) &hdr, 1);
    closure->write_func (closure->closure,
                       (unsigned char *) &u, len);
    if (string->deflate) {
      uint32_t u32 = to_be32 (string->deflate);
      closure->write_func (closure->closure,
                       (unsigned char *) &u32, 4);
    }
    closure->write_func (closure->closure,
                       (unsigned char *) string->string, string->len);

    return CSI_STATUS_SUCCESS;
}

static csi_status_t
_translate_push (csi_t *ctx, csi_object_t *obj)
{
    struct _translate_closure *closure = ctx->scanner.closure;

    if (0) {
      fprintf (stderr, "push ");
      fprintf_obj (stderr, ctx, obj);
    }

    switch (csi_object_get_type (obj)) {
    case CSI_OBJECT_TYPE_NAME:
      return _translate_name (ctx, obj->datum.name, FALSE, closure);

    case CSI_OBJECT_TYPE_OPERATOR:
      return _translate_operator (ctx, obj->datum.op, FALSE, closure);

    case CSI_OBJECT_TYPE_INTEGER:
      return _translate_integer (ctx, obj->datum.integer, closure);

    case CSI_OBJECT_TYPE_REAL:
      return _translate_real (ctx, obj->datum.real, closure);

    case CSI_OBJECT_TYPE_STRING:
      return _translate_string (ctx, obj->datum.string, closure);

    case CSI_OBJECT_TYPE_NULL:
    case CSI_OBJECT_TYPE_BOOLEAN:
    case CSI_OBJECT_TYPE_MARK:
    case CSI_OBJECT_TYPE_ARRAY:
    case CSI_OBJECT_TYPE_DICTIONARY:
    case CSI_OBJECT_TYPE_FILE:
    case CSI_OBJECT_TYPE_MATRIX:
    case CSI_OBJECT_TYPE_CONTEXT:
    case CSI_OBJECT_TYPE_FONT:
    case CSI_OBJECT_TYPE_PATTERN:
    case CSI_OBJECT_TYPE_SCALED_FONT:
    case CSI_OBJECT_TYPE_SURFACE:
      longjmp (ctx->scanner.jmpbuf,  _csi_error (CSI_STATUS_INVALID_SCRIPT));
      break;
    }

    csi_object_free (ctx, obj);
    return CSI_STATUS_SUCCESS;
}

static csi_status_t
_translate_execute (csi_t *ctx, csi_object_t *obj)
{
    struct _translate_closure *closure = ctx->scanner.closure;

    if (0) {
      fprintf (stderr, "exec ");
      fprintf_obj (stderr, ctx, obj);
    }

    switch (csi_object_get_type (obj)) {
    case CSI_OBJECT_TYPE_NAME:
      return _translate_name (ctx, obj->datum.name, TRUE, closure);

    case CSI_OBJECT_TYPE_OPERATOR:
      return _translate_operator (ctx, obj->datum.op, TRUE, closure);

    case CSI_OBJECT_TYPE_INTEGER:
      return _translate_integer (ctx, obj->datum.integer, closure);

    case CSI_OBJECT_TYPE_REAL:
      return _translate_real (ctx, obj->datum.real, closure);

    case CSI_OBJECT_TYPE_STRING:
      return _translate_string (ctx, obj->datum.string, closure);

    case CSI_OBJECT_TYPE_NULL:
    case CSI_OBJECT_TYPE_BOOLEAN:
    case CSI_OBJECT_TYPE_MARK:
    case CSI_OBJECT_TYPE_ARRAY:
    case CSI_OBJECT_TYPE_DICTIONARY:
    case CSI_OBJECT_TYPE_FILE:
    case CSI_OBJECT_TYPE_MATRIX:
    case CSI_OBJECT_TYPE_CONTEXT:
    case CSI_OBJECT_TYPE_FONT:
    case CSI_OBJECT_TYPE_PATTERN:
    case CSI_OBJECT_TYPE_SCALED_FONT:
    case CSI_OBJECT_TYPE_SURFACE:
      longjmp (ctx->scanner.jmpbuf,  _csi_error (CSI_STATUS_INVALID_SCRIPT));
      break;
    }

    return CSI_STATUS_SUCCESS;
}

static csi_status_t
build_opcodes (csi_t *ctx, csi_dictionary_t **out)
{
    csi_object_t obj;
    csi_dictionary_t *dict;
    const csi_operator_def_t *def;
    csi_status_t status;
    int opcode = OPCODE << 8;

    status = csi_dictionary_new (ctx, &obj);
    if (_csi_unlikely (status))
      return status;

    dict = obj.datum.dictionary;

    csi_integer_new (&obj, opcode++);
    status = csi_dictionary_put (ctx, dict, 0, &obj);
    if (_csi_unlikely (status))
      goto FAIL;

    for (def = _csi_operators (); def->name != NULL; def++) {
      csi_object_t name;
      csi_dictionary_entry_t *entry;
      int code;

      entry = _csi_hash_table_lookup (&dict->hash_table,
                                    (csi_hash_entry_t *) &def->op);
      if (entry == NULL) {
          code = opcode++;
          csi_integer_new (&obj, code);
          status = csi_dictionary_put (ctx, dict, (csi_name_t) def->op, &obj);
          if (_csi_unlikely (status))
            goto FAIL;
      } else {
          code = entry->value.datum.integer;
          csi_integer_new (&obj, code);
      }
      assert (ctx->opcode[code & 0xff] == def->op);

      status = csi_name_new_static (ctx, &name, def->name);
      if (_csi_unlikely (status))
          goto FAIL;

      status = csi_dictionary_put (ctx, dict, name.datum.name, &obj);
      if (_csi_unlikely (status))
          goto FAIL;
    }

    *out = dict;
    return CSI_STATUS_SUCCESS;

FAIL:
    csi_dictionary_free (ctx, dict);
    return status;
}

csi_status_t
_csi_translate_file (csi_t *ctx,
                   csi_file_t *file,
                 cairo_write_func_t write_func,
                 void *closure)
{
    csi_status_t status;
    struct _translate_closure translator;

    if ((status = setjmp (ctx->scanner.jmpbuf)))
      return status;

    status = build_opcodes (ctx, &translator.opcodes);
    if (_csi_unlikely (status))
      return status;

    translator.write_func = write_func;
    translator.closure = closure;
    ctx->scanner.closure = &translator;

    ctx->scanner.bind = 1;
    ctx->scanner.push = _translate_push;
    ctx->scanner.execute = _translate_execute;

    _scan_file (ctx, file);

    ctx->scanner.bind = 0;
    ctx->scanner.push = _scan_push;
    ctx->scanner.execute = _scan_execute;

    csi_dictionary_free (ctx, translator.opcodes);

    return CSI_STATUS_SUCCESS;
}

Generated by  Doxygen 1.6.0   Back to index