1
/*
2
 * Copyright © 2008 Chris Wilson <chris@chris-wilson.co.uk>
3
 *
4
 * This library is free software; you can redistribute it and/or
5
 * modify it either under the terms of the GNU Lesser General Public
6
 * License version 2.1 as published by the Free Software Foundation
7
 * (the "LGPL") or, at your option, under the terms of the Mozilla
8
 * Public License Version 1.1 (the "MPL"). If you do not alter this
9
 * notice, a recipient may use your version of this file under either
10
 * the MPL or the LGPL.
11
 *
12
 * You should have received a copy of the LGPL along with this library
13
 * in the file COPYING-LGPL-2.1; if not, write to the Free Software
14
 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
15
 * You should have received a copy of the MPL along with this library
16
 * in the file COPYING-MPL-1.1
17
 *
18
 * The contents of this file are subject to the Mozilla Public License
19
 * Version 1.1 (the "License"); you may not use this file except in
20
 * compliance with the License. You may obtain a copy of the License at
21
 * http://www.mozilla.org/MPL/
22
 *
23
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
24
 * OF ANY KIND, either express or implied. See the LGPL or the MPL for
25
 * the specific language governing rights and limitations.
26
 *
27
 * The Original Code is the cairo graphics library.
28
 *
29
 * The Initial Developer of the Original Code is Chris Wilson.
30
 *
31
 * Contributor(s):
32
 *	Chris Wilson <chris@chris-wilson.co.uk>
33
 */
34

            
35
#include "config.h"
36

            
37
#include "cairo-script-private.h"
38

            
39
#include <limits.h> /* INT_MAX */
40
#include <math.h> /* pow */
41
#include <stdio.h> /* EOF */
42
#include <stdint.h> /* for {INT,UINT}*_{MIN,MAX} */
43
#include <stdlib.h> /* malloc/free */
44
#include <string.h> /* memset */
45
#include <assert.h>
46
#include <zlib.h>
47

            
48
#if HAVE_LZO
49
#include <lzo2a.h>
50
#endif
51

            
52
#define DEBUG_SCAN 0
53

            
54
#if WORDS_BIGENDIAN
55
#define le16(x) bswap_16 (x)
56
#define le32(x) bswap_32 (x)
57
#define be16(x) x
58
#define be32(x) x
59
#define to_be32(x) x
60
#else
61
#define le16(x) x
62
#define le32(x) x
63
#define be16(x) bswap_16 (x)
64
#define be32(x) bswap_32 (x)
65
#define to_be32(x) bswap_32 (x)
66
#endif
67

            
68
/*
69
 * whitespace:
70
 * 0 - nul
71
 * 9 - tab
72
 * A - LF
73
 * C - FF
74
 * D - CR
75
 *
76
 * syntax delimiters
77
 * ( = 28, ) = 29 - literal strings
78
 * < = 3C, > = 3E - hex/base85 strings, dictionary name
79
 * [ = 5B, ] = 5D - array
80
 * { = 7B, } = 7C - procedure
81
 * / = 5C - literal marker
82
 * % = 25 - comment
83
 */
