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