diff options
Diffstat (limited to 'fitz/obj_parse.c')
-rw-r--r-- | fitz/obj_parse.c | 492 |
1 files changed, 492 insertions, 0 deletions
diff --git a/fitz/obj_parse.c b/fitz/obj_parse.c new file mode 100644 index 00000000..48d59538 --- /dev/null +++ b/fitz/obj_parse.c @@ -0,0 +1,492 @@ +#include "fitz-base.h" +#include "fitz-stream.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) +{ + fz_error *error; + 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; + + error = fz_newname(obj, buf); + if (error) + return fz_rethrow(error, "cannot create name"); + return fz_okay; +} + +static fz_error *parsenumber(fz_obj **obj, char **sp) +{ + fz_error *error; + 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, '.')) + error = fz_newreal(obj, atof(buf)); + else + error = fz_newint(obj, atoi(buf)); + + if (error) + return fz_rethrow(error, "cannot parse number"); + return fz_okay; +} + +static fz_error *parsedict(fz_obj **obj, char **sp, struct vap *v) +{ + fz_error *error = fz_okay; + fz_obj *dict = nil; + fz_obj *key = nil; + fz_obj *val = nil; + char *s = *sp; + + error = fz_newdict(&dict, 8); + if (error) + return fz_rethrow(error, "cannot create dict"); + + s += 2; /* skip "<<" */ + + while (*s) + { + skipwhite(&s); + + /* end-of-dict marker >> */ + if (*s == '>') + { + s ++; + if (*s == '>') + { + s ++; + break; + } + error = fz_throw("malformed >> marker"); + goto cleanup; + } + + /* non-name as key, bail */ + if (*s != '/') + { + error = fz_throw("key is not a name"); + goto cleanup; + } + + error = parsename(&key, &s); + if (error) + { + error = fz_rethrow(error, "cannot parse key"); + goto cleanup; + } + + skipwhite(&s); + + error = parseobj(&val, &s, v); + if (error) + { + error = fz_rethrow(error, "cannot parse value"); + goto cleanup; + } + + error = fz_dictput(dict, key, val); + if (error) + { + error = fz_rethrow(error, "cannot insert dict entry"); + goto cleanup; + } + + fz_dropobj(val); val = nil; + fz_dropobj(key); key = nil; + } + + *obj = dict; + *sp = s; + return fz_okay; + +cleanup: + if (val) fz_dropobj(val); + if (key) fz_dropobj(key); + if (dict) fz_dropobj(dict); + *obj = nil; + *sp = s; + return error; /* already rethrown */ +} + +static fz_error *parsearray(fz_obj **obj, char **sp, struct vap *v) +{ + fz_error *error; + fz_obj *a; + fz_obj *o; + char *s = *sp; + + error = fz_newarray(&a, 8); + if (error) + return fz_rethrow(error, "cannot create array"); + + s ++; /* skip '[' */ + + while (*s) + { + skipwhite(&s); + + if (*s == ']') + { + s ++; + break; + } + + error = parseobj(&o, &s, v); + if (error) + { + fz_dropobj(a); + return fz_rethrow(error, "cannot parse item"); + } + + error = fz_arraypush(a, o); + if (error) + { + fz_dropobj(o); + fz_dropobj(a); + return fz_rethrow(error, "cannot add item to array"); + } + + fz_dropobj(o); + } + + *obj = a; + *sp = s; + return fz_okay; +} + +static fz_error *parsestring(fz_obj **obj, char **sp) +{ + fz_error *error; + 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; + + error = fz_newstring(obj, buf, p - buf - 1); + if (error) + return fz_rethrow(error, "cannot create string"); + return fz_okay; +} + +static fz_error *parsehexstring(fz_obj **obj, char **sp) +{ + fz_error *error; + 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; + error = fz_newstring(obj, buf, p - buf); + if (error) + return fz_rethrow(error, "cannot create string"); + return fz_okay; +} + +static fz_error *parseobj(fz_obj **obj, char **sp, struct vap *v) +{ + fz_error *error; + char buf[32]; + int oid, gid, len; + char *tmp; + char *s = *sp; + + if (*s == '\0') + return fz_throw("end of data"); + + skipwhite(&s); + + error = fz_okay; + + if (v != nil && *s == '%') + { + s ++; + + switch (*s) + { + case 'p': error = fz_newpointer(obj, va_arg(v->ap, void*)); break; + case 'o': *obj = fz_keepobj(va_arg(v->ap, fz_obj*)); break; + case 'b': error = fz_newbool(obj, va_arg(v->ap, int)); break; + case 'i': error = fz_newint(obj, va_arg(v->ap, int)); break; + case 'f': error = fz_newreal(obj, (float)va_arg(v->ap, double)); break; + case 'n': error = fz_newname(obj, va_arg(v->ap, char*)); break; + case 'r': + oid = va_arg(v->ap, int); + gid = va_arg(v->ap, int); + error = fz_newindirect(obj, oid, gid); + break; + case 's': + tmp = va_arg(v->ap, char*); + error = fz_newstring(obj, tmp, strlen(tmp)); + break; + case '#': + tmp = va_arg(v->ap, char*); + len = va_arg(v->ap, int); + error = fz_newstring(obj, tmp, len); + break; + default: + error = fz_throw("unknown format specifier in packobj: '%c'", *s); + break; + } + + if (error) + error = fz_rethrow(error, "cannot create object for %% format"); + + s ++; + } + + else if (*s == '/') + { + error = parsename(obj, &s); + if (error) + error = fz_rethrow(error, "cannot parse name"); + } + + else if (*s == '(') + { + error = parsestring(obj, &s); + if (error) + error = fz_rethrow(error, "cannot parse string"); + } + + else if (*s == '<') + { + if (s[1] == '<') + { + error = parsedict(obj, &s, v); + if (error) + error = fz_rethrow(error, "cannot parse dict"); + } + else + { + error = parsehexstring(obj, &s); + if (error) + error = fz_rethrow(error, "cannot parse hex string"); + } + } + + else if (*s == '[') + { + error = parsearray(obj, &s, v); + if (error) + error = fz_rethrow(error, "cannot parse array"); + } + + else if (*s == '-' || *s == '.' || (*s >= '0' && *s <= '9')) + { + error = parsenumber(obj, &s); + if (error) + error = fz_rethrow(error, "cannot parse number"); + } + + else if (isregular(*s)) + { + parsekeyword(&s, buf, buf + sizeof buf); + + if (strcmp("true", buf) == 0) + { + error = fz_newbool(obj, 1); + if (error) + error = fz_rethrow(error, "cannot create bool (true)"); + } + else if (strcmp("false", buf) == 0) + { + error = fz_newbool(obj, 0); + if (error) + error = fz_rethrow(error, "cannot create bool (false)"); + } + else if (strcmp("null", buf) == 0) + { + error = fz_newnull(obj); + if (error) + error = fz_rethrow(error, "cannot create null object"); + } + else + error = fz_throw("undefined keyword %s", buf); + } + + else + error = fz_throw("syntax error: unknown byte 0x%d", *s); + + *sp = s; + return error; /* already rethrown */ +} + +fz_error * +fz_packobj(fz_obj **op, char *fmt, ...) +{ + fz_error *error; + struct vap v; + va_list ap; + + va_start(ap, fmt); + va_copy(v.ap, ap); + + error = parseobj(op, &fmt, &v); + + va_end(ap); + + if (error) + return fz_rethrow(error, "cannot parse object"); + return fz_okay; +} + +fz_error * +fz_parseobj(fz_obj **op, char *str) +{ + return parseobj(op, &str, nil); +} + |