84

            
85
static void
86
fprintf_obj (FILE *stream, csi_t *ctx, const csi_object_t *obj)
87
{
88
    switch (csi_object_get_type (obj)) {
89
	case CSI_OBJECT_TYPE_NULL:
90
	    fprintf (stream, "NULL\n");
91
	    break;
92

            
93
	    /* atomics */
94
	case CSI_OBJECT_TYPE_BOOLEAN:
95
	    fprintf (stream, "boolean: %s\n",
96
		    obj->datum.boolean ? "true" : "false");
97
	    break;
98
	case CSI_OBJECT_TYPE_INTEGER:
99
	    fprintf (stream, "integer: %ld\n", obj->datum.integer);
100
	    break;
101
	case CSI_OBJECT_TYPE_MARK:
102
	    fprintf (stream, "mark\n");
103
	    break;
104
	case CSI_OBJECT_TYPE_NAME:
105
	    fprintf (stream, "name: %s\n", (char *) obj->datum.name);
106
	    break;
107
	case CSI_OBJECT_TYPE_OPERATOR:
108
	    fprintf (stream, "operator: %p\n", obj->datum.ptr);
109
	    break;
110
	case CSI_OBJECT_TYPE_REAL:
111
	    fprintf (stream, "real: %g\n", obj->datum.real);
112
	    break;
113

            
114
	    /* compound */
115
	case CSI_OBJECT_TYPE_ARRAY:
116
	    fprintf (stream, "array\n");
117
	    break;
118
	case CSI_OBJECT_TYPE_DICTIONARY:
119
	    fprintf (stream, "dictionary\n");
120
	    break;
121
	case CSI_OBJECT_TYPE_FILE:
122
	    fprintf (stream, "file\n");
123
	    break;
124
	case CSI_OBJECT_TYPE_MATRIX:
125
	    fprintf (stream, "matrix: [%g %g %g %g %g %g]\n",
126
		    obj->datum.matrix->matrix.xx,
127
		    obj->datum.matrix->matrix.yx,
128
		    obj->datum.matrix->matrix.xy,
129
		    obj->datum.matrix->matrix.yy,
130
		    obj->datum.matrix->matrix.x0,
131
		    obj->datum.matrix->matrix.y0);
132
	    break;
133
	case CSI_OBJECT_TYPE_STRING:
134
	    fprintf (stream, "string: len=%ld, defate=%ld, method=%d\n",
135
		     obj->datum.string->len, obj->datum.string->deflate, obj->datum.string->method);
136
	    break;
137

            
138
	    /* cairo */
139
	case CSI_OBJECT_TYPE_CONTEXT:
140
	    fprintf (stream, "context\n");
141
	    break;
142
	case CSI_OBJECT_TYPE_FONT:
143
	    fprintf (stream, "font\n");
144
	    break;
145
	case CSI_OBJECT_TYPE_PATTERN:
146
	    fprintf (stream, "pattern\n");
147
	    break;
148
	case CSI_OBJECT_TYPE_SCALED_FONT:
149
	    fprintf (stream, "scaled-font\n");
150
	    break;
151
	case CSI_OBJECT_TYPE_SURFACE:
152
	    fprintf (stream, "surface\n");
153
	    break;
154
    }
155
}
156

            
157
/* takes ownership of obj */
158
static inline csi_status_t
159
scan_push (csi_t *ctx, csi_object_t *obj)
160
{
161
    return ctx->scanner.push (ctx, obj);
162
}
163

            
164
static inline csi_status_t
165
scan_execute (csi_t *ctx, csi_object_t *obj)
166
{
167
    return ctx->scanner.execute (ctx, obj);
168
}
169

            
170
static cairo_status_t
171
buffer_init (csi_t *ctx, csi_buffer_t *buffer)
172
{
173
    cairo_status_t status = CSI_STATUS_SUCCESS;
174

            
175
    buffer->size = 16384;
176
    buffer->base = _csi_alloc (ctx, buffer->size);
177
    if (_csi_unlikely (buffer->base == NULL)) {
178
	status = _csi_error (CSI_STATUS_NO_MEMORY);
179
	buffer->size = 0;
180
    }
181

            
182
    buffer->ptr = buffer->base;
183
    buffer->end = buffer->base + buffer->size;
184

            
185
    return status;
186
}
187

            
188
static void
189
buffer_fini (csi_t *ctx, csi_buffer_t *buffer)
190
{
191
    _csi_free (ctx, buffer->base);
192
}
193

            
194
static void
195
_buffer_grow (csi_t *ctx, csi_scanner_t *scan)
196
{
197
    int newsize;
198
    int offset;
199
    char *base;
200

            
201
    if (_csi_unlikely (scan->buffer.size > INT_MAX / 2))
202
	longjmp (scan->jump_buffer,  _csi_error (CSI_STATUS_NO_MEMORY));
203

            
204
    offset = scan->buffer.ptr - scan->buffer.base;
205
    newsize = scan->buffer.size * 2;
206
    base = _csi_realloc (ctx, scan->buffer.base, newsize);
207
    if (_csi_unlikely (base == NULL))
208
	longjmp (scan->jump_buffer,  _csi_error (CSI_STATUS_NO_MEMORY));
209

            
210
    scan->buffer.base = base;
211
    scan->buffer.ptr  = base + offset;
212
    scan->buffer.end  = base + newsize;
213
    scan->buffer.size = newsize;
214
}
215

            
216
static inline void
217
buffer_check (csi_t *ctx, csi_scanner_t *scan, int count)
218
{
219
    if (_csi_unlikely (scan->buffer.ptr + count > scan->buffer.end))
220
	_buffer_grow (ctx, scan);
221
}
222

            
223
static inline void
224
buffer_add (csi_buffer_t *buffer, int c)
225
{
226
    *buffer->ptr++ = c;
227
}
228

            
229
static inline void
230
buffer_reset (csi_buffer_t *buffer)
231
{
232
    buffer->ptr = buffer->base;
233
}
234

            
235
static void
236
token_start (csi_scanner_t *scan)
237
{
238
    buffer_reset (&scan->buffer);
239
}
240

            
241
static void
242
token_add (csi_t *ctx, csi_scanner_t *scan, int c)
243
{
244
    buffer_check (ctx, scan, 1);
245
    buffer_add (&scan->buffer, c);
246
}
247

            
248
static void
249
token_add_unchecked (csi_scanner_t *scan, int c)
250
{
251
    buffer_add (&scan->buffer, c);
252
}
253

            
254
csi_boolean_t
255
_csi_parse_number (csi_object_t *obj, const char *s, int len)
256
{
257
    int radix = 0;
258
    long long mantissa = 0;
259
    int exponent = 0;
260
    int sign = 1;
261
    int decimal = -1;
262
    int exponent_sign = 0;
263
    const char * const end = s + len;
264

            
265
    switch (*s) {
266
    case '0':
267
    case '1':
268
    case '2':
269
    case '3':
270
    case '4':
271
    case '5':
272
    case '6':
273
    case '7':
274
    case '8':
275
    case '9':
276
	mantissa = *s - '0';
277
    case '+':
278
	break;
279
    case '-':
280
	sign = -1;
281
	break;
282
    case '.':
283
	decimal = 0;
284
	break;
285
    default:
286
	return FALSE;
287
    }
288

            
289
    while (++s < end) {
290
	if (*s < '0') {
291
	    if (*s == '.') {
292
		if (_csi_unlikely (radix))
293
		    return FALSE;
294
		if (_csi_unlikely (decimal != -1))
295
		    return FALSE;
296
		if (_csi_unlikely (exponent_sign))
297
		    return FALSE;
298

            
299
		decimal = 0;
300
	    } else if (*s == '!') {
301
		if (_csi_unlikely (radix))
302
		    return FALSE;
303
		if (_csi_unlikely (decimal != -1))
304
		    return FALSE;
305
		if (_csi_unlikely (exponent_sign))
306
		    return FALSE;
307

            
308
		radix = mantissa;
309
		mantissa = 0;
310

            
311
		if (_csi_unlikely (radix < 2 || radix > 36))
312
		    return FALSE;
313
	    } else
314
		return FALSE;
315
	} else if (*s <= '9') {
316
	    int v = *s - '0';
317
	    if (_csi_unlikely (radix && v >= radix))
318
		return FALSE;
319

            
320
	    if (exponent_sign) {
321
		exponent = 10 * exponent + v;
322
	    } else {
323
		if (radix)
324
		    mantissa = radix * mantissa + v;
325
		else
326
		    mantissa = 10 * mantissa + v;
327
		if (decimal != -1)
328
		    decimal++;
329
	    }
330
	} else if (*s == 'E' || * s== 'e') {
331
	    if (radix == 0) {
332
		if (_csi_unlikely (s + 1 == end))
333
		    return FALSE;
334

            
335
		exponent_sign = 1;
336
		if (s[1] == '-') {
337
		    exponent_sign = -1;
338
		    s++;
339
		} else if (s[1] == '+')
340
		    s++;
341
	    } else {
342
		int v = 0xe;
343

            
344
		if (_csi_unlikely (v >= radix))
345
		    return FALSE;
346

            
347
		mantissa = radix * mantissa + v;
348
	    }
349
	} else if (*s < 'A') {
350
	    return FALSE;
351
	} else if (*s <= 'Z') {
352
	    int v = *s - 'A' + 0xA;
353

            
354
	    if (_csi_unlikely (v >= radix))
355
		return FALSE;
356

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

            
363
	    if (_csi_unlikely (v >= radix))
364
		return FALSE;
365

            
366
	    mantissa = radix * mantissa + v;
367
	} else
368
	    return FALSE;
369
    }
370

            
371
    if (exponent_sign || decimal != -1) {
372
	if (mantissa == 0) {
373
	    obj->type = CSI_OBJECT_TYPE_REAL;
374
	    obj->datum.real = 0.;
375
	    return TRUE;
376
	} else {
377
	    int e;
378
	    double v;
379

            
380
	    v = mantissa;
381
	    e = exponent * exponent_sign;
382
	    if (decimal != -1)
383
		e -= decimal;
384
	    switch (e) {
385
	    case -7: v *= 0.0000001; break;
386
	    case -6: v *= 0.000001; break;
387
	    case -5: v *= 0.00001; break;
388
	    case -4: v *= 0.0001; break;
389
	    case -3: v *= 0.001; break;
390
	    case -2: v *= 0.01; break;
391
	    case -1: v *= 0.1; break;
392
	    case  0: break;
393
	    case  1: v *= 10; break;
394
	    case  2: v *= 100; break;
395
	    case  3: v *= 1000; break;
396
	    case  4: v *= 10000; break;
397
	    case  5: v *= 100000; break;
398
	    case  6: v *= 1000000; break;
399
	    default:
400
		    v *= pow (10, e); /* XXX */
401
		    break;
402
	    }
403

            
404
	    obj->type = CSI_OBJECT_TYPE_REAL;
405
	    obj->datum.real = sign * v;
406
	    return TRUE;
407
	}
408
    } else {
409
	obj->type = CSI_OBJECT_TYPE_INTEGER;
410
	obj->datum.integer = sign * mantissa;
411
	return TRUE;
412
    }
413
}
414

            
415
static void
416
token_end (csi_t *ctx, csi_scanner_t *scan, csi_file_t *src)
417
{
418
    cairo_status_t status;
419
    char *s;
420
    csi_object_t obj;
421
    int len;
422

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

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

            
434
    s = scan->buffer.base;
435
    len = scan->buffer.ptr - scan->buffer.base;
436

            
437
    if (_csi_likely (! scan->bind)) {
438
	if (s[0] == '{') { /* special case procedures */
439
	    if (scan->build_procedure.type != CSI_OBJECT_TYPE_NULL) {
440
		status = _csi_stack_push (ctx,
441
					  &scan->procedure_stack,
442
					  &scan->build_procedure);
443
		if (_csi_unlikely (status))
444
		    longjmp (scan->jump_buffer, status);
445
	    }
446

            
447
	    status = csi_array_new (ctx, 0, &scan->build_procedure);
448
	    if (_csi_unlikely (status))
449
		longjmp (scan->jump_buffer, status);
450

            
451
	    scan->build_procedure.type |= CSI_OBJECT_ATTR_EXECUTABLE;
452
	    return;
453
	} else if (s[0] == '}') {
454
	    if (_csi_unlikely
455
		(scan->build_procedure.type == CSI_OBJECT_TYPE_NULL))
456
	    {
457
		longjmp (scan->jump_buffer, _csi_error (CSI_STATUS_INVALID_SCRIPT));
458
	    }
459

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

            
463
		next = _csi_stack_peek (&scan->procedure_stack, 0);
464
		status = csi_array_append (ctx, next->datum.array,
465
					   &scan->build_procedure);
466
		scan->build_procedure = *next;
467
		scan->procedure_stack.len--;
468
	    } else {
469
		status = scan_push (ctx, &scan->build_procedure);
470
		scan->build_procedure.type = CSI_OBJECT_TYPE_NULL;
471
	    }
472
	    if (_csi_unlikely (status))
473
		longjmp (scan->jump_buffer, status);
474

            
475
	    return;
476
	}
477
    }
