summaryrefslogtreecommitdiff
path: root/libs/cairo-1.16.0/util/cairo-script/cairo-script-file.c
diff options
context:
space:
mode:
authorsanine <sanine.not@pm.me>2022-10-12 12:03:23 -0500
committersanine <sanine.not@pm.me>2022-10-12 12:03:23 -0500
commit530ffd0b7d3c39757b20f00716e486b5caf89aff (patch)
tree76b35fdf57317038acf6b828871f6ae25fce2ebe /libs/cairo-1.16.0/util/cairo-script/cairo-script-file.c
parent3dbe9332e47c143a237db12440f134caebd1cfbe (diff)
add cairo
Diffstat (limited to 'libs/cairo-1.16.0/util/cairo-script/cairo-script-file.c')
-rw-r--r--libs/cairo-1.16.0/util/cairo-script/cairo-script-file.c1109
1 files changed, 1109 insertions, 0 deletions
diff --git a/libs/cairo-1.16.0/util/cairo-script/cairo-script-file.c b/libs/cairo-1.16.0/util/cairo-script/cairo-script-file.c
new file mode 100644
index 0000000..e7c5376
--- /dev/null
+++ b/libs/cairo-1.16.0/util/cairo-script/cairo-script-file.c
@@ -0,0 +1,1109 @@
+/*
+ * Copyright © 2008 Chris Wilson <chris@chris-wilson.co.uk>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Chris Wilson.
+ *
+ * Contributor(s):
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "config.h"
+
+#include "cairo-script-private.h"
+
+#include <stdio.h>
+#include <limits.h> /* INT_MAX */
+#include <string.h>
+#include <zlib.h>
+
+#if HAVE_LZO
+#include <lzo/lzo2a.h>
+#endif
+
+#define CHUNK_SIZE 32768
+
+#define OWN_STREAM 0x1
+
+csi_status_t
+csi_file_new (csi_t *ctx,
+ csi_object_t *obj,
+ const char *path, const char *mode)
+{
+ csi_file_t *file;
+
+ file = _csi_slab_alloc (ctx, sizeof (csi_file_t));
+ if (file == NULL)
+ return _csi_error (CAIRO_STATUS_NO_MEMORY);
+
+ file->base.type = CSI_OBJECT_TYPE_FILE;
+ file->base.ref = 1;
+
+ file->data = NULL;
+ file->type = STDIO;
+ file->flags = OWN_STREAM;
+ file->src = fopen (path, mode);
+ if (file->src == NULL) {
+ _csi_slab_free (ctx, file, sizeof (csi_file_t));
+ return _csi_error (CAIRO_STATUS_FILE_NOT_FOUND);
+ }
+
+ file->data = _csi_alloc (ctx, CHUNK_SIZE);
+ if (file->data == NULL) {
+ _csi_slab_free (ctx, file, sizeof (csi_file_t));
+ return _csi_error (CAIRO_STATUS_NO_MEMORY);
+ }
+ file->bp = file->data;
+ file->rem = 0;
+
+ obj->type = CSI_OBJECT_TYPE_FILE;
+ obj->datum.file = file;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+csi_status_t
+csi_file_new_for_stream (csi_t *ctx,
+ csi_object_t *obj,
+ FILE *stream)
+{
+ csi_file_t *file;
+
+ file = _csi_slab_alloc (ctx, sizeof (csi_file_t));
+ if (file == NULL)
+ return _csi_error (CAIRO_STATUS_NO_MEMORY);
+
+ file->base.type = CSI_OBJECT_TYPE_FILE;
+ file->base.ref = 1;
+
+ file->data = NULL;
+ file->type = STDIO;
+ file->flags = 0;
+ file->src = stream;
+ if (file->src == NULL) {
+ _csi_slab_free (ctx, file, sizeof (csi_file_t));
+ return _csi_error (CAIRO_STATUS_FILE_NOT_FOUND);
+ }
+
+ file->data = _csi_alloc (ctx, CHUNK_SIZE);
+ if (file->data == NULL) {
+ _csi_slab_free (ctx, file, sizeof (csi_file_t));
+ return _csi_error (CAIRO_STATUS_NO_MEMORY);
+ }
+ file->bp = file->data;
+ file->rem = 0;
+
+ obj->type = CSI_OBJECT_TYPE_FILE;
+ obj->datum.file = file;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+csi_status_t
+csi_file_new_for_bytes (csi_t *ctx,
+ csi_object_t *obj,
+ const char *bytes,
+ unsigned int length)
+{
+ csi_file_t *file;
+
+ file = _csi_slab_alloc (ctx, sizeof (csi_file_t));
+ if (file == NULL)
+ return _csi_error (CAIRO_STATUS_NO_MEMORY);
+
+ file->base.type = CSI_OBJECT_TYPE_FILE;
+ file->base.ref = 1;
+
+ file->type = BYTES;
+ file->src = (uint8_t *) bytes;
+ file->data = (uint8_t *) bytes;
+ file->bp = (uint8_t *) bytes;
+ file->rem = length;
+
+ obj->type = CSI_OBJECT_TYPE_FILE;
+ obj->datum.file = file;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+csi_status_t
+csi_file_new_from_string (csi_t *ctx,
+ csi_object_t *obj,
+ csi_string_t *src)
+{
+ csi_file_t *file;
+
+ file = _csi_slab_alloc (ctx, sizeof (csi_file_t));
+ if (_csi_unlikely (file == NULL))
+ return _csi_error (CAIRO_STATUS_NO_MEMORY);
+
+ file->base.type = CSI_OBJECT_TYPE_FILE;
+ file->base.ref = 1;
+
+ if (src->deflate) {
+ uLongf len = src->deflate;
+ csi_object_t tmp_obj;
+ csi_string_t *tmp_str;
+ csi_status_t status;
+
+ status = csi_string_new (ctx, &tmp_obj, NULL, src->deflate);
+ if (_csi_unlikely (status))
+ return status;
+
+ tmp_str = tmp_obj.datum.string;
+ switch (src->method) {
+ case NONE:
+ default:
+ status = _csi_error (CAIRO_STATUS_NO_MEMORY);
+ break;
+
+ case ZLIB:
+#if HAVE_ZLIB
+ if (uncompress ((Bytef *) tmp_str->string, &len,
+ (Bytef *) src->string, src->len) != Z_OK)
+#endif
+ status = _csi_error (CAIRO_STATUS_NO_MEMORY);
+ break;
+ case LZO:
+#if HAVE_LZO
+ if (lzo2a_decompress ((lzo_bytep) src->string, src->len,
+ (lzo_bytep) tmp_str->string, &len,
+ NULL))
+#endif
+ status = _csi_error (CAIRO_STATUS_NO_MEMORY);
+ break;
+ }
+ if (_csi_unlikely (status)) {
+ csi_string_free (ctx, tmp_str);
+ _csi_slab_free (ctx, file, sizeof (csi_file_t));
+ return status;
+ }
+
+ file->src = tmp_str;
+ file->data = tmp_str->string;
+ file->rem = tmp_str->len;
+ } else {
+ file->src = src; src->base.ref++;
+ file->data = src->string;
+ file->rem = src->len;
+ }
+ file->type = BYTES;
+ file->bp = file->data;
+
+ obj->type = CSI_OBJECT_TYPE_FILE;
+ obj->datum.file = file;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static csi_status_t
+_csi_file_new_filter (csi_t *ctx,
+ csi_object_t *obj,
+ csi_object_t *src,
+ const csi_filter_funcs_t *funcs,
+ void *data)
+{
+ csi_file_t *file;
+ csi_object_t src_file;
+ csi_status_t status;
+
+ file = _csi_slab_alloc (ctx, sizeof (csi_file_t));
+ if (file == NULL)
+ return _csi_error (CAIRO_STATUS_NO_MEMORY);
+
+ obj->type = CSI_OBJECT_TYPE_FILE;
+ obj->datum.file = file;
+
+ file->base.type = CSI_OBJECT_TYPE_FILE;
+ file->base.ref = 1;
+
+ file->type = FILTER;
+ file->data = data;
+ file->filter = funcs;
+ status = csi_object_as_file (ctx, src, &src_file);
+ if (status) {
+ csi_object_free (ctx, obj);
+ return status;
+ }
+ file->src = src_file.datum.file;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+
+#if 0
+csi_status_t
+csi_file_new_from_stream (csi_t *ctx,
+ FILE *file,
+ csi_object_t **out)
+{
+ csi_file_t *obj;
+
+ obj = (csi_file_t *) _csi_object_new (ctx, CSI_OBJECT_TYPE_FILE);
+ if (obj == NULL)
+ return _csi_error (CAIRO_STATUS_NO_MEMORY);
+
+ obj->type = STDIO;
+ obj->src = file;
+ obj->data = _csi_alloc (ctx, CHUNK_SIZE);
+ if (obj->data == NULL) {
+ csi_object_free (&obj->base);
+ return _csi_error (CAIRO_STATUS_UNDEFINED_FILENAME_ERROR);
+ }
+ obj->bp = obj->data;
+ obj->rem = 0;
+
+ *out = &obj->base;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static csi_object_t *
+_csi_file_new_from_procedure (csi_t *ctx, csi_object_t *src)
+{
+ csi_file_t *obj;
+
+ obj = (csi_file_t *) _csi_object_new (ctx, CSI_OBJECT_TYPE_FILE);
+ if (obj == NULL)
+ return NULL;
+
+ obj->type = PROCEDURE;
+ obj->src = csi_object_reference (src);
+ obj->data = NULL;
+
+ return &obj->base;
+}
+#endif
+
+typedef struct _ascii85_decode_data {
+ uint8_t buf[CHUNK_SIZE];
+ uint8_t *bp;
+ short bytes_available;
+ short eod;
+} _ascii85_decode_data_t;
+
+static int
+_getc_skip_whitespace (csi_file_t *src)
+{
+ int c;
+
+ do switch ((c = csi_file_getc (src))) {
+ case 0x0:
+ case 0x9:
+ case 0xa:
+ case 0xc:
+ case 0xd:
+ case 0x20:
+ continue;
+
+ default:
+ return c;
+ } while (TRUE);
+
+ return c;
+}
+
+static void
+_ascii85_decode (csi_file_t *file)
+{
+ _ascii85_decode_data_t *data = file->data;
+ unsigned int n;
+
+ if (data->eod)
+ return;
+
+ data->bp = data->buf;
+
+ n = 0;
+ do {
+ unsigned int v = _getc_skip_whitespace (file->src);
+ if (v == 'z') {
+ data->buf[n+0] = 0;
+ data->buf[n+1] = 0;
+ data->buf[n+2] = 0;
+ data->buf[n+3] = 0;
+ } else if (v == '~') {
+ _getc_skip_whitespace (file->src); /* == '>' || IO_ERROR */
+ data->eod = TRUE;
+ break;
+ } else if (v < '!' || v > 'u') {
+ /* IO_ERROR */
+ data->eod = TRUE;
+ break;
+ } else {
+ unsigned int i;
+
+ v -= '!';
+ for (i = 1; i < 5; i++) {
+ int c = _getc_skip_whitespace (file->src);
+ if (c == '~') { /* short tuple */
+ _getc_skip_whitespace (file->src); /* == '>' || IO_ERROR */
+ data->eod = TRUE;
+ switch (i) {
+ case 0:
+ case 1:
+ /* IO_ERROR */
+ break;
+ case 2:
+ v = v * (85*85*85) + 85*85*85 -1;
+ goto odd1;
+ case 3:
+ v = v * (85*85) + 85*85 -1;
+ goto odd2;
+ case 4:
+ v = v * 85 + 84;
+ data->buf[n+2] = v >> 8 & 0xff;
+odd2:
+ data->buf[n+1] = v >> 16 & 0xff;
+odd1:
+ data->buf[n+0] = v >> 24 & 0xff;
+ data->bytes_available = n + i - 1;
+ return;
+ }
+ break;
+ }
+ v = 85*v + c-'!';
+ }
+
+ data->buf[n+0] = v >> 24 & 0xff;
+ data->buf[n+1] = v >> 16 & 0xff;
+ data->buf[n+2] = v >> 8 & 0xff;
+ data->buf[n+3] = v >> 0 & 0xff;
+ }
+ n += 4;
+ } while (n < sizeof (data->buf) && data->eod == FALSE);
+
+ data->bytes_available = n;
+}
+
+static int
+_ascii85_decode_getc (csi_file_t *file)
+{
+ _ascii85_decode_data_t *data = file->data;
+
+ if (data->bytes_available == 0) {
+ _ascii85_decode (file);
+
+ if (data->bytes_available == 0)
+ return EOF;
+ }
+
+ data->bytes_available--;
+ return *data->bp++;
+}
+
+static void
+_ascii85_decode_putc (csi_file_t *file, int c)
+{
+ _ascii85_decode_data_t *data = file->data;
+ data->bytes_available++;
+ data->bp--;
+}
+
+static int
+_ascii85_decode_read (csi_file_t *file, uint8_t *buf, int len)
+{
+ _ascii85_decode_data_t *data = file->data;
+
+ if (data->bytes_available == 0) {
+ _ascii85_decode (file);
+
+ if (data->bytes_available == 0)
+ return 0;
+ }
+
+ if (len > data->bytes_available)
+ len = data->bytes_available;
+ memcpy (buf, data->bp, len);
+ data->bp += len;
+ data->bytes_available -= len;
+ return len;
+}
+
+csi_status_t
+csi_file_new_ascii85_decode (csi_t *ctx,
+ csi_object_t *obj,
+ csi_dictionary_t *dict,
+ csi_object_t *src)
+{
+ static const csi_filter_funcs_t funcs = {
+ _ascii85_decode_getc,
+ _ascii85_decode_putc,
+ _ascii85_decode_read,
+ _csi_free,
+ };
+ _ascii85_decode_data_t *data;
+
+ data = _csi_alloc0 (ctx, sizeof (_ascii85_decode_data_t));
+ if (data == NULL)
+ return _csi_error (CAIRO_STATUS_NO_MEMORY);
+
+ return _csi_file_new_filter (ctx, obj, src, &funcs, data);
+}
+
+#if HAVE_ZLIB
+#include <zlib.h>
+
+typedef struct _deflate_decode_data {
+ z_stream zlib_stream;
+
+ uint8_t in[CHUNK_SIZE];
+ uint8_t out[CHUNK_SIZE];
+
+ int bytes_available;
+ uint8_t *bp;
+} _deflate_decode_data_t;
+
+static void
+_deflate_decode (csi_file_t *file)
+{
+ _deflate_decode_data_t *data = file->data;
+ uint8_t *bp;
+ int len;
+
+ data->zlib_stream.next_out = data->out;
+ data->zlib_stream.avail_out = sizeof (data->out);
+
+ bp = data->in;
+ len = sizeof (data->in);
+ if (data->zlib_stream.avail_in) {
+ memmove (data->in,
+ data->zlib_stream.next_in,
+ data->zlib_stream.avail_in);
+ len -= data->zlib_stream.avail_in;
+ bp += data->zlib_stream.avail_in;
+ }
+
+ len = csi_file_read (file->src, bp, len);
+
+ data->zlib_stream.next_in = data->in;
+ data->zlib_stream.avail_in += len;
+
+ inflate (&data->zlib_stream, len == 0 ? Z_FINISH : Z_NO_FLUSH);
+
+ data->bytes_available = data->zlib_stream.next_out - data->out;
+ data->bp = data->out;
+}
+
+static int
+_deflate_decode_getc (csi_file_t *file)
+{
+ _deflate_decode_data_t *data = file->data;
+
+ if (data->bytes_available == 0) {
+ _deflate_decode (file);
+
+ if (data->bytes_available == 0)
+ return EOF;
+ }
+
+ data->bytes_available--;
+ return *data->bp++;
+}
+
+static void
+_deflate_decode_putc (csi_file_t *file, int c)
+{
+ _deflate_decode_data_t *data = file->data;
+ data->bytes_available++;
+ data->bp--;
+}
+
+static int
+_deflate_decode_read (csi_file_t *file, uint8_t *buf, int len)
+{
+ _deflate_decode_data_t *data = file->data;
+
+ if (data->bytes_available == 0) {
+ _deflate_decode (file);
+
+ if (data->bytes_available == 0)
+ return 0;
+ }
+
+ if (len > (int) data->bytes_available)
+ len = data->bytes_available;
+ memcpy (buf, data->bp, len);
+ data->bp += len;
+ data->bytes_available -= len;
+ return len;
+}
+
+static void
+_deflate_destroy (csi_t *ctx, void *closure)
+{
+ _deflate_decode_data_t *data;
+
+ data = closure;
+
+ inflateEnd (&data->zlib_stream);
+
+ _csi_free (ctx, data);
+}
+
+csi_status_t
+csi_file_new_deflate_decode (csi_t *ctx,
+ csi_object_t *obj,
+ csi_dictionary_t *dict,
+ csi_object_t *src)
+{
+ static const csi_filter_funcs_t funcs = {
+ _deflate_decode_getc,
+ _deflate_decode_putc,
+ _deflate_decode_read,
+ _deflate_destroy,
+ };
+ _deflate_decode_data_t *data;
+
+ data = _csi_alloc (ctx, sizeof (_deflate_decode_data_t));
+ if (data == NULL)
+ return _csi_error (CAIRO_STATUS_NO_MEMORY);
+
+ data->zlib_stream.zalloc = Z_NULL;
+ data->zlib_stream.zfree = Z_NULL;
+ data->zlib_stream.opaque = Z_NULL;
+ data->zlib_stream.next_in = data->in;
+ data->zlib_stream.avail_in = 0;
+ data->zlib_stream.next_out = data->out;
+ data->zlib_stream.avail_out = sizeof (data->out);
+ data->bytes_available = 0;
+
+ if (inflateInit (&data->zlib_stream) != Z_OK) {
+ _csi_free (ctx, data);
+ return _csi_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ return _csi_file_new_filter (ctx, obj, src, &funcs, data);
+}
+#endif
+
+#if 0
+static int
+hex_value (int c)
+{
+ if (c < '0')
+ return EOF;
+ if (c <= '9')
+ return c - '0';
+ c |= 32;
+ if (c < 'a')
+ return EOF;
+ if (c <= 'f')
+ return c - 'a' + 0xa;
+ return EOF;
+}
+
+/* Adobe Type 1 Font Format book: p63 */
+typedef struct _decrypt_data {
+ uint8_t putback[32];
+ uint8_t nputback;
+ csi_bool_t is_hexadecimal;
+ unsigned short R;
+ int eod;
+} _decrypt_data_t;
+
+static uint8_t
+_decrypt (unsigned short *R, uint8_t cypher)
+{
+#define c1 52845
+#define c2 22719
+ uint8_t plain;
+
+ plain = cypher ^ (*R >> 8);
+ *R = (cypher + *R) * c1 + c2;
+ return plain;
+#undef c1
+#undef c2
+}
+
+int
+csi_decrypt (uint8_t *in, int length,
+ unsigned short salt, int binary,
+ uint8_t *out)
+{
+ const uint8_t * const end = in + length;
+ uint8_t *base = out;
+
+ while (in < end) {
+ int c;
+
+ if (! binary) {
+ int c_hi = -1, c_lo = 0;
+
+ while (in < end && (c_hi = *in++)) {
+ switch (c_hi) {
+ case 0x0:
+ case 0x9:
+ case 0xa:
+ case 0xc:
+ case 0xd:
+ case 0x20:
+ continue;
+
+ default:
+ break;
+ }
+ }
+ if (c_hi < 0)
+ break;
+
+ while (in < end && (c_lo = *in++)) {
+ switch (c_lo) {
+ case 0x0:
+ case 0x9:
+ case 0xa:
+ case 0xc:
+ case 0xd:
+ case 0x20:
+ continue;
+
+ default:
+ break;
+ }
+ }
+
+ c = (hex_value (c_hi) << 4) | hex_value (c_lo);
+ } else
+ c = *in++;
+
+ *out++ = _decrypt (&salt, c);
+ }
+
+ return out - base;
+}
+
+static uint8_t
+_encrypt (unsigned short *R, uint8_t plain)
+{
+#define c1 52845
+#define c2 22719
+ uint8_t cypher;
+
+ cypher = plain ^ (*R >> 8);
+ *R = (cypher + *R) * c1 + c2;
+ return cypher;
+#undef c1
+#undef c2
+}
+
+int
+csi_encrypt (uint8_t *in, int length,
+ unsigned short salt, int discard, int binary,
+ uint8_t *out)
+{
+ const char hex[]="0123456789abcdef";
+ const uint8_t * const end = in + length;
+ uint8_t *base = out;
+ int col = 0;
+
+ while (discard--) {
+ if (! binary) {
+ int c = _encrypt (&salt, ' ');
+ *out++ = hex[(c >> 4) & 0xf];
+ *out++ = hex[(c >> 0) & 0xf];
+ } else
+ *out++ = _encrypt (&salt, 0);
+ }
+
+ while (in < end) {
+ int c;
+
+ c = _encrypt (&salt, *in++);
+ if (! binary) {
+ if (col == 78) {
+ *out++ = '\n';
+ col = 0;
+ }
+ *out++ = hex[(c >> 4) & 0xf];
+ *out++ = hex[(c >> 0) & 0xf];
+ col += 2;
+ } else
+ *out++ = c;
+ }
+
+ return out - base;
+}
+
+static int
+_decrypt_getc (csi_file_t *file)
+{
+ _decrypt_data_t *data = file->data;
+ int c;
+
+ if (data->nputback)
+ return data->putback[--data->nputback];
+
+ if (data->is_hexadecimal) {
+ int c_hi, c_lo;
+
+ c_hi = _getc_skip_whitespace (file->src);
+ c_lo = _getc_skip_whitespace (file->src);
+ c = (hex_value (c_hi) << 4) | hex_value (c_lo);
+ } else
+ c = csi_file_getc (file->src);
+
+ if (c == EOF)
+ return EOF;
+
+ return _decrypt (&data->R, c);
+}
+
+static void
+_decrypt_putc (csi_file_t *file, int c)
+{
+ _decrypt_data_t *data;
+
+ data = file->data;
+
+ data->putback[data->nputback++] = c;
+}
+
+csi_object_t *
+csi_file_new_decrypt (csi_t *ctx, csi_object_t *src, int salt, int discard)
+{
+ csi_object_t *obj;
+ _decrypt_data_t *data;
+ int n;
+
+ data = _csi_alloc0 (ctx, sizeof (_decrypt_data_t));
+ if (data == NULL)
+ return NULL;
+
+ data->R = salt;
+
+ obj = _csi_file_new_filter (ctx, src,
+ _decrypt_getc,
+ _decrypt_putc,
+ NULL,
+ _csi_free,
+ data);
+ if (obj == NULL)
+ return NULL;
+
+ /* XXX determine encoding, eexec only? */
+ data->is_hexadecimal = salt != 4330;
+ for (n = 0; n < discard; n++) {
+ int c;
+ c = csi_file_getc (obj);
+ if (c == EOF) {
+ return obj;
+ }
+ }
+ return obj;
+}
+#endif
+
+csi_status_t
+_csi_file_execute (csi_t *ctx, csi_file_t *obj)
+{
+ return _csi_scan_file (ctx, obj);
+}
+
+int
+csi_file_getc (csi_file_t *file)
+{
+ int c;
+
+ if (_csi_unlikely (file->src == NULL))
+ return EOF;
+
+ switch (file->type) {
+ case STDIO:
+ if (_csi_likely (file->rem)) {
+ c = *file->bp++;
+ file->rem--;
+ } else {
+ file->rem = fread (file->bp = file->data, 1, CHUNK_SIZE, file->src);
+ case BYTES:
+ if (_csi_likely (file->rem)) {
+ c = *file->bp++;
+ file->rem--;
+ } else
+ c = EOF;
+ }
+ break;
+
+ case PROCEDURE:
+#if 0
+ if (file->data == NULL) {
+ csi_status_t status;
+ csi_object_t *string;
+
+RERUN_PROCEDURE:
+ status = csi_object_execute (file->src);
+ if (status)
+ return EOF;
+
+ string = csi_pop_operand (file->base.ctx);
+ if (string == NULL)
+ return EOF;
+ file->data = csi_object_as_file (file->base.ctx, string);
+ csi_object_free (string);
+ if (file->data == NULL)
+ return EOF;
+ }
+ c = csi_file_getc (file->data);
+ if (c == EOF) {
+ csi_object_free (file->data);
+ file->data = NULL;
+ goto RERUN_PROCEDURE;
+ }
+#else
+ c = EOF;
+#endif
+ break;
+
+ case FILTER:
+ c = file->filter->filter_getc (file);
+ break;
+
+ default:
+ c = EOF;
+ break;
+ }
+
+ return c;
+}
+
+int
+csi_file_read (csi_file_t *file, void *buf, int len)
+{
+ int ret;
+
+ if (file->src == NULL)
+ return 0;
+
+ switch (file->type) {
+ case STDIO:
+ if (file->rem > 0) {
+ ret = len;
+ if (file->rem < ret)
+ ret = file->rem;
+ memcpy (buf, file->bp, ret);
+ file->bp += ret;
+ file->rem -= ret;
+ } else
+ ret = fread (buf, 1, len, file->src);
+ break;
+
+ case BYTES:
+ if (file->rem > 0) {
+ ret = len;
+ if (file->rem < ret)
+ ret = file->rem;
+ memcpy (buf, file->bp, ret);
+ file->bp += ret;
+ file->rem -= ret;
+ } else
+ ret = 0;
+ break;
+
+ case PROCEDURE:
+#if 0
+ if (file->data == NULL) {
+ csi_status_t status;
+ csi_object_t *string;
+
+RERUN_PROCEDURE:
+ status = csi_object_execute (file->src);
+ if (status)
+ return 0;
+
+ string = csi_pop_operand (file->base.ctx);
+ if (string == NULL)
+ return 0;
+ file->data = csi_object_as_file (file->base.ctx, string);
+ csi_object_free (string);
+ if (file->data == NULL)
+ return 0;
+ }
+ ret = csi_file_read (file->data, buf, len);
+ if (ret == 0) {
+ csi_object_free (file->data);
+ file->data = NULL;
+ goto RERUN_PROCEDURE;
+ }
+#else
+ ret = 0;
+#endif
+ break;
+
+ case FILTER:
+ ret = file->filter->filter_read (file, buf, len);
+ break;
+
+ default:
+ ret = 0;
+ break;
+ }
+
+ return ret;
+}
+
+void
+csi_file_putc (csi_file_t *file, int c)
+{
+ if (file->src == NULL)
+ return;
+
+ switch ((int) file->type) {
+ case STDIO:
+ case BYTES:
+ file->bp--;
+ file->rem++;
+ break;
+ case FILTER:
+ file->filter->filter_putc (file, c);
+ break;
+ default:
+ break;
+ }
+}
+
+void
+csi_file_flush (csi_file_t *file)
+{
+ if (file->src == NULL)
+ return;
+
+ switch ((int) file->type) {
+ case FILTER: /* need to eat EOD */
+ while (csi_file_getc (file) != EOF)
+ ;
+ break;
+ default:
+ break;
+ }
+}
+
+void
+csi_file_close (csi_t *ctx, csi_file_t *file)
+{
+ if (file->src == NULL)
+ return;
+
+ switch (file->type) {
+ case STDIO:
+ if (file->flags & OWN_STREAM)
+ fclose (file->src);
+ break;
+ case BYTES:
+ if (file->src != file->data) {
+ csi_string_t *src = file->src;
+ if (src != NULL && --src->base.ref == 0)
+ csi_string_free (ctx, src);
+ }
+ break;
+ case FILTER:
+ {
+ csi_file_t *src = file->src;
+ if (src != NULL && --src->base.ref == 0)
+ _csi_file_free (ctx, src);
+ }
+ break;
+ case PROCEDURE:
+ default:
+ break;
+ }
+ file->src = NULL;
+}
+
+void
+_csi_file_free (csi_t *ctx, csi_file_t *file)
+{
+ csi_file_flush (file);
+ /* XXX putback */
+ csi_file_close (ctx, file);
+
+ switch (file->type) {
+ case BYTES:
+ break;
+ case PROCEDURE:
+#if 0
+ csi_object_free (ctx, file->data);
+#endif
+ break;
+ case STDIO:
+ _csi_free (ctx, file->data);
+ break;
+ case FILTER:
+ file->filter->filter_destroy (ctx, file->data);
+ break;
+ default:
+ break;
+ }
+
+ _csi_slab_free (ctx, file, sizeof (csi_file_t));
+}
+
+csi_status_t
+_csi_file_as_string (csi_t *ctx,
+ csi_file_t *file,
+ csi_object_t *obj)
+{
+ char *bytes;
+ unsigned int len;
+ unsigned int allocated;
+ csi_status_t status;
+
+ allocated = 16384;
+ bytes = _csi_alloc (ctx, allocated);
+ if (bytes == NULL)
+ return _csi_error (CAIRO_STATUS_NO_MEMORY);
+
+ len = 0;
+ do {
+ int ret;
+
+ ret = csi_file_read (file, bytes + len, allocated - len);
+ if (ret == 0)
+ break;
+
+ len += ret;
+ if (len + 1 > allocated / 2) {
+ char *newbytes;
+ int newlen;
+
+ if (_csi_unlikely (allocated > INT_MAX / 2))
+ return _csi_error (CAIRO_STATUS_NO_MEMORY);
+
+ newlen = allocated * 2;
+ newbytes = _csi_realloc (ctx, bytes, newlen);
+ if (_csi_unlikely (newbytes == NULL)) {
+ _csi_free (ctx, bytes);
+ return _csi_error (CAIRO_STATUS_NO_MEMORY);
+ }
+ bytes = newbytes;
+ allocated = newlen;
+ }
+ } while (TRUE);
+
+ bytes[len] = '\0'; /* better safe than sorry! */
+ status = csi_string_new_from_bytes (ctx, obj, bytes, len);
+ if (status) {
+ _csi_free (ctx, bytes);
+ return status;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+