summaryrefslogtreecommitdiff
path: root/util/cairo-script/cairo-script-scanner.c
diff options
context:
space:
mode:
Diffstat (limited to 'util/cairo-script/cairo-script-scanner.c')
-rwxr-xr-xutil/cairo-script/cairo-script-scanner.c1918
1 files changed, 1918 insertions, 0 deletions
diff --git a/util/cairo-script/cairo-script-scanner.c b/util/cairo-script/cairo-script-scanner.c
new file mode 100755
index 000000000..b9d445bb3
--- /dev/null
+++ b/util/cairo-script/cairo-script-scanner.c
@@ -0,0 +1,1918 @@
+/*
+ * 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 <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->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);
+ if (next == NULL) {
+ longjmp (scan->jmpbuf, _csi_error (CSI_STATUS_NULL_POINTER));
+ return;
+ }
+
+ 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 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);
+ 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->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;
+}
+
+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 (work == NULL) {
+ free (mem);
+ return CSI_STATUS_NO_MEMORY;
+ }
+
+ 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)
+ {
+ if (buf_len <= 8 + 2*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 (work == NULL) {
+ free (mem);
+ return CSI_STATUS_NO_MEMORY;
+ }
+
+ 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;
+ free (mem);
+ } else {
+ free (buf);
+ method = LZO;
+ deflate = buf_len;
+ buf_len = mem_len;
+ buf = mem;
+ assert(deflate);
+ }
+ }
+ else
+ {
+ free (buf);
+ free (mem);
+ 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.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;
+}