478

            
479
    if (s[0] == '/') {
480
	if (len >= 2 && s[1] == '/') { /* substituted name */
481
	    status = csi_name_new (ctx, &obj, s + 2, len - 2);
482
	    if (_csi_unlikely (status))
483
		longjmp (scan->jump_buffer, status);
484

            
485
	    status = _csi_name_lookup (ctx, obj.datum.name, &obj);
486
	} else { /* literal name */
487
	    status = csi_name_new (ctx, &obj, s + 1, len - 1);
488
	}
489
	if (_csi_unlikely (status))
490
	    longjmp (scan->jump_buffer, status);
491
    } else {
492
	if (! _csi_parse_number (&obj, s, len)) {
493
	    status = csi_name_new (ctx, &obj, s, len);
494
	    if (_csi_unlikely (status))
495
		longjmp (scan->jump_buffer, status);
496

            
497
	    obj.type |= CSI_OBJECT_ATTR_EXECUTABLE;
498
	}
499
    }
500

            
501
    /* consume whitespace after token, before calling the interpreter */
502
    if (scan->build_procedure.type != CSI_OBJECT_TYPE_NULL) {
503
	status = csi_array_append (ctx,
504
				   scan->build_procedure.datum.array,
505
				   &obj);
506
    } else if (obj.type & CSI_OBJECT_ATTR_EXECUTABLE) {
507
	status = scan_execute (ctx, &obj);
508
	csi_object_free (ctx, &obj);
509
    } else {
510
	status = scan_push (ctx, &obj);
511
    }
512
    if (_csi_unlikely (status))
513
	longjmp (scan->jump_buffer, status);
514
}
515

            
516
static void
517
string_add (csi_t *ctx, csi_scanner_t *scan, int c)
518
{
519
    buffer_check (ctx, scan, 1);
520
    buffer_add (&scan->buffer, c);
521
}
522

            
523
static void
524
string_end (csi_t *ctx, csi_scanner_t *scan)
525
{
526
    csi_object_t obj;
527
    cairo_status_t status;
528

            
529
    status = csi_string_new (ctx,
530
			     &obj,
531
			     scan->buffer.base,
532
			     scan->buffer.ptr - scan->buffer.base);
533
    if (_csi_unlikely (status))
534
	longjmp (scan->jump_buffer, status);
535

            
536
    if (scan->build_procedure.type != CSI_OBJECT_TYPE_NULL)
537
	status = csi_array_append (ctx,
538
				   scan->build_procedure.datum.array,
539
				   &obj);
540
    else
541
	status = scan_push (ctx, &obj);
542
    if (_csi_unlikely (status))
543
	longjmp (scan->jump_buffer, status);
544
}
545

            
546
static int
547
hex_value (int c)
548
{
549
    if (c < '0')
550
	return EOF;
551
    if (c <= '9')
552
	return c - '0';
553
    c |= 32;
554
    if (c < 'a')
555
	return EOF;
556
    if (c <= 'f')
557
	return c - 'a' + 0xa;
558
    return EOF;
559
}
560

            
561
static void
562
hex_add (csi_t *ctx, csi_scanner_t *scan, int c)
563
{
564
    if (scan->accumulator_count == 0) {
565
	scan->accumulator |= hex_value (c) << 4;
566
	scan->accumulator_count = 1;
567
    } else {
568
	scan->accumulator |= hex_value (c) << 0;
569
	buffer_check (ctx, scan, 1);
570
	buffer_add (&scan->buffer, scan->accumulator);
571

            
572
	scan->accumulator = 0;
573
	scan->accumulator_count = 0;
574
    }
575
}
576

            
577
static void
578
hex_end (csi_t *ctx, csi_scanner_t *scan)
579
{
580
    csi_object_t obj;
581
    cairo_status_t status;
582

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

            
586
    status = csi_string_new (ctx,
587
			     &obj,
588
			     scan->buffer.base,
589
			     scan->buffer.ptr - scan->buffer.base);
590
    if (_csi_unlikely (status))
591
	longjmp (scan->jump_buffer, status);
592

            
593
    if (scan->build_procedure.type != CSI_OBJECT_TYPE_NULL)
594
	status = csi_array_append (ctx,
595
				   scan->build_procedure.datum.array,
596
				   &obj);
597
    else
598
	status = scan_push (ctx, &obj);
599
    if (_csi_unlikely (status))
600
	longjmp (scan->jump_buffer, status);
601
}
602

            
603
static void
604
base85_add (csi_t *ctx, csi_scanner_t *scan, int c)
605
{
606
    if (c == 'z') {
607
	if (_csi_unlikely (scan->accumulator_count != 0))
608
	    longjmp (scan->jump_buffer, _csi_error (CSI_STATUS_INVALID_SCRIPT));
609

            
610
	buffer_check (ctx, scan, 4);
611
	buffer_add (&scan->buffer, 0);
612
	buffer_add (&scan->buffer, 0);
613
	buffer_add (&scan->buffer, 0);
614
	buffer_add (&scan->buffer, 0);
615
    } else if (_csi_unlikely (c < '!' || c > 'u')) {
616
	longjmp (scan->jump_buffer, _csi_error (CSI_STATUS_INVALID_SCRIPT));
617
    } else {
618
	scan->accumulator = scan->accumulator*85 + c - '!';
619
	if (++scan->accumulator_count == 5) {
620
	    buffer_check (ctx, scan, 4);
621
	    buffer_add (&scan->buffer, (scan->accumulator >> 24) & 0xff);
622
	    buffer_add (&scan->buffer, (scan->accumulator >> 16) & 0xff);
623
	    buffer_add (&scan->buffer, (scan->accumulator >>  8) & 0xff);
624
	    buffer_add (&scan->buffer, (scan->accumulator >>  0) & 0xff);
625

            
626
	    scan->accumulator = 0;
627
	    scan->accumulator_count = 0;
628
	}
629
    }
630
}
631

            
632
static void
633
base85_end (csi_t *ctx, csi_scanner_t *scan, cairo_bool_t deflate)
634
{
635
    csi_object_t obj;
636
    cairo_status_t status;
637

            
638
    buffer_check (ctx, scan, 4);
639

            
640
    switch (scan->accumulator_count) {
641
    case 0:
642
	break;
643
    case 1:
644
	longjmp (scan->jump_buffer, _csi_error (CSI_STATUS_INVALID_SCRIPT));
645
	break;
646

            
647
    case 2:
648
	scan->accumulator = scan->accumulator * (85*85*85) + 85*85*85 -1;
649
	buffer_add (&scan->buffer, (scan->accumulator >> 24) & 0xff);
650
	break;
651
    case 3:
652
	scan->accumulator = scan->accumulator * (85*85) + 85*85 -1;
653
	buffer_add (&scan->buffer, (scan->accumulator >> 24) & 0xff);
654
	buffer_add (&scan->buffer, (scan->accumulator >> 16) & 0xff);
655
	break;
656
    case 4:
657
	scan->accumulator = scan->accumulator * 85 + 84;
658
	buffer_add (&scan->buffer, (scan->accumulator >> 24) & 0xff);
659
	buffer_add (&scan->buffer, (scan->accumulator >> 16) & 0xff);
660
	buffer_add (&scan->buffer, (scan->accumulator >>  8) & 0xff);
661
	break;
662
    }
663

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

            
668
	status = csi_string_deflate_new (ctx, &obj,
669
					 source,
670
					 (Bytef *) scan->buffer.ptr - source,
671
					 len);
672
	if (_csi_unlikely (status))
673
	    longjmp (scan->jump_buffer, status);
674
    } else {
675
	status = csi_string_new (ctx,
676
				 &obj,
677
				 scan->buffer.base,
678
				 scan->buffer.ptr - scan->buffer.base);
679
	if (_csi_unlikely (status))
680
	    longjmp (scan->jump_buffer, status);
681
    }
