diff options
Diffstat (limited to 'libs/cairo-1.16.0/util/cairo-script/cairo-script-scanner.c')
-rw-r--r-- | libs/cairo-1.16.0/util/cairo-script/cairo-script-scanner.c | 1904 |
1 files changed, 1904 insertions, 0 deletions
diff --git a/libs/cairo-1.16.0/util/cairo-script/cairo-script-scanner.c b/libs/cairo-1.16.0/util/cairo-script/cairo-script-scanner.c new file mode 100644 index 0000000..3dfb3a9 --- /dev/null +++ b/libs/cairo-1.16.0/util/cairo-script/cairo-script-scanner.c @@ -0,0 +1,1904 @@ +/* + * 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 "config.h" + +#include "cairo-script-private.h" + +#include <limits.h> /* INT_MAX */ +#include <math.h> /* pow */ +#include <stdio.h> /* EOF */ +#include <stdint.h> /* for {INT,UINT}*_{MIN,MAX} */ +#include <stdlib.h> /* malloc/free */ +#include <string.h> /* memset */ +#include <assert.h> +#include <zlib.h> + +#if HAVE_LZO +#include <lzo/lzo2a.h> +#endif + +#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, defate=%ld, method=%d\n", + obj->datum.string->len, obj->datum.string->deflate, obj->datum.string->method); + 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->jump_buffer, _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->jump_buffer, _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->jump_buffer, status); + } + + status = csi_array_new (ctx, 0, &scan->build_procedure); + if (_csi_unlikely (status)) + longjmp (scan->jump_buffer, 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->jump_buffer, _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->jump_buffer, 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->jump_buffer, 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->jump_buffer, status); + } else { + if (! _csi_parse_number (&obj, s, len)) { + status = csi_name_new (ctx, &obj, s, len); + if (_csi_unlikely (status)) + longjmp (scan->jump_buffer, 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->jump_buffer, 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->jump_buffer, 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->jump_buffer, 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->jump_buffer, 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->jump_buffer, 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->jump_buffer, _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->jump_buffer, _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->jump_buffer, _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->jump_buffer, status); + } else { + status = csi_string_new (ctx, + &obj, + scan->buffer.base, + scan->buffer.ptr - scan->buffer.base); + if (_csi_unlikely (status)) + longjmp (scan->jump_buffer, 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->jump_buffer, 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->jump_buffer, 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->jump_buffer, status); +} + +static 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->jump_buffer, _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->jump_buffer, status); + + if (compressed) { + uint32_t u32; + scan_read (scan, src, &u32, 4); + obj->datum.string->deflate = be32 (u32); + obj->datum.string->method = compressed; + } + + 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; + +#define STRING_LZO 154 + case STRING_LZO: + scan_read (scan, src, &u.u32, 4); + string_read (ctx, scan, src, be32 (u.u32), LZO, &obj); + break; + + /* unassigned */ + case 155: + case 156: + case 157: + case 158: + case 159: + longjmp (scan->jump_buffer, _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->jump_buffer, 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->jump_buffer, _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->jump_buffer, _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->jump_buffer, _csi_error (CSI_STATUS_INVALID_SCRIPT)); + } + } + longjmp (scan->jump_buffer, _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->jump_buffer, _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->jump_buffer, _csi_error (CSI_STATUS_INVALID_SCRIPT)); + + default: + base64_add (ctx, scan, c); + break; + } + } + longjmp (scan->jump_buffer, _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.jump_buffer))) { + 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; +} + +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; + void *buf; + unsigned long hdr_len, buf_len, deflate; + int method; + + buf = string->string; + buf_len = string->len; + deflate = string->deflate; + method = string->method; + +#if HAVE_LZO + if (method == NONE && buf_len > 16) { + unsigned long mem_len = 2*string->len > LZO2A_999_MEM_COMPRESS ? 2*string->len : LZO2A_999_MEM_COMPRESS; + void *mem = malloc (mem_len); + void *work = malloc(LZO2A_999_MEM_COMPRESS); + + if (lzo2a_999_compress ((lzo_bytep) buf, buf_len, + (lzo_bytep) mem, &mem_len, + work) == 0 && + 8+2*mem_len < buf_len) + { + method = LZO; + deflate = buf_len; + buf_len = mem_len; + buf = mem; + } + else + { + free (mem); + } + + free (work); + } +#if HAVE_ZLIB + if (method == ZLIB) { + buf_len = string->deflate; + buf = malloc (string->deflate); + if (uncompress ((Bytef *) buf, &buf_len, + (Bytef *) string->string, string->len) == Z_OK) + { + assert(string->len > 0); + if (buf_len <= 8 + 2*((unsigned long)string->len)) { + method = NONE; + deflate = 0; + } else { + unsigned long mem_len = 2*string->deflate; + void *mem = malloc (mem_len); + void *work = malloc(LZO2A_999_MEM_COMPRESS); + + if (lzo2a_999_compress ((lzo_bytep) buf, buf_len, + (lzo_bytep) mem, &mem_len, + work) == 0) + { + if (8 + mem_len > buf_len) { + method = NONE; + deflate = 0; + } else { + free (buf); + method = LZO; + deflate = buf_len; + buf_len = mem_len; + buf = mem; + assert(deflate); + } + } + else + { + free (buf); + buf = string->string; + buf_len = string->len; + } + + free (work); + } + } + else + { + free (buf); + buf = string->string; + buf_len = string->len; + } + } +#endif +#endif + + if (method == LZO) { + hdr = STRING_LZO; + u.u32 = to_be32 (buf_len); + hdr_len = 4; + } else { +#if WORDS_BIGENDIAN + if (buf_len <= UINT8_MAX) { + hdr = STRING_1; + u.u8 = buf_len; + hdr_len = 1; + } else if (buf_len <= UINT16_MAX) { + hdr = STRING_2_MSB; + u.u16 = buf_len; + hdr_len = 2; + } else { + hdr = STRING_4_MSB; + u.u32 = buf_len; + hdr_len = 4; + } +#else + if (buf_len <= UINT8_MAX) { + hdr = STRING_1; + u.u8 = buf_len; + hdr_len = 1; + } else if (buf_len <= UINT16_MAX) { + hdr = STRING_2_LSB; + u.u16 = buf_len; + hdr_len = 2; + } else { + hdr = STRING_4_LSB; + u.u32 = buf_len; + hdr_len = 4; + } +#endif + if (deflate) { + assert (method == ZLIB); + hdr |= STRING_DEFLATE; + } + } + + closure->write_func (closure->closure, (unsigned char *) &hdr, 1); + closure->write_func (closure->closure, (unsigned char *) &u, hdr_len); + if (deflate) { + uint32_t u32 = to_be32 (deflate); + closure->write_func (closure->closure, (unsigned char *) &u32, 4); + } + closure->write_func (closure->closure, (unsigned char *) buf, buf_len); + + if (buf != string->string) + free (buf); + + 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.jump_buffer, _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.jump_buffer, _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.jump_buffer))) + 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; +} |