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