682

            
683
    if (scan->build_procedure.type != CSI_OBJECT_TYPE_NULL)
684
	status = csi_array_append (ctx,
685
				   scan->build_procedure.datum.array,
686
				   &obj);
687
    else
688
	status = scan_push (ctx, &obj);
689
    if (_csi_unlikely (status))
690
	longjmp (scan->jump_buffer, status);
691
}
692

            
693
static void
694
base64_add (csi_t *ctx, csi_scanner_t *scan, int c)
695
{
696
    int val;
697

            
698
    /* Convert Base64 character to its 6 bit nibble */
699
    val = scan->accumulator;
700
    if (c =='/') {
701
	val = (val << 6) | 63;
702
    } else if (c =='+') {
703
	val = (val << 6) | 62;
704
    } else if (c >='A' && c <='Z') {
705
	val = (val << 6) | (c -'A');
706
    } else if (c >='a' && c <='z') {
707
	val = (val << 6) | (c -'a' + 26);
708
    } else if (c >='0' && c <='9') {
709
	val = (val << 6) | (c -'0' + 52);
710
    }
711

            
712
    buffer_check (ctx, scan, 1);
713
    switch (scan->accumulator_count++) {
714
    case 0:
715
	break;
716

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

            
722
    case 2:
723
	buffer_add (&scan->buffer, (val >> 2) & 0xFF);
724
	val &= 0x3;
725
	break;
726

            
727
    case 3:
728
	buffer_add (&scan->buffer, (val >> 0) & 0xFF);
729
	scan->accumulator_count = 0;
730
	val = 0;
731
	break;
732
    }
733

            
734
     if (c == '=') {
735
	scan->accumulator_count = 0;
736
	scan->accumulator = 0;
737
     } else {
738
	 scan->accumulator = val;
739
     }
740
}
741

            
742
static void
743
base64_end (csi_t *ctx, csi_scanner_t *scan)
744
{
745
    csi_object_t obj;
746
    cairo_status_t status;
747

            
748
    switch (scan->accumulator_count) {
749
    case 0:
750
	break;
751
    case 2:
752
	base64_add (ctx, scan, (scan->accumulator << 2) & 0x3f);
753
	base64_add (ctx, scan, '=');
754
	break;
755
    case 1:
756
	base64_add (ctx, scan, (scan->accumulator << 4) & 0x3f);
757
	base64_add (ctx, scan, '=');
758
	base64_add (ctx, scan, '=');
759
	break;
760
    }
761

            
762
    status = csi_string_new (ctx,
763
			     &obj,
764
			     scan->buffer.base,
765
			     scan->buffer.ptr - scan->buffer.base);
766
    if (_csi_unlikely (status))
767
	longjmp (scan->jump_buffer, status);
768

            
769
    if (scan->build_procedure.type != CSI_OBJECT_TYPE_NULL)
770
	status = csi_array_append (ctx,
771
				   scan->build_procedure.datum.array,
772
				   &obj);
773
    else
774
	status = scan_push (ctx, &obj);
775
    if (_csi_unlikely (status))
776
	longjmp (scan->jump_buffer, status);
777
}
778

            
779
static void
780
scan_read (csi_scanner_t *scan, csi_file_t *src, void *ptr, int len)
781
{
782
    uint8_t *data = ptr;
783
    do {
784
	int ret = csi_file_read (src, data, len);
785
	if (_csi_unlikely (ret == 0))
786
	    longjmp (scan->jump_buffer, _csi_error (CSI_STATUS_READ_ERROR));
787
	data += ret;
788
	len -= ret;
789
    } while (_csi_unlikely (len));
790
}
791

            
792
static void
793
string_read (csi_t *ctx,
794
	     csi_scanner_t *scan,
795
	     csi_file_t *src,
796
	     int len,
797
	     int compressed,
798
	     csi_object_t *obj)
