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')
-rw-r--r--util/cairo-script/cairo-script-scanner.c1179
1 files changed, 1179 insertions, 0 deletions
diff --git a/util/cairo-script/cairo-script-scanner.c b/util/cairo-script/cairo-script-scanner.c
new file mode 100644
index 00000000..484acd96
--- /dev/null
+++ b/util/cairo-script/cairo-script-scanner.c
@@ -0,0 +1,1179 @@
+/*
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 <stdio.h> /* EOF */
+#include <string.h> /* memset */
+#include <math.h> /* pow */
+
+/*
+ * 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 cairo_status_t
+_csi_buffer_init (csi_t *ctx, csi_buffer_t *buffer)
+{
+ buffer->status = CSI_STATUS_SUCCESS;
+ buffer->size = 16384;
+ buffer->base = _csi_alloc (ctx, buffer->size);
+ if (_csi_unlikely (buffer->base == NULL)) {
+ buffer->status = _csi_error (CSI_STATUS_NO_MEMORY);
+ buffer->size = 0;
+ }
+
+ buffer->ptr = buffer->base;
+ buffer->end = buffer->base + buffer->size;
+
+ return buffer->status;
+}
+
+static void
+_csi_buffer_fini (csi_t *ctx, csi_buffer_t *buffer)
+{
+ _csi_free (ctx, buffer->base);
+}
+
+static cairo_status_t
+_csi_buffer_grow (csi_t *ctx, csi_buffer_t *buffer)
+{
+ int newsize;
+ int offset;
+ char *base;
+
+ if (_csi_unlikely (buffer->status))
+ return buffer->status;
+
+ if (_csi_unlikely (buffer->size > INT32_MAX / 2))
+ return buffer->status = _csi_error (CSI_STATUS_NO_MEMORY);
+
+ offset = buffer->ptr - buffer->base;
+ newsize = buffer->size * 2;
+ base = _csi_realloc (ctx, buffer->base, newsize);
+ if (_csi_unlikely (base == NULL))
+ return buffer->status = _csi_error (CSI_STATUS_NO_MEMORY);
+
+ buffer->base = base;
+ buffer->ptr = base + offset;
+ buffer->end = base + newsize;
+ buffer->size = newsize;
+
+ return CSI_STATUS_SUCCESS;
+}
+
+static inline csi_boolean_t
+_csi_buffer_check (csi_t *ctx, csi_buffer_t *buffer, int count)
+{
+ if (_csi_unlikely (buffer->ptr + count > buffer->end)) {
+ if (_csi_buffer_grow (ctx, buffer))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static inline void
+_csi_buffer_add (csi_buffer_t *buffer, int c)
+{
+ *buffer->ptr++ = c;
+}
+
+static inline void
+_csi_buffer_reset (csi_buffer_t *buffer)
+{
+ buffer->ptr = buffer->base;
+}
+
+static inline int
+scan_getc (csi_scanner_t *scan, csi_file_t *src)
+{
+ if (_csi_unlikely (scan->status))
+ return EOF;
+
+ return csi_file_getc (src);
+}
+
+static inline int
+scan_read (csi_scanner_t *scan, csi_file_t *src, void *buf, int len)
+{
+ return csi_file_read (src, buf, len);
+}
+
+static inline void
+scan_putc (csi_scanner_t *scan, csi_file_t *src, int c)
+{
+ csi_file_putc (src, c);
+}
+
+static inline void
+reset (csi_scanner_t *scan)
+{
+ scan->state = NONE;
+}
+
+static void
+token_start (csi_scanner_t *scan)
+{
+ scan->state = TOKEN;
+ _csi_buffer_reset (&scan->buffer);
+}
+
+static void
+token_add (csi_t *ctx, csi_scanner_t *scan, int c)
+{
+ if (_csi_likely (_csi_buffer_check (ctx, &scan->buffer, 1)))
+ _csi_buffer_add (&scan->buffer, c);
+}
+
+static void
+token_add_unchecked (csi_scanner_t *scan, int c)
+{
+ _csi_buffer_add (&scan->buffer, c);
+}
+
+static csi_boolean_t
+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 (radix)
+ return FALSE;
+ if (decimal != -1)
+ return FALSE;
+ if (exponent_sign)
+ return FALSE;
+
+ decimal = 0;
+ } else if (*s == '!') {
+ if (radix)
+ return FALSE;
+ if (decimal != -1)
+ return FALSE;
+ if (exponent_sign)
+ return FALSE;
+
+ radix = mantissa;
+ mantissa = 0;
+
+ if (radix < 2 || radix > 36)
+ return FALSE;
+ } else
+ return FALSE;
+ } else if (*s <= '9') {
+ int v = *s - '0';
+ if (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 (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 (v >= radix)
+ return FALSE;
+
+ mantissa = radix * mantissa + v;
+ }
+ } else if (*s < 'A') {
+ return FALSE;
+ } else if (*s <= 'Z') {
+ int v = *s - 'A' + 0xA;
+
+ if (v >= radix)
+ return FALSE;
+
+ mantissa = radix * mantissa + v;
+ } else if (*s < 'a') {
+ return FALSE;
+ } else if (*s <= 'z') {
+ int v = *s - 'a' + 0xa;
+
+ if (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;
+ if (e != 0)
+ v *= pow (10, e); /* XXX */
+
+ 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)
+{
+ 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;
+
+ scan->status = scan->buffer.status;
+ if (_csi_unlikely (scan->status))
+ return;
+
+ s = scan->buffer.base;
+ len = scan->buffer.ptr - scan->buffer.base;
+
+ if (s[0] == '{') { /* special case procedures */
+ if (scan->build_procedure.type != CSI_OBJECT_TYPE_NULL)
+ scan->status = _csi_stack_push (ctx,
+ &scan->procedure_stack,
+ &scan->build_procedure);
+
+ scan->status = csi_array_new (ctx, &scan->build_procedure);
+ scan->build_procedure.type |= CSI_OBJECT_ATTR_EXECUTABLE;
+ reset (scan);
+ return;
+ } else if (s[0] == '}') {
+ csi_object_t *next;
+
+ if (_csi_unlikely
+ (scan->build_procedure.type == CSI_OBJECT_TYPE_NULL))
+ {
+ scan->status = _csi_error (CSI_STATUS_INVALID_SCRIPT);
+ return;
+ }
+
+ if (scan->procedure_stack.len) {
+ next = _csi_stack_peek (&scan->procedure_stack, 0);
+ scan->status = csi_array_append (ctx, next->datum.array,
+ &scan->build_procedure);
+ scan->build_procedure = *next;
+ scan->procedure_stack.len--;
+ } else {
+ scan->status = _csi_push_ostack (ctx, &scan->build_procedure);
+ scan->build_procedure.type = CSI_OBJECT_TYPE_NULL;
+ }
+
+ reset (scan);
+ return;
+ }
+
+ if (s[0] == '/') {
+ if (len >= 2 && s[1] == '/') { /* substituted name */
+ scan->status = csi_name_new (ctx, &obj, s + 2, len - 2);
+ if (_csi_unlikely (scan->status))
+ return;
+
+ scan->status = _csi_name_lookup (ctx, obj.datum.name, &obj);
+ } else { /* literal name */
+ scan->status = csi_name_new (ctx, &obj, s + 1, len - 1);
+ }
+ } else {
+ if (! parse_number (&obj, s, len)) {
+ scan->status = csi_name_new (ctx, &obj, s, len);
+ obj.type |= CSI_OBJECT_ATTR_EXECUTABLE;
+ }
+ }
+ if (_csi_unlikely (scan->status))
+ return;
+
+ /* consume whitespace after token, before calling the interpreter */
+ reset (scan);
+
+ if (scan->build_procedure.type != CSI_OBJECT_TYPE_NULL) {
+ scan->status = csi_array_append (ctx,
+ scan->build_procedure.datum.array,
+ &obj);
+ } else if (obj.type & CSI_OBJECT_ATTR_EXECUTABLE) {
+ scan->status = csi_object_execute (ctx, csi_object_reference (&obj));
+ csi_object_free (ctx, &obj);
+ } else
+ scan->status = _csi_push_ostack (ctx, &obj);
+}
+
+static void
+comment_start (csi_scanner_t *scan)
+{
+ /* XXX check for '!' interpreter mode?, '%' dsc setup? */
+ scan->state = COMMENT;
+}
+
+static void
+comment_end (csi_scanner_t *scan)
+{
+ reset (scan);
+}
+
+static void
+string_start (csi_scanner_t *scan)
+{
+ scan->state = STRING;
+ scan->string_p = 1;
+ _csi_buffer_reset (&scan->buffer);
+}
+
+static void
+string_inc_p (csi_scanner_t *scan)
+{
+ scan->string_p++;
+}
+
+static int
+string_dec_p (csi_scanner_t *scan)
+{
+ return --scan->string_p == 0;
+}
+
+static void
+string_add (csi_t *ctx, csi_scanner_t *scan, int c)
+{
+ if (_csi_likely (_csi_buffer_check (ctx, &scan->buffer, 1)))
+ _csi_buffer_add (&scan->buffer, c);
+}
+
+static void
+string_end (csi_t *ctx, csi_scanner_t *scan)
+{
+ csi_object_t obj;
+
+ scan->status = scan->buffer.status;
+ if (_csi_unlikely (scan->status))
+ return;
+
+ scan->status = csi_string_new (ctx,
+ &obj,
+ scan->buffer.base,
+ scan->buffer.ptr - scan->buffer.base);
+ if (_csi_unlikely (scan->status))
+ return;
+
+ if (scan->build_procedure.type != CSI_OBJECT_TYPE_NULL)
+ scan->status = csi_array_append (ctx,
+ scan->build_procedure.datum.array,
+ &obj);
+ else
+ scan->status = _csi_push_ostack (ctx, &obj);
+
+ reset (scan);
+}
+
+static void
+hex_start (csi_scanner_t *scan)
+{
+ scan->state = HEX;
+ scan->accumulator_count = 0;
+ scan->accumulator = 0;
+
+ _csi_buffer_reset (&scan->buffer);
+}
+
+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;
+ if (_csi_likely (_csi_buffer_check (ctx, &scan->buffer, 1)))
+ _csi_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;
+
+ if (scan->accumulator_count)
+ hex_add (ctx, scan, '0');
+
+ scan->status = scan->buffer.status;
+ if (_csi_unlikely (scan->status))
+ return;
+
+ scan->status = csi_string_new (ctx,
+ &obj,
+ scan->buffer.base,
+ scan->buffer.ptr - scan->buffer.base);
+ if (_csi_unlikely (scan->status))
+ return;
+
+ if (scan->build_procedure.type != CSI_OBJECT_TYPE_NULL)
+ scan->status = csi_array_append (ctx,
+ scan->build_procedure.datum.array,
+ &obj);
+ else
+ scan->status = _csi_push_ostack (ctx, &obj);
+
+ reset (scan);
+}
+
+static void
+base85_start (csi_scanner_t *scan)
+{
+ scan->state = BASE85;
+ scan->accumulator = 0;
+ scan->accumulator_count = 0;
+
+ _csi_buffer_reset (&scan->buffer);
+}
+
+static void
+base85_add (csi_t *ctx, csi_scanner_t *scan, int c)
+{
+ if (c == 'z') {
+ if (_csi_unlikely (scan->accumulator_count != 0)) {
+ scan->status = _csi_error (CSI_STATUS_INVALID_SCRIPT);
+ return;
+ }
+ if (_csi_likely (_csi_buffer_check (ctx, &scan->buffer, 4))) {
+ _csi_buffer_add (&scan->buffer, 0);
+ _csi_buffer_add (&scan->buffer, 0);
+ _csi_buffer_add (&scan->buffer, 0);
+ _csi_buffer_add (&scan->buffer, 0);
+ }
+ } else if (_csi_unlikely (c < '!' || c > 'u')) {
+ scan->status = _csi_error (CSI_STATUS_INVALID_SCRIPT);
+ return;
+ } else {
+ scan->accumulator = scan->accumulator*85 + c - '!';
+ if (++scan->accumulator_count == 5) {
+ if (_csi_likely (_csi_buffer_check (ctx, &scan->buffer, 4))) {
+ _csi_buffer_add (&scan->buffer,
+ (scan->accumulator >> 24) & 0xff);
+ _csi_buffer_add (&scan->buffer,
+ (scan->accumulator >> 16) & 0xff);
+ _csi_buffer_add (&scan->buffer,
+ (scan->accumulator >> 8) & 0xff);
+ _csi_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)
+{
+ csi_object_t obj;
+
+ if (_csi_unlikely (! _csi_buffer_check (ctx, &scan->buffer, 4))) {
+ scan->status = scan->buffer.status;
+ return;
+ }
+
+ switch (scan->accumulator_count) {
+ case 0:
+ break;
+ case 1:
+ scan->status = _csi_error (CSI_STATUS_INVALID_SCRIPT);
+ break;
+
+ case 2:
+ scan->accumulator = scan->accumulator * (85*85*85) + 85*85*85 -1;
+ _csi_buffer_add (&scan->buffer, (scan->accumulator >> 24) & 0xff);
+ break;
+ case 3:
+ scan->accumulator = scan->accumulator * (85*85) + 85*85 -1;
+ _csi_buffer_add (&scan->buffer, (scan->accumulator >> 24) & 0xff);
+ _csi_buffer_add (&scan->buffer, (scan->accumulator >> 16) & 0xff);
+ break;
+ case 4:
+ scan->accumulator = scan->accumulator * 85 + 84;
+ _csi_buffer_add (&scan->buffer, (scan->accumulator >> 24) & 0xff);
+ _csi_buffer_add (&scan->buffer, (scan->accumulator >> 16) & 0xff);
+ _csi_buffer_add (&scan->buffer, (scan->accumulator >> 8) & 0xff);
+ break;
+ }
+
+ scan->status = csi_string_new (ctx,
+ &obj,
+ scan->buffer.base,
+ scan->buffer.ptr - scan->buffer.base);
+ if (_csi_unlikely (scan->status))
+ return;
+
+ if (scan->build_procedure.type != CSI_OBJECT_TYPE_NULL)
+ scan->status = csi_array_append (ctx,
+ scan->build_procedure.datum.array,
+ &obj);
+ else
+ scan->status = _csi_push_ostack (ctx, &obj);
+
+ reset (scan);
+}
+
+static int
+scan_none (csi_t *ctx,
+ csi_scanner_t *scan,
+ csi_file_t *src)
+{
+ int c, next;
+ union {
+ int i;
+ float f;
+ } u;
+
+ while ((c = scan_getc (scan, src)) != EOF) {
+ csi_object_t obj = { CSI_OBJECT_TYPE_NULL };
+
+ switch (c) {
+ case 0x0:
+ case 0x9:
+ case 0xa:
+ case 0xc:
+ case 0xd:
+ case 0x20: /* ignore whitespace */
+ break;
+
+ case '%':
+ comment_start (scan);
+ return 1;
+
+ case '(':
+ string_start (scan);
+ return 1;
+
+ case '[': /* needs special case */
+ case ']':
+ case '{':
+ case '}':
+ token_start (scan);
+ token_add_unchecked (scan, c);
+ token_end (ctx, scan, src);
+ return 1;
+
+ case '<':
+ next = scan_getc (scan, src);
+ switch (next) {
+ case EOF:
+ scan_putc (scan, src, '<');
+ return 0;
+ case '<':
+ /* dictionary name */
+ token_start (scan);
+ token_add_unchecked (scan, '<');
+ token_add_unchecked (scan, '<');
+ token_end (ctx, scan, src);
+ return 1;
+ case '~':
+ base85_start (scan);
+ return 1;
+ default:
+ scan_putc (scan, src, next);
+ hex_start (scan);
+ return 1;
+ }
+ break;
+
+ /* binary token */
+ case 128:
+ case 129:
+ case 130:
+ case 131:
+ /* binary object sequence */
+ break;
+ case 132: /* 32-bit integer, MSB */
+ break;
+ case 133: /* 32-bit integer, LSB */
+ break;
+ case 134: /* 16-bit integer, MSB */
+ break;
+ case 135: /* 16-bit integer, LSB */
+ break;
+ case 136: /* 8-bit integer */
+ break;
+ case 137: /* 16/32-bit fixed point */
+ break;
+ case 138: /* 32-bit real, MSB */
+ scan_read (scan, src, &u.i, 4);
+#if ! WORDS_BIGENDIAN
+ u.i = bswap_32 (u.i);
+#endif
+ scan->status = csi_real_new (ctx, &obj, u.f);
+ break;
+ case 139: /* 32-bit real, LSB */
+ scan_read (scan, src, &u.f, 4);
+#if WORDS_BIGENDIAN
+ u.i = bswap_32 (u.i);
+#endif
+ scan->status = csi_real_new (ctx, &obj, u.f);
+ break;
+ case 140: /* 32-bit real, native */
+ scan_read (scan, src, &u.f, 4);
+ scan->status = csi_real_new (ctx, &obj, u.f);
+ break;
+ case 141: /* boolean */
+ break;
+ case 142: /* string of length 1n */
+ break;
+ case 143: /* string of length 2n (MSB) */
+ break;
+ case 144: /* string of length 2n (LSB) */
+ break;
+ case 145: /* literal system name */
+ break;
+ case 146: /* executable system name */
+ break;
+ case 147: /* reserved */
+ break;
+ case 148: /* reserved */
+ break;
+ case 149: /* homogeneous array */
+ break;
+
+ /* unassigned */
+ case 150:
+ case 151:
+ case 152:
+ case 153:
+ case 154:
+ case 155:
+ case 156:
+ case 157:
+ case 158:
+ case 159:
+ scan->status = _csi_error (CSI_STATUS_INVALID_SCRIPT);
+ return 0;
+
+ case '#': /* PDF 1.2 escape code */
+ {
+ int c_hi = scan_getc (scan, src);
+ int c_lo = scan_getc (scan, src);
+ c = (hex_value (c_hi) << 4) | hex_value (c_lo);
+ }
+ /* fall-through */
+ default:
+ token_start (scan);
+ token_add_unchecked (scan, c);
+ return 1;
+ }
+
+ if (obj.type != CSI_OBJECT_TYPE_NULL) {
+ if (scan->build_procedure.type != CSI_OBJECT_TYPE_NULL)
+ scan->status = csi_array_append (ctx,
+ scan->build_procedure.datum.array,
+ &obj);
+ else
+ scan->status = csi_object_execute (ctx, &obj);
+ }
+ }
+
+ return 0;
+}
+
+static int
+scan_token (csi_t *ctx, csi_scanner_t *scan, csi_file_t *src)
+{
+ int c;
+
+ while ((c = scan_getc (scan, src)) != EOF) {
+ switch (c) {
+ case 0x0:
+ case 0x9:
+ case 0xa:
+ case 0xc:
+ case 0xd:
+ case 0x20:
+ token_end (ctx, scan, src);
+ return 1;
+
+ /* syntax delimiters */
+ case '%':
+ token_end (ctx, scan, src);
+ comment_start (scan);
+ return 1;
+ /* syntax error? */
+ case '(':
+ token_end (ctx, scan, src);
+ string_start (scan);
+ return 1;
+ /* XXX syntax error? */
+ case ')':
+ token_end (ctx, scan, src);
+ return 1;
+ 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, '/');
+ return 1;
+
+ case '{':
+ case '}':
+ case ']':
+ token_end (ctx, scan, src);
+ token_start (scan);
+ token_add_unchecked (scan, c);
+ token_end (ctx, scan, src);
+ return 1;
+
+ case '<':
+ scan_putc (scan, src, '<');
+ token_end (ctx, scan, src);
+ return 1;
+
+ case '#': /* PDF 1.2 escape code */
+ {
+ int c_hi = scan_getc (scan, src);
+ int c_lo = scan_getc (scan, 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 0;
+}
+
+static int
+scan_hex (csi_t *ctx, csi_scanner_t *scan, csi_file_t *src)
+{
+ int c;
+
+ while ((c = scan_getc (scan, src)) != EOF) {
+ switch (c) {
+ case 0x0:
+ case 0x9:
+ case 0xa:
+ case 0xc:
+ case 0xd:
+ case 0x20: /* ignore whitespace */
+ break;
+
+ case '>':
+ hex_end (ctx, scan); /* fixup odd digit with '0' */
+ return 1;
+
+ 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:
+ scan->status = _csi_error (CSI_STATUS_INVALID_SCRIPT);
+ return 0;
+ }
+ }
+
+ scan->status = _csi_error (CSI_STATUS_INVALID_SCRIPT);
+ return 0;
+}
+
+static int
+scan_base85 (csi_t *ctx, csi_scanner_t *scan, csi_file_t *src)
+{
+ int c, next;
+
+ while ((c = scan_getc (scan, src)) != EOF) {
+ switch (c) {
+ case '~':
+ next = scan_getc (scan, src);
+ switch (next) {
+ case EOF:
+ return 0;
+
+ case '>':
+ base85_end (ctx, scan);
+ return 1;
+ }
+ scan_putc (scan, src, next);
+
+ /* fall-through */
+ default:
+ base85_add (ctx, scan, c);
+ break;
+ }
+ }
+
+ scan->status = _csi_error (CSI_STATUS_INVALID_SCRIPT);
+ return 0;
+}
+
+static int
+scan_string (csi_t *ctx, csi_scanner_t *scan, csi_file_t *src)
+{
+ int c, next;
+
+ while ((c = scan_getc (scan, src)) != EOF) {
+ switch (c) {
+ case '\\': /* escape */
+ next = scan_getc (scan, src);
+ switch (next) {
+ case EOF:
+ return 0;
+
+ case 'n':
+ string_add (ctx, scan, '\n');
+ break;
+ case 'r':
+ string_add (ctx, scan, '\r');
+ break;
+ case 't':
+ string_add (ctx, scan, '\r');
+ 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 = scan_getc (scan, src);
+ switch (next) {
+ case EOF:
+ return 0;
+
+ case '0': case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ c = 8*c + next-'0';
+ break;
+
+ default:
+ scan_putc (scan, src, next);
+ goto octal_code_done;
+ }
+ }
+ octal_code_done:
+ string_add (ctx, scan, c);
+ }
+ break;
+
+ case 0xa:
+ /* skip the newline */
+ next = scan_getc (scan, src); /* might be compound LFCR */
+ switch (next) {
+ case EOF:
+ return 0;
+ case 0xc:
+ break;
+ default:
+ scan_putc (scan, src, next);
+ break;
+ }
+ break;
+ case 0xc:
+ break;
+
+ default:
+ /* ignore the '\' */
+ break;
+ }
+ break;
+
+ case '(':
+ string_inc_p (scan);
+ string_add (ctx, scan, c);
+ break;
+
+ case ')':
+ if (string_dec_p (scan)) {
+ string_end (ctx, scan);
+ return 1;
+ } else
+ string_add (ctx, scan, c);
+ break;
+
+ default:
+ string_add (ctx, scan, c);
+ break;
+ }
+ }
+
+ scan->status = _csi_error (CSI_STATUS_INVALID_SCRIPT);
+ return 0;
+}
+
+static int
+scan_comment (csi_t *ctx, csi_scanner_t *scan, csi_file_t *src)
+{
+ int c;
+
+ /* discard until newline */
+ while ((c = scan_getc (scan, src)) != EOF) {
+ switch (c) {
+ case 0xa:
+ case 0xc:
+ comment_end (scan);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+csi_status_t
+_csi_scan_file (csi_t *ctx, csi_scanner_t *scan, csi_file_t *src)
+{
+ static int (* const func[]) (csi_t *, csi_scanner_t *, csi_file_t *) = {
+ scan_none,
+ scan_token,
+ scan_comment,
+ scan_string,
+ scan_hex,
+ scan_base85,
+ };
+
+ while (func[scan->state] (ctx, scan, src))
+ ;
+
+ return scan->status;
+}
+
+#if 0
+cairo_status_t
+_csi_tokenize_string (csi_t *ctx,
+ const char *code, int len,
+ csi_object_t **array_out)
+{
+ csi_scanner_t scan;
+ csi_object_t *src;
+ cairo_status_t status;
+
+ status = _csi_scanner_init (&scan, ctx);
+ if (status)
+ return status;
+
+ scan.build_procedure = csi_array_new (ctx, 0);
+ if (scan.build_procedure == NULL)
+ goto CLEANUP_SCAN;
+ csi_object_set_literal (scan.build_procedure, FALSE);
+
+ src = csi_file_new_for_string (ctx, (const uint8_t *) code, len);
+ if (src == NULL) {
+ status = _csi_error (CSI_STATUS_NO_MEMORY);
+ goto CLEANUP_SCAN;
+ }
+
+ status = _csi_scan_object (&scan, src);
+ if (status)
+ goto CLEANUP_SRC;
+
+ *array_out = scan.build_procedure;
+ scan.build_procedure = NULL;
+
+CLEANUP_SRC:
+ csi_object_free (src);
+
+CLEANUP_SCAN:
+ _csi_scanner_fini (&scan);
+
+ return status;
+}
+#endif
+
+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 = _csi_buffer_init (ctx, &scanner->buffer);
+ if (status)
+ return status;
+
+ status = _csi_stack_init (ctx, &scanner->procedure_stack, 4);
+ if (status)
+ return status;
+
+ reset (scanner);
+
+ return CSI_STATUS_SUCCESS;
+}
+
+void
+_csi_scanner_fini (csi_t *ctx, csi_scanner_t *scanner)
+{
+ _csi_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);
+}