summaryrefslogtreecommitdiff
path: root/object
diff options
context:
space:
mode:
Diffstat (limited to 'object')
-rw-r--r--object/array.c182
-rw-r--r--object/dict.c260
-rw-r--r--object/parse.c400
-rw-r--r--object/print.c332
-rw-r--r--object/simple.c230
5 files changed, 1404 insertions, 0 deletions
diff --git a/object/array.c b/object/array.c
new file mode 100644
index 00000000..d7beeb38
--- /dev/null
+++ b/object/array.c
@@ -0,0 +1,182 @@
+#include <fitz.h>
+
+fz_error *
+fz_newarray(fz_obj **op, int initialcap)
+{
+ fz_obj *obj;
+ int i;
+
+ obj = *op = fz_malloc(sizeof (fz_obj));
+ if (!obj) return fz_outofmem;
+
+ obj->kind = FZ_ARRAY;
+ obj->refcount = 1;
+
+ obj->u.a.len = 0;
+ obj->u.a.cap = initialcap > 0 ? initialcap : 6;
+
+ obj->u.a.items = fz_malloc(sizeof (fz_obj*) * obj->u.a.cap);
+ if (!obj->u.a.items) { fz_free(obj); return fz_outofmem; }
+
+ for (i = 0; i < obj->u.a.cap; i++)
+ obj->u.a.items[i] = nil;
+
+ return nil;
+}
+
+fz_error *
+fz_copyarray(fz_obj **op, fz_obj *obj)
+{
+ fz_error *err;
+ fz_obj *new;
+ int i;
+
+ if (!fz_isarray(obj))
+ return fz_throw("typecheck in copyarray");
+
+ err = fz_newarray(&new, fz_arraylen(obj));
+ if (err) return err;
+ *op = new;
+
+ for (i = 0; i < fz_arraylen(obj); i++) {
+ err = fz_arraypush(new, fz_arrayget(obj, i));
+ if (err) { fz_freearray(new); return err; }
+ }
+
+ return nil;
+}
+
+fz_error *
+fz_deepcopyarray(fz_obj **op, fz_obj *obj)
+{
+ fz_error *err;
+ fz_obj *new;
+ fz_obj *val;
+ int i;
+
+ if (!fz_isarray(obj))
+ return fz_throw("typecheck in deepcopyarray");
+
+ err = fz_newarray(&new, fz_arraylen(obj));
+ if (err) return err;
+ *op = new;
+
+ for (i = 0; i < fz_arraylen(obj); i++)
+ {
+ val = fz_arrayget(obj, i);
+
+ if (fz_isarray(val)) {
+ err = fz_deepcopyarray(&val, val);
+ if (err) { fz_freearray(new); return err; }
+ err = fz_arraypush(new, val);
+ if (err) { fz_dropobj(val); fz_freearray(new); return err; }
+ fz_dropobj(val);
+ }
+
+ else if (fz_isdict(val)) {
+ err = fz_deepcopydict(&val, val);
+ if (err) { fz_freearray(new); return err; }
+ err = fz_arraypush(new, val);
+ if (err) { fz_dropobj(val); fz_freearray(new); return err; }
+ fz_dropobj(val);
+ }
+
+ else {
+ err = fz_arraypush(new, val);
+ if (err) { fz_freearray(new); return err; }
+ }
+ }
+
+ return nil;
+}
+
+int
+fz_arraylen(fz_obj *obj)
+{
+ if (!fz_isarray(obj))
+ return 0;
+ return obj->u.a.len;
+}
+
+fz_obj *
+fz_arrayget(fz_obj *obj, int i)
+{
+ if (!fz_isarray(obj))
+ return nil;
+
+ if (i < 0 || i >= obj->u.a.len)
+ return nil;
+
+ return obj->u.a.items[i];
+}
+
+fz_error *
+fz_arrayput(fz_obj *obj, int i, fz_obj *item)
+{
+ if (!fz_isarray(obj))
+ return fz_throw("typecheck in arrayput");
+ if (i < 0)
+ return fz_throw("rangecheck in arrayput: %d < 0", i);
+ if (i >= obj->u.a.len)
+ return fz_throw("rangecheck in arrayput: %d > %d", i, obj->u.a.len);
+
+ if (obj->u.a.items[i])
+ fz_dropobj(obj->u.a.items[i]);
+ obj->u.a.items[i] = fz_keepobj(item);
+
+ return nil;
+}
+
+static fz_error *
+growarray(fz_obj *obj)
+{
+ fz_obj **newitems;
+ int newcap;
+ int i;
+
+ newcap = obj->u.a.cap * 2;
+ newitems = fz_realloc(obj->u.a.items, sizeof (fz_obj*) * newcap);
+ if (!newitems) return fz_outofmem;
+
+ obj->u.a.items = newitems;
+ for (i = obj->u.a.cap ; i < newcap; i++)
+ obj->u.a.items[i] = nil;
+ obj->u.a.cap = newcap;
+
+ return nil;
+}
+
+fz_error *
+fz_arraypush(fz_obj *obj, fz_obj *item)
+{
+ fz_error *err;
+
+ if (!fz_isarray(obj))
+ return fz_throw("typecheck in arraypush");
+
+ if (obj->u.a.len + 1 > obj->u.a.cap) {
+ err = growarray(obj);
+ if (err) return err;
+ }
+
+ obj->u.a.items[obj->u.a.len] = fz_keepobj(item);
+ obj->u.a.len++;
+
+ return nil;
+}
+
+void
+fz_freearray(fz_obj *obj)
+{
+ int i;
+
+ assert(obj->kind == FZ_ARRAY);
+
+ for (i = 0; i < obj->u.a.len; i++)
+ if (obj->u.a.items[i])
+ fz_dropobj(obj->u.a.items[i]);
+
+ fz_free(obj->u.a.items);
+ fz_free(obj);
+}
+
diff --git a/object/dict.c b/object/dict.c
new file mode 100644
index 00000000..fd2f3382
--- /dev/null
+++ b/object/dict.c
@@ -0,0 +1,260 @@
+#include <fitz.h>
+
+fz_error *
+fz_newdict(fz_obj **op, int initialcap)
+{
+ fz_obj *obj;
+ int i;
+
+ obj = *op = fz_malloc(sizeof (fz_obj));
+ if (!obj) return fz_outofmem;
+
+ obj->kind = FZ_DICT;
+ obj->refcount = 1;
+
+ obj->u.d.len = 0;
+ obj->u.d.cap = initialcap > 0 ? initialcap : 10;
+
+ obj->u.d.items = fz_malloc(sizeof(struct fz_keyval_s) * obj->u.d.cap);
+ if (!obj->u.d.items) { fz_free(obj); return fz_outofmem; }
+
+ for (i = 0; i < obj->u.d.cap; i++) {
+ obj->u.d.items[i].k = nil;
+ obj->u.d.items[i].v = nil;
+ }
+
+ return nil;
+}
+
+fz_error *
+fz_copydict(fz_obj **op, fz_obj *obj)
+{
+ fz_error *err;
+ fz_obj *new;
+ int i;
+
+ if (!fz_isdict(obj))
+ return fz_throw("typecheck in copydict");
+
+ err = fz_newdict(&new, obj->u.d.cap);
+ if (err) return err;
+ *op = new;
+
+ for (i = 0; i < fz_dictlen(obj); i++) {
+ err = fz_dictput(new, fz_dictgetkey(obj, i), fz_dictgetval(obj, i));
+ if (err) { fz_freedict(new); return err; }
+ }
+
+ return nil;
+}
+
+fz_error *
+fz_deepcopydict(fz_obj **op, fz_obj *obj)
+{
+ fz_error *err;
+ fz_obj *new;
+ fz_obj *val;
+ int i;
+
+ if (!fz_isdict(obj))
+ return fz_throw("typecheck in deepcopydict");
+
+ err = fz_newdict(&new, obj->u.d.cap);
+ if (err) return err;
+ *op = new;
+
+ for (i = 0; i < fz_dictlen(obj); i++)
+ {
+ val = fz_dictgetval(obj, i);
+
+ if (fz_isarray(val)) {
+ err = fz_deepcopyarray(&val, val);
+ if (err) { fz_freedict(new); return err; }
+ err = fz_dictput(new, fz_dictgetkey(obj, i), val);
+ if (err) { fz_dropobj(val); fz_freedict(new); return err; }
+ fz_dropobj(val);
+ }
+
+ else if (fz_isdict(val)) {
+ err = fz_deepcopydict(&val, val);
+ if (err) { fz_freedict(new); return err; }
+ err = fz_dictput(new, fz_dictgetkey(obj, i), val);
+ if (err) { fz_dropobj(val); fz_freedict(new); return err; }
+ fz_dropobj(val);
+ }
+
+ else {
+ err = fz_dictput(new, fz_dictgetkey(obj, i), val);
+ if (err) { fz_freedict(new); return err; }
+ }
+ }
+
+ return nil;
+}
+
+static fz_error *
+growdict(fz_obj *obj)
+{
+ struct fz_keyval_s *newitems;
+ int newcap;
+ int i;
+
+ newcap = obj->u.d.cap * 2;
+
+ newitems = fz_realloc(obj->u.d.items, sizeof(struct fz_keyval_s) * newcap);
+ if (!newitems) return fz_outofmem;
+
+ obj->u.d.items = newitems;
+ for (i = obj->u.d.cap; i < newcap; i++) {
+ obj->u.d.items[i].k = nil;
+ obj->u.d.items[i].v = nil;
+ }
+ obj->u.d.cap = newcap;
+
+ return nil;
+}
+
+int
+fz_dictlen(fz_obj *obj)
+{
+ if (!fz_isdict(obj))
+ return 0;
+ return obj->u.d.len;
+}
+
+fz_obj *
+fz_dictgetkey(fz_obj *obj, int i)
+{
+ if (!fz_isdict(obj))
+ return nil;
+
+ if (i < 0 || i >= obj->u.d.len)
+ return nil;
+
+ return obj->u.d.items[i].k;
+}
+
+fz_obj *
+fz_dictgetval(fz_obj *obj, int i)
+{
+ if (!fz_isdict(obj))
+ return nil;
+
+ if (i < 0 || i >= obj->u.d.len)
+ return nil;
+
+ return obj->u.d.items[i].v;
+}
+
+fz_obj *
+fz_dictgets(fz_obj *obj, char *key)
+{
+ int i;
+
+ if (!fz_isdict(obj))
+ return nil;
+
+ for (i = 0; i < obj->u.d.len; i++)
+ if (strcmp(fz_toname(obj->u.d.items[i].k), key) == 0)
+ return obj->u.d.items[i].v;
+
+ return nil;
+}
+
+fz_obj *
+fz_dictget(fz_obj *obj, fz_obj *key)
+{
+ return fz_dictgets(obj, fz_toname(key));
+}
+
+fz_error *
+fz_dictput(fz_obj *obj, fz_obj *key, fz_obj *val)
+{
+ fz_error *err;
+ int i;
+ char *s;
+
+ if (!fz_isdict(obj))
+ return fz_throw("typecheck in dictput");
+ if (!fz_isname(key))
+ return fz_throw("typecheck in dictput");
+
+ s = fz_toname(key);
+
+ for (i = 0; i < obj->u.d.len; i++) {
+ if (strcmp(fz_toname(obj->u.d.items[i].k), s) == 0) {
+ fz_dropobj(obj->u.d.items[i].v);
+ obj->u.d.items[i].v = fz_keepobj(val);
+ return nil;
+ }
+ }
+
+ if (obj->u.d.len + 1 > obj->u.d.cap) {
+ err = growdict(obj);
+ if (err) return err;
+ }
+
+ obj->u.d.items[obj->u.d.len].k = fz_keepobj(key);
+ obj->u.d.items[obj->u.d.len].v = fz_keepobj(val);
+ obj->u.d.len ++;
+
+ return nil;
+}
+
+fz_error *
+fz_dictputs(fz_obj *obj, char *key, fz_obj *val)
+{
+ fz_error *err;
+ fz_obj *keyobj;
+ err = fz_newname(&keyobj, key);
+ if (err) return err;
+ err = fz_dictput(obj, keyobj, val);
+ fz_dropobj(keyobj);
+ return err;
+}
+
+fz_error *
+fz_dictdels(fz_obj *obj, char *key)
+{
+ int i;
+
+ if (!fz_isdict(obj))
+ return fz_throw("typecheck in dictdel");
+
+ for (i = 0; i < obj->u.d.len; i++) {
+ if (strcmp(fz_toname(obj->u.d.items[i].k), key) == 0) {
+ fz_dropobj(obj->u.d.items[i].k);
+ fz_dropobj(obj->u.d.items[i].v);
+ obj->u.d.items[i] = obj->u.d.items[obj->u.d.len-1];
+ obj->u.d.len --;
+ }
+ }
+
+ return nil;
+}
+
+fz_error *
+fz_dictdel(fz_obj *obj, fz_obj *key)
+{
+ return fz_dictdels(obj, fz_toname(key));
+}
+
+void
+fz_freedict(fz_obj *obj)
+{
+ int i;
+
+ if (!fz_isdict(obj))
+ return;
+
+ for (i = 0; i < obj->u.d.len; i++) {
+ if (obj->u.d.items[i].k)
+ fz_dropobj(obj->u.d.items[i].k);
+ if (obj->u.d.items[i].v)
+ fz_dropobj(obj->u.d.items[i].v);
+ }
+
+ fz_free(obj->u.d.items);
+ fz_free(obj);
+}
+
diff --git a/object/parse.c b/object/parse.c
new file mode 100644
index 00000000..c065a132
--- /dev/null
+++ b/object/parse.c
@@ -0,0 +1,400 @@
+#include <fitz.h>
+
+struct vap { va_list ap; };
+
+static inline int iswhite(int ch)
+{
+ return
+ ch == '\000' ||
+ ch == '\011' ||
+ ch == '\012' ||
+ ch == '\014' ||
+ ch == '\015' ||
+ ch == '\040';
+}
+
+static inline int isdelim(int ch)
+{
+ return
+ ch == '(' || ch == ')' ||
+ ch == '<' || ch == '>' ||
+ ch == '[' || ch == ']' ||
+ ch == '{' || ch == '}' ||
+ ch == '/' ||
+ ch == '%';
+}
+
+static inline int isregular(int ch)
+{
+ return !isdelim(ch) && !iswhite(ch) && ch != EOF;
+}
+
+static fz_error *parseobj(fz_obj **obj, char **sp, struct vap *v);
+
+static inline int fromhex(char ch)
+{
+ if (ch >= '0' && ch <= '9')
+ return ch - '0';
+ else if (ch >= 'A' && ch <= 'F')
+ return ch - 'A' + 0xA;
+ else if (ch >= 'a' && ch <= 'f')
+ return ch - 'a' + 0xA;
+ return 0;
+}
+
+static inline void skipwhite(char **sp)
+{
+ char *s = *sp;
+ while (iswhite(*s))
+ s ++;
+ *sp = s;
+}
+
+static void parsekeyword(char **sp, char *b, char *eb)
+{
+ char *s = *sp;
+ while (b < eb && isregular(*s))
+ *b++ = *s++;
+ *b++ = 0;
+ *sp = s;
+}
+
+static fz_error *parsename(fz_obj **obj, char **sp)
+{
+ char buf[64];
+ char *s = *sp;
+ char *p = buf;
+
+ s ++; /* skip '/' */
+ while (p < buf + sizeof buf - 1 && isregular(*s))
+ *p++ = *s++;
+ *p++ = 0;
+ *sp = s;
+
+ return fz_newname(obj, buf);
+}
+
+static fz_error *parsenumber(fz_obj **obj, char **sp)
+{
+ char buf[32];
+ char *s = *sp;
+ char *p = buf;
+
+ while (p < buf + sizeof buf - 1)
+ {
+ if (s[0] == '-' || s[0] == '.' || (s[0] >= '0' && s[0] <= '9'))
+ *p++ = *s++;
+ else
+ break;
+ }
+ *p++ = 0;
+ *sp = s;
+
+ if (strchr(buf, '.'))
+ return fz_newreal(obj, atof(buf));
+ return fz_newint(obj, atoi(buf));
+}
+
+static fz_error *parsedict(fz_obj **obj, char **sp, struct vap *v)
+{
+ fz_error *err = nil;
+ fz_obj *dict = nil;
+ fz_obj *key = nil;
+ fz_obj *val = nil;
+ char *s = *sp;
+
+ err = fz_newdict(&dict, 8);
+ if (err) return err;
+ *obj = dict;
+
+ s += 2; /* skip "<<" */
+
+ while (*s)
+ {
+ skipwhite(&s);
+
+ /* end-of-dict marker >> */
+ if (*s == '>') {
+ s ++;
+ if (*s == '>') {
+ s ++;
+ break;
+ }
+ err = fz_throw("syntaxerror in parsedict");
+ goto error;
+ }
+
+ /* non-name as key, bail */
+ if (*s != '/') {
+ err = fz_throw("syntaxerror in parsedict");
+ goto error;
+ }
+
+ err = parsename(&key, &s);
+ if (err) goto error;
+
+ skipwhite(&s);
+
+ err = parseobj(&val, &s, v);
+ if (err) goto error;
+
+ err = fz_dictput(dict, key, val);
+ if (err) goto error;
+
+ fz_dropobj(val); val = nil;
+ fz_dropobj(key); key = nil;
+ }
+
+ *sp = s;
+ return nil;
+
+error:
+ if (val) fz_dropobj(val);
+ if (key) fz_dropobj(key);
+ if (dict) fz_freedict(dict);
+ *obj = nil;
+ *sp = s;
+ return err;
+}
+
+static fz_error *parsearray(fz_obj **obj, char **sp, struct vap *v)
+{
+ fz_error *err;
+ fz_obj *a;
+ fz_obj *o;
+ char *s = *sp;
+
+ err = fz_newarray(&a, 8);
+ if (err) return err;
+ *obj = a;
+
+ s ++; /* skip '[' */
+
+ while (*s)
+ {
+ skipwhite(&s);
+
+ if (*s == ']') {
+ s ++;
+ break;
+ }
+
+ err = parseobj(&o, &s, v);
+ if (err) { *obj = nil; fz_freearray(a); return err; }
+
+ err = fz_arraypush(a, o);
+ if (err) { fz_dropobj(o); *obj = nil; fz_freearray(a); return err; }
+
+ fz_dropobj(o);
+ }
+
+ *sp = s;
+ return nil;
+}
+
+static fz_error *parsestring(fz_obj **obj, char **sp)
+{
+ char buf[512];
+ char *s = *sp;
+ char *p = buf;
+ int balance = 1;
+ int oct;
+
+ s ++; /* skip '(' */
+
+ while (*s && p < buf + sizeof buf)
+ {
+ if (*s == '(')
+ {
+ balance ++;
+ *p++ = *s++;
+ }
+ else if (*s == ')')
+ {
+ balance --;
+ *p++ = *s++;
+ }
+ else if (*s == '\\')
+ {
+ s ++;
+ if (*s >= '0' && *s <= '9')
+ {
+ oct = *s - '0';
+ s ++;
+ if (*s >= '0' && *s <= '9')
+ {
+ oct = oct * 8 + (*s - '0');
+ s ++;
+ if (*s >= '0' && *s <= '9')
+ {
+ oct = oct * 8 + (*s - '0');
+ s ++;
+ }
+ }
+ *p++ = oct;
+ }
+ else switch (*s)
+ {
+ case 'n': *p++ = '\n'; s++; break;
+ case 'r': *p++ = '\r'; s++; break;
+ case 't': *p++ = '\t'; s++; break;
+ case 'b': *p++ = '\b'; s++; break;
+ case 'f': *p++ = '\f'; s++; break;
+ default: *p++ = *s++; break;
+ }
+ }
+ else
+ *p++ = *s++;
+
+ if (balance == 0)
+ break;
+ }
+
+ *sp = s;
+ return fz_newstring(obj, buf, p - buf - 1);
+}
+
+static fz_error *parsehexstring(fz_obj **obj, char **sp)
+{
+ char buf[512];
+ char *s = *sp;
+ char *p = buf;
+ int a, b;
+
+ s ++; /* skip '<' */
+
+ while (*s && p < buf + sizeof buf)
+ {
+ skipwhite(&s);
+ if (*s == '>') {
+ s ++;
+ break;
+ }
+ a = *s++;
+
+ if (*s == '\0')
+ break;
+
+ skipwhite(&s);
+ if (*s == '>') {
+ s ++;
+ break;
+ }
+ b = *s++;
+
+ *p++ = fromhex(a) * 16 + fromhex(b);
+ }
+
+ *sp = s;
+ return fz_newstring(obj, buf, p - buf);
+}
+
+static fz_error *parseobj(fz_obj **obj, char **sp, struct vap *v)
+{
+ fz_error *err;
+ char buf[32];
+ int oid, gid, len;
+ char *tmp;
+ char *s = *sp;
+
+ if (*s == '\0')
+ return fz_throw("syntaxerror in parseobj: end-of-string");
+
+ skipwhite(&s);
+
+ err = nil;
+
+ if (v != nil && *s == '%')
+ {
+ s ++;
+ switch (*s)
+ {
+ case 'o': *obj = fz_keepobj(va_arg(v->ap, fz_obj*)); break;
+ case 'b': err = fz_newbool(obj, va_arg(v->ap, int)); break;
+ case 'i': err = fz_newint(obj, va_arg(v->ap, int)); break;
+ case 'f': err = fz_newreal(obj, (float)va_arg(v->ap, double)); break;
+ case 'n': err = fz_newname(obj, va_arg(v->ap, char*)); break;
+ case 'r':
+ oid = va_arg(v->ap, int);
+ gid = va_arg(v->ap, int);
+ err = fz_newindirect(obj, oid, gid);
+ break;
+ case 's':
+ tmp = va_arg(v->ap, char*);
+ err = fz_newstring(obj, tmp, strlen(tmp));
+ break;
+ case '#':
+ tmp = va_arg(v->ap, char*);
+ len = va_arg(v->ap, int);
+ err = fz_newstring(obj, tmp, len);
+ break;
+ default:
+ err = fz_throw("unknown format specifier in packobj: '%c'", *s);
+ break;
+ }
+ s ++;
+ }
+
+ else if (*s == '/')
+ err = parsename(obj, &s);
+
+ else if (*s == '(')
+ err = parsestring(obj, &s);
+
+ else if (*s == '<') {
+ if (s[1] == '<')
+ err = parsedict(obj, &s, v);
+ else
+ err = parsehexstring(obj, &s);
+ }
+
+ else if (*s == '[')
+ err = parsearray(obj, &s, v);
+
+ else if (*s == '-' || *s == '.' || (*s >= '0' && *s <= '9'))
+ err = parsenumber(obj, &s);
+
+ else if (isregular(*s))
+ {
+ parsekeyword(&s, buf, buf + sizeof buf);
+
+ if (strcmp("true", buf) == 0)
+ err = fz_newbool(obj, 1);
+ else if (strcmp("false", buf) == 0)
+ err = fz_newbool(obj, 0);
+ else if (strcmp("null", buf) == 0)
+ err = fz_newnull(obj);
+ else
+ err = fz_throw("syntaxerror in parseobj: undefined keyword %s", buf);
+ }
+
+ else
+ err = fz_throw("syntaxerror in parseobj");
+
+ *sp = s;
+ return err;
+}
+
+fz_error *
+fz_packobj(fz_obj **op, char *fmt, ...)
+{
+ fz_error *err;
+ struct vap v;
+ va_list ap;
+
+ va_start(ap, fmt);
+ va_copy(v.ap, ap);
+
+ err = parseobj(op, &fmt, &v);
+
+ va_end(ap);
+
+ return err;
+}
+
+fz_error *
+fz_parseobj(fz_obj **op, char *str)
+{
+ return parseobj(op, &str, nil);
+}
+
diff --git a/object/print.c b/object/print.c
new file mode 100644
index 00000000..af6941f9
--- /dev/null
+++ b/object/print.c
@@ -0,0 +1,332 @@
+#include <fitz.h>
+
+struct fmt
+{
+ FILE *file;
+ char *buf;
+ int len;
+
+ int indent;
+ int tight;
+ int col;
+ int sep;
+ int last;
+};
+
+static void fmtobj(struct fmt *fmt, fz_obj *obj);
+
+static inline int isdelim(int ch)
+{
+ return ch == '(' || ch == ')' ||
+ ch == '<' || ch == '>' ||
+ ch == '[' || ch == ']' ||
+ ch == '{' || ch == '}' ||
+ ch == '/' ||
+ ch == '%';
+}
+
+static inline void fmtputc(struct fmt *fmt, int c)
+{
+ if (fmt->sep && !isdelim(fmt->last) && !isdelim(c)) {
+ fmt->sep = 0;
+ fmtputc(fmt, ' ');
+ }
+ fmt->sep = 0;
+
+ if (fmt->file)
+ putc(c, fmt->file);
+ if (fmt->buf)
+ fmt->buf[fmt->len] = c;
+
+ if (c == '\n')
+ fmt->col = 0;
+ else
+ fmt->col ++;
+
+ fmt->len ++;
+
+ fmt->last = c;
+}
+
+static void fmtindent(struct fmt *fmt)
+{
+ int i = fmt->indent;
+ while (i--) {
+ fmtputc(fmt, ' ');
+ fmtputc(fmt, ' ');
+ }
+}
+
+static inline void fmtputs(struct fmt *fmt, char *s)
+{
+ while (*s)
+ fmtputc(fmt, *s++);
+}
+
+static inline void fmtsep(struct fmt *fmt)
+{
+ fmt->sep = 1;
+}
+
+static void fmtstr(struct fmt *fmt, fz_obj *obj)
+{
+ int i;
+ int c;
+
+ fmtputc(fmt, '(');
+ for (i = 0; i < obj->u.s.len; i++)
+ {
+ c = obj->u.s.buf[i];
+ if (c == '\n')
+ fmtputs(fmt, "\\n");
+ else if (c == '\r')
+ fmtputs(fmt, "\\r");
+ else if (c == '\t')
+ fmtputs(fmt, "\\t");
+ else if (c == '\b')
+ fmtputs(fmt, "\\b");
+ else if (c == '\f')
+ fmtputs(fmt, "\\f");
+ else if (c == '(')
+ fmtputs(fmt, "\\(");
+ else if (c == ')')
+ fmtputs(fmt, "\\)");
+ else if (c < 32 || c > 126) {
+ char buf[16];
+ fmtputc(fmt, '\\');
+ sprintf(buf, "%o", c);
+ fmtputs(fmt, buf);
+ //fmtputc(fmt, ((c >> 6) & 7) + '0');
+ //fmtputc(fmt, ((c >> 3) & 7) + '0');
+ //fmtputc(fmt, ((c) & 7) + '0');
+ }
+ else
+ fmtputc(fmt, c);
+ }
+ fmtputc(fmt, ')');
+}
+
+static void fmthex(struct fmt *fmt, fz_obj *obj)
+{
+ int i;
+ int c;
+
+ fmtputc(fmt, '<');
+ for (i = 0; i < obj->u.s.len; i++) {
+ c = (obj->u.s.buf[i] >> 4) & 0x0f;
+ fmtputc(fmt, c < 0xA ? c + '0' : c + 'A' - 0xA);
+ c = (obj->u.s.buf[i]) & 0x0f;
+ fmtputc(fmt, c < 0xA ? c + '0' : c + 'A' - 0xA);
+ }
+ fmtputc(fmt, '>');
+}
+
+static void fmtarray(struct fmt *fmt, fz_obj *obj)
+{
+ int i;
+
+ if (fmt->tight) {
+ fmtputc(fmt, '[');
+ for (i = 0; i < fz_arraylen(obj); i++) {
+ fmtobj(fmt, fz_arrayget(obj, i));
+ fmtsep(fmt);
+ }
+ fmtputc(fmt, ']');
+ }
+ else {
+ fmtputs(fmt, "[ ");
+ for (i = 0; i < fz_arraylen(obj); i++) {
+ if (fmt->col > 60) {
+ fmtputc(fmt, '\n');
+ fmtindent(fmt);
+ }
+ fmtobj(fmt, fz_arrayget(obj, i));
+ fmtputc(fmt, ' ');
+ }
+ fmtputc(fmt, ']');
+ fmtsep(fmt);
+ }
+}
+
+static void fmtdict(struct fmt *fmt, fz_obj *obj)
+{
+ int i;
+ fz_obj *key, *val;
+
+ if (fmt->tight) {
+ fmtputs(fmt, "<<");
+ for (i = 0; i < fz_dictlen(obj); i++) {
+ fmtobj(fmt, fz_dictgetkey(obj, i));
+ fmtsep(fmt);
+ fmtobj(fmt, fz_dictgetval(obj, i));
+ fmtsep(fmt);
+ }
+ fmtputs(fmt, ">>");
+ }
+ else {
+ fmtputs(fmt, "<<\n");
+ fmt->indent ++;
+ for (i = 0; i < fz_dictlen(obj); i++) {
+ key = fz_dictgetkey(obj, i);
+ val = fz_dictgetval(obj, i);
+ fmtindent(fmt);
+ fmtobj(fmt, key);
+ fmtputc(fmt, ' ');
+ if (fz_isarray(val))
+ fmt->indent ++;
+ fmtobj(fmt, val);
+ fmtputc(fmt, '\n');
+ if (fz_isarray(val))
+ fmt->indent --;
+ }
+ fmt->indent --;
+ fmtindent(fmt);
+ fmtputs(fmt, ">>");
+ }
+}
+
+static void fmtobj(struct fmt *fmt, fz_obj *obj)
+{
+ char buf[256];
+
+ if (!obj) {
+ fmtputs(fmt, "<nil>");
+ return;
+ }
+
+ switch (obj->kind)
+ {
+ case FZ_NULL:
+ fmtputs(fmt, "null");
+ break;
+ case FZ_BOOL:
+ fmtputs(fmt, fz_tobool(obj) ? "true" : "false");
+ break;
+ case FZ_INT:
+ sprintf(buf, "%d", fz_toint(obj));
+ fmtputs(fmt, buf);
+ break;
+ case FZ_REAL:
+ sprintf(buf, "%g", fz_toreal(obj));
+ if (strchr(buf, 'e')) /* bad news! */
+ sprintf(buf, fabs(fz_toreal(obj)) > 1 ? "%1.1f" : "%1.8f", fz_toreal(obj));
+ fmtputs(fmt, buf);
+ break;
+ case FZ_STRING:
+ {
+ int added = 0;
+ int i, c;
+ for (i = 0; i < obj->u.s.len; i++) {
+ c = (unsigned char)obj->u.s.buf[i];
+ if (strchr("()\\\n\r\t\b\f", c) != 0)
+ added ++;
+ else if (c < 8)
+ added ++;
+ else if (c < 32)
+ added += 2;
+ else if (c >= 127)
+ added += 3;
+ }
+ if (added < obj->u.s.len)
+ fmtstr(fmt, obj);
+ else
+ fmthex(fmt, obj);
+ }
+ break;
+ case FZ_NAME:
+ sprintf(buf, "/%s", fz_toname(obj));
+ fmtputs(fmt, buf);
+ break;
+ case FZ_ARRAY:
+ fmtarray(fmt, obj);
+ break;
+ case FZ_DICT:
+ fmtdict(fmt, obj);
+ break;
+ case FZ_INDIRECT:
+ sprintf(buf, "%d %d R", obj->u.r.oid, obj->u.r.gid);
+ fmtputs(fmt, buf);
+ break;
+ case FZ_POINTER:
+ sprintf(buf, "$%p", obj->u.p);
+ fmtputs(fmt, buf);
+ break;
+ default:
+ sprintf(buf, "<unknown object type %d>", obj->kind);
+ fmtputs(fmt, buf);
+ break;
+ }
+}
+
+int
+fz_sprintobj(char *s, fz_obj *obj)
+{
+ struct fmt fmt;
+
+ fmt.indent = 0;
+ fmt.tight = 0;
+ fmt.col = 0;
+ fmt.sep = 0;
+ fmt.last = 0;
+
+ fmt.file = nil;
+ fmt.buf = s;
+ fmt.len = 0;
+ fmtobj(&fmt, obj);
+ return fmt.len;
+}
+
+int
+fz_sprintcobj(char *s, fz_obj *obj)
+{
+ struct fmt fmt;
+
+ fmt.indent = 0;
+ fmt.tight = 1;
+ fmt.col = 0;
+ fmt.sep = 0;
+ fmt.last = 0;
+
+ fmt.file = nil;
+ fmt.buf = s;
+ fmt.len = 0;
+ fmtobj(&fmt, obj);
+ return fmt.len;
+}
+
+int
+fz_fprintobj(FILE *file, fz_obj *obj)
+{
+ struct fmt fmt;
+
+ fmt.indent = 0;
+ fmt.tight = 0;
+ fmt.col = 0;
+ fmt.sep = 0;
+ fmt.last = 0;
+
+ fmt.file = file;
+ fmt.buf = nil;
+ fmt.len = 0;
+ fmtobj(&fmt, obj);
+ return fmt.len;
+}
+
+int
+fz_fprintcobj(FILE *file, fz_obj *obj)
+{
+ struct fmt fmt;
+
+ fmt.indent = 0;
+ fmt.tight = 1;
+ fmt.col = 0;
+ fmt.sep = 0;
+ fmt.last = 0;
+
+ fmt.file = file;
+ fmt.buf = nil;
+ fmt.len = 0;
+ fmtobj(&fmt, obj);
+ return fmt.len;
+}
+
diff --git a/object/simple.c b/object/simple.c
new file mode 100644
index 00000000..02da3cec
--- /dev/null
+++ b/object/simple.c
@@ -0,0 +1,230 @@
+#include <fitz.h>
+
+#define NEWOBJ(KIND,SIZE) \
+ fz_obj *o; \
+ o = *op = fz_malloc(SIZE); \
+ if (!o) return fz_outofmem; \
+ o->kind = KIND; \
+ o->refcount = 1
+
+fz_error *
+fz_newnull(fz_obj **op)
+{
+ NEWOBJ(FZ_NULL, sizeof (fz_obj));
+ return nil;
+}
+
+fz_error *
+fz_newbool(fz_obj **op, int b)
+{
+ NEWOBJ(FZ_BOOL, sizeof (fz_obj));
+ o->u.b = b;
+ return nil;
+}
+
+fz_error *
+fz_newint(fz_obj **op, int i)
+{
+ NEWOBJ(FZ_INT, sizeof (fz_obj));
+ o->u.i = i;
+ return nil;
+}
+
+fz_error *
+fz_newreal(fz_obj **op, float f)
+{
+ NEWOBJ(FZ_REAL, sizeof (fz_obj));
+ o->u.f = f;
+ return nil;
+}
+
+fz_error *
+fz_newstring(fz_obj **op, char *str, int len)
+{
+ NEWOBJ(FZ_STRING, offsetof(fz_obj, u.s.buf) + len);
+ o->u.s.len = len;
+ memcpy(o->u.s.buf, str, len);
+ return nil;
+}
+
+fz_error *
+fz_newname(fz_obj **op, char *str)
+{
+ NEWOBJ(FZ_NAME, offsetof(fz_obj, u.n) + strlen(str) + 1);
+ strcpy(o->u.n, str);
+ return nil;
+}
+
+fz_error *
+fz_newindirect(fz_obj **op, int objid, int genid)
+{
+ NEWOBJ(FZ_INDIRECT, sizeof (fz_obj));
+ o->u.r.oid = objid;
+ o->u.r.gid = genid;
+ return nil;
+}
+
+fz_error *
+fz_newpointer(fz_obj **op, void *p)
+{
+ NEWOBJ(FZ_POINTER, sizeof (fz_obj));
+ o->u.p = p;
+ return nil;
+}
+
+fz_obj *
+fz_keepobj(fz_obj *o)
+{
+ o->refcount ++;
+ return o;
+}
+
+fz_obj *
+fz_dropobj(fz_obj *o)
+{
+ o->refcount --;
+ if (o->refcount == 0) {
+ if (o->kind == FZ_ARRAY)
+ fz_freearray(o);
+ else if (o->kind == FZ_DICT)
+ fz_freedict(o);
+ else
+ fz_free(o);
+ }
+ return nil;
+}
+
+int
+fz_isnull(fz_obj *obj)
+{
+ return obj ? obj->kind == FZ_NULL : 0;
+}
+
+int
+fz_isbool(fz_obj *obj)
+{
+ return obj ? obj->kind == FZ_BOOL : 0;
+}
+
+int
+fz_isint(fz_obj *obj)
+{
+ return obj ? obj->kind == FZ_INT : 0;
+}
+
+int
+fz_isreal(fz_obj *obj)
+{
+ return obj ? obj->kind == FZ_REAL : 0;
+}
+
+int
+fz_isstring(fz_obj *obj)
+{
+ return obj ? obj->kind == FZ_STRING : 0;
+}
+
+int
+fz_isname(fz_obj *obj)
+{
+ return obj ? obj->kind == FZ_NAME : 0;
+}
+
+int
+fz_isarray(fz_obj *obj)
+{
+ return obj ? obj->kind == FZ_ARRAY : 0;
+}
+
+int
+fz_isdict(fz_obj *obj)
+{
+ return obj ? obj->kind == FZ_DICT : 0;
+}
+
+int
+fz_isindirect(fz_obj *obj)
+{
+ return obj ? obj->kind == FZ_INDIRECT : 0;
+}
+
+int
+fz_ispointer(fz_obj *obj)
+{
+ return obj ? obj->kind == FZ_POINTER : 0;
+}
+
+int
+fz_tobool(fz_obj *obj)
+{
+ if (fz_isbool(obj))
+ return obj->u.b;
+ return 0;
+}
+
+int
+fz_toint(fz_obj *obj)
+{
+ if (fz_isint(obj))
+ return obj->u.i;
+ return 0;
+}
+
+float
+fz_toreal(fz_obj *obj)
+{
+ if (fz_isreal(obj))
+ return obj->u.f;
+ if (fz_isint(obj))
+ return obj->u.i;
+ return 0;
+}
+
+char *
+fz_toname(fz_obj *obj)
+{
+ if (fz_isname(obj))
+ return obj->u.n;
+ return "";
+}
+
+char *
+fz_tostringbuf(fz_obj *obj)
+{
+ if (fz_isstring(obj))
+ return obj->u.s.buf;
+ return "";
+}
+
+int
+fz_tostringlen(fz_obj *obj)
+{
+ if (fz_isstring(obj))
+ return obj->u.s.len;
+ return 0;
+}
+
+int
+fz_toobjid(fz_obj *obj)
+{
+ if (fz_isindirect(obj))
+ return obj->u.r.oid;
+ return 0;
+}
+
+int
+fz_togenid(fz_obj *obj)
+{
+ if (fz_isindirect(obj))
+ return obj->u.r.gid;
+ return 0;
+}
+
+void *
+fz_topointer(fz_obj *obj)
+{
+ if (fz_ispointer(obj))
+ return obj->u.p;
+ return nil;
+}
+