799
{
800
    csi_status_t status;
801

            
802
    status = csi_string_new (ctx, obj, NULL, len);
803
    if (_csi_unlikely (status))
804
	longjmp (scan->jump_buffer, status);
805

            
806
    if (compressed) {
807
	uint32_t u32;
808
	scan_read (scan, src, &u32, 4);
809
	obj->datum.string->deflate = be32 (u32);
810
	obj->datum.string->method = compressed;
811
    }
812

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

            
818
static void
819
_scan_file (csi_t *ctx, csi_file_t *src)
820
{
821
    csi_scanner_t *scan = &ctx->scanner;
822
    int c, next;
823
    union {
824
	int8_t i8;
825
	uint8_t u8;
826
	int16_t i16;
827
	uint16_t u16;
828
	int32_t i32;
829
	uint32_t u32;
830
	float f;
831
    } u;
832
    int deflate = 0;
833
    int string_p;
834

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

            
839
	switch (c) {
840
	case 0xa:
841
	    scan->line_number++;
842
	case 0x0:
843
	case 0x9:
844
	case 0xc:
845
	case 0xd:
846
	case 0x20: /* ignore whitespace */
847
	    break;
848

            
849
	case '%':
850
	    goto scan_comment;
851

            
852
	case '(':
853
	    goto scan_string;
854

            
855
	case '[': /* needs special case */
856
	case ']':
857
	case '{':
858
	case '}':
859
	    token_start (scan);
860
	    token_add_unchecked (scan, c);
861
	    token_end (ctx, scan, src);
862
	    goto scan_none;
863

            
864
	case '<':
865
	    next = csi_file_getc (src);
866
	    switch (next) {
867
	    case EOF:
868
		csi_file_putc (src, '<');
869
		return;
870
	    case '<':
871
		/* dictionary name */
872
		token_start (scan);
873
		token_add_unchecked (scan, '<');
874
		token_add_unchecked (scan, '<');
875
		token_end (ctx, scan, src);
876
		goto scan_none;
877
	    case '|':
878
		deflate = 1;
879
	    case '~':
880
		goto scan_base85;
881
	    case '{':
882
		goto scan_base64;
883
	    default:
884
		csi_file_putc (src, next);
885
		goto scan_hex;
886
	    }
887
	    break;
888

            
889
	    /* binary token */
890
#define MSB_INT8 128
891
#define MSB_UINT8 129
892
#define MSB_INT16 130
893
#define MSB_UINT16 131
894
#define MSB_INT32 132
895
#define LSB_INT8 MSB_INT8
896
#define LSB_UINT8 MSB_UINT8
897
#define LSB_INT16 133
898
#define LSB_UINT16 134
899
#define LSB_INT32 135
900
#define MSB_FLOAT32 140
901
#define LSB_FLOAT32 141
902
	case MSB_INT8:
903
	    scan_read (scan, src, &u.i8, 1);
904
	    csi_integer_new (&obj, u.i8);
905
	    break;
906
	case MSB_UINT8:
907
	    scan_read (scan, src, &u.u8, 1);
908
	    csi_integer_new (&obj, u.u8);
909
	    break;
910
	case MSB_INT16:
911
	    scan_read (scan, src, &u.i16, 2);
912
	    csi_integer_new (&obj, be16 (u.i16));
913
	    break;
914
	case LSB_INT16:
915
	    scan_read (scan, src, &u.i16, 2);
916
	    csi_integer_new (&obj, le16 (u.i16));
917
	    break;
918
	case MSB_UINT16:
919
	    scan_read (scan, src, &u.u16, 2);
920
	    csi_integer_new (&obj, be16 (u.u16));
921
	    break;
922
	case LSB_UINT16:
923
	    scan_read (scan, src, &u.u16, 2);
924
	    csi_integer_new (&obj, le16 (u.u16));
925
	    break;
926
	case MSB_INT32:
927
	    scan_read (scan, src, &u.i32, 4);
928
	    csi_integer_new (&obj, be32 (u.i32));
929
	    break;
930
	case LSB_INT32:
931
	    scan_read (scan, src, &u.i32, 4);
932
	    csi_integer_new (&obj, le32 (u.i32));
933
	    break;
934

            
935
	case 136: /* 16.16 msb */
936
	    scan_read (scan, src, &u.i32, 4);
937
	    csi_real_new (&obj, be32 (u.i32) / 65536.);
938
	    break;
939
	case 137: /* 16.16 lsb */
940
	    scan_read (scan, src, &u.i32, 4);
941
	    csi_real_new (&obj, le32 (u.i32) / 65536.);
942
	    break;
943
	case 138: /* 24.8 msb */
944
	    scan_read (scan, src, &u.i32, 4);
945
	    csi_real_new (&obj, be32 (u.i32) / 256.);
946
	    break;
947
	case 139: /* 24.8 lsb */
948
	    scan_read (scan, src, &u.i32, 4);
949
	    csi_real_new (&obj, le32 (u.i32) / 256.);
950
	    break;
951
	case MSB_FLOAT32:
952
	    scan_read (scan, src, &u.i32, 4);
953
	    u.i32 = be32 (u.i32);
954
	    csi_real_new (&obj, u.f);
955
	    break;
956
	case LSB_FLOAT32:
957
	    scan_read (scan, src, &u.i32, 4);
958
	    u.i32 = le32 (u.i32);
959
	    csi_real_new (&obj, u.f);
960
	    break;
961

            
962
#define STRING_1 142
963
#define STRING_2_MSB 144
964
#define STRING_2_LSB 146
965
#define STRING_4_MSB 148
966
#define STRING_4_LSB 150
967
#define STRING_DEFLATE 1
968
	case STRING_1:
969
	case STRING_1 | STRING_DEFLATE:
970
	    scan_read (scan, src, &u.u8, 1);
971
	    string_read (ctx, scan, src, u.u8, c & STRING_DEFLATE, &obj);
972
	    break;
973
	case STRING_2_MSB:
974
	case STRING_2_MSB | STRING_DEFLATE:
975
	    scan_read (scan, src, &u.u16, 2);
976
	    string_read (ctx, scan, src, be16 (u.u16),  c & STRING_DEFLATE, &obj);
977
	    break;
978
	case STRING_2_LSB:
979
	case STRING_2_LSB | STRING_DEFLATE:
980
	    scan_read (scan, src, &u.u16, 2);
981
	    string_read (ctx, scan, src, le16 (u.u16), c & STRING_DEFLATE, &obj);
982
	    break;
983
	case STRING_4_MSB:
984
	case STRING_4_MSB | STRING_DEFLATE:
985
	    scan_read (scan, src, &u.u32, 4);
986
	    string_read (ctx, scan, src, be32 (u.u32), c & STRING_DEFLATE, &obj);
987
	    break;
988
	case STRING_4_LSB:
989
	case STRING_4_LSB | STRING_DEFLATE:
990
	    scan_read (scan, src, &u.u32, 4);
991
	    string_read (ctx, scan, src, le32 (u.u32), c & STRING_DEFLATE, &obj);
992
	    break;
993

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

            
	case OPCODE | 1:
	    scan_read (scan, src, &u.u8, 1);
	    csi_operator_new (&obj, ctx->opcode[u.u8]);
	    obj.type &= ~CSI_OBJECT_ATTR_EXECUTABLE;
	    break;
#define STRING_LZO 154
	case STRING_LZO:
	    scan_read (scan, src, &u.u32, 4);
	    string_read (ctx, scan, src, be32 (u.u32), LZO, &obj);
	    break;
	    /* unassigned */
	case 155:
	case 156:
	case 157:
	case 158:
	case 159:
	    longjmp (scan->jump_buffer, _csi_error (CSI_STATUS_INVALID_SCRIPT));
	case '#': /* PDF 1.2 escape code */
	    {
		int c_hi = csi_file_getc (src);
		int c_lo = csi_file_getc (src);
		c = (hex_value (c_hi) << 4) | hex_value (c_lo);
	    }
	    /* fall-through */
	default:
	    token_start (scan);
	    token_add_unchecked (scan, c);
	    goto scan_token;
	}
	if (obj.type != CSI_OBJECT_TYPE_NULL) {
	    cairo_status_t status;
	    if (scan->build_procedure.type != CSI_OBJECT_TYPE_NULL) {
		status = csi_array_append (ctx,
					   scan->build_procedure.datum.array,
					   &obj);
	    } else if (obj.type & CSI_OBJECT_ATTR_EXECUTABLE) {
		status = scan_execute (ctx, &obj);
		csi_object_free (ctx, &obj);
	    } else {
		status = scan_push (ctx, &obj);
	    }
	    if (_csi_unlikely (status))
		longjmp (scan->jump_buffer, status);
	}
    }
    return;
scan_token:
    while ((c = csi_file_getc (src)) != EOF) {
	switch (c) {
	case 0xa:
	    scan->line_number++;
	    /* fall through */
	case 0x0:
	case 0x9:
	case 0xc:
	case 0xd:
	case 0x20:
	    token_end (ctx, scan, src);
	    goto scan_none;
	    /* syntax delimiters */
	case '%':
	    token_end (ctx, scan, src);
	    goto scan_comment;
	    /* syntax error? */
	case '(':
	    token_end (ctx, scan, src);
	    goto scan_string;
	    /* XXX syntax error? */
	case ')':
	    token_end (ctx, scan, src);
	    goto scan_none;
	case '/':
	    /* need to special case '^//?' */
	    if (scan->buffer.ptr > scan->buffer.base+1 ||
		scan->buffer.base[0] != '/')
	    {
		token_end (ctx, scan, src);
		token_start (scan);
	    }
	    token_add_unchecked (scan, '/');
	    goto scan_token;
	case '{':
	case '}':
	case ']':
	    token_end (ctx, scan, src);
	    token_start (scan);
	    token_add_unchecked (scan, c);
	    token_end (ctx, scan, src);
	    goto scan_none;
	case '<':
	    csi_file_putc (src, '<');
	    token_end (ctx, scan, src);
	    goto scan_none;
	case '#': /* PDF 1.2 escape code */
	    {
		int c_hi = csi_file_getc (src);
		int c_lo = csi_file_getc (src);
		c = (hex_value (c_hi) << 4) | hex_value (c_lo);
	    }
	    /* fall-through */
	default:
	    token_add (ctx, scan, c);
	    break;
	}
    }
    token_end (ctx, scan, src);
    return;
scan_comment:
    /* discard until newline */
    while ((c = csi_file_getc (src)) != EOF) {
	switch (c) {
	case 0xa:
	    scan->line_number++;
	case 0xc:
	    goto scan_none;
	}
    }
    return;
scan_string:
    buffer_reset (&scan->buffer);
    string_p = 1;
    while ((c = csi_file_getc (src)) != EOF) {
	switch (c) {
	case '\\': /* escape */
	    next = csi_file_getc (src);
	    switch (next) {
	    case EOF:
		longjmp (scan->jump_buffer, _csi_error (CSI_STATUS_INVALID_SCRIPT));
	    case 'n':
		string_add (ctx, scan, '\n');
		break;
	    case 'r':
		string_add (ctx, scan, '\r');
		break;
	    case 't':
		string_add (ctx, scan, '\t');
		break;
	    case 'b':
		string_add (ctx, scan, '\b');
		break;
	    case 'f':
		string_add (ctx, scan, '\f');
		break;
	    case '\\':
		string_add (ctx, scan, '\\');
		break;
	    case '(':
		string_add (ctx, scan, '(');
		break;
	    case ')':
		string_add (ctx, scan, ')');
		break;
	    case '0': case '1': case '2': case '3':
	    case '4': case '5': case '6': case '7':
		{ /* octal code: \d{1,3} */
		    int i;
		    c = next - '0';
		    for (i = 0; i < 2; i++) {
			next = csi_file_getc (src);
			switch (next) {
			case EOF:
			    return;
			case '0': case '1': case '2': case '3':
			case '4': case '5': case '6': case '7':
			    c = 8*c + next-'0';
			    break;
			default:
			    csi_file_putc (src, next);
			    goto octal_code_done;
			}
		    }
  octal_code_done:
		    string_add (ctx, scan, c);
		}
		break;
	    case 0xa:
		/* skip the newline */
		next = csi_file_getc (src); /* might be compound LFCR */
		switch (next) {
		case EOF:
		    return;
		case 0xc:
		    break;
		default:
		    csi_file_putc (src, next);
		    break;
		}
		scan->line_number++;
		break;
	    case 0xc:
		break;
	    default:
		/* ignore the '\' */
		break;
	    }
	    break;
	case '(':
	    string_p++;
	    string_add (ctx, scan, c);
	    break;
	case ')':
	    if (--string_p == 0) {
		string_end (ctx, scan);
		goto scan_none;
	    }
	    /* fall through */
	default:
	    string_add (ctx, scan, c);
	    break;
	}
    }
    longjmp (scan->jump_buffer, _csi_error (CSI_STATUS_INVALID_SCRIPT));
scan_hex:
    buffer_reset (&scan->buffer);
    scan->accumulator_count = 0;
    scan->accumulator = 0;
    while ((c = csi_file_getc (src)) != EOF) {
	switch (c) {
	case 0xa:
	    scan->line_number++;
	case 0x0:
	case 0x9:
	case 0xc:
	case 0xd:
	case 0x20: /* ignore whitespace */
	    break;
	case '>':
	    hex_end (ctx, scan); /* fixup odd digit with '0' */
	    goto scan_none;
	case '0':
	case '1':
	case '2':
	case '3':
	case '4':
	case '5':
	case '6':
	case '7':
	case '8':
	case '9':
	case 'a':
	case 'b':
	case 'c':
	case 'd':
	case 'e':
	case 'f':
	case 'A':
	case 'B':
	case 'C':
	case 'D':
	case 'E':
	case 'F':
	    hex_add (ctx, scan, c);
	    break;
	default:
	    longjmp (scan->jump_buffer, _csi_error (CSI_STATUS_INVALID_SCRIPT));
	}
    }
    longjmp (scan->jump_buffer, _csi_error (CSI_STATUS_INVALID_SCRIPT));
scan_base85:
    buffer_reset (&scan->buffer);
    scan->accumulator = 0;
    scan->accumulator_count = 0;
    while ((c = csi_file_getc (src)) != EOF) {
	switch (c) {
	case '~':
	    next = csi_file_getc (src);
	    switch (next) {
	    case EOF:
		return;
	    case '>':
		base85_end (ctx, scan, deflate);
		deflate = 0;
		goto scan_none;
	    }
	    csi_file_putc (src, next);
	    /* fall-through */
	default:
	    base85_add (ctx, scan, c);
	    break;
	}
    }
    longjmp (scan->jump_buffer, _csi_error (CSI_STATUS_INVALID_SCRIPT));
scan_base64:
    buffer_reset (&scan->buffer);
    scan->accumulator = 0;
    scan->accumulator_count = 0;
    while ((c = csi_file_getc (src)) != EOF) {
	switch (c) {
	case '}':
	    next = csi_file_getc (src);
	    switch (next) {
	    case EOF:
		return;
	    case '>':
		base64_end (ctx, scan);
		goto scan_none;
	    }
	    longjmp (scan->jump_buffer, _csi_error (CSI_STATUS_INVALID_SCRIPT));
	default:
	    base64_add (ctx, scan, c);
	    break;
	}
    }
    longjmp (scan->jump_buffer, _csi_error (CSI_STATUS_INVALID_SCRIPT));
}
static csi_status_t
_scan_push (csi_t *ctx, csi_object_t *obj)
{
    if (DEBUG_SCAN) {
	fprintf (stderr, "push ");
	fprintf_obj (stderr, ctx, obj);
    }
    return _csi_push_ostack (ctx, obj);
}
static csi_status_t
_scan_execute (csi_t *ctx, csi_object_t *obj)
{
    if (DEBUG_SCAN) {
	fprintf (stderr, "exec ");
	fprintf_obj (stderr, ctx, obj);
    }
    return csi_object_execute (ctx, obj);
}
csi_status_t
_csi_scanner_init (csi_t *ctx, csi_scanner_t *scanner)
{
    csi_status_t status;
    memset (scanner, 0, sizeof (csi_scanner_t));
    status = buffer_init (ctx, &scanner->buffer);
    if (status)
	return status;
    status = _csi_stack_init (ctx, &scanner->procedure_stack, 4);
    if (status)
	return status;
    scanner->bind = 0;
    scanner->push = _scan_push;
    scanner->execute = _scan_execute;
    return CSI_STATUS_SUCCESS;
}
void
_csi_scanner_fini (csi_t *ctx, csi_scanner_t *scanner)
{
    buffer_fini (ctx, &scanner->buffer);
    _csi_stack_fini (ctx, &scanner->procedure_stack);
    if (scanner->build_procedure.type != CSI_OBJECT_TYPE_NULL)
	csi_object_free (ctx, &scanner->build_procedure);
}
csi_status_t
_csi_scan_file (csi_t *ctx, csi_file_t *src)
{
    csi_status_t status;
    int old_line_number;
    /* This function needs to be reentrant to handle recursive scanners.
     * i.e. one script executes a second.
     */
    if (ctx->scanner.depth++ == 0) {
	if ((status = setjmp (ctx->scanner.jump_buffer))) {
	    ctx->scanner.depth = 0;
	    return status;
	}
    }
    old_line_number = ctx->scanner.line_number;
    ctx->scanner.line_number = 0;
    _scan_file (ctx, src);
    ctx->scanner.line_number = old_line_number;
    --ctx->scanner.depth;
    return CSI_STATUS_SUCCESS;
}
struct _translate_closure {
    csi_dictionary_t *opcodes;
    cairo_write_func_t write_func;
    void *closure;
};
static csi_status_t
_translate_name (csi_t *ctx,
	         csi_name_t name,
		 csi_boolean_t executable,
		 struct _translate_closure *closure)
{
    if (executable) {
	csi_dictionary_entry_t *entry;
	uint16_t u16;
	/* Bind executable names.
	 * XXX This may break some scripts that overload system operators.
	 */
	entry = _csi_hash_table_lookup (&closure->opcodes->hash_table,
		                        (csi_hash_entry_t *) &name);
	if (entry == NULL)
	    goto STRING;
	u16 = entry->value.datum.integer;
	u16 = be16 (u16);
	closure->write_func (closure->closure, (unsigned char *) &u16, 2);
    } else {
	closure->write_func (closure->closure, (unsigned char *) "/", 1);
STRING:
	closure->write_func (closure->closure,
		             (unsigned char *) name,
			     strlen ((char *) name));
	closure->write_func (closure->closure, (unsigned char *) "\n", 1);
    }
    return CSI_STATUS_SUCCESS;
}
static csi_status_t
_translate_operator (csi_t *ctx,
	             csi_operator_t op,
		     csi_boolean_t executable,
		     struct _translate_closure *closure)
{
    csi_dictionary_entry_t *entry;
    uint16_t u16;
    entry = _csi_hash_table_lookup (&closure->opcodes->hash_table,
	                            (csi_hash_entry_t *) &op);
    if (entry == NULL)
	return _csi_error (CSI_STATUS_INVALID_SCRIPT);
    u16 = entry->value.datum.integer;
    if (! executable)
	u16 += 1 << 8;
    u16 = be16 (u16);
    closure->write_func (closure->closure, (unsigned char *) &u16, 2);
    return CSI_STATUS_SUCCESS;
}
static csi_status_t
_translate_integer (csi_t *ctx,
	            csi_integer_t i,
		    struct _translate_closure *closure)
{
    uint8_t hdr;
    union {
	int8_t i8;
	uint8_t u8;
	int16_t i16;
	uint16_t u16;
	int32_t i32;
	uint32_t u32;
    } u;
    int len;
#if WORDS_BIGENDIAN
    if (i < INT16_MIN) {
	hdr = MSB_INT32;
	len = 4;
	u.i32 = i;
    } else if (i < INT8_MIN) {
	hdr = MSB_INT16;
	len = 2;
	u.i16 = i;
    } else if (i < 0) {
	hdr = MSB_INT8;
	len = 1;
	u.i8 = i;
    } else if (i <= UINT8_MAX) {
	hdr = MSB_UINT8;
	len = 1;
	u.u8 = i;
    } else if (i <= UINT16_MAX) {
	hdr = MSB_UINT16;
	len = 2;
	u.u16 = i;
    } else {
	hdr = MSB_INT32;
	len = 4;
	u.u32 = i;
    }
#else
    if (i < INT16_MIN) {
	hdr = LSB_INT32;
	len = 4;
	u.i32 = i;
    } else if (i < INT8_MIN) {
	hdr = LSB_INT16;
	len = 2;
	u.i16 = i;
    } else if (i < 0) {
	hdr = LSB_INT8;
	len = 1;
	u.i8 = i;
    } else if (i <= UINT8_MAX) {
	hdr = LSB_UINT8;
	len = 1;
	u.u8 = i;
    } else if (i <= UINT16_MAX) {
	hdr = LSB_UINT16;
	len = 2;
	u.u16 = i;
    } else {
	hdr = LSB_INT32;
	len = 4;
	u.u32 = i;
    }
#endif
    closure->write_func (closure->closure, (unsigned char *) &hdr, 1);
    closure->write_func (closure->closure, (unsigned char *) &u, len);
    return CSI_STATUS_SUCCESS;
}
static csi_status_t
_translate_real (csi_t *ctx,
	         csi_real_t real,
		 struct _translate_closure *closure)
{
    uint8_t hdr;
    if ((double)real >= INT32_MIN && (double)real <= INT32_MAX && (int) real == real)
	return _translate_integer (ctx, real, closure);
#if WORDS_BIGENDIAN
    hdr = MSB_FLOAT32;
#else
    hdr = LSB_FLOAT32;
#endif
    closure->write_func (closure->closure, (unsigned char *) &hdr, 1);
    closure->write_func (closure->closure, (unsigned char *) &real, 4);
    return CSI_STATUS_SUCCESS;
}
static csi_status_t
_translate_string (csi_t *ctx,
	           csi_string_t *string,
		   struct _translate_closure *closure)
{
    uint8_t hdr;
    union {
	uint8_t u8;
	uint16_t u16;
	uint32_t u32;
    } u;
    void *buf;
    unsigned long hdr_len, buf_len, deflate;
    int method;
    buf = string->string;
    buf_len = string->len;
    deflate = string->deflate;
    method = string->method;
#if HAVE_LZO
    if (method == NONE && buf_len > 16) {
	unsigned long mem_len = 2*string->len > LZO2A_999_MEM_COMPRESS ? 2*string->len : LZO2A_999_MEM_COMPRESS;
	void *mem = malloc (mem_len);
	void *work = malloc(LZO2A_999_MEM_COMPRESS);
	if (lzo2a_999_compress ((lzo_bytep) buf, buf_len,
				(lzo_bytep) mem, &mem_len,
				work) == 0 &&
	    8+2*mem_len < buf_len)
	{
	    method = LZO;
	    deflate = buf_len;
	    buf_len = mem_len;
	    buf = mem;
	}
	else
	{
	    free (mem);
	}
	free (work);
    }
#if HAVE_ZLIB
    if (method == ZLIB) {
	buf_len = string->deflate;
	buf = malloc (string->deflate);
	if (uncompress ((Bytef *) buf, &buf_len,
			(Bytef *) string->string, string->len) == Z_OK)
	{
	    assert(string->len > 0);
	    if (buf_len <= 8 + 2*((unsigned long)string->len)) {
		method = NONE;
		deflate = 0;
	    } else {
		unsigned long mem_len = 2*string->deflate;
		void *mem = malloc (mem_len);
		void *work = malloc(LZO2A_999_MEM_COMPRESS);
		if (lzo2a_999_compress ((lzo_bytep) buf, buf_len,
					(lzo_bytep) mem, &mem_len,
					work) == 0)
		{
		    if (8 + mem_len > buf_len) {
			method = NONE;
			deflate = 0;
		    } else {
			free (buf);
			method = LZO;
			deflate = buf_len;
			buf_len = mem_len;
			buf = mem;
			assert(deflate);
		    }
		}
		else
		{
		    free (buf);
		    buf = string->string;
		    buf_len = string->len;
		}
		free (work);
	    }
	}
	else
	{
	    free (buf);
	    buf = string->string;
	    buf_len = string->len;
	}
    }
#endif
#endif
    if (method == LZO) {
	hdr = STRING_LZO;
	u.u32 = to_be32 (buf_len);
	hdr_len = 4;
    } else {
#if WORDS_BIGENDIAN
	if (buf_len <= UINT8_MAX) {
	    hdr = STRING_1;
	    u.u8 = buf_len;
	    hdr_len = 1;
	} else if (buf_len <= UINT16_MAX) {
	    hdr = STRING_2_MSB;
	    u.u16 = buf_len;
	    hdr_len = 2;
	} else {
	    hdr = STRING_4_MSB;
	    u.u32 = buf_len;
	    hdr_len = 4;
	}
#else
	if (buf_len <= UINT8_MAX) {
	    hdr = STRING_1;
	    u.u8 = buf_len;
	    hdr_len = 1;
	} else if (buf_len <= UINT16_MAX) {
	    hdr = STRING_2_LSB;
	    u.u16 = buf_len;
	    hdr_len = 2;
	} else {
	    hdr = STRING_4_LSB;
	    u.u32 = buf_len;
	    hdr_len = 4;
	}
#endif
	if (deflate) {
	    assert (method == ZLIB);
	    hdr |= STRING_DEFLATE;
	}
    }
    closure->write_func (closure->closure, (unsigned char *) &hdr, 1);
    closure->write_func (closure->closure, (unsigned char *) &u, hdr_len);
    if (deflate) {
	uint32_t u32 = to_be32 (deflate);
	closure->write_func (closure->closure, (unsigned char *) &u32, 4);
    }
    closure->write_func (closure->closure, (unsigned char *) buf, buf_len);
    if (buf != string->string)
	free (buf);
    return CSI_STATUS_SUCCESS;
}
static csi_status_t
_translate_push (csi_t *ctx, csi_object_t *obj)
{
    struct _translate_closure *closure = ctx->scanner.closure;
    if (0) {
	fprintf (stderr, "push ");
	fprintf_obj (stderr, ctx, obj);
    }
    switch (csi_object_get_type (obj)) {
    case CSI_OBJECT_TYPE_NAME:
	return _translate_name (ctx, obj->datum.name, FALSE, closure);
    case CSI_OBJECT_TYPE_OPERATOR:
	return _translate_operator (ctx, obj->datum.op, FALSE, closure);
    case CSI_OBJECT_TYPE_INTEGER:
	return _translate_integer (ctx, obj->datum.integer, closure);
    case CSI_OBJECT_TYPE_REAL:
	return _translate_real (ctx, obj->datum.real, closure);
    case CSI_OBJECT_TYPE_STRING:
	return _translate_string (ctx, obj->datum.string, closure);
    case CSI_OBJECT_TYPE_NULL:
    case CSI_OBJECT_TYPE_BOOLEAN:
    case CSI_OBJECT_TYPE_MARK:
    case CSI_OBJECT_TYPE_ARRAY:
    case CSI_OBJECT_TYPE_DICTIONARY:
    case CSI_OBJECT_TYPE_FILE:
    case CSI_OBJECT_TYPE_MATRIX:
    case CSI_OBJECT_TYPE_CONTEXT:
    case CSI_OBJECT_TYPE_FONT:
    case CSI_OBJECT_TYPE_PATTERN:
    case CSI_OBJECT_TYPE_SCALED_FONT:
    case CSI_OBJECT_TYPE_SURFACE:
	longjmp (ctx->scanner.jump_buffer,  _csi_error (CSI_STATUS_INVALID_SCRIPT));
	break;
    }
    csi_object_free (ctx, obj);
    return CSI_STATUS_SUCCESS;
}
static csi_status_t
_translate_execute (csi_t *ctx, csi_object_t *obj)
{
    struct _translate_closure *closure = ctx->scanner.closure;
    if (0) {
	fprintf (stderr, "exec ");
	fprintf_obj (stderr, ctx, obj);
    }
    switch (csi_object_get_type (obj)) {
    case CSI_OBJECT_TYPE_NAME:
	return _translate_name (ctx, obj->datum.name, TRUE, closure);
    case CSI_OBJECT_TYPE_OPERATOR:
	return _translate_operator (ctx, obj->datum.op, TRUE, closure);
    case CSI_OBJECT_TYPE_INTEGER:
	return _translate_integer (ctx, obj->datum.integer, closure);
    case CSI_OBJECT_TYPE_REAL:
	return _translate_real (ctx, obj->datum.real, closure);
    case CSI_OBJECT_TYPE_STRING:
	return _translate_string (ctx, obj->datum.string, closure);
    case CSI_OBJECT_TYPE_NULL:
    case CSI_OBJECT_TYPE_BOOLEAN:
    case CSI_OBJECT_TYPE_MARK:
    case CSI_OBJECT_TYPE_ARRAY:
    case CSI_OBJECT_TYPE_DICTIONARY:
    case CSI_OBJECT_TYPE_FILE:
    case CSI_OBJECT_TYPE_MATRIX:
    case CSI_OBJECT_TYPE_CONTEXT:
    case CSI_OBJECT_TYPE_FONT:
    case CSI_OBJECT_TYPE_PATTERN:
    case CSI_OBJECT_TYPE_SCALED_FONT:
    case CSI_OBJECT_TYPE_SURFACE:
	longjmp (ctx->scanner.jump_buffer,  _csi_error (CSI_STATUS_INVALID_SCRIPT));
	break;
    }
    return CSI_STATUS_SUCCESS;
}
static csi_status_t
build_opcodes (csi_t *ctx, csi_dictionary_t **out)
{
    csi_object_t obj;
    csi_dictionary_t *dict;
    const csi_operator_def_t *def;
    csi_status_t status;
    int opcode = OPCODE << 8;
    status = csi_dictionary_new (ctx, &obj);
    if (_csi_unlikely (status))
	return status;
    dict = obj.datum.dictionary;
    csi_integer_new (&obj, opcode++);
    status = csi_dictionary_put (ctx, dict, 0, &obj);
    if (_csi_unlikely (status))
	goto FAIL;
    for (def = _csi_operators (); def->name != NULL; def++) {
	csi_object_t name;
	csi_dictionary_entry_t *entry;
	int code;
	entry = _csi_hash_table_lookup (&dict->hash_table,
		                        (csi_hash_entry_t *) &def->op);
	if (entry == NULL) {
	    code = opcode++;
	    csi_integer_new (&obj, code);
	    status = csi_dictionary_put (ctx, dict, (csi_name_t) def->op, &obj);
	    if (_csi_unlikely (status))
		goto FAIL;
	} else {
	    code = entry->value.datum.integer;
	    csi_integer_new (&obj, code);
	}
	assert (ctx->opcode[code & 0xff] == def->op);
	status = csi_name_new_static (ctx, &name, def->name);
	if (_csi_unlikely (status))
	    goto FAIL;
	status = csi_dictionary_put (ctx, dict, name.datum.name, &obj);
	if (_csi_unlikely (status))
	    goto FAIL;
    }
    *out = dict;
    return CSI_STATUS_SUCCESS;
FAIL:
    csi_dictionary_free (ctx, dict);
    return status;
}
csi_status_t
_csi_translate_file (csi_t *ctx,
	             csi_file_t *file,
		     cairo_write_func_t write_func,
		     void *closure)
{
    csi_status_t status;
    struct _translate_closure translator;
    if ((status = setjmp (ctx->scanner.jump_buffer)))
	return status;
    status = build_opcodes (ctx, &translator.opcodes);
    if (_csi_unlikely (status))
	return status;
    translator.write_func = write_func;
    translator.closure = closure;
    ctx->scanner.closure = &translator;
    ctx->scanner.bind = 1;
    ctx->scanner.push = _translate_push;
    ctx->scanner.execute = _translate_execute;
    _scan_file (ctx, file);
    ctx->scanner.bind = 0;
    ctx->scanner.push = _scan_push;
    ctx->scanner.execute = _scan_execute;
    csi_dictionary_free (ctx, translator.opcodes);
    return CSI_STATUS_SUCCESS;
}