Unified · Split
| @@ -1,40 +1,34 @@ | ||
| 1 | 1 | #include <u.h> |
| 2 | 2 | #include <libc.h> |
| 3 | −#include <ctype.h> | |
| 3 | +#include <bio.h> | |
| 4 | 4 | #include "pdf.h" |
| 5 | 5 | |
| 6 | +/* 7.3.6 Array Objects */ | |
| 7 | + | |
| 6 | 8 | Object * |
| 7 | −pdfarray(char *p, char **e, int len) | |
| 9 | +pdfarray(Biobuf *b) | |
| 8 | 10 | { |
| 9 | 11 | Object *o, *m; |
| 10 | 12 | Object **a; |
| 11 | − | |
| 12 | − o = nil; | |
| 13 | − if(len < 3 || p[0] != '['){ | |
| 14 | − werrstr("invalid array"); | |
| 15 | − goto err; | |
| 16 | − } | |
| 17 | − p++; | |
| 18 | − len--; | |
| 13 | + int c, noel; | |
| 19 | 14 | |
| 20 | 15 | o = calloc(1, sizeof(*o)); |
| 21 | 16 | o->type = Oarray; |
| 22 | − for(; len >= 1;){ | |
| 23 | − for(; len > 0 && isws(*p); p++, len--); | |
| 24 | − if(len < 1){ | |
| 25 | − werrstr("too short"); | |
| 26 | − goto err; | |
| 27 | − } | |
| 17 | + Bgetc(b); /* throw away '[' */ | |
| 28 | 18 | |
| 29 | − if(p[0] == ']'){ | |
| 30 | − p++; | |
| 19 | + for(noel = 0;;){ | |
| 20 | + if((c = Bgetc(b)) < 0 || c == ']') | |
| 31 | 21 | break; |
| 22 | + if(noel){ | |
| 23 | + werrstr("no ']'"); | |
| 24 | + goto err; | |
| 32 | 25 | } |
| 33 | 26 | |
| 34 | − if((m = pdfobject(p, e, len)) == nil) | |
| 35 | − goto err; | |
| 36 | − len -= *e - p; | |
| 37 | − p = *e; | |
| 27 | + Bungetc(b); | |
| 28 | + if((m = pdfobject(b)) == nil){ | |
| 29 | + noel = 1; | |
| 30 | + continue; | |
| 31 | + } | |
| 38 | 32 | |
| 39 | 33 | if((a = realloc(o->array.e, (o->array.ne+1)*sizeof(Object*))) == nil){ |
| 40 | 34 | freeobject(m); |
| @@ -45,11 +39,14 @@ pdfarray(char *p, char **e, int len) · 4 unmodified lines | ||
| 45 | 39 | a[o->array.ne++] = m; |
| 46 | 40 | } |
| 47 | 41 | |
| 48 | − *e = p; | |
| 42 | + if(c != ']'){ | |
| 43 | + werrstr("no ']'"); | |
| 44 | + goto err; | |
| 45 | + } | |
| 49 | 46 | |
| 50 | 47 | return o; |
| 51 | 48 | err: |
| 49 | + werrstr("array: %r"); | |
| 52 | 50 | freeobject(o); |
| 53 | − | |
| 54 | 51 | return nil; |
| 55 | 52 | } |
| @@ -1,70 +1,77 @@ | ||
| 1 | 1 | #include <u.h> |
| 2 | 2 | #include <libc.h> |
| 3 | −#include <ctype.h> | |
| 3 | +#include <bio.h> | |
| 4 | 4 | #include "pdf.h" |
| 5 | 5 | |
| 6 | +/* 7.3.7 Dictionary Objects */ | |
| 7 | + | |
| 6 | 8 | Object * |
| 7 | −pdfdict(char *p, char **e, int len) | |
| 9 | +pdfdict(Biobuf *b) | |
| 8 | 10 | { |
| 9 | 11 | Object *o, *k, *v; |
| 10 | 12 | KeyValue *kv; |
| 13 | + int c, nokey; | |
| 11 | 14 | |
| 12 | − o = nil; | |
| 13 | − if(len < 4 || p[0] != '<' || p[1] != '<'){ | |
| 14 | − werrstr("invalid dict"); | |
| 15 | − goto err; | |
| 16 | − } | |
| 17 | − p += 2; | |
| 18 | − len -= 2; | |
| 15 | + /* skip '<<' */ | |
| 16 | + Bseek(b, 2, 1); | |
| 19 | 17 | |
| 18 | + k = v = nil; | |
| 20 | 19 | o = calloc(1, sizeof(*o)); |
| 21 | 20 | o->type = Odict; |
| 22 | − for(; len >= 2;){ | |
| 23 | − for(; len > 2 && isws(*p); p++, len--); | |
| 24 | − if(len < 1){ | |
| 25 | − werrstr("too short"); | |
| 21 | + for(nokey = 0;;){ | |
| 22 | + if((c = Bgetc(b)) < 0) | |
| 23 | + goto err; | |
| 24 | + if(c == '>'){ | |
| 25 | + if(Bgetc(b) == '>') | |
| 26 | + break; | |
| 27 | + werrstr("no '>>'"); | |
| 26 | 28 | goto err; |
| 27 | 29 | } |
| 28 | − | |
| 29 | − if(p[0] == '>' && p[1] == '>'){ | |
| 30 | − p += 2; | |
| 31 | − break; | |
| 30 | + if(nokey){ | |
| 31 | + werrstr("no '>>'"); | |
| 32 | + goto err; | |
| 32 | 33 | } |
| 33 | 34 | |
| 34 | − if((k = pdfobject(p, e, len)) == nil) | |
| 35 | + Bungetc(b); | |
| 36 | + if((k = pdfobject(b)) == nil){ | |
| 37 | + nokey = 1; | |
| 38 | + continue; | |
| 39 | + } | |
| 40 | + if((v = pdfobject(b)) == nil) | |
| 35 | 41 | goto err; |
| 36 | 42 | if(k->type != Oname){ |
| 37 | − freeobject(k); | |
| 38 | 43 | werrstr("expected name as a key"); |
| 39 | 44 | goto err; |
| 40 | 45 | } |
| 41 | − len -= *e - p; | |
| 42 | − p = *e; | |
| 43 | 46 | |
| 44 | − if((v = pdfobject(p, e, len)) == nil){ | |
| 45 | − freeobject(k); | |
| 47 | + if((kv = realloc(o->dict.kv, (o->dict.nkv+1)*sizeof(KeyValue))) == nil) | |
| 46 | 48 | goto err; |
| 47 | − } | |
| 48 | − len -= *e - p; | |
| 49 | − p = *e; | |
| 50 | − | |
| 51 | − if((kv = realloc(o->dict.kv, (o->dict.nkv+1)*sizeof(KeyValue))) == nil){ | |
| 52 | − freeobject(k); | |
| 53 | − freeobject(v); | |
| 54 | − goto err; | |
| 55 | − } | |
| 56 | 49 | |
| 57 | 50 | o->dict.kv = kv; |
| 58 | 51 | kv[o->dict.nkv].key = strdup(k->name); |
| 59 | 52 | freeobject(k); |
| 60 | 53 | kv[o->dict.nkv++].value = v; |
| 54 | + k = v = nil; | |
| 61 | 55 | } |
| 62 | 56 | |
| 63 | − *e = p; | |
| 64 | − | |
| 65 | 57 | return o; |
| 66 | 58 | err: |
| 67 | 59 | freeobject(o); |
| 60 | + freeobject(k); | |
| 61 | + freeobject(v); | |
| 62 | + werrstr("dict: %r"); | |
| 68 | 63 | |
| 69 | 64 | return nil; |
| 70 | 65 | } |
| 66 | + | |
| 67 | +Object * | |
| 68 | +pdfdictget(Object *o, char *name) | |
| 69 | +{ | |
| 70 | + int i; | |
| 71 | + | |
| 72 | + if(o == nil || o->type != Odict || name == nil) | |
| 73 | + return nil; | |
| 74 | + for(i = 0; i < o->dict.nkv && strcmp(name, o->dict.kv[i].key) != 0; i++); | |
| 75 | + | |
| 76 | + return i < o->dict.nkv ? o->dict.kv[i].value : nil; | |
| 77 | +} | |
| @@ -1,23 +1,34 @@ | ||
| 1 | 1 | #include <u.h> |
| 2 | 2 | #include <libc.h> |
| 3 | +#include <bio.h> | |
| 3 | 4 | #include "pdf.h" |
| 4 | 5 | |
| 5 | 6 | int |
| 6 | 7 | pdfeval(Pdf *pdf, Object *o) |
| 7 | 8 | { |
| 9 | + Object *d; | |
| 8 | 10 | Xref *x; |
| 9 | 11 | int i; |
| 10 | 12 | |
| 11 | − if(o->type != Oindir) | |
| 13 | + if(o == nil || o->type != Oindir) | |
| 12 | 14 | return 0; |
| 13 | 15 | |
| 14 | − for(i = 0; i < pdf->nxref; i++){ | |
| 15 | − x = &pdf->xref[i]; | |
| 16 | − if(x->id == o->indir.id) | |
| 17 | − return 0; | |
| 16 | + for(i = 0; i < pdf->nxref && pdf->xref[i].id != o->indir.id; i++); | |
| 17 | + if(i >= pdf->nxref){ | |
| 18 | + werrstr("no object id %d in xref", o->indir.id); | |
| 19 | + return -1; | |
| 18 | 20 | } |
| 21 | + x = &pdf->xref[i]; | |
| 19 | 22 | |
| 20 | − werrstr("no object id %d in xref", o->indir.id); | |
| 23 | + if(Bseek(pdf->bio, x->off, 0) != x->off){ | |
| 24 | + werrstr("xref seek failed"); | |
| 25 | + return -1; | |
| 26 | + } | |
| 27 | + if((d = pdfobject(pdf->bio)) == nil) | |
| 28 | + return -1; | |
| 29 | + *o = *d; | |
| 30 | + memset(d, 0, sizeof(*d)); | |
| 31 | + freeobject(d); | |
| 21 | 32 | |
| 22 | − return -1; | |
| 33 | + return 0; | |
| 23 | 34 | } |
| @@ -1,5 +1,6 @@ | ||
| 1 | 1 | #include <u.h> |
| 2 | 2 | #include <libc.h> |
| 3 | +#include <bio.h> | |
| 3 | 4 | #include "pdf.h" |
| 4 | 5 | |
| 5 | 6 | /* |
| @@ -1,8 +1,11 @@ | ||
| 1 | 1 | #include <u.h> |
| 2 | 2 | #include <libc.h> |
| 3 | 3 | #include <thread.h> |
| 4 | +#include <bio.h> | |
| 4 | 5 | #include "pdf.h" |
| 5 | 6 | |
| 7 | +int mainstacksize = 32768; | |
| 8 | + | |
| 6 | 9 | static void |
| 7 | 10 | usage(void) |
| 8 | 11 | { |
| @@ -15,6 +18,7 @@ threadmain(int argc, char **argv) · 6 unmodified lines | ||
| 15 | 18 | { |
| 16 | 19 | int fd; |
| 17 | 20 | Pdf *pdf; |
| 21 | + Object *v; | |
| 18 | 22 | |
| 19 | 23 | quotefmtinstall(); |
| 20 | 24 | |
| @@ -40,7 +44,11 @@ threadmain(int argc, char **argv) · 19 unmodified lines | ||
| 40 | 44 | if((fd = open(argv[0], OREAD)) < 0) |
| 41 | 45 | sysfatal("%r"); |
| 42 | 46 | if((pdf = pdfopen(fd)) == nil) |
| 43 | − sysfatal("pdfopen: %r"); | |
| 47 | + sysfatal("%r"); | |
| 48 | + if((v = pdfdictget(pdf->info, "Creator")) != nil) | |
| 49 | + fprint(2, "creator: %s\n", v->str); | |
| 50 | + if((v = pdfdictget(pdf->info, "Producer")) != nil) | |
| 51 | + fprint(2, "producer: %s\n", v->str); | |
| 44 | 52 | pdfclose(pdf); |
| 45 | 53 | |
| 46 | 54 | threadexitsall(nil); |
| @@ -1,10 +1,11 @@ | ||
| 1 | 1 | #include <u.h> |
| 2 | 2 | #include <libc.h> |
| 3 | +#include <bio.h> | |
| 3 | 4 | #include "pdf.h" |
| 4 | 5 | |
| 5 | 6 | /* 7.2.2 whitespace */ |
| 6 | 7 | int |
| 7 | −isws(char c) | |
| 8 | +isws(int c) | |
| 8 | 9 | { |
| 9 | 10 | return /* \0 is missing on purpose */ |
| 10 | 11 | c == '\t' || c == '\n' || c == '\f' || c == '\r' || |
| @@ -13,7 +14,7 @@ isws(char c) · 2 unmodified lines | ||
| 13 | 14 | |
| 14 | 15 | /* 7.2.2 delimeters */ |
| 15 | 16 | int |
| 16 | −isdelim(char c) | |
| 17 | +isdelim(int c) | |
| 17 | 18 | { |
| 18 | 19 | return |
| 19 | 20 | c == '(' || c == ')' || c == '<' || c == '>' || |
| @@ -1,104 +1,132 @@ | ||
| 1 | 1 | #include <u.h> |
| 2 | 2 | #include <libc.h> |
| 3 | +#include <bio.h> | |
| 3 | 4 | #include "pdf.h" |
| 4 | 5 | |
| 5 | −int | |
| 6 | −pdfname(char *p, char **e, int len) | |
| 6 | +/* 7.3.5 Name Objects */ | |
| 7 | + | |
| 8 | +Object * | |
| 9 | +pdfname(Biobuf *b) | |
| 7 | 10 | { |
| 8 | − int i, o; | |
| 11 | + Object *o; | |
| 12 | + char *s, *r, hex[3]; | |
| 13 | + int c, sz, maxsz; | |
| 9 | 14 | |
| 10 | − if(len < 2){ | |
| 11 | − werrstr("too short"); | |
| 12 | − goto err; | |
| 13 | − } | |
| 15 | + Bgetc(b); /* skip '/' */ | |
| 14 | 16 | |
| 15 | − if(p[0] != '/'){ | |
| 16 | − werrstr("invalid first char"); | |
| 17 | + maxsz = 32; | |
| 18 | + if((s = malloc(maxsz)) == nil) | |
| 17 | 19 | goto err; |
| 18 | − } | |
| 19 | 20 | |
| 20 | − for(i = 1, o = 0; i < len; i++){ | |
| 21 | − if(p[i] < '!' || p[i] > '~'){ | |
| 22 | − if(isws(p[i]) || isdelim(p[i])) | |
| 21 | + for(sz = 0;;){ | |
| 22 | + if((c = Bgetc(b)) < 0){ | |
| 23 | + if(c == Beof) | |
| 23 | 24 | break; |
| 24 | − werrstr("invalid char %02x", p[i]); | |
| 25 | 25 | goto err; |
| 26 | 26 | } |
| 27 | − if(p[i] == '#'){ | |
| 28 | − i++; | |
| 29 | − if(i+2 > len){ | |
| 30 | − werrstr("hex too short"); | |
| 27 | + | |
| 28 | + if(isws(c) || isdelim(c)){ | |
| 29 | + Bungetc(b); | |
| 30 | + break; | |
| 31 | + } | |
| 32 | + if(c < '!' || c > '~'){ | |
| 33 | + werrstr("invalid char %02x", c); | |
| 34 | + goto err; | |
| 35 | + } | |
| 36 | + if(c == '#'){ | |
| 37 | + if((c = Bgetc(b)) < 0) | |
| 31 | 38 | goto err; |
| 32 | − } | |
| 33 | − if(dec16((uchar*)p+o, 1, p+i, 2) != 1){ | |
| 39 | + hex[0] = c; | |
| 40 | + if((c = Bgetc(b)) < 0) | |
| 41 | + goto err; | |
| 42 | + hex[1] = c; | |
| 43 | + if(dec16((uchar*)hex, 1, hex, 2) != 1){ | |
| 34 | 44 | werrstr("invalid hex"); |
| 35 | 45 | goto err; |
| 36 | 46 | } |
| 37 | − o++; | |
| 38 | − i++; | |
| 39 | − }else{ | |
| 40 | − p[o++] = p[i]; | |
| 47 | + c = hex[0]; | |
| 41 | 48 | } |
| 49 | + if(sz+1 >= maxsz){ | |
| 50 | + maxsz *= 2; | |
| 51 | + if((r = realloc(s, maxsz)) == nil) | |
| 52 | + goto err; | |
| 53 | + s = r; | |
| 54 | + } | |
| 55 | + s[sz++] = c; | |
| 42 | 56 | } |
| 43 | 57 | |
| 44 | − p[o] = 0; | |
| 45 | − *e = p + i; | |
| 58 | + if((o = malloc(sizeof(*o) + sz + 1)) != nil){ | |
| 59 | + memmove(o->name, s, sz); | |
| 60 | + o->name[sz] = 0; | |
| 61 | + o->type = Oname; | |
| 62 | + free(s); | |
| 63 | + return o; | |
| 64 | + } | |
| 46 | 65 | |
| 47 | − return o; | |
| 48 | 66 | err: |
| 49 | 67 | werrstr("name: %r"); |
| 50 | − return -1; | |
| 68 | + free(s); | |
| 69 | + return nil; | |
| 51 | 70 | } |
| 52 | 71 | |
| 53 | 72 | #ifdef TEST |
| 54 | 73 | static struct { |
| 55 | − char *i; | |
| 56 | − int len; | |
| 57 | − char *o; | |
| 58 | − int r; | |
| 59 | − int e; | |
| 74 | + char *in; | |
| 75 | + char *out; | |
| 60 | 76 | }t[] = { |
| 61 | − {"/SimpleName", 11, "SimpleName", 10}, | |
| 62 | − {"WrongName", 9, nil, -1}, | |
| 63 | − {"/.$()", 5, ".$()", 4}, | |
| 64 | − {"/#30", 4, "0", 1}, | |
| 65 | − {"/#3", 3, nil, -1}, | |
| 66 | − {"/#G0", 4, nil, -1}, | |
| 67 | − {"/#", 2, nil, -1}, | |
| 68 | − {"/Wrong Char", 11, nil, -1}, | |
| 69 | − {"/\xff", 2, nil, -1}, | |
| 77 | + {"/SimpleName", "SimpleName"}, | |
| 78 | + {"/.$()", ".$"}, | |
| 79 | + {"/#30", "0"}, | |
| 80 | + {"/#3", nil}, | |
| 81 | + {"/#G0", nil}, | |
| 82 | + {"/#", nil}, | |
| 83 | + {"/Space Between", "Space"}, | |
| 84 | + {"/Two/Names", "Two"}, | |
| 85 | + {"/\xff", nil,}, | |
| 70 | 86 | }; |
| 71 | 87 | |
| 88 | +static char *s; | |
| 89 | +static int off, n; | |
| 90 | + | |
| 91 | +static int | |
| 92 | +rd(Biobufhdr *, void *data, long sz) | |
| 93 | +{ | |
| 94 | + if(sz > n-off) | |
| 95 | + sz = n-off; | |
| 96 | + memmove(data, s+off, sz); | |
| 97 | + off += sz; | |
| 98 | + return sz; | |
| 99 | +} | |
| 100 | + | |
| 72 | 101 | void |
| 73 | 102 | test_pdfname(void) |
| 74 | 103 | { |
| 75 | − char *e; | |
| 76 | − int i, r; | |
| 104 | + Object *o; | |
| 105 | + Biobuf b; | |
| 106 | + int i; | |
| 77 | 107 | |
| 78 | 108 | fprint(2, "pdfname\n"); |
| 79 | 109 | for(i = 0; i < nelem(t); i++){ |
| 110 | + s = t[i].in; | |
| 111 | + n = strlen(s); | |
| 112 | + off = 0; | |
| 113 | + Binit(&b, -1, OREAD); | |
| 114 | + Biofn(&b, rd); | |
| 115 | + | |
| 80 | 116 | fprint(2, "\t%d: ", i); |
| 81 | − r = pdfname(t[i].i, &e, t[i].len); | |
| 82 | − if(r != t[i].r){ | |
| 83 | − fprint(2, "expected r=%d, got %d", t[i].r, r); | |
| 84 | − if(r < 0) | |
| 85 | − fprint(2, " (%r)\n"); | |
| 86 | − else | |
| 87 | − fprint(2, "\n"); | |
| 88 | − continue; | |
| 89 | − }else if(r >= 0){ | |
| 90 | − if(t[i].i+t[i].len != e){ | |
| 91 | − fprint(2, "expected e=%p, got %p\n", t[i].i+t[i].len, e); | |
| 92 | − continue; | |
| 93 | − }else if(strcmp(t[i].o, t[i].i) != 0){ | |
| 94 | − fprint(2, "expected %q, got %q\n", t[i].o, t[i].i); | |
| 95 | − continue; | |
| 96 | − } | |
| 97 | − } | |
| 98 | − fprint(2, "OK"); | |
| 99 | − if(r < 0) | |
| 100 | − fprint(2, " (%r)"); | |
| 101 | − fprint(2, "\n"); | |
| 117 | + o = pdfname(&b); | |
| 118 | + if(o == nil && t[i].out != nil) | |
| 119 | + fprint(2, "ERROR: expected %q, got error: %r\n", t[i].out); | |
| 120 | + else if(o != nil && t[i].out == nil) | |
| 121 | + fprint(2, "ERROR: expected error, got %q\n", o->name); | |
| 122 | + else if(o == nil && t[i].out == nil) | |
| 123 | + fprint(2, "OK (%r)\n"); | |
| 124 | + else if(strcmp(o->name, t[i].out) != 0) | |
| 125 | + fprint(2, "ERROR: expected %q, got %q\n", t[i].out, o->name); | |
| 126 | + else | |
| 127 | + fprint(2, "OK\n"); | |
| 128 | + freeobject(o); | |
| 129 | + Bterm(&b); | |
| 102 | 130 | } |
| 103 | 131 | } |
| 104 | 132 | #endif |
| @@ -1,105 +1,165 @@ | ||
| 1 | 1 | #include <u.h> |
| 2 | 2 | #include <libc.h> |
| 3 | 3 | #include <ctype.h> |
| 4 | +#include <bio.h> | |
| 4 | 5 | #include "pdf.h" |
| 5 | 6 | |
| 7 | +Object *pdfstring(Biobuf *b); | |
| 8 | +Object *pdfname(Biobuf *b); | |
| 9 | +Object *pdfarray(Biobuf *b); | |
| 10 | +Object *pdfdict(Biobuf *b); | |
| 11 | + | |
| 12 | +static Object null = { | |
| 13 | + .type = Onull, | |
| 14 | +}; | |
| 15 | + | |
| 16 | +/* General function to parse an object of any type. */ | |
| 6 | 17 | Object * |
| 7 | −pdfobject(char *p, char **e, int len) | |
| 18 | +pdfobject(void *b) | |
| 8 | 19 | { |
| 9 | − Object *o; | |
| 10 | − char *s, *p0; | |
| 11 | − int sz, gen; | |
| 12 | − | |
| 13 | − o = nil; | |
| 14 | − for(; len > 0 && isws(*p); p++, len--); | |
| 15 | − if(len < 1){ | |
| 16 | − werrstr("too short"); | |
| 17 | − goto err; | |
| 18 | − } | |
| 20 | + Object *o, *o2; | |
| 21 | + vlong off; | |
| 22 | + int c, tf; | |
| 19 | 23 | |
| 20 | − if(*p < 1){ | |
| 21 | − werrstr("unexpected non-ascii char"); | |
| 24 | + o = o2 = nil; | |
| 25 | + do; while(isws(c = Bgetc(b))); | |
| 26 | + if(c < 0) | |
| 22 | 27 | goto err; |
| 23 | − } | |
| 24 | 28 | |
| 25 | − switch(*p){ | |
| 29 | + switch(c){ | |
| 26 | 30 | case '<': /* dictionary or a string */ |
| 27 | − if(len < 2){ | |
| 28 | − werrstr("too short"); | |
| 29 | − goto err; | |
| 30 | − } | |
| 31 | − if(p[1] == '<'){ /* dictionary */ | |
| 32 | − o = pdfdict(p, e, len); | |
| 33 | − break; | |
| 31 | + c = Bgetc(b); | |
| 32 | + if(c == '<'){ | |
| 33 | + Bseek(b, -2, 1); | |
| 34 | + return pdfdict(b); | |
| 34 | 35 | } |
| 36 | + Bungetc(b); | |
| 35 | 37 | /* fall through */ |
| 36 | 38 | |
| 37 | − case '(': /* string */ | |
| 38 | − if((sz = pdfstring(p, e, len)) < 0) | |
| 39 | − goto err; | |
| 40 | − if((o = malloc(sizeof(*o)+sz+1)) != nil){ | |
| 41 | − o->type = Ostr; | |
| 42 | − o->str = (char*)(o+1); | |
| 43 | − strcpy(o->str, p); | |
| 44 | − } | |
| 45 | − break; | |
| 39 | + case '(': | |
| 40 | + Bungetc(b); | |
| 41 | + return pdfstring(b); | |
| 46 | 42 | |
| 47 | 43 | case '/': |
| 48 | − if((sz = pdfname(p, e, len)) < 0) | |
| 49 | − goto err; | |
| 50 | − if((o = malloc(sizeof(*o)+sz+1)) != nil){ | |
| 51 | − o->type = Oname; | |
| 52 | − o->str = (char*)(o+1); | |
| 53 | − strcpy(o->str, p); | |
| 54 | − } | |
| 55 | − break; | |
| 44 | + Bungetc(b); | |
| 45 | + return pdfname(b); | |
| 56 | 46 | |
| 57 | 47 | case '[': |
| 58 | − o = pdfarray(p, e, len); | |
| 59 | − break; | |
| 48 | + Bungetc(b); | |
| 49 | + return pdfarray(b); | |
| 50 | + | |
| 51 | + case 'n': | |
| 52 | + off = Boffset(b); | |
| 53 | + if(Bgetc(b) == 'u' && Bgetc(b) == 'l' && Bgetc(b) == 'l' && (isws(c = Bgetc(b)) || isdelim(c))){ | |
| 54 | + Bungetc(b); | |
| 55 | + return &null; | |
| 56 | + } | |
| 57 | + Bseek(b, off, 0); | |
| 58 | + c = 'f'; | |
| 59 | + goto unexpected; | |
| 60 | + | |
| 61 | + case 't': | |
| 62 | + off = Boffset(b); | |
| 63 | + tf = 1; | |
| 64 | + if(Bgetc(b) == 'r' && Bgetc(b) == 'u' && Bgetc(b) == 'e' && (isws(c = Bgetc(b)) || isdelim(c))) | |
| 65 | + goto bool; | |
| 66 | + Bseek(b, off, 0); | |
| 67 | + c = 't'; | |
| 68 | + goto unexpected; | |
| 69 | + | |
| 70 | + case 'f': | |
| 71 | + off = Boffset(b); | |
| 72 | + tf = 0; | |
| 73 | + if(Bgetc(b) == 'a' && Bgetc(b) == 'l' && Bgetc(b) == 's' && Bgetc(b) == 'e' && (isws(c = Bgetc(b)) || isdelim(c))) | |
| 74 | + goto bool; | |
| 75 | + Bseek(b, off, 0); | |
| 76 | + c = 'f'; | |
| 77 | + goto unexpected; | |
| 78 | +bool: | |
| 79 | + Bungetc(b); | |
| 80 | + if((o = malloc(sizeof(*o))) == nil) | |
| 81 | + goto err; | |
| 82 | + o->type = Obool; | |
| 83 | + o->bool = tf; | |
| 84 | + return o; | |
| 60 | 85 | |
| 61 | 86 | default: |
| 62 | − if(isdigit(*p)){ | |
| 63 | − if((o = malloc(sizeof(*o))) != nil){ | |
| 64 | − o->type = Onum; | |
| 65 | − o->num = strtod(p, e); | |
| 66 | − sz = len - (*e - p); | |
| 67 | − p0 = *e; | |
| 68 | − s = p0; | |
| 69 | − for(; sz > 0 && isws(*s); s++, sz--); | |
| 70 | − if(sz > 0 && isdigit(*s)){ | |
| 71 | − gen = strtod(s, &p); | |
| 72 | − sz -= (p - s); | |
| 73 | − s = p; | |
| 74 | − for(; sz > 0 && isws(*s); s++, sz--); | |
| 75 | − if(sz > 0 && *s == 'R'){ /* indirect object */ | |
| 76 | − s++; | |
| 77 | − o->type = Oindir; | |
| 78 | − o->indir.id = o->num; | |
| 79 | − o->indir.gen = gen; | |
| 80 | − p0 = s; | |
| 81 | − } | |
| 82 | − } | |
| 83 | − *e = p0; | |
| 87 | + if((o = malloc(sizeof(*o))) == nil) | |
| 88 | + goto err; | |
| 89 | + if(!isdigit(c)){ | |
| 90 | +unexpected: | |
| 91 | + Bungetc(b); | |
| 92 | + werrstr("unexpected char '%c'", c); | |
| 93 | + goto err; | |
| 94 | + } | |
| 95 | + /* it could be a number or an indirect object */ | |
| 96 | + Bungetc(b); | |
| 97 | + Bgetd(b, &o->num); /* get the first number */ | |
| 98 | + off = Boffset(b); /* seek here if not an indirect object later */ | |
| 99 | + | |
| 100 | + if((o2 = pdfobject(b)) != nil && o2->type == Onum){ /* second object is number too */ | |
| 101 | + do; while(isws(c = Bgetc(b))); | |
| 102 | + if(c < 0) | |
| 103 | + goto err; | |
| 104 | + if(c == 'R'){ /* indirect object */ | |
| 105 | + o->type = Oindir; | |
| 106 | + o->indir.id = o->num; | |
| 107 | + o->indir.gen = o2->num; | |
| 108 | + freeobject(o2); | |
| 109 | + return o; | |
| 110 | + } | |
| 111 | + if(c == 'o' && Bgetc(b) == 'b' && Bgetc(b) == 'j'){ /* object */ | |
| 112 | + freeobject(o2); | |
| 113 | + /* FIXME put into a map */ | |
| 114 | + return pdfobject(b); | |
| 84 | 115 | } |
| 85 | − break; | |
| 86 | 116 | } |
| 87 | − werrstr("unexpected char %c", *p); | |
| 88 | − goto err; | |
| 89 | − } | |
| 90 | 117 | |
| 91 | − if(o != nil) | |
| 118 | + /* just a number, go back and return it */ | |
| 119 | + o->type = Onum; | |
| 120 | + if(Bseek(b, off, 0) != off){ | |
| 121 | + werrstr("seek failed"); | |
| 122 | + goto err; | |
| 123 | + } | |
| 92 | 124 | return o; |
| 125 | + } | |
| 126 | + | |
| 93 | 127 | err: |
| 94 | 128 | werrstr("object: %r"); |
| 95 | 129 | freeobject(o); |
| 130 | + freeobject(o2); | |
| 96 | 131 | return nil; |
| 97 | 132 | } |
| 98 | 133 | |
| 99 | 134 | void |
| 100 | 135 | freeobject(Object *o) |
| 101 | 136 | { |
| 137 | + int i; | |
| 138 | + | |
| 102 | 139 | if(o == nil) |
| 103 | 140 | return; |
| 141 | + | |
| 142 | + switch(o->type){ | |
| 143 | + case Onull: | |
| 144 | + return; | |
| 145 | + | |
| 146 | + case Obool: | |
| 147 | + case Onum: | |
| 148 | + case Ostr: | |
| 149 | + case Oname: | |
| 150 | + break; | |
| 151 | + | |
| 152 | + case Oarray: | |
| 153 | + for(i = 0; i < o->array.ne; i++) | |
| 154 | + freeobject(o->array.e[i]); | |
| 155 | + free(o->array.e); | |
| 156 | + break; | |
| 157 | + | |
| 158 | + case Odict: | |
| 159 | + case Ostream: | |
| 160 | + case Oindir: | |
| 161 | + break; | |
| 162 | + } | |
| 163 | + | |
| 104 | 164 | free(o); |
| 105 | 165 | } |
| @@ -33,8 +33,8 @@ xrefread(Pdf *pdf, int xref0, int nxref) · 32 unmodified lines | ||
| 33 | 33 | /* store non-free objects only */ |
| 34 | 34 | newnxref = pdf->nxref; |
| 35 | 35 | for(e = s, i = 0; i < nxref; i++, e += 20){ |
| 36 | − if(e[10] != ' ' || e[18] != '\r' || e[19] != '\n'){ | |
| 37 | − werrstr("invalid xref line"); | |
| 36 | + if(!isspace(e[10]) || !isspace(e[18]) || !isspace(e[19])){ | |
| 37 | + werrstr("invalid xref line (%d/%d)", i, nxref); | |
| 38 | 38 | goto err; |
| 39 | 39 | } |
| 40 | 40 | xref.id = xref0 + i; |
| @@ -76,15 +76,11 @@ static int · 35 unmodified lines | ||
| 76 | 76 | trailerread(Pdf *pdf) |
| 77 | 77 | { |
| 78 | 78 | int i; |
| 79 | − char *s, *e; | |
| 80 | 79 | Object *o; |
| 81 | 80 | KeyValue *kv; |
| 82 | 81 | |
| 83 | − o = nil; | |
| 84 | − if((s = Brdstr(pdf->bio, 0, 1)) == nil || (o = pdfobject(s, &e, Blinelen(pdf->bio))) == nil) | |
| 82 | + if((o = pdfobject(pdf->bio)) == nil) | |
| 85 | 83 | goto err; |
| 86 | − free(s); | |
| 87 | − s = nil; | |
| 88 | 84 | |
| 89 | 85 | if(o->type != Odict){ |
| 90 | 86 | werrstr("isn't a dictionary"); |
| @@ -92,7 +88,6 @@ trailerread(Pdf *pdf) · 1 unmodified line | ||
| 92 | 88 | } |
| 93 | 89 | |
| 94 | 90 | for(i = 0, kv = o->dict.kv; i < o->dict.nkv; i++, kv++){ |
| 95 | − fprint(2, "# %s %p\n", kv->key, kv->value); | |
| 96 | 91 | if(strcmp(kv->key, "Root") == 0) |
| 97 | 92 | pdf->root = kv->value; |
| 98 | 93 | else if(strcmp(kv->key, "Info") == 0) |
| @@ -112,7 +107,6 @@ trailerread(Pdf *pdf) · 13 unmodified lines | ||
| 112 | 107 | return 0; |
| 113 | 108 | err: |
| 114 | 109 | freeobject(o); |
| 115 | − free(s); | |
| 116 | 110 | return -1; |
| 117 | 111 | } |
| 118 | 112 | |
| @@ -127,6 +121,7 @@ pdfopen(int fd) · 8 unmodified lines | ||
| 127 | 121 | int xreftb; /* 7.5.4 xref table offset from the beginning of the file */ |
| 128 | 122 | int i, n, off; |
| 129 | 123 | |
| 124 | + b = nil; | |
| 130 | 125 | if((pdf = calloc(1, sizeof(*pdf))) == nil || (b = Bfdopen(fd, OREAD)) == nil) |
| 131 | 126 | goto err; |
| 132 | 127 | pdf->bio = b; |
| @@ -204,6 +199,7 @@ badxref: · 71 unmodified lines | ||
| 204 | 199 | |
| 205 | 200 | return pdf; |
| 206 | 201 | err: |
| 202 | + werrstr("pdfopen: %r [at %p]", (void*)Boffset(b)); | |
| 207 | 203 | pdfclose(pdf); |
| 208 | 204 | return nil; |
| 209 | 205 | } |
| @@ -20,8 +20,11 @@ struct Object { · 19 unmodified lines | ||
| 20 | 20 | union { |
| 21 | 21 | int bool; |
| 22 | 22 | double num; |
| 23 | − char *str; | |
| 24 | − char *name; | |
| 23 | + struct { | |
| 24 | + int len; | |
| 25 | + char str[1]; | |
| 26 | + }; | |
| 27 | + char name[1]; | |
| 25 | 28 | |
| 26 | 29 | struct { |
| 27 | 30 | u32int id; |
| @@ -50,8 +53,8 @@ struct Pdf { · 22 unmodified lines | ||
| 50 | 53 | Xref *xref; |
| 51 | 54 | int nxref; /* 7.5.4 xref subsection number of objects */ |
| 52 | 55 | |
| 53 | − Object *root; /* 7.5.5 root object */ | |
| 54 | − Object *info; /* 7.5.5 info dictionary */ | |
| 56 | + Object *root; /* 7.7.2 root object */ | |
| 57 | + Object *info; /* 14.3.3 info dictionary */ | |
| 55 | 58 | }; |
| 56 | 59 | |
| 57 | 60 | struct Xref { |
| @@ -63,45 +66,16 @@ struct Xref { · 5 unmodified lines | ||
| 63 | 66 | Pdf *pdfopen(int fd); |
| 64 | 67 | void pdfclose(Pdf *pdf); |
| 65 | 68 | |
| 66 | −/* | |
| 67 | − * General function to parse an object of any type. | |
| 68 | − */ | |
| 69 | −Object *pdfobject(char *p, char **e, int len); | |
| 70 | − | |
| 69 | +Object *pdfobject(void *b); | |
| 71 | 70 | void freeobject(Object *o); |
| 72 | 71 | |
| 73 | −/* | |
| 74 | − * 7.3.4 String Objects | |
| 75 | − * | |
| 76 | − * Rewrites the string in place with null termination and returns the | |
| 77 | − * length in bytes, without the null terminator. | |
| 78 | − * Returns < 0 if parsing failed. | |
| 79 | − * (*e) is advanced to the position after the string pointed by (p). | |
| 80 | − */ | |
| 81 | −int pdfstring(char *p, char **e, int len); | |
| 82 | − | |
| 83 | −/* | |
| 84 | − * 7.3.5 Name Objects | |
| 85 | − * | |
| 86 | − * Works the same way as pdfstring, but for name objects. | |
| 87 | − */ | |
| 88 | −int pdfname(char *p, char **e, int len); | |
| 89 | − | |
| 90 | −/* | |
| 91 | − * 7.3.6 Array Objects | |
| 92 | − */ | |
| 93 | −Object *pdfarray(char *p, char **e, int len); | |
| 94 | − | |
| 95 | −/* | |
| 96 | − * 7.3.7 Dictionary Objects | |
| 97 | − */ | |
| 98 | −Object *pdfdict(char *p, char **e, int len); | |
| 99 | − | |
| 100 | 72 | /* |
| 101 | 73 | * If the object is indirect, resolve it. Operation is not recursive, ie |
| 102 | 74 | * values of a dictionary won't be resolved automatically. |
| 103 | 75 | */ |
| 104 | 76 | int pdfeval(Pdf *pdf, Object *o); |
| 105 | 77 | |
| 106 | −int isws(char c); | |
| 107 | −int isdelim(char c); | |
| 78 | +int isws(int c); | |
| 79 | +int isdelim(int c); | |
| 80 | + | |
| 81 | +Object *pdfdictget(Object *o, char *name); | |
| @@ -1,8 +1,11 @@ | ||
| 1 | 1 | #include <u.h> |
| 2 | 2 | #include <libc.h> |
| 3 | +#include <bio.h> | |
| 3 | 4 | #include "pdf.h" |
| 4 | 5 | |
| 5 | −static int esc[] = { | |
| 6 | +/* 7.3.4 String Objects */ | |
| 7 | + | |
| 8 | +static char esc[] = { | |
| 6 | 9 | ['n'] = '\n', |
| 7 | 10 | ['r'] = '\r', |
| 8 | 11 | ['t'] = '\t', |
| @@ -14,175 +17,207 @@ static int esc[] = { · 5 unmodified lines | ||
| 14 | 17 | ['\n'] = -1, |
| 15 | 18 | }; |
| 16 | 19 | |
| 17 | −static int | |
| 18 | −pdfstringhex(char *p, char **e, int len) | |
| 20 | +static Object * | |
| 21 | +stringhex(Biobuf *b) | |
| 19 | 22 | { |
| 20 | − int i; | |
| 23 | + char *s; | |
| 24 | + Object *o; | |
| 25 | + int len, n; | |
| 21 | 26 | |
| 22 | − for(i = 1; i < len; i += 1){ | |
| 23 | − if(p[i] == '>') | |
| 24 | − break; | |
| 27 | + if((s = Brdstr(b, '>', 0)) == nil) | |
| 28 | + return nil; | |
| 29 | + len = Blinelen(b) - 1; | |
| 30 | + if(s[len] != '>'){ | |
| 31 | + werrstr("no '>'"); | |
| 32 | + free(s); | |
| 33 | + return nil; | |
| 25 | 34 | } |
| 26 | − if(i >= len){ | |
| 27 | − werrstr("hex not closed"); | |
| 28 | − return -1; | |
| 29 | − } | |
| 30 | − p[i] = '0'; /* the final zero may be missing */ | |
| 31 | − *e = p+i+1; | |
| 32 | − i = dec16((uchar*)p, i/2, p+1, i) == i/2 ? i/2 : -1; | |
| 33 | − if(i < 0) | |
| 35 | + s[len] = '0'; /* the final zero may be missing */ | |
| 36 | + n = len/2; | |
| 37 | + o = nil; | |
| 38 | + if(dec16((uchar*)s, n, s+1, len) != n){ | |
| 34 | 39 | werrstr("invalid hex"); |
| 35 | − p[i] = 0; | |
| 36 | − return i; | |
| 40 | + }else if((o = malloc(sizeof(*o) + n + 1)) != nil){ | |
| 41 | + memmove(o->str, s, n); | |
| 42 | + o->str[n] = 0; | |
| 43 | + o->len = n; | |
| 44 | + o->type = Ostr; | |
| 45 | + } | |
| 46 | + | |
| 47 | + free(s); | |
| 48 | + return o; | |
| 37 | 49 | } |
| 38 | 50 | |
| 39 | −int | |
| 40 | −pdfstring(char *p, char **e, int len) | |
| 51 | +Object * | |
| 52 | +pdfstring(Biobuf *b) | |
| 41 | 53 | { |
| 42 | − Rune r; | |
| 43 | − int c, j, i, o, n, paren; | |
| 54 | + Object *o; | |
| 55 | + char *s, *r; | |
| 44 | 56 | char oct[4]; |
| 57 | + int i, c, paren, sz, maxsz; | |
| 45 | 58 | |
| 46 | − if(len < 2){ | |
| 47 | − werrstr("too short"); | |
| 48 | − goto err; | |
| 49 | − } | |
| 59 | + maxsz = 64; | |
| 60 | + if((s = malloc(maxsz)) == nil) | |
| 61 | + return nil; | |
| 50 | 62 | |
| 51 | − paren = 0; | |
| 52 | − for(i = o = 0; i < len;){ | |
| 53 | − if((n = chartorune(&r, p+i)) == 1 && r == Runeerror){ | |
| 54 | − werrstr("rune error at byte %d", n); | |
| 55 | − return -1; | |
| 56 | − } | |
| 63 | + for(paren = sz = 0;;){ | |
| 64 | + if((c = Bgetc(b)) <= 0) | |
| 65 | + break; | |
| 57 | 66 | |
| 58 | − if(i == 0){ | |
| 59 | − if(r == '('){ | |
| 60 | − paren = 1; | |
| 61 | − i++; | |
| 62 | − continue; | |
| 63 | − } | |
| 64 | − if(r == '<'){ | |
| 65 | − len = pdfstringhex(p, e, len); | |
| 66 | − if(len < 0) | |
| 67 | − goto err; | |
| 68 | − return len; | |
| 67 | + switch(c){ | |
| 68 | + case '<': | |
| 69 | + if(sz == 0){ | |
| 70 | + Bungetc(b); | |
| 71 | + return stringhex(b); | |
| 69 | 72 | } |
| 70 | − werrstr("invalid first char"); | |
| 71 | − goto err; | |
| 72 | − } | |
| 73 | + break; | |
| 73 | 74 | |
| 74 | − if(r == '\\'){ | |
| 75 | − if(++i >= len){ | |
| 76 | − werrstr("escaped char out of string len"); | |
| 77 | − goto err; | |
| 78 | − } | |
| 79 | − if((n = chartorune(&r, p+i)) == 1 && r == Runeerror){ | |
| 80 | − werrstr("rune error at byte %d", i); | |
| 81 | − goto err; | |
| 82 | − } | |
| 83 | − if(r >= '0' && r <= '9'){ /* octal */ | |
| 84 | − n = 0; | |
| 85 | − for(j = 0; j < 3 && i < len && p[i] >= '0' && p[i] <= '9'; j++, i++) | |
| 86 | − oct[j] = p[i]; | |
| 87 | − oct[j] = 0; | |
| 88 | − c = strtol(oct, nil, 8); | |
| 89 | − }else if(r >= nelem(esc) || (c = esc[r]) == 0){ | |
| 90 | − werrstr("unknown escape char at byte %d", i); | |
| 91 | − goto err; | |
| 92 | − } | |
| 93 | − r = c; | |
| 94 | − i += n; | |
| 95 | − if(c < 0) | |
| 96 | − continue; | |
| 97 | − }else if(r == '('){ | |
| 75 | + case '(': | |
| 98 | 76 | paren++; |
| 99 | − i++; | |
| 100 | 77 | continue; |
| 101 | − }else if(r == ')'){ | |
| 78 | + | |
| 79 | + case ')': | |
| 102 | 80 | paren--; |
| 103 | − i++; | |
| 104 | − if(paren == 0) | |
| 81 | + if(paren < 1){ | |
| 82 | + c = 0; | |
| 105 | 83 | break; |
| 84 | + } | |
| 106 | 85 | continue; |
| 107 | − }else{ | |
| 108 | − i += n; | |
| 86 | + | |
| 87 | + case '\\': | |
| 88 | + if((c = Bgetc(b)) <= 0) | |
| 89 | + break; | |
| 90 | + if(c >= '0' && c <= '7'){ /* octal */ | |
| 91 | + oct[0] = c; | |
| 92 | + for(i = 1; i < 3 && (c = Bgetc(b)) >= '0' && c <= '7'; i++) | |
| 93 | + oct[i] = c; | |
| 94 | + if(c <= 0) | |
| 95 | + break; | |
| 96 | + if(c < '0' || c > '7') | |
| 97 | + Bungetc(b); | |
| 98 | + oct[i] = 0; | |
| 99 | + c = strtol(oct, nil, 8); | |
| 100 | + }else if(c >= nelem(esc) || (c = esc[c]) == 0){ | |
| 101 | + werrstr("unknown escape char %c", c); | |
| 102 | + goto err; | |
| 103 | + }else if(c < 0){ | |
| 104 | + continue; | |
| 105 | + } | |
| 106 | + break; | |
| 107 | + | |
| 108 | + default: | |
| 109 | + if(paren < 1){ | |
| 110 | + werrstr("unexpected char '%c'", c); | |
| 111 | + goto err; | |
| 112 | + } | |
| 113 | + break; | |
| 109 | 114 | } |
| 110 | 115 | |
| 111 | − o += runetochar(p+o, &r); | |
| 116 | + if(c <= 0) | |
| 117 | + break; | |
| 118 | + if(sz+1 > maxsz){ | |
| 119 | + maxsz *= 2; | |
| 120 | + if((r = realloc(s, maxsz)) == nil) | |
| 121 | + goto err; | |
| 122 | + s = r; | |
| 123 | + } | |
| 124 | + s[sz++] = c; | |
| 112 | 125 | } |
| 113 | 126 | |
| 114 | − if(paren > 0){ | |
| 115 | − werrstr("non-closed paren"); | |
| 127 | + if(paren != 0){ | |
| 128 | + werrstr("bad paren"); | |
| 129 | + goto err; | |
| 130 | + } | |
| 131 | + if(c < 0){ | |
| 132 | + werrstr("short"); | |
| 116 | 133 | goto err; |
| 117 | 134 | } |
| 118 | 135 | |
| 119 | − p[o] = 0; | |
| 120 | − *e = p + i; | |
| 136 | + if(c >= 0 && (o = malloc(sizeof(*o) + sz + 1)) != nil){ | |
| 137 | + memmove(o->str, s, sz); | |
| 138 | + o->str[sz] = 0; | |
| 139 | + o->len = sz; | |
| 140 | + o->type = Ostr; | |
| 141 | + free(s); | |
| 142 | + return o; | |
| 143 | + } | |
| 121 | 144 | |
| 122 | − return o; | |
| 123 | 145 | err: |
| 146 | + free(s); | |
| 124 | 147 | werrstr("string: %r"); |
| 125 | − return -1; | |
| 148 | + return nil; | |
| 126 | 149 | } |
| 127 | 150 | |
| 128 | 151 | #ifdef TEST |
| 129 | 152 | static struct { |
| 130 | − char *i; | |
| 131 | − int len; | |
| 132 | − char *o; | |
| 133 | − int r; | |
| 134 | − int e; | |
| 153 | + char *in; | |
| 154 | + char *out; | |
| 135 | 155 | }t[] = { |
| 136 | − {"(simple string)", 15, "simple string", 13}, | |
| 137 | − {"(non-closed paren", 17, nil, -1}, | |
| 138 | − {"wrong first char", 16, nil, -1}, | |
| 139 | − {"(parens((()((())))()))", 22, "parens", 6}, | |
| 140 | − {"(\\0053)", 7, "\x053", 2}, | |
| 141 | − {"(\\053)", 6, "+", 1}, | |
| 142 | − {"(\\53)", 5, "+", 1}, | |
| 143 | − {"()", 2, "", 0}, | |
| 144 | − {")", 1, nil, -1}, | |
| 145 | − {"(\\)\\()", 6, ")(", 2}, | |
| 146 | − {"(\\\\)", 4, "\\", 1}, | |
| 147 | − {"a", 1, nil, -1}, | |
| 148 | − {"(1\\\n2)", 6, "12", 2}, | |
| 149 | − {"<323130>", 8, "210", 3}, | |
| 150 | − {"<32313>", 7, "210", 3}, | |
| 151 | − {"<>", 2, "", 0}, | |
| 152 | − {"<", 1, nil, -1}, | |
| 153 | − {"<zz>", 4, nil, -1}, | |
| 156 | + {"", nil}, | |
| 157 | + {"(test, success)", "test, success"}, | |
| 158 | + {"(simple string)", "simple string"}, | |
| 159 | + {"(non-closed paren", nil}, | |
| 160 | + {"wrong first char", nil}, | |
| 161 | + {"(parens((()((())))()))", "parens"}, | |
| 162 | + {"(\\0053)", "\x053"}, | |
| 163 | + {"(\\053)", "+"}, | |
| 164 | + {"(\\53)", "+"}, | |
| 165 | + {"()", ""}, | |
| 166 | + {")", nil}, | |
| 167 | + {"(\\)\\()", ")("}, | |
| 168 | + {"(\\\\)", "\\"}, | |
| 169 | + {"a", nil}, | |
| 170 | + {"(1\\\n2)", "12"}, | |
| 171 | + {"<323130>", "210"}, | |
| 172 | + {"<32313>", "210"}, | |
| 173 | + {"<>", ""}, | |
| 174 | + {"<", nil}, | |
| 175 | + {"<zz>", nil}, | |
| 176 | + {">", nil}, | |
| 154 | 177 | }; |
| 155 | 178 | |
| 179 | +static char *s; | |
| 180 | +static int off, n; | |
| 181 | + | |
| 182 | +static int | |
| 183 | +rd(Biobufhdr *, void *data, long sz) | |
| 184 | +{ | |
| 185 | + if(sz > n-off) | |
| 186 | + sz = n-off; | |
| 187 | + memmove(data, s+off, sz); | |
| 188 | + off += sz; | |
| 189 | + return sz; | |
| 190 | +} | |
| 191 | + | |
| 156 | 192 | void |
| 157 | 193 | test_pdfstring(void) |
| 158 | 194 | { |
| 159 | − char *e; | |
| 160 | − int i, r; | |
| 195 | + Object *o; | |
| 196 | + Biobuf b; | |
| 197 | + int i; | |
| 161 | 198 | |
| 162 | 199 | fprint(2, "pdfstring\n"); |
| 163 | 200 | for(i = 0; i < nelem(t); i++){ |
| 201 | + s = t[i].in; | |
| 202 | + n = strlen(s); | |
| 203 | + off = 0; | |
| 204 | + Binit(&b, -1, OREAD); | |
| 205 | + Biofn(&b, rd); | |
| 206 | + | |
| 164 | 207 | fprint(2, "\t%d: ", i); |
| 165 | − r = pdfstring(t[i].i, &e, t[i].len); | |
| 166 | − if(r != t[i].r){ | |
| 167 | − fprint(2, "expected r=%d, got %d", t[i].r, r); | |
| 168 | − if(r < 0) | |
| 169 | − fprint(2, " (%r)\n"); | |
| 170 | − else | |
| 171 | − fprint(2, "\n"); | |
| 172 | − continue; | |
| 173 | − }else if(r >= 0){ | |
| 174 | − if(t[i].i+t[i].len != e){ | |
| 175 | − fprint(2, "expected e=%p, got %p\n", t[i].i+t[i].len, e); | |
| 176 | − continue; | |
| 177 | − }else if(strcmp(t[i].o, t[i].i) != 0){ | |
| 178 | − fprint(2, "expected %q, got %q\n", t[i].o, t[i].i); | |
| 179 | − continue; | |
| 180 | − } | |
| 181 | − } | |
| 182 | − fprint(2, "OK"); | |
| 183 | − if(r < 0) | |
| 184 | − fprint(2, " (%r)"); | |
| 185 | − fprint(2, "\n"); | |
| 208 | + o = pdfstring(&b); | |
| 209 | + if(o == nil && t[i].out != nil) | |
| 210 | + fprint(2, "ERROR: expected %q, got error: %r\n", t[i].out); | |
| 211 | + else if(o != nil && t[i].out == nil) | |
| 212 | + fprint(2, "ERROR: expected error, got %q\n", o->str); | |
| 213 | + else if(o == nil && t[i].out == nil) | |
| 214 | + fprint(2, "OK (%r)\n"); | |
| 215 | + else if(strcmp(o->str, t[i].out) != 0) | |
| 216 | + fprint(2, "ERROR: expected %q, got %q\n", t[i].out, o->str); | |
| 217 | + else | |
| 218 | + fprint(2, "OK\n"); | |
| 219 | + freeobject(o); | |
| 220 | + Bterm(&b); | |
| 186 | 221 | } |
| 187 | 222 | } |
| 188 | 223 | #endif |