diff options
Diffstat (limited to 'util/cairo-script/cairo-script-scanner.c')
-rw-r--r-- | util/cairo-script/cairo-script-scanner.c | 1179 |
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); +} |