tizen 2.3.1 release
[framework/graphics/cairo.git] / util / cairo-script / cairo-script-file.c
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 "cairo-script-private.h"
36
37 #include <stdio.h>
38 #include <limits.h> /* INT_MAX */
39 #include <string.h>
40 #include <zlib.h>
41
42 #if HAVE_LZO
43 #include <lzo/lzo2a.h>
44 #endif
45
46 #define CHUNK_SIZE 32768
47
48 #define OWN_STREAM 0x1
49
50 csi_status_t
51 csi_file_new (csi_t *ctx,
52               csi_object_t *obj,
53               const char *path, const char *mode)
54 {
55     csi_file_t *file;
56
57     file = _csi_slab_alloc (ctx, sizeof (csi_file_t));
58     if (file == NULL)
59         return _csi_error (CAIRO_STATUS_NO_MEMORY);
60
61     file->base.type = CSI_OBJECT_TYPE_FILE;
62     file->base.ref = 1;
63
64     file->data = NULL;
65     file->type = STDIO;
66     file->flags = OWN_STREAM;
67     file->src = fopen (path, mode);
68     if (file->src == NULL) {
69         _csi_slab_free (ctx, file, sizeof (csi_file_t));
70         return _csi_error (CAIRO_STATUS_FILE_NOT_FOUND);
71     }
72
73     file->data = _csi_alloc (ctx, CHUNK_SIZE);
74     if (file->data == NULL) {
75         _csi_slab_free (ctx, file, sizeof (csi_file_t));
76         return _csi_error (CAIRO_STATUS_NO_MEMORY);
77     }
78     file->bp = file->data;
79     file->rem = 0;
80
81     obj->type = CSI_OBJECT_TYPE_FILE;
82     obj->datum.file = file;
83     return CAIRO_STATUS_SUCCESS;
84 }
85
86 csi_status_t
87 csi_file_new_for_stream (csi_t *ctx,
88                          csi_object_t *obj,
89                          FILE *stream)
90 {
91     csi_file_t *file;
92
93     file = _csi_slab_alloc (ctx, sizeof (csi_file_t));
94     if (file == NULL)
95         return _csi_error (CAIRO_STATUS_NO_MEMORY);
96
97     file->base.type = CSI_OBJECT_TYPE_FILE;
98     file->base.ref = 1;
99
100     file->data = NULL;
101     file->type = STDIO;
102     file->flags = 0;
103     file->src = stream;
104     if (file->src == NULL) {
105         _csi_slab_free (ctx, file, sizeof (csi_file_t));
106         return _csi_error (CAIRO_STATUS_FILE_NOT_FOUND);
107     }
108
109     file->data = _csi_alloc (ctx, CHUNK_SIZE);
110     if (file->data == NULL) {
111         _csi_slab_free (ctx, file, sizeof (csi_file_t));
112         return _csi_error (CAIRO_STATUS_NO_MEMORY);
113     }
114     file->bp = file->data;
115     file->rem = 0;
116
117     obj->type = CSI_OBJECT_TYPE_FILE;
118     obj->datum.file = file;
119     return CAIRO_STATUS_SUCCESS;
120 }
121
122 csi_status_t
123 csi_file_new_for_bytes (csi_t *ctx,
124                         csi_object_t *obj,
125                         const char *bytes,
126                         unsigned int length)
127 {
128     csi_file_t *file;
129
130     file = _csi_slab_alloc (ctx, sizeof (csi_file_t));
131     if (file == NULL)
132         return _csi_error (CAIRO_STATUS_NO_MEMORY);
133
134     file->base.type = CSI_OBJECT_TYPE_FILE;
135     file->base.ref = 1;
136
137     file->type = BYTES;
138     file->src  = (uint8_t *) bytes;
139     file->data = (uint8_t *) bytes;
140     file->bp   = (uint8_t *) bytes;
141     file->rem  = length;
142
143     obj->type = CSI_OBJECT_TYPE_FILE;
144     obj->datum.file = file;
145     return CAIRO_STATUS_SUCCESS;
146 }
147
148 csi_status_t
149 csi_file_new_from_string (csi_t *ctx,
150                           csi_object_t *obj,
151                           csi_string_t *src)
152 {
153     csi_file_t *file;
154
155     file = _csi_slab_alloc (ctx, sizeof (csi_file_t));
156     if (_csi_unlikely (file == NULL))
157         return _csi_error (CAIRO_STATUS_NO_MEMORY);
158
159     file->base.type = CSI_OBJECT_TYPE_FILE;
160     file->base.ref = 1;
161
162     if (src->deflate) {
163         uLongf len = src->deflate;
164         csi_object_t tmp_obj;
165         csi_string_t *tmp_str;
166         csi_status_t status;
167
168         status = csi_string_new (ctx, &tmp_obj,  NULL, src->deflate);
169         if (_csi_unlikely (status))
170             return status;
171
172         tmp_str = tmp_obj.datum.string;
173         switch (src->method) {
174         case NONE:
175         default:
176             status = _csi_error (CAIRO_STATUS_NO_MEMORY);
177             break;
178
179 #if HAVE_ZLIB
180         case ZLIB:
181             if (uncompress ((Bytef *) tmp_str->string, &len,
182                             (Bytef *) src->string, src->len) != Z_OK)
183                 status = _csi_error (CAIRO_STATUS_NO_MEMORY);
184             break;
185 #endif
186 #if HAVE_LZO
187         case LZO:
188             if (lzo2a_decompress ((lzo_bytep) src->string, src->len,
189                                   (lzo_bytep) tmp_str->string, &len,
190                                   NULL))
191                 status = _csi_error (CAIRO_STATUS_NO_MEMORY);
192             break;
193 #endif
194         }
195         if (_csi_unlikely (status)) {
196             csi_string_free (ctx, tmp_str);
197             _csi_slab_free (ctx, file, sizeof (csi_file_t));
198             return status;
199         }
200
201         file->src  = tmp_str;
202         file->data = tmp_str->string;
203         file->rem  = tmp_str->len;
204     } else {
205         file->src  = src; src->base.ref++;
206         file->data = src->string;
207         file->rem  = src->len;
208     }
209     file->type = BYTES;
210     file->bp   = file->data;
211
212     obj->type = CSI_OBJECT_TYPE_FILE;
213     obj->datum.file = file;
214     return CAIRO_STATUS_SUCCESS;
215 }
216
217 static csi_status_t
218 _csi_file_new_filter (csi_t *ctx,
219                       csi_object_t *obj,
220                       csi_object_t *src,
221                       const csi_filter_funcs_t *funcs,
222                       void *data)
223 {
224     csi_file_t *file;
225     csi_object_t src_file;
226     csi_status_t status;
227
228     file = _csi_slab_alloc (ctx, sizeof (csi_file_t));
229     if (file == NULL)
230         return _csi_error (CAIRO_STATUS_NO_MEMORY);
231
232     obj->type = CSI_OBJECT_TYPE_FILE;
233     obj->datum.file = file;
234
235     file->base.type = CSI_OBJECT_TYPE_FILE;
236     file->base.ref = 1;
237
238     file->type = FILTER;
239     file->data = data;
240     file->filter = funcs;
241     status = csi_object_as_file (ctx, src, &src_file);
242     if (status) {
243         csi_object_free (ctx, obj);
244         return status;
245     }
246     file->src = src_file.datum.file;
247
248     return CAIRO_STATUS_SUCCESS;
249 }
250
251
252 #if 0
253 csi_status_t
254 csi_file_new_from_stream (csi_t *ctx,
255                           FILE *file,
256                           csi_object_t **out)
257 {
258     csi_file_t *obj;
259
260     obj = (csi_file_t *) _csi_object_new (ctx, CSI_OBJECT_TYPE_FILE);
261     if (obj == NULL)
262         return _csi_error (CAIRO_STATUS_NO_MEMORY);
263
264     obj->type = STDIO;
265     obj->src = file;
266     obj->data = _csi_alloc (ctx, CHUNK_SIZE);
267     if (obj->data == NULL) {
268         csi_object_free (&obj->base);
269         return _csi_error (CAIRO_STATUS_UNDEFINED_FILENAME_ERROR);
270     }
271     obj->bp = obj->data;
272     obj->rem = 0;
273
274     *out = &obj->base;
275     return CAIRO_STATUS_SUCCESS;
276 }
277
278 static csi_object_t *
279 _csi_file_new_from_procedure (csi_t *ctx, csi_object_t *src)
280 {
281     csi_file_t *obj;
282
283     obj = (csi_file_t *) _csi_object_new (ctx, CSI_OBJECT_TYPE_FILE);
284     if (obj == NULL)
285         return NULL;
286
287     obj->type = PROCEDURE;
288     obj->src = csi_object_reference (src);
289     obj->data = NULL;
290
291     return &obj->base;
292 }
293 #endif
294
295 typedef struct _ascii85_decode_data {
296     uint8_t buf[CHUNK_SIZE];
297     uint8_t *bp;
298     short bytes_available;
299     short eod;
300 } _ascii85_decode_data_t;
301
302 static int
303 _getc_skip_whitespace (csi_file_t *src)
304 {
305     int c;
306
307     do switch ((c = csi_file_getc (src))) {
308     case 0x0:
309     case 0x9:
310     case 0xa:
311     case 0xc:
312     case 0xd:
313     case 0x20:
314         continue;
315
316     default:
317         return c;
318     } while (TRUE);
319
320     return c;
321 }
322
323 static void
324 _ascii85_decode (csi_file_t *file)
325 {
326     _ascii85_decode_data_t *data = file->data;
327     unsigned int n;
328
329     if (data->eod)
330         return;
331
332     data->bp = data->buf;
333
334     n = 0;
335     do {
336         unsigned int v = _getc_skip_whitespace (file->src);
337         if (v == 'z') {
338             data->buf[n+0] = 0;
339             data->buf[n+1] = 0;
340             data->buf[n+2] = 0;
341             data->buf[n+3] = 0;
342         } else if (v == '~') {
343             _getc_skip_whitespace (file->src); /* == '>' || IO_ERROR */
344             data->eod = TRUE;
345             break;
346         } else if (v < '!' || v > 'u') {
347             /* IO_ERROR */
348             data->eod = TRUE;
349             break;
350         } else {
351             unsigned int i;
352
353             v -= '!';
354             for (i = 1; i < 5; i++) {
355                 int c = _getc_skip_whitespace (file->src);
356                 if (c == '~') { /* short tuple */
357                     _getc_skip_whitespace (file->src); /* == '>' || IO_ERROR */
358                     data->eod = TRUE;
359                     switch (i) {
360                     case 0:
361                     case 1:
362                         /* IO_ERROR */
363                         break;
364                     case 2:
365                         v = v * (85*85*85) + 85*85*85 -1;
366                         goto odd1;
367                     case 3:
368                         v = v * (85*85) + 85*85 -1;
369                         goto odd2;
370                     case 4:
371                         v = v * 85 + 84;
372                         data->buf[n+2] = v >> 8 & 0xff;
373 odd2:
374                         data->buf[n+1] = v >> 16 & 0xff;
375 odd1:
376                         data->buf[n+0] = v >> 24 & 0xff;
377                         data->bytes_available = n + i - 1;
378                         return;
379                     }
380                     break;
381                 }
382                 v = 85*v + c-'!';
383             }
384
385             data->buf[n+0] = v >> 24 & 0xff;
386             data->buf[n+1] = v >> 16 & 0xff;
387             data->buf[n+2] = v >> 8 & 0xff;
388             data->buf[n+3] = v >> 0 & 0xff;
389         }
390         n += 4;
391     } while (n < sizeof (data->buf) && data->eod == FALSE);
392
393     data->bytes_available = n;
394 }
395
396 static int
397 _ascii85_decode_getc (csi_file_t *file)
398 {
399     _ascii85_decode_data_t *data = file->data;
400
401     if (data->bytes_available == 0) {
402         _ascii85_decode (file);
403
404         if (data->bytes_available == 0)
405             return EOF;
406     }
407
408     data->bytes_available--;
409     return *data->bp++;
410 }
411
412 static void
413 _ascii85_decode_putc (csi_file_t *file, int c)
414 {
415     _ascii85_decode_data_t *data = file->data;
416     data->bytes_available++;
417     data->bp--;
418 }
419
420 static int
421 _ascii85_decode_read (csi_file_t *file, uint8_t *buf, int len)
422 {
423     _ascii85_decode_data_t *data = file->data;
424
425     if (data->bytes_available == 0) {
426         _ascii85_decode (file);
427
428         if (data->bytes_available == 0)
429             return 0;
430     }
431
432     if (len > data->bytes_available)
433         len = data->bytes_available;
434     memcpy (buf, data->bp, len);
435     data->bp += len;
436     data->bytes_available -= len;
437     return len;
438 }
439
440 csi_status_t
441 csi_file_new_ascii85_decode (csi_t *ctx,
442                              csi_object_t *obj,
443                              csi_dictionary_t *dict,
444                              csi_object_t *src)
445 {
446     static const csi_filter_funcs_t funcs = {
447         _ascii85_decode_getc,
448         _ascii85_decode_putc,
449         _ascii85_decode_read,
450         _csi_free,
451     };
452     _ascii85_decode_data_t *data;
453
454     data = _csi_alloc0 (ctx, sizeof (_ascii85_decode_data_t));
455     if (data == NULL)
456         return _csi_error (CAIRO_STATUS_NO_MEMORY);
457
458     return _csi_file_new_filter (ctx, obj, src, &funcs, data);
459 }
460
461 #if HAVE_ZLIB
462 #include <zlib.h>
463
464 typedef struct _deflate_decode_data {
465     z_stream zlib_stream;
466
467     uint8_t in[CHUNK_SIZE];
468     uint8_t out[CHUNK_SIZE];
469
470     int bytes_available;
471     uint8_t *bp;
472 } _deflate_decode_data_t;
473
474 static void
475 _deflate_decode (csi_file_t *file)
476 {
477     _deflate_decode_data_t *data = file->data;
478     uint8_t *bp;
479     int len;
480
481     data->zlib_stream.next_out = data->out;
482     data->zlib_stream.avail_out = sizeof (data->out);
483
484     bp = data->in;
485     len = sizeof (data->in);
486     if (data->zlib_stream.avail_in) {
487         memmove (data->in,
488                  data->zlib_stream.next_in,
489                  data->zlib_stream.avail_in);
490         len -= data->zlib_stream.avail_in;
491         bp += data->zlib_stream.avail_in;
492     }
493
494     len = csi_file_read (file->src, bp, len);
495
496     data->zlib_stream.next_in = data->in;
497     data->zlib_stream.avail_in += len;
498
499     inflate (&data->zlib_stream, len == 0 ? Z_FINISH : Z_NO_FLUSH);
500
501     data->bytes_available = data->zlib_stream.next_out - data->out;
502     data->bp = data->out;
503 }
504
505 static int
506 _deflate_decode_getc (csi_file_t *file)
507 {
508     _deflate_decode_data_t *data = file->data;
509
510     if (data->bytes_available == 0) {
511         _deflate_decode (file);
512
513         if (data->bytes_available == 0)
514             return EOF;
515     }
516
517     data->bytes_available--;
518     return *data->bp++;
519 }
520
521 static void
522 _deflate_decode_putc (csi_file_t *file, int c)
523 {
524     _deflate_decode_data_t *data = file->data;
525     data->bytes_available++;
526     data->bp--;
527 }
528
529 static int
530 _deflate_decode_read (csi_file_t *file, uint8_t *buf, int len)
531 {
532     _deflate_decode_data_t *data = file->data;
533
534     if (data->bytes_available == 0) {
535         _deflate_decode (file);
536
537         if (data->bytes_available == 0)
538             return 0;
539     }
540
541     if (len > (int) data->bytes_available)
542         len = data->bytes_available;
543     memcpy (buf, data->bp, len);
544     data->bp += len;
545     data->bytes_available -= len;
546     return len;
547 }
548
549 static void
550 _deflate_destroy (csi_t *ctx, void *closure)
551 {
552     _deflate_decode_data_t *data;
553
554     data = closure;
555
556     inflateEnd (&data->zlib_stream);
557
558     _csi_free (ctx, data);
559 }
560
561 csi_status_t
562 csi_file_new_deflate_decode (csi_t *ctx,
563                              csi_object_t *obj,
564                              csi_dictionary_t *dict,
565                              csi_object_t *src)
566 {
567     static const csi_filter_funcs_t funcs = {
568         _deflate_decode_getc,
569         _deflate_decode_putc,
570         _deflate_decode_read,
571         _deflate_destroy,
572     };
573     _deflate_decode_data_t *data;
574
575     data = _csi_alloc (ctx, sizeof (_deflate_decode_data_t));
576     if (data == NULL)
577         return _csi_error (CAIRO_STATUS_NO_MEMORY);
578
579     data->zlib_stream.zalloc = Z_NULL;
580     data->zlib_stream.zfree = Z_NULL;
581     data->zlib_stream.opaque = Z_NULL;
582     data->zlib_stream.next_in = data->in;
583     data->zlib_stream.avail_in = 0;
584     data->zlib_stream.next_out = data->out;
585     data->zlib_stream.avail_out = sizeof (data->out);
586     data->bytes_available = 0;
587
588     if (inflateInit (&data->zlib_stream) != Z_OK) {
589         _csi_free (ctx, data);
590         return _csi_error (CAIRO_STATUS_NO_MEMORY);
591     }
592
593     return _csi_file_new_filter (ctx, obj, src, &funcs, data);
594 }
595 #endif
596
597 #if 0
598 static int
599 hex_value (int c)
600 {
601     if (c < '0')
602         return EOF;
603     if (c <= '9')
604         return c - '0';
605     c |= 32;
606     if (c < 'a')
607         return EOF;
608     if (c <= 'f')
609         return c - 'a' + 0xa;
610     return EOF;
611 }
612
613 /* Adobe Type 1 Font Format book: p63 */
614 typedef struct _decrypt_data {
615     uint8_t putback[32];
616     uint8_t nputback;
617     csi_bool_t is_hexadecimal;
618     unsigned short R;
619     int eod;
620 } _decrypt_data_t;
621
622 static uint8_t
623 _decrypt (unsigned short *R, uint8_t cypher)
624 {
625 #define c1 52845
626 #define c2 22719
627     uint8_t plain;
628
629     plain = cypher ^ (*R >> 8);
630     *R = (cypher + *R) * c1 + c2;
631     return plain;
632 #undef c1
633 #undef c2
634 }
635
636 int
637 csi_decrypt (uint8_t *in, int length,
638              unsigned short salt, int binary,
639              uint8_t *out)
640 {
641     const uint8_t * const end = in + length;
642     uint8_t *base = out;
643
644     while (in < end) {
645         int c;
646
647         if (! binary) {
648             int c_hi = -1, c_lo = 0;
649
650             while (in < end && (c_hi = *in++)) {
651                 switch (c_hi) {
652                 case 0x0:
653                 case 0x9:
654                 case 0xa:
655                 case 0xc:
656                 case 0xd:
657                 case 0x20:
658                     continue;
659
660                 default:
661                     break;
662                 }
663             }
664             if (c_hi < 0)
665                 break;
666
667             while (in < end && (c_lo = *in++)) {
668                 switch (c_lo) {
669                 case 0x0:
670                 case 0x9:
671                 case 0xa:
672                 case 0xc:
673                 case 0xd:
674                 case 0x20:
675                     continue;
676
677                 default:
678                     break;
679                 }
680             }
681
682             c = (hex_value (c_hi) << 4) | hex_value (c_lo);
683         } else
684             c = *in++;
685
686         *out++ = _decrypt (&salt, c);
687     }
688
689     return out - base;
690 }
691
692 static uint8_t
693 _encrypt (unsigned short *R, uint8_t plain)
694 {
695 #define c1 52845
696 #define c2 22719
697     uint8_t cypher;
698
699     cypher = plain ^ (*R >> 8);
700     *R = (cypher + *R) * c1 + c2;
701     return cypher;
702 #undef c1
703 #undef c2
704 }
705
706 int
707 csi_encrypt (uint8_t *in, int length,
708              unsigned short salt, int discard, int binary,
709              uint8_t *out)
710 {
711     const char hex[]="0123456789abcdef";
712     const uint8_t * const end = in + length;
713     uint8_t *base = out;
714     int col = 0;
715
716     while (discard--) {
717         if (! binary) {
718             int c = _encrypt (&salt, ' ');
719             *out++ = hex[(c >> 4) & 0xf];
720             *out++ = hex[(c >> 0) & 0xf];
721         } else
722             *out++ = _encrypt (&salt, 0);
723     }
724
725     while (in < end) {
726         int c;
727
728         c = _encrypt (&salt, *in++);
729         if (! binary) {
730             if (col == 78) {
731                 *out++ = '\n';
732                 col = 0;
733             }
734             *out++ = hex[(c >> 4) & 0xf];
735             *out++ = hex[(c >> 0) & 0xf];
736             col += 2;
737         } else
738             *out++ = c;
739     }
740
741     return out - base;
742 }
743
744 static int
745 _decrypt_getc (csi_file_t *file)
746 {
747     _decrypt_data_t *data = file->data;
748     int c;
749
750     if (data->nputback)
751         return data->putback[--data->nputback];
752
753     if (data->is_hexadecimal) {
754         int c_hi, c_lo;
755
756         c_hi = _getc_skip_whitespace (file->src);
757         c_lo = _getc_skip_whitespace (file->src);
758         c = (hex_value (c_hi) << 4) | hex_value (c_lo);
759     } else
760         c = csi_file_getc (file->src);
761
762     if (c == EOF)
763         return EOF;
764
765     return _decrypt (&data->R, c);
766 }
767
768 static void
769 _decrypt_putc (csi_file_t *file, int c)
770 {
771     _decrypt_data_t *data;
772
773     data = file->data;
774
775     data->putback[data->nputback++] = c;
776 }
777
778 csi_object_t *
779 csi_file_new_decrypt (csi_t *ctx, csi_object_t *src, int salt, int discard)
780 {
781     csi_object_t *obj;
782     _decrypt_data_t *data;
783     int n;
784
785     data = _csi_alloc0 (ctx, sizeof (_decrypt_data_t));
786     if (data == NULL)
787         return NULL;
788
789     data->R = salt;
790
791     obj = _csi_file_new_filter (ctx, src,
792                                 _decrypt_getc,
793                                 _decrypt_putc,
794                                 NULL,
795                                 _csi_free,
796                                 data);
797     if (obj == NULL)
798         return NULL;
799
800     /* XXX determine encoding, eexec only? */
801     data->is_hexadecimal = salt != 4330;
802     for (n = 0; n < discard; n++) {
803         int c;
804         c = csi_file_getc (obj);
805         if (c == EOF) {
806             return obj;
807         }
808     }
809     return obj;
810 }
811 #endif
812
813 csi_status_t
814 _csi_file_execute (csi_t *ctx, csi_file_t *obj)
815 {
816     return _csi_scan_file (ctx, obj);
817 }
818
819 int
820 csi_file_getc (csi_file_t *file)
821 {
822     int c;
823
824     if (_csi_unlikely (file->src == NULL))
825         return EOF;
826
827     switch (file->type) {
828     case STDIO:
829         if (_csi_likely (file->rem)) {
830             c = *file->bp++;
831             file->rem--;
832         } else {
833             file->rem = fread (file->bp = file->data, 1, CHUNK_SIZE, file->src);
834     case BYTES:
835             if (_csi_likely (file->rem)) {
836                 c = *file->bp++;
837                 file->rem--;
838             } else
839                 c = EOF;
840         }
841         break;
842
843     case PROCEDURE:
844 #if 0
845         if (file->data == NULL) {
846             csi_status_t status;
847             csi_object_t *string;
848
849 RERUN_PROCEDURE:
850             status = csi_object_execute (file->src);
851             if (status)
852                 return EOF;
853
854             string = csi_pop_operand (file->base.ctx);
855             if (string == NULL)
856                 return EOF;
857             file->data = csi_object_as_file (file->base.ctx, string);
858             csi_object_free (string);
859             if (file->data == NULL)
860                 return EOF;
861         }
862         c = csi_file_getc (file->data);
863         if (c == EOF) {
864             csi_object_free (file->data);
865             file->data = NULL;
866             goto RERUN_PROCEDURE;
867         }
868 #else
869         c = EOF;
870 #endif
871         break;
872
873     case FILTER:
874         c = file->filter->filter_getc (file);
875         break;
876
877     default:
878         c = EOF;
879         break;
880     }
881
882     return c;
883 }
884
885 int
886 csi_file_read (csi_file_t *file, void *buf, int len)
887 {
888     int ret;
889
890     if (file->src == NULL)
891         return 0;
892
893     switch (file->type) {
894     case STDIO:
895         if (file->rem > 0) {
896             ret = len;
897             if (file->rem < ret)
898                 ret = file->rem;
899             memcpy (buf, file->bp, ret);
900             file->bp  += ret;
901             file->rem -= ret;
902         } else
903             ret = fread (buf, 1, len, file->src);
904         break;
905
906     case BYTES:
907         if (file->rem > 0) {
908             ret = len;
909             if (file->rem < ret)
910                 ret = file->rem;
911             memcpy (buf, file->bp, ret);
912             file->bp  += ret;
913             file->rem -= ret;
914         } else
915             ret = 0;
916         break;
917
918     case PROCEDURE:
919 #if 0
920         if (file->data == NULL) {
921             csi_status_t status;
922             csi_object_t *string;
923
924 RERUN_PROCEDURE:
925             status = csi_object_execute (file->src);
926             if (status)
927                 return 0;
928
929             string = csi_pop_operand (file->base.ctx);
930             if (string == NULL)
931                 return 0;
932             file->data = csi_object_as_file (file->base.ctx, string);
933             csi_object_free (string);
934             if (file->data == NULL)
935                 return 0;
936         }
937         ret = csi_file_read (file->data, buf, len);
938         if (ret == 0) {
939             csi_object_free (file->data);
940             file->data = NULL;
941             goto RERUN_PROCEDURE;
942         }
943 #else
944         ret = 0;
945 #endif
946         break;
947
948     case FILTER:
949         ret = file->filter->filter_read (file, buf, len);
950         break;
951
952     default:
953         ret = 0;
954         break;
955     }
956
957     return ret;
958 }
959
960 void
961 csi_file_putc (csi_file_t *file, int c)
962 {
963     if (file->src == NULL)
964         return;
965
966     switch ((int) file->type) {
967     case STDIO:
968     case BYTES:
969         file->bp--;
970         file->rem++;
971         break;
972     case FILTER:
973         file->filter->filter_putc (file, c);
974         break;
975     default:
976         break;
977     }
978 }
979
980 void
981 csi_file_flush (csi_file_t *file)
982 {
983     if (file->src == NULL)
984         return;
985
986     switch ((int) file->type) {
987     case FILTER: /* need to eat EOD */
988         while (csi_file_getc (file) != EOF)
989             ;
990         break;
991     default:
992         break;
993     }
994 }
995
996 void
997 csi_file_close (csi_t *ctx, csi_file_t *file)
998 {
999     if (file->src == NULL)
1000         return;
1001
1002     switch (file->type) {
1003     case STDIO:
1004         if (file->flags & OWN_STREAM)
1005             fclose (file->src);
1006         break;
1007     case BYTES:
1008         if (file->src != file->data) {
1009             csi_string_t *src = file->src;
1010             if (src != NULL && --src->base.ref == 0)
1011                 csi_string_free (ctx, src);
1012         }
1013         break;
1014     case FILTER:
1015         {
1016             csi_file_t *src = file->src;
1017             if (src != NULL && --src->base.ref == 0)
1018                 _csi_file_free (ctx, src);
1019         }
1020         break;
1021     case PROCEDURE:
1022     default:
1023         break;
1024     }
1025     file->src = NULL;
1026 }
1027
1028 void
1029 _csi_file_free (csi_t *ctx, csi_file_t *file)
1030 {
1031     csi_file_flush (file);
1032     /* XXX putback */
1033     csi_file_close (ctx, file);
1034
1035     switch (file->type) {
1036     case BYTES:
1037         break;
1038     case PROCEDURE:
1039 #if 0
1040         csi_object_free (ctx, file->data);
1041 #endif
1042         break;
1043     case STDIO:
1044         _csi_free (ctx, file->data);
1045         break;
1046     case FILTER:
1047         file->filter->filter_destroy (ctx, file->data);
1048         break;
1049     default:
1050         break;
1051     }
1052
1053     _csi_slab_free (ctx, file, sizeof (csi_file_t));
1054 }
1055
1056 csi_status_t
1057 _csi_file_as_string (csi_t *ctx,
1058                      csi_file_t *file,
1059                      csi_object_t *obj)
1060 {
1061     char *bytes;
1062     unsigned int len;
1063     unsigned int allocated;
1064     csi_status_t status;
1065
1066     len = 0;
1067     allocated = 16384;
1068     bytes = _csi_alloc (ctx, allocated);
1069     if (bytes == NULL)
1070         return _csi_error (CAIRO_STATUS_NO_MEMORY);
1071
1072     len = 0;
1073     do {
1074         int ret;
1075
1076         ret = csi_file_read (file, bytes + len, allocated - len);
1077         if (ret == 0)
1078             break;
1079
1080         len += ret;
1081         if (len + 1 > allocated / 2) {
1082             char *newbytes;
1083             int newlen;
1084
1085             if (_csi_unlikely (allocated > INT_MAX / 2))
1086                 return _csi_error (CAIRO_STATUS_NO_MEMORY);
1087
1088             newlen = allocated * 2;
1089             newbytes = _csi_realloc (ctx, bytes, newlen);
1090             if (_csi_unlikely (newbytes == NULL)) {
1091                 _csi_free (ctx, bytes);
1092                 return _csi_error (CAIRO_STATUS_NO_MEMORY);
1093             }
1094             bytes = newbytes;
1095             allocated = newlen;
1096         }
1097     } while (TRUE);
1098
1099     bytes[len] = '\0'; /* better safe than sorry! */
1100     status = csi_string_new_from_bytes (ctx, obj, bytes, len);
1101     if (status) {
1102         _csi_free (ctx, bytes);
1103         return status;
1104     }
1105
1106     return CAIRO_STATUS_SUCCESS;
1107 }
1108