1 /*
2 Copyright (c) 2005-2010, Troy D. Hanson http://tpl.sourceforge.net
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
7
8 * Redistributions of source code must retain the above copyright
9 notice, this list of conditions and the following disclaimer.
10
11 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
12 IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
13 TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
14 PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
15 OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
16 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
17 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
18 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
19 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
20 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
21 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
22 */
23
24 #define TPL_VERSION 1.5
25
26 #include <stdlib.h> /* malloc */
27 #include <stdarg.h> /* va_list */
28 #include <string.h> /* memcpy, memset, strchr */
29 #include <stdio.h> /* printf (tpl_hook.oops default function) */
30
31 #ifndef _WIN32
32 #include <unistd.h> /* for ftruncate */
33 #else
34 #include <io.h>
35 #define ftruncate(x,y) _chsize(x,y)
36 #endif
37 #include <sys/types.h> /* for 'open' */
38 #include <sys/stat.h> /* for 'open' */
39 #include <fcntl.h> /* for 'open' */
40 #include <errno.h>
41 #ifndef _WIN32
42 #include <inttypes.h> /* uint32_t, uint64_t, etc */
43 #else
44 typedef unsigned short ushort;
45 typedef __int16 int16_t;
46 typedef __int32 int32_t;
47 typedef __int64 int64_t;
48 typedef unsigned __int16 uint16_t;
49 typedef unsigned __int32 uint32_t;
50 typedef unsigned __int64 uint64_t;
51 #endif
52
53
54 #if ( defined __MINGW32__ || defined _WIN32 )
55 #include "win/mman.h" /* mmap */
56 #else
57 #include <sys/mman.h> /* mmap */
58 #endif
59
60 #include "hanson-tpl.h"
61
62 #define TPL_GATHER_BUFLEN 8192
63 #define TPL_MAGIC "tpl"
64
65 /* macro to add a structure to a doubly-linked list */
66 #define DL_ADD(head,add) \
67 do { \
68 if (head) { \
69 (add)->prev = (head)->prev; \
70 (head)->prev->next = (add); \
71 (head)->prev = (add); \
72 (add)->next = NULL; \
73 } else { \
74 (head)=(add); \
75 (head)->prev = (head); \
76 (head)->next = NULL; \
77 } \
78 } while (0);
79
80 #define fatal_oom() tpl_hook.fatal("out of memory\n")
81
82 /* bit flags (internal). preceded by the external flags in tpl.h */
83 #define TPL_WRONLY (1 << 9) /* app has initiated tpl packing */
84 #define TPL_RDONLY (1 << 10) /* tpl was loaded (for unpacking) */
85 #define TPL_XENDIAN (1 << 11) /* swap endianness when unpacking */
86 #define TPL_OLD_STRING_FMT (1 << 12) /* tpl has strings in 1.2 format */
87
88 /* values for the flags byte that appears after the magic prefix */
89 #define TPL_SUPPORTED_BITFLAGS 3
90 #define TPL_FL_BIGENDIAN (1 << 0)
91 #define TPL_FL_NULLSTRINGS (1 << 1)
92
93 /* char values for node type */
94 #define TPL_TYPE_ROOT 0
95 #define TPL_TYPE_INT32 1
96 #define TPL_TYPE_UINT32 2
97 #define TPL_TYPE_BYTE 3
98 #define TPL_TYPE_STR 4
99 #define TPL_TYPE_ARY 5
100 #define TPL_TYPE_BIN 6
101 #define TPL_TYPE_DOUBLE 7
102 #define TPL_TYPE_INT64 8
103 #define TPL_TYPE_UINT64 9
104 #define TPL_TYPE_INT16 10
105 #define TPL_TYPE_UINT16 11
106 #define TPL_TYPE_POUND 12
107
108 /* error codes */
109 #define ERR_NOT_MINSIZE (-1)
110 #define ERR_MAGIC_MISMATCH (-2)
111 #define ERR_INCONSISTENT_SZ (-3)
112 #define ERR_FMT_INVALID (-4)
113 #define ERR_FMT_MISSING_NUL (-5)
114 #define ERR_FMT_MISMATCH (-6)
115 #define ERR_FLEN_MISMATCH (-7)
116 #define ERR_INCONSISTENT_SZ2 (-8)
117 #define ERR_INCONSISTENT_SZ3 (-9)
118 #define ERR_INCONSISTENT_SZ4 (-10)
119 #define ERR_UNSUPPORTED_FLAGS (-11)
120
121 /* access to A(...) nodes by index */
122 typedef struct tpl_pidx {
123 struct tpl_node *node;
124 struct tpl_pidx *next,*prev;
125 } tpl_pidx;
126
127 /* A(...) node datum */
128 typedef struct tpl_atyp {
129 uint32_t num; /* num elements */
130 size_t sz; /* size of each backbone's datum */
131 struct tpl_backbone *bb,*bbtail;
132 void *cur;
133 } tpl_atyp;
134
135 /* backbone to extend A(...) lists dynamically */
136 typedef struct tpl_backbone {
137 struct tpl_backbone *next;
138 /* when this structure is malloc'd, extra space is alloc'd at the
139 * end to store the backbone "datum", and data points to it. */
140 #if defined(__STDC_VERSION__) && __STDC_VERSION__ < 199901
141 char *data;
142 #else
143 char data[];
144 #endif
145 } tpl_backbone;
146
147 /* mmap record */
148 typedef struct tpl_mmap_rec {
149 int fd;
150 void *text;
151 size_t text_sz;
152 } tpl_mmap_rec;
153
154 /* root node datum */
155 typedef struct tpl_root_data {
156 int flags;
157 tpl_pidx *pidx;
158 tpl_mmap_rec mmap;
159 char *fmt;
160 int *fxlens, num_fxlens;
161 } tpl_root_data;
162
163 /* node type to size mapping */
164 struct tpl_type_t {
165 char c;
166 int sz;
167 };
168
169
170 /* Internal prototypes */
171 static tpl_node *tpl_node_new(tpl_node *parent);
172 static tpl_node *tpl_find_i(tpl_node *n, int i);
173 static void *tpl_cpv(void *datav, void *data, size_t sz);
174 static void *tpl_extend_backbone(tpl_node *n);
175 static char *tpl_fmt(tpl_node *r);
176 static void *tpl_dump_atyp(tpl_node *n, tpl_atyp* at, void *dv);
177 static size_t tpl_ser_osz(tpl_node *n);
178 static void tpl_free_atyp(tpl_node *n,tpl_atyp *atyp);
179 static int tpl_dump_to_mem(tpl_node *r, void *addr, size_t sz);
180 static int tpl_mmap_file(char *filename, tpl_mmap_rec *map_rec);
181 static int tpl_mmap_output_file(char *filename, size_t sz, void **text_out);
182 static int tpl_cpu_bigendian(void);
183 static int tpl_needs_endian_swap(void *);
184 static void tpl_byteswap(void *word, int len);
185 static int tpl_serlen(tpl_node *r, tpl_node *n, void *dv, size_t *serlen);
186 static int tpl_unpackA0(tpl_node *r);
187 static int tpl_oops(const char *fmt, ...);
188 static int tpl_gather_mem( char *buf, size_t len, tpl_gather_t **gs, tpl_gather_cb *cb, void *data);
189 static int tpl_gather_nonblocking( int fd, tpl_gather_t **gs, tpl_gather_cb *cb, void *data);
190 static int tpl_gather_blocking(int fd, void **img, size_t *sz);
191 static tpl_node *tpl_map_va(char *fmt, va_list ap);
192
193 /* This is used internally to help calculate padding when a 'double'
194 * follows a smaller datatype in a structure. Normally under gcc
195 * on x86, d will be aligned at +4, however use of -malign-double
196 * causes d to be aligned at +8 (this is actually faster on x86).
197 * Also SPARC and x86_64 seem to align always on +8.
198 */
199 struct tpl_double_alignment_detector {
200 char a;
201 double d; /* some platforms align this on +4, others on +8 */
202 };
203
204 /* this is another case where alignment varies. mac os x/gcc was observed
205 * to align the int64_t at +4 under -m32 and at +8 under -m64 */
206 struct tpl_int64_alignment_detector {
207 int i;
208 int64_t j; /* some platforms align this on +4, others on +8 */
209 };
210
211 typedef struct {
212 size_t inter_elt_len; /* padded inter-element len; i.e. &a[1].field - &a[0].field */
213 tpl_node *iter_start_node; /* node to jump back to, as we start each new iteration */
214 size_t iternum; /* current iteration number (total req'd. iter's in n->num) */
215 } tpl_pound_data;
216
217 /* Hooks for customizing tpl mem alloc, error handling, etc. Set defaults. */
218 tpl_hook_t tpl_hook = {
219 /* .oops = */ tpl_oops,
220 /* .malloc = */ malloc,
221 /* .realloc = */ realloc,
222 /* .free = */ free,
223 /* .fatal = */ tpl_fatal,
224 /* .gather_max = */ 0 /* max tpl size (bytes) for tpl_gather */
225 };
226
227 static const char tpl_fmt_chars[] = "AS($)BiucsfIUjv#"; /* valid format chars */
228 static const char tpl_datapeek_ok_chars[] = "iucsfIUjv"; /* valid in datapeek */
229 static const struct tpl_type_t tpl_types[] = {
230 /* [TPL_TYPE_ROOT] = */ {'r', 0},
231 /* [TPL_TYPE_INT32] = */ {'i', sizeof(int32_t)},
232 /* [TPL_TYPE_UINT32] = */ {'u', sizeof(uint32_t)},
233 /* [TPL_TYPE_BYTE] = */ {'c', sizeof(char)},
234 /* [TPL_TYPE_STR] = */ {'s', sizeof(char*)},
235 /* [TPL_TYPE_ARY] = */ {'A', 0},
236 /* [TPL_TYPE_BIN] = */ {'B', 0},
237 /* [TPL_TYPE_DOUBLE] = */ {'f', 8}, /* not sizeof(double) as that varies */
238 /* [TPL_TYPE_INT64] = */ {'I', sizeof(int64_t)},
239 /* [TPL_TYPE_UINT64] = */ {'U', sizeof(uint64_t)},
240 /* [TPL_TYPE_INT16] = */ {'j', sizeof(int16_t)},
241 /* [TPL_TYPE_UINT16] = */ {'v', sizeof(uint16_t)},
242 /* [TPL_TYPE_POUND] = */ {'#', 0},
243 };
244
245 /* default error-reporting function. Just writes to stderr. */
tpl_oops(const char * fmt,...)246 static int tpl_oops(const char *fmt, ...) {
247 va_list ap;
248 va_start(ap,fmt);
249 vfprintf(stderr,fmt,ap);
250 va_end(ap);
251 return 0;
252 }
253
254
tpl_node_new(tpl_node * parent)255 static tpl_node *tpl_node_new(tpl_node *parent) {
256 tpl_node *n;
257 if ((n=tpl_hook.malloc(sizeof(tpl_node))) == NULL) {
258 fatal_oom();
259 }
260 n->addr=NULL;
261 n->data=NULL;
262 n->num=1;
263 n->ser_osz=0;
264 n->children=NULL;
265 n->next=NULL;
266 n->parent=parent;
267 return n;
268 }
269
270 /* Used in S(..) formats to pack several fields from a structure based on
271 * only the structure address. We need to calculate field addresses
272 * manually taking into account the size of the fields and intervening padding.
273 * The wrinkle is that double is not normally aligned on x86-32 but the
274 * -malign-double compiler option causes it to be. Double are aligned
275 * on Sparc, and apparently on 64 bit x86. We use a helper structure
276 * to detect whether double is aligned in this compilation environment.
277 */
calc_field_addr(tpl_node * parent,int type,char * struct_addr,int ordinal)278 static char *calc_field_addr(tpl_node *parent, int type,char *struct_addr, int ordinal) {
279 tpl_node *prev;
280 int offset;
281 int align_sz;
282
283 if (ordinal == 1) return struct_addr; /* first field starts on structure address */
284
285 /* generate enough padding so field addr is divisible by it's align_sz. 4, 8, etc */
286 prev = parent->children->prev;
287 switch(type) {
288 case TPL_TYPE_DOUBLE:
289 align_sz = sizeof(struct tpl_double_alignment_detector) > 12 ? 8 : 4;
290 break;
291 case TPL_TYPE_INT64:
292 case TPL_TYPE_UINT64:
293 align_sz = sizeof(struct tpl_int64_alignment_detector) > 12 ? 8 : 4;
294 break;
295 default:
296 align_sz = tpl_types[type].sz;
297 break;
298 }
299 offset = ((uintptr_t)prev->addr - (uintptr_t)struct_addr)
300 + (tpl_types[prev->type].sz * prev->num);
301 offset = (offset + align_sz - 1) / align_sz * align_sz;
302 return struct_addr + offset;
303 }
304
tpl_map(char * fmt,...)305 TPL_API tpl_node *tpl_map(char *fmt,...) {
306 va_list ap;
307 tpl_node *tn;
308
309 va_start(ap,fmt);
310 tn = tpl_map_va(fmt, ap);
311 va_end(ap);
312 return tn;
313 }
314
tpl_map_va(char * fmt,va_list ap)315 static tpl_node *tpl_map_va(char *fmt, va_list ap) {
316 int lparen_level=0,expect_lparen=0,t=0,in_structure=0,ordinal=0;
317 int in_nested_structure=0;
318 char *c, *peek, *struct_addr=NULL, *struct_next;
319 tpl_node *root,*parent,*n=NULL,*preceding,*iter_start_node=NULL,
320 *struct_widest_node=NULL, *np; tpl_pidx *pidx;
321 tpl_pound_data *pd;
322 int *fxlens, num_fxlens, pound_num, pound_prod, applies_to_struct;
323 int contig_fxlens[10]; /* temp space for contiguous fxlens */
324 unsigned int num_contig_fxlens, i, j;
325 ptrdiff_t inter_elt_len=0; /* padded element length of contiguous structs in array */
326
327
328 root = tpl_node_new(NULL);
329 root->type = TPL_TYPE_ROOT;
330 root->data = (tpl_root_data*)tpl_hook.malloc(sizeof(tpl_root_data));
331 if (!root->data) fatal_oom();
332 memset((tpl_root_data*)root->data,0,sizeof(tpl_root_data));
333
334 /* set up root nodes special ser_osz to reflect overhead of preamble */
335 root->ser_osz = sizeof(uint32_t); /* tpl leading length */
336 root->ser_osz += strlen(fmt) + 1; /* fmt + NUL-terminator */
337 root->ser_osz += 4; /* 'tpl' magic prefix + flags byte */
338
339 parent=root;
340
341 c=fmt;
342 while (*c != '\0') {
343 switch (*c) {
344 case 'c':
345 case 'i':
346 case 'u':
347 case 'j':
348 case 'v':
349 case 'I':
350 case 'U':
351 case 'f':
352 if (*c=='c') t=TPL_TYPE_BYTE;
353 else if (*c=='i') t=TPL_TYPE_INT32;
354 else if (*c=='u') t=TPL_TYPE_UINT32;
355 else if (*c=='j') t=TPL_TYPE_INT16;
356 else if (*c=='v') t=TPL_TYPE_UINT16;
357 else if (*c=='I') t=TPL_TYPE_INT64;
358 else if (*c=='U') t=TPL_TYPE_UINT64;
359 else if (*c=='f') t=TPL_TYPE_DOUBLE;
360
361 if (expect_lparen) goto fail;
362 n = tpl_node_new(parent);
363 n->type = t;
364 if (in_structure) {
365 if (ordinal == 1) {
366 /* for S(...)# iteration. Apply any changes to case 's' too!!! */
367 iter_start_node = n;
368 struct_widest_node = n;
369 }
370 if (tpl_types[n->type].sz > tpl_types[struct_widest_node->type].sz) {
371 struct_widest_node = n;
372 }
373 n->addr = calc_field_addr(parent,n->type,struct_addr,ordinal++);
374 } else n->addr = (void*)va_arg(ap,void*);
375 n->data = tpl_hook.malloc(tpl_types[t].sz);
376 if (!n->data) fatal_oom();
377 if (n->parent->type == TPL_TYPE_ARY)
378 ((tpl_atyp*)(n->parent->data))->sz += tpl_types[t].sz;
379 DL_ADD(parent->children,n);
380 break;
381 case 's':
382 if (expect_lparen) goto fail;
383 n = tpl_node_new(parent);
384 n->type = TPL_TYPE_STR;
385 if (in_structure) {
386 if (ordinal == 1) {
387 iter_start_node = n; /* for S(...)# iteration */
388 struct_widest_node = n;
389 }
390 if (tpl_types[n->type].sz > tpl_types[struct_widest_node->type].sz) {
391 struct_widest_node = n;
392 }
393 n->addr = calc_field_addr(parent,n->type,struct_addr,ordinal++);
394 } else n->addr = (void*)va_arg(ap,void*);
395 n->data = tpl_hook.malloc(sizeof(char*));
396 if (!n->data) fatal_oom();
397 *(char**)(n->data) = NULL;
398 if (n->parent->type == TPL_TYPE_ARY)
399 ((tpl_atyp*)(n->parent->data))->sz += sizeof(void*);
400 DL_ADD(parent->children,n);
401 break;
402 case '#':
403 /* apply a 'num' to preceding atom */
404 if (!parent->children) goto fail;
405 preceding = parent->children->prev; /* first child's prev is 'last child'*/
406 t = preceding->type;
407 applies_to_struct = (*(c-1) == ')') ? 1 : 0;
408 if (!applies_to_struct) {
409 if (!(t == TPL_TYPE_BYTE || t == TPL_TYPE_INT32 ||
410 t == TPL_TYPE_UINT32 || t == TPL_TYPE_DOUBLE ||
411 t == TPL_TYPE_UINT64 || t == TPL_TYPE_INT64 ||
412 t == TPL_TYPE_UINT16 || t == TPL_TYPE_INT16 ||
413 t == TPL_TYPE_STR )) goto fail;
414 }
415 /* count up how many contiguous # and form their product */
416 pound_prod=1;
417 num_contig_fxlens=0;
418 for(peek=c; *peek == '#'; peek++) {
419 pound_num = va_arg(ap, int);
420 if (pound_num < 1) {
421 tpl_hook.fatal("non-positive iteration count %d\n", pound_num);
422 }
423 if (num_contig_fxlens >= (sizeof(contig_fxlens)/sizeof(contig_fxlens[0]))) {
424 tpl_hook.fatal("contiguous # exceeds hardcoded limit\n");
425 } else {
426 contig_fxlens[num_contig_fxlens++] = pound_num;
427 pound_prod *= pound_num;
428 }
429 }
430 /* increment c to skip contiguous # so its points to last one */
431 c = peek-1;
432 /* differentiate atom-# from struct-# by noting preceding rparen */
433 if (applies_to_struct) { /* insert # node to induce looping */
434 n = tpl_node_new(parent);
435 n->type = TPL_TYPE_POUND;
436 n->num = pound_prod;
437 n->data = tpl_hook.malloc(sizeof(tpl_pound_data));
438 if (!n->data) fatal_oom();
439 pd = (tpl_pound_data*)n->data;
440 pd->inter_elt_len = inter_elt_len;
441 pd->iter_start_node = iter_start_node;
442 pd->iternum = 0;
443 DL_ADD(parent->children,n);
444 /* multiply the 'num' and data space on each atom in the structure */
445 for(np = iter_start_node; np != n; np = np->next) {
446 if (n->parent->type == TPL_TYPE_ARY) {
447 ((tpl_atyp*)(n->parent->data))->sz +=
448 tpl_types[np->type].sz * (np->num * (n->num - 1));
449 }
450 np->data = tpl_hook.realloc(np->data, tpl_types[np->type].sz *
451 np->num * n->num);
452 if (!np->data) fatal_oom();
453 memset(np->data, 0, tpl_types[np->type].sz * np->num * n->num);
454 }
455 } else { /* simple atom-# form does not require a loop */
456 preceding->num = pound_prod;
457 preceding->data = tpl_hook.realloc(preceding->data,
458 tpl_types[t].sz * preceding->num);
459 if (!preceding->data) fatal_oom();
460 memset(preceding->data,0,tpl_types[t].sz * preceding->num);
461 if (n->parent->type == TPL_TYPE_ARY) {
462 ((tpl_atyp*)(n->parent->data))->sz += tpl_types[t].sz *
463 (preceding->num-1);
464 }
465 }
466 root->ser_osz += (sizeof(uint32_t) * num_contig_fxlens);
467
468 j = ((tpl_root_data*)root->data)->num_fxlens; /* before incrementing */
469 (((tpl_root_data*)root->data)->num_fxlens) += num_contig_fxlens;
470 num_fxlens = ((tpl_root_data*)root->data)->num_fxlens; /* new value */
471 fxlens = ((tpl_root_data*)root->data)->fxlens;
472 fxlens = tpl_hook.realloc(fxlens, sizeof(int) * num_fxlens);
473 if (!fxlens) fatal_oom();
474 ((tpl_root_data*)root->data)->fxlens = fxlens;
475 for(i=0; i < num_contig_fxlens; i++) fxlens[j++] = contig_fxlens[i];
476
477 break;
478 case 'B':
479 if (expect_lparen) goto fail;
480 if (in_structure) goto fail;
481 n = tpl_node_new(parent);
482 n->type = TPL_TYPE_BIN;
483 n->addr = (tpl_bin*)va_arg(ap,void*);
484 n->data = tpl_hook.malloc(sizeof(tpl_bin*));
485 if (!n->data) fatal_oom();
486 *((tpl_bin**)n->data) = NULL;
487 if (n->parent->type == TPL_TYPE_ARY)
488 ((tpl_atyp*)(n->parent->data))->sz += sizeof(tpl_bin);
489 DL_ADD(parent->children,n);
490 break;
491 case 'A':
492 if (in_structure) goto fail;
493 n = tpl_node_new(parent);
494 n->type = TPL_TYPE_ARY;
495 DL_ADD(parent->children,n);
496 parent = n;
497 expect_lparen=1;
498 pidx = (tpl_pidx*)tpl_hook.malloc(sizeof(tpl_pidx));
499 if (!pidx) fatal_oom();
500 pidx->node = n;
501 pidx->next = NULL;
502 DL_ADD(((tpl_root_data*)(root->data))->pidx,pidx);
503 /* set up the A's tpl_atyp */
504 n->data = (tpl_atyp*)tpl_hook.malloc(sizeof(tpl_atyp));
505 if (!n->data) fatal_oom();
506 ((tpl_atyp*)(n->data))->num = 0;
507 ((tpl_atyp*)(n->data))->sz = 0;
508 ((tpl_atyp*)(n->data))->bb = NULL;
509 ((tpl_atyp*)(n->data))->bbtail = NULL;
510 ((tpl_atyp*)(n->data))->cur = NULL;
511 if (n->parent->type == TPL_TYPE_ARY)
512 ((tpl_atyp*)(n->parent->data))->sz += sizeof(void*);
513 break;
514 case 'S':
515 if (in_structure) goto fail;
516 expect_lparen=1;
517 ordinal=1; /* index upcoming atoms in S(..) */
518 in_structure=1+lparen_level; /* so we can tell where S fmt ends */
519 struct_addr = (char*)va_arg(ap,void*);
520 break;
521 case '$': /* nested structure */
522 if (!in_structure) goto fail;
523 expect_lparen=1;
524 in_nested_structure++;
525 break;
526 case ')':
527 lparen_level--;
528 if (lparen_level < 0) goto fail;
529 if (*(c-1) == '(') goto fail;
530 if (in_nested_structure) in_nested_structure--;
531 else if (in_structure && (in_structure-1 == lparen_level)) {
532 /* calculate delta between contiguous structures in array */
533 struct_next = calc_field_addr(parent, struct_widest_node->type,
534 struct_addr, ordinal++);
535 inter_elt_len = struct_next - struct_addr;
536 in_structure=0;
537 }
538 else parent = parent->parent; /* rparen ends A() type, not S() type */
539 break;
540 case '(':
541 if (!expect_lparen) goto fail;
542 expect_lparen=0;
543 lparen_level++;
544 break;
545 default:
546 tpl_hook.oops("unsupported option %c\n", *c);
547 goto fail;
548 }
549 c++;
550 }
551 if (lparen_level != 0) goto fail;
552
553 /* copy the format string, save for convenience */
554 ((tpl_root_data*)(root->data))->fmt = tpl_hook.malloc(strlen(fmt)+1);
555 if (((tpl_root_data*)(root->data))->fmt == NULL)
556 fatal_oom();
557 memcpy(((tpl_root_data*)(root->data))->fmt,fmt,strlen(fmt)+1);
558
559 return root;
560
561 fail:
562 tpl_hook.oops("failed to parse %s\n", fmt);
563 tpl_free(root);
564 return NULL;
565 }
566
tpl_unmap_file(tpl_mmap_rec * mr)567 static int tpl_unmap_file( tpl_mmap_rec *mr) {
568
569 if ( munmap( mr->text, mr->text_sz ) == -1 ) {
570 tpl_hook.oops("Failed to munmap: %s\n", strerror(errno));
571 }
572 close(mr->fd);
573 mr->text = NULL;
574 mr->text_sz = 0;
575 return 0;
576 }
577
tpl_free_keep_map(tpl_node * r)578 static void tpl_free_keep_map(tpl_node *r) {
579 int mmap_bits = (TPL_RDONLY|TPL_FILE);
580 int ufree_bits = (TPL_MEM|TPL_UFREE);
581 tpl_node *nxtc,*c;
582 int find_next_node=0,looking,i;
583 size_t sz;
584
585 /* For mmap'd files, or for 'ufree' memory images , do appropriate release */
586 if ((((tpl_root_data*)(r->data))->flags & mmap_bits) == mmap_bits) {
587 tpl_unmap_file( &((tpl_root_data*)(r->data))->mmap);
588 } else if ((((tpl_root_data*)(r->data))->flags & ufree_bits) == ufree_bits) {
589 tpl_hook.free( ((tpl_root_data*)(r->data))->mmap.text );
590 }
591
592 c = r->children;
593 if (c) {
594 while(c->type != TPL_TYPE_ROOT) { /* loop until we come back to root node */
595 switch (c->type) {
596 case TPL_TYPE_BIN:
597 /* free any binary buffer hanging from tpl_bin */
598 if ( *((tpl_bin**)(c->data)) ) {
599 if ( (*((tpl_bin**)(c->data)))->addr ) {
600 tpl_hook.free( (*((tpl_bin**)(c->data)))->addr );
601 }
602 *((tpl_bin**)c->data) = NULL; /* reset tpl_bin */
603 }
604 find_next_node=1;
605 break;
606 case TPL_TYPE_STR:
607 /* free any packed (copied) string */
608 for(i=0; i < c->num; i++) {
609 char *str = ((char**)c->data)[i];
610 if (str) {
611 tpl_hook.free(str);
612 ((char**)c->data)[i] = NULL;
613 }
614 }
615 find_next_node=1;
616 break;
617 case TPL_TYPE_INT32:
618 case TPL_TYPE_UINT32:
619 case TPL_TYPE_INT64:
620 case TPL_TYPE_UINT64:
621 case TPL_TYPE_BYTE:
622 case TPL_TYPE_DOUBLE:
623 case TPL_TYPE_INT16:
624 case TPL_TYPE_UINT16:
625 case TPL_TYPE_POUND:
626 find_next_node=1;
627 break;
628 case TPL_TYPE_ARY:
629 c->ser_osz = 0; /* zero out the serialization output size */
630
631 sz = ((tpl_atyp*)(c->data))->sz; /* save sz to use below */
632 tpl_free_atyp(c,c->data);
633
634 /* make new atyp */
635 c->data = (tpl_atyp*)tpl_hook.malloc(sizeof(tpl_atyp));
636 if (!c->data) fatal_oom();
637 ((tpl_atyp*)(c->data))->num = 0;
638 ((tpl_atyp*)(c->data))->sz = sz; /* restore bb datum sz */
639 ((tpl_atyp*)(c->data))->bb = NULL;
640 ((tpl_atyp*)(c->data))->bbtail = NULL;
641 ((tpl_atyp*)(c->data))->cur = NULL;
642
643 c = c->children;
644 break;
645 default:
646 tpl_hook.fatal("unsupported format character\n");
647 break;
648 }
649
650 if (find_next_node) {
651 find_next_node=0;
652 looking=1;
653 while(looking) {
654 if (c->next) {
655 nxtc=c->next;
656 c=nxtc;
657 looking=0;
658 } else {
659 if (c->type == TPL_TYPE_ROOT) break; /* root node */
660 else {
661 nxtc=c->parent;
662 c=nxtc;
663 }
664 }
665 }
666 }
667 }
668 }
669
670 ((tpl_root_data*)(r->data))->flags = 0; /* reset flags */
671 }
672
tpl_free(tpl_node * r)673 TPL_API void tpl_free(tpl_node *r) {
674 int mmap_bits = (TPL_RDONLY|TPL_FILE);
675 int ufree_bits = (TPL_MEM|TPL_UFREE);
676 tpl_node *nxtc,*c;
677 int find_next_node=0,looking,i;
678 tpl_pidx *pidx,*pidx_nxt;
679
680 /* For mmap'd files, or for 'ufree' memory images , do appropriate release */
681 if ((((tpl_root_data*)(r->data))->flags & mmap_bits) == mmap_bits) {
682 tpl_unmap_file( &((tpl_root_data*)(r->data))->mmap);
683 } else if ((((tpl_root_data*)(r->data))->flags & ufree_bits) == ufree_bits) {
684 tpl_hook.free( ((tpl_root_data*)(r->data))->mmap.text );
685 }
686
687 c = r->children;
688 if (c) {
689 while(c->type != TPL_TYPE_ROOT) { /* loop until we come back to root node */
690 switch (c->type) {
691 case TPL_TYPE_BIN:
692 /* free any binary buffer hanging from tpl_bin */
693 if ( *((tpl_bin**)(c->data)) ) {
694 if ( (*((tpl_bin**)(c->data)))->sz != 0 ) {
695 tpl_hook.free( (*((tpl_bin**)(c->data)))->addr );
696 }
697 tpl_hook.free(*((tpl_bin**)c->data)); /* free tpl_bin */
698 }
699 tpl_hook.free(c->data); /* free tpl_bin* */
700 find_next_node=1;
701 break;
702 case TPL_TYPE_STR:
703 /* free any packed (copied) string */
704 for(i=0; i < c->num; i++) {
705 char *str = ((char**)c->data)[i];
706 if (str) {
707 tpl_hook.free(str);
708 ((char**)c->data)[i] = NULL;
709 }
710 }
711 tpl_hook.free(c->data);
712 find_next_node=1;
713 break;
714 case TPL_TYPE_INT32:
715 case TPL_TYPE_UINT32:
716 case TPL_TYPE_INT64:
717 case TPL_TYPE_UINT64:
718 case TPL_TYPE_BYTE:
719 case TPL_TYPE_DOUBLE:
720 case TPL_TYPE_INT16:
721 case TPL_TYPE_UINT16:
722 case TPL_TYPE_POUND:
723 tpl_hook.free(c->data);
724 find_next_node=1;
725 break;
726 case TPL_TYPE_ARY:
727 tpl_free_atyp(c,c->data);
728 if (c->children) c = c->children; /* normal case */
729 else find_next_node=1; /* edge case, handle bad format A() */
730 break;
731 default:
732 tpl_hook.fatal("unsupported format character\n");
733 break;
734 }
735
736 if (find_next_node) {
737 find_next_node=0;
738 looking=1;
739 while(looking) {
740 if (c->next) {
741 nxtc=c->next;
742 tpl_hook.free(c);
743 c=nxtc;
744 looking=0;
745 } else {
746 if (c->type == TPL_TYPE_ROOT) break; /* root node */
747 else {
748 nxtc=c->parent;
749 tpl_hook.free(c);
750 c=nxtc;
751 }
752 }
753 }
754 }
755 }
756 }
757
758 /* free root */
759 for(pidx=((tpl_root_data*)(r->data))->pidx; pidx; pidx=pidx_nxt) {
760 pidx_nxt = pidx->next;
761 tpl_hook.free(pidx);
762 }
763 tpl_hook.free(((tpl_root_data*)(r->data))->fmt);
764 if (((tpl_root_data*)(r->data))->num_fxlens > 0) {
765 tpl_hook.free(((tpl_root_data*)(r->data))->fxlens);
766 }
767 tpl_hook.free(r->data); /* tpl_root_data */
768 tpl_hook.free(r);
769 }
770
771
772 /* Find the i'th packable ('A' node) */
tpl_find_i(tpl_node * n,int i)773 static tpl_node *tpl_find_i(tpl_node *n, int i) {
774 int j=0;
775 tpl_pidx *pidx;
776 if (n->type != TPL_TYPE_ROOT) return NULL;
777 if (i == 0) return n; /* packable 0 is root */
778 for(pidx=((tpl_root_data*)(n->data))->pidx; pidx; pidx=pidx->next) {
779 if (++j == i) return pidx->node;
780 }
781 return NULL;
782 }
783
tpl_cpv(void * datav,void * data,size_t sz)784 static void *tpl_cpv(void *datav, void *data, size_t sz) {
785 if (sz>0) memcpy(datav,data,sz);
786 return (void*)((uintptr_t)datav + sz);
787 }
788
tpl_extend_backbone(tpl_node * n)789 static void *tpl_extend_backbone(tpl_node *n) {
790 tpl_backbone *bb;
791 bb = (tpl_backbone*)tpl_hook.malloc(sizeof(tpl_backbone) +
792 ((tpl_atyp*)(n->data))->sz ); /* datum hangs on coattails of bb */
793 if (!bb) fatal_oom();
794 #if defined(__STDC_VERSION__) && __STDC_VERSION__ < 199901
795 bb->data = (char*)((uintptr_t)bb + sizeof(tpl_backbone));
796 #endif
797 memset(bb->data,0,((tpl_atyp*)(n->data))->sz);
798 bb->next = NULL;
799 /* Add the new backbone to the tail, also setting head if necessary */
800 if (((tpl_atyp*)(n->data))->bb == NULL) {
801 ((tpl_atyp*)(n->data))->bb = bb;
802 ((tpl_atyp*)(n->data))->bbtail = bb;
803 } else {
804 ((tpl_atyp*)(n->data))->bbtail->next = bb;
805 ((tpl_atyp*)(n->data))->bbtail = bb;
806 }
807
808 ((tpl_atyp*)(n->data))->num++;
809 return bb->data;
810 }
811
812 /* Get the format string corresponding to a given tpl (root node) */
tpl_fmt(tpl_node * r)813 static char *tpl_fmt(tpl_node *r) {
814 return ((tpl_root_data*)(r->data))->fmt;
815 }
816
817 /* Get the fmt # lengths as a contiguous buffer of ints (length num_fxlens) */
tpl_fxlens(tpl_node * r,int * num_fxlens)818 static int *tpl_fxlens(tpl_node *r, int *num_fxlens) {
819 *num_fxlens = ((tpl_root_data*)(r->data))->num_fxlens;
820 return ((tpl_root_data*)(r->data))->fxlens;
821 }
822
823 /* called when serializing an 'A' type node into a buffer which has
824 * already been set up with the proper space. The backbone is walked
825 * which was obtained from the tpl_atyp header passed in.
826 */
tpl_dump_atyp(tpl_node * n,tpl_atyp * at,void * dv)827 static void *tpl_dump_atyp(tpl_node *n, tpl_atyp* at, void *dv) {
828 tpl_backbone *bb;
829 tpl_node *c;
830 void *datav;
831 uint32_t slen;
832 tpl_bin *binp;
833 char *strp;
834 tpl_atyp *atypp;
835 tpl_pound_data *pd;
836 int i;
837 size_t itermax;
838
839 /* handle 'A' nodes */
840 dv = tpl_cpv(dv,&at->num,sizeof(uint32_t)); /* array len */
841 for(bb=at->bb; bb; bb=bb->next) {
842 datav = bb->data;
843 c=n->children;
844 while(c) {
845 switch (c->type) {
846 case TPL_TYPE_BYTE:
847 case TPL_TYPE_DOUBLE:
848 case TPL_TYPE_INT32:
849 case TPL_TYPE_UINT32:
850 case TPL_TYPE_INT64:
851 case TPL_TYPE_UINT64:
852 case TPL_TYPE_INT16:
853 case TPL_TYPE_UINT16:
854 dv = tpl_cpv(dv,datav,tpl_types[c->type].sz * c->num);
855 datav = (void*)((uintptr_t)datav + tpl_types[c->type].sz * c->num);
856 break;
857 case TPL_TYPE_BIN:
858 /* dump the buffer length followed by the buffer */
859 memcpy(&binp,datav,sizeof(tpl_bin*)); /* cp to aligned */
860 slen = binp->sz;
861 dv = tpl_cpv(dv,&slen,sizeof(uint32_t));
862 dv = tpl_cpv(dv,binp->addr,slen);
863 datav = (void*)((uintptr_t)datav + sizeof(tpl_bin*));
864 break;
865 case TPL_TYPE_STR:
866 /* dump the string length followed by the string */
867 for(i=0; i < c->num; i++) {
868 memcpy(&strp,datav,sizeof(char*)); /* cp to aligned */
869 slen = strp ? (strlen(strp)+1) : 0;
870 dv = tpl_cpv(dv,&slen,sizeof(uint32_t));
871 if (slen > 1) dv = tpl_cpv(dv,strp,slen-1);
872 datav = (void*)((uintptr_t)datav + sizeof(char*));
873 }
874 break;
875 case TPL_TYPE_ARY:
876 memcpy(&atypp,datav,sizeof(tpl_atyp*)); /* cp to aligned */
877 dv = tpl_dump_atyp(c,atypp,dv);
878 datav = (void*)((uintptr_t)datav + sizeof(void*));
879 break;
880 case TPL_TYPE_POUND:
881 /* iterate over the preceding nodes */
882 pd = (tpl_pound_data*)c->data;
883 itermax = c->num;
884 if (++(pd->iternum) < itermax) {
885 c = pd->iter_start_node;
886 continue;
887 } else { /* loop complete. */
888 pd->iternum = 0;
889 }
890 break;
891 default:
892 tpl_hook.fatal("unsupported format character\n");
893 break;
894 }
895 c=c->next;
896 }
897 }
898 return dv;
899 }
900
901 /* figure the serialization output size needed for tpl whose root is n*/
tpl_ser_osz(tpl_node * n)902 static size_t tpl_ser_osz(tpl_node *n) {
903 tpl_node *c, *np;
904 size_t sz, itermax;
905 tpl_bin *binp;
906 char *strp;
907 tpl_pound_data *pd;
908 int i;
909
910 /* handle the root node ONLY (subtree's ser_osz have been bubbled-up) */
911 if (n->type != TPL_TYPE_ROOT) {
912 tpl_hook.fatal("internal error: tpl_ser_osz on non-root node\n");
913 }
914
915 sz = n->ser_osz; /* start with fixed overhead, already stored */
916 c=n->children;
917 while (c) {
918 switch (c->type) {
919 case TPL_TYPE_BYTE:
920 case TPL_TYPE_DOUBLE:
921 case TPL_TYPE_INT32:
922 case TPL_TYPE_UINT32:
923 case TPL_TYPE_INT64:
924 case TPL_TYPE_UINT64:
925 case TPL_TYPE_INT16:
926 case TPL_TYPE_UINT16:
927 sz += tpl_types[c->type].sz * c->num;
928 break;
929 case TPL_TYPE_BIN:
930 sz += sizeof(uint32_t); /* binary buf len */
931 memcpy(&binp,c->data,sizeof(tpl_bin*)); /* cp to aligned */
932 sz += binp->sz;
933 break;
934 case TPL_TYPE_STR:
935 for(i=0; i < c->num; i++) {
936 sz += sizeof(uint32_t); /* string len */
937 memcpy(&strp,&((char**)c->data)[i],sizeof(char*)); /* cp to aligned */
938 sz += strp ? strlen(strp) : 0;
939 }
940 break;
941 case TPL_TYPE_ARY:
942 sz += sizeof(uint32_t); /* array len */
943 sz += c->ser_osz; /* bubbled-up child array ser_osz */
944 break;
945 case TPL_TYPE_POUND:
946 /* iterate over the preceding nodes */
947 itermax = c->num;
948 pd = (tpl_pound_data*)c->data;
949 if (++(pd->iternum) < itermax) {
950 for(np=pd->iter_start_node; np != c; np = np->next) {
951 np->data = (char*)(np->data) +
952 (tpl_types[np->type].sz * np->num);
953 }
954 c = pd->iter_start_node;
955 continue;
956 } else { /* loop complete. */
957 pd->iternum = 0;
958 for(np=pd->iter_start_node; np != c; np = np->next) {
959 np->data = (char*)(np->data) - ((itermax-1) *
960 tpl_types[np->type].sz *
961 np->num);
962 }
963 }
964 break;
965 default:
966 tpl_hook.fatal("unsupported format character\n");
967 break;
968 }
969 c=c->next;
970 }
971 return sz;
972 }
973
974
tpl_dump(tpl_node * r,int mode,...)975 TPL_API int tpl_dump(tpl_node *r, int mode, ...) {
976 va_list ap;
977 char *filename, *bufv;
978 void **addr_out,*buf, *pa_addr;
979 int fd,rc=0;
980 size_t sz,*sz_out, pa_sz;
981
982 if (((tpl_root_data*)(r->data))->flags & TPL_RDONLY) { /* unusual */
983 tpl_hook.oops("error: tpl_dump called for a loaded tpl\n");
984 return -1;
985 }
986
987 sz = tpl_ser_osz(r); /* compute the size needed to serialize */
988
989 va_start(ap,mode);
990 if (mode & TPL_FILE) {
991 filename = va_arg(ap,char*);
992 fd = tpl_mmap_output_file(filename, sz, &buf);
993 if (fd == -1) rc = -1;
994 else {
995 rc = tpl_dump_to_mem(r,buf,sz);
996 if (msync(buf,sz,MS_SYNC) == -1) {
997 tpl_hook.oops("msync failed on fd %d: %s\n", fd, strerror(errno));
998 }
999 if (munmap(buf, sz) == -1) {
1000 tpl_hook.oops("munmap failed on fd %d: %s\n", fd, strerror(errno));
1001 }
1002 close(fd);
1003 }
1004 } else if (mode & TPL_FD) {
1005 fd = va_arg(ap, int);
1006 if ( (buf = tpl_hook.malloc(sz)) == NULL) fatal_oom();
1007 tpl_dump_to_mem(r,buf,sz);
1008 bufv = buf;
1009 do {
1010 rc = write(fd,bufv,sz);
1011 if (rc > 0) {
1012 sz -= rc;
1013 bufv += rc;
1014 } else if (rc == -1) {
1015 if (errno == EINTR || errno == EAGAIN) continue;
1016 va_end(ap);
1017 tpl_hook.oops("error writing to fd %d: %s\n", fd, strerror(errno));
1018 free(buf);
1019 return -1;
1020 }
1021 } while (sz > 0);
1022 free(buf);
1023 rc = 0;
1024 } else if (mode & TPL_MEM) {
1025 if (mode & TPL_PREALLOCD) { /* caller allocated */
1026 pa_addr = (void*)va_arg(ap, void*);
1027 pa_sz = va_arg(ap, size_t);
1028 if (pa_sz < sz) {
1029 va_end(ap);
1030 tpl_hook.oops("tpl_dump: buffer too small, need %d bytes\n", sz);
1031 return -1;
1032 }
1033 rc=tpl_dump_to_mem(r,pa_addr,sz);
1034 } else { /* we allocate */
1035 addr_out = (void**)va_arg(ap, void*);
1036 sz_out = va_arg(ap, size_t*);
1037 if ( (buf = tpl_hook.malloc(sz)) == NULL) fatal_oom();
1038 *sz_out = sz;
1039 *addr_out = buf;
1040 rc=tpl_dump_to_mem(r,buf,sz);
1041 }
1042 } else if (mode & TPL_GETSIZE) {
1043 sz_out = va_arg(ap, size_t*);
1044 *sz_out = sz;
1045 } else {
1046 tpl_hook.oops("unsupported tpl_dump mode %d\n", mode);
1047 rc=-1;
1048 }
1049 va_end(ap);
1050 return rc;
1051 }
1052
1053 /* This function expects the caller to have set up a memory buffer of
1054 * adequate size to hold the serialized tpl. The sz parameter must be
1055 * the result of tpl_ser_osz(r).
1056 */
tpl_dump_to_mem(tpl_node * r,void * addr,size_t sz)1057 static int tpl_dump_to_mem(tpl_node *r,void *addr,size_t sz) {
1058 uint32_t slen, sz32;
1059 int *fxlens, num_fxlens, i;
1060 void *dv;
1061 char *fmt,flags;
1062 tpl_node *c, *np;
1063 tpl_pound_data *pd;
1064 size_t itermax;
1065
1066 fmt = tpl_fmt(r);
1067 flags = 0;
1068 if (tpl_cpu_bigendian()) flags |= TPL_FL_BIGENDIAN;
1069 if (strchr(fmt,'s')) flags |= TPL_FL_NULLSTRINGS;
1070 sz32 = sz;
1071
1072 dv = addr;
1073 dv = tpl_cpv(dv,TPL_MAGIC,3); /* copy tpl magic prefix */
1074 dv = tpl_cpv(dv,&flags,1); /* copy flags byte */
1075 dv = tpl_cpv(dv,&sz32,sizeof(uint32_t));/* overall length (inclusive) */
1076 dv = tpl_cpv(dv,fmt,strlen(fmt)+1); /* copy format with NUL-term */
1077 fxlens = tpl_fxlens(r,&num_fxlens);
1078 dv = tpl_cpv(dv,fxlens,num_fxlens*sizeof(uint32_t));/* fmt # lengths */
1079
1080 /* serialize the tpl content, iterating over direct children of root */
1081 c = r->children;
1082 while (c) {
1083 switch (c->type) {
1084 case TPL_TYPE_BYTE:
1085 case TPL_TYPE_DOUBLE:
1086 case TPL_TYPE_INT32:
1087 case TPL_TYPE_UINT32:
1088 case TPL_TYPE_INT64:
1089 case TPL_TYPE_UINT64:
1090 case TPL_TYPE_INT16:
1091 case TPL_TYPE_UINT16:
1092 dv = tpl_cpv(dv,c->data,tpl_types[c->type].sz * c->num);
1093 break;
1094 case TPL_TYPE_BIN:
1095 slen = (*(tpl_bin**)(c->data))->sz;
1096 dv = tpl_cpv(dv,&slen,sizeof(uint32_t)); /* buffer len */
1097 dv = tpl_cpv(dv,(*(tpl_bin**)(c->data))->addr,slen); /* buf */
1098 break;
1099 case TPL_TYPE_STR:
1100 for(i=0; i < c->num; i++) {
1101 char *str = ((char**)c->data)[i];
1102 slen = str ? strlen(str)+1 : 0;
1103 dv = tpl_cpv(dv,&slen,sizeof(uint32_t)); /* string len */
1104 if (slen>1) dv = tpl_cpv(dv,str,slen-1); /*string*/
1105 }
1106 break;
1107 case TPL_TYPE_ARY:
1108 dv = tpl_dump_atyp(c,(tpl_atyp*)c->data,dv);
1109 break;
1110 case TPL_TYPE_POUND:
1111 pd = (tpl_pound_data*)c->data;
1112 itermax = c->num;
1113 if (++(pd->iternum) < itermax) {
1114
1115 /* in start or midst of loop. advance data pointers. */
1116 for(np=pd->iter_start_node; np != c; np = np->next) {
1117 np->data = (char*)(np->data) +
1118 (tpl_types[np->type].sz * np->num);
1119 }
1120 /* do next iteration */
1121 c = pd->iter_start_node;
1122 continue;
1123
1124 } else { /* loop complete. */
1125
1126 /* reset iteration index and addr/data pointers. */
1127 pd->iternum = 0;
1128 for(np=pd->iter_start_node; np != c; np = np->next) {
1129 np->data = (char*)(np->data) - ((itermax-1) *
1130 tpl_types[np->type].sz *
1131 np->num);
1132 }
1133
1134 }
1135 break;
1136 default:
1137 tpl_hook.fatal("unsupported format character\n");
1138 break;
1139 }
1140 c = c->next;
1141 }
1142
1143 return 0;
1144 }
1145
tpl_cpu_bigendian()1146 static int tpl_cpu_bigendian() {
1147 unsigned i = 1;
1148 char *c;
1149 c = (char*)&i;
1150 return (c[0] == 1 ? 0 : 1);
1151 }
1152
1153
1154 /*
1155 * algorithm for sanity-checking a tpl image:
1156 * scan the tpl whilst not exceeding the buffer size (bufsz) ,
1157 * formulating a calculated (expected) size of the tpl based
1158 * on walking its data. When calcsize has been calculated it
1159 * should exactly match the buffer size (bufsz) and the internal
1160 * recorded size (intlsz)
1161 */
tpl_sanity(tpl_node * r,int excess_ok)1162 static int tpl_sanity(tpl_node *r, int excess_ok) {
1163 uint32_t intlsz;
1164 int found_nul=0,rc, octothorpes=0, num_fxlens, *fxlens, flen;
1165 void *d, *dv;
1166 char intlflags, *fmt, c, *mapfmt;
1167 size_t bufsz, serlen;
1168
1169 d = ((tpl_root_data*)(r->data))->mmap.text;
1170 bufsz = ((tpl_root_data*)(r->data))->mmap.text_sz;
1171
1172 dv = d;
1173 if (bufsz < (4 + sizeof(uint32_t) + 1)) return ERR_NOT_MINSIZE; /* min sz: magic+flags+len+nul */
1174 if (memcmp(dv,TPL_MAGIC, 3) != 0) return ERR_MAGIC_MISMATCH; /* missing tpl magic prefix */
1175 if (tpl_needs_endian_swap(dv)) ((tpl_root_data*)(r->data))->flags |= TPL_XENDIAN;
1176 dv = (void*)((uintptr_t)dv + 3);
1177 memcpy(&intlflags,dv,sizeof(char)); /* extract flags */
1178 if (intlflags & ~TPL_SUPPORTED_BITFLAGS) return ERR_UNSUPPORTED_FLAGS;
1179 /* TPL1.3 stores strings with a "length+1" prefix to discern NULL strings from
1180 empty strings from non-empty strings; TPL1.2 only handled the latter two.
1181 So we need to be mindful of which string format we're reading from. */
1182 if (!(intlflags & TPL_FL_NULLSTRINGS)) {
1183 ((tpl_root_data*)(r->data))->flags |= TPL_OLD_STRING_FMT;
1184 }
1185 dv = (void*)((uintptr_t)dv + 1);
1186 memcpy(&intlsz,dv,sizeof(uint32_t)); /* extract internal size */
1187 if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN) tpl_byteswap(&intlsz, sizeof(uint32_t));
1188 if (!excess_ok && (intlsz != bufsz)) return ERR_INCONSISTENT_SZ; /* inconsisent buffer/internal size */
1189 dv = (void*)((uintptr_t)dv + sizeof(uint32_t));
1190
1191 /* dv points to the start of the format string. Look for nul w/in buf sz */
1192 fmt = (char*)dv;
1193 while ((uintptr_t)dv-(uintptr_t)d < bufsz && !found_nul) {
1194 if ( (c = *(char*)dv) != '\0') {
1195 if (strchr(tpl_fmt_chars,c) == NULL)
1196 return ERR_FMT_INVALID; /* invalid char in format string */
1197 if ( (c = *(char*)dv) == '#') octothorpes++;
1198 dv = (void*)((uintptr_t)dv + 1);
1199 }
1200 else found_nul = 1;
1201 }
1202 if (!found_nul) return ERR_FMT_MISSING_NUL; /* runaway format string */
1203 dv = (void*)((uintptr_t)dv + 1); /* advance to octothorpe lengths buffer */
1204
1205 /* compare the map format to the format of this tpl image */
1206 mapfmt = tpl_fmt(r);
1207 rc = strcmp(mapfmt,fmt);
1208 if (rc != 0) return ERR_FMT_MISMATCH;
1209
1210 /* compare octothorpe lengths in image to the mapped values */
1211 if ((((uintptr_t)dv + (octothorpes * 4)) - (uintptr_t)d) > bufsz) return ERR_INCONSISTENT_SZ4;
1212 fxlens = tpl_fxlens(r,&num_fxlens); /* mapped fxlens */
1213 while(num_fxlens--) {
1214 memcpy(&flen,dv,sizeof(uint32_t)); /* stored flen */
1215 if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN) tpl_byteswap(&flen, sizeof(uint32_t));
1216 if (flen != *fxlens) return ERR_FLEN_MISMATCH;
1217 dv = (void*)((uintptr_t)dv + sizeof(uint32_t));
1218 fxlens++;
1219 }
1220
1221 /* dv now points to beginning of data */
1222 rc = tpl_serlen(r,r,dv,&serlen); /* get computed serlen of data part */
1223 if (rc == -1) return ERR_INCONSISTENT_SZ2; /* internal inconsistency in tpl image */
1224 serlen += ((uintptr_t)dv - (uintptr_t)d); /* add back serlen of preamble part */
1225 if (excess_ok && (bufsz < serlen)) return ERR_INCONSISTENT_SZ3;
1226 if (!excess_ok && (serlen != bufsz)) return ERR_INCONSISTENT_SZ3; /* buffer/internal sz exceeds serlen */
1227 return 0;
1228 }
1229
tpl_find_data_start(void * d)1230 static void *tpl_find_data_start(void *d) {
1231 int octothorpes=0;
1232 d = (void*)((uintptr_t)d + 4); /* skip TPL_MAGIC and flags byte */
1233 d = (void*)((uintptr_t)d + 4); /* skip int32 overall len */
1234 while(*(char*)d != '\0') {
1235 if (*(char*)d == '#') octothorpes++;
1236 d = (void*)((uintptr_t)d + 1);
1237 }
1238 d = (void*)((uintptr_t)d + 1); /* skip NUL */
1239 d = (void*)((uintptr_t)d + (octothorpes * sizeof(uint32_t))); /* skip # array lens */
1240 return d;
1241 }
1242
tpl_needs_endian_swap(void * d)1243 static int tpl_needs_endian_swap(void *d) {
1244 char *c;
1245 int cpu_is_bigendian;
1246 c = (char*)d;
1247 cpu_is_bigendian = tpl_cpu_bigendian();
1248 return ((c[3] & TPL_FL_BIGENDIAN) == cpu_is_bigendian) ? 0 : 1;
1249 }
1250
tpl_size_for(char c)1251 static size_t tpl_size_for(char c) {
1252 register size_t i;
1253 for(i=0; i < sizeof(tpl_types)/sizeof(tpl_types[0]); i++) {
1254 if (tpl_types[i].c == c) return tpl_types[i].sz;
1255 }
1256 return 0;
1257 }
1258
tpl_peek(int mode,...)1259 TPL_API char* tpl_peek(int mode, ...) {
1260 va_list ap;
1261 int xendian=0,found_nul=0,old_string_format=0;
1262 char *filename=NULL, *datapeek_f=NULL, *datapeek_c=NULL, *datapeek_s=NULL;
1263 void *addr=NULL, *dv, *datapeek_p=NULL;
1264 size_t sz=0, fmt_len, first_atom, num_fxlens=0;
1265 uint32_t datapeek_ssz, datapeek_csz, datapeek_flen;
1266 tpl_mmap_rec mr = {0,NULL,0};
1267 char *fmt,*fmt_cpy=NULL,c;
1268 uint32_t intlsz, **fxlens=NULL, *num_fxlens_out=NULL, *fxlensv;
1269
1270 va_start(ap,mode);
1271 if ((mode & TPL_FXLENS) && (mode & TPL_DATAPEEK)) {
1272 tpl_hook.oops("TPL_FXLENS and TPL_DATAPEEK mutually exclusive\n");
1273 goto fail;
1274 }
1275 if (mode & TPL_FILE) filename = va_arg(ap,char *);
1276 else if (mode & TPL_MEM) {
1277 addr = va_arg(ap,void *);
1278 sz = va_arg(ap,size_t);
1279 } else {
1280 tpl_hook.oops("unsupported tpl_peek mode %d\n", mode);
1281 goto fail;
1282 }
1283 if (mode & TPL_DATAPEEK) {
1284 datapeek_f = va_arg(ap, char*);
1285 }
1286 if (mode & TPL_FXLENS) {
1287 num_fxlens_out = va_arg(ap,uint32_t *);
1288 fxlens = va_arg(ap,uint32_t **);
1289 *num_fxlens_out = 0;
1290 *fxlens = NULL;
1291 }
1292
1293 if (mode & TPL_FILE) {
1294 if (tpl_mmap_file(filename, &mr) != 0) {
1295 tpl_hook.oops("tpl_peek failed for file %s\n", filename);
1296 goto fail;
1297 }
1298 addr = mr.text;
1299 sz = mr.text_sz;
1300 }
1301
1302 dv = addr;
1303 if (sz < (4 + sizeof(uint32_t) + 1)) goto fail; /* min sz */
1304 if (memcmp(dv,TPL_MAGIC, 3) != 0) goto fail; /* missing tpl magic prefix */
1305 if (tpl_needs_endian_swap(dv)) xendian=1;
1306 if ((((char*)dv)[3] & TPL_FL_NULLSTRINGS)==0) old_string_format=1;
1307 dv = (void*)((uintptr_t)dv + 4);
1308 memcpy(&intlsz,dv,sizeof(uint32_t)); /* extract internal size */
1309 if (xendian) tpl_byteswap(&intlsz, sizeof(uint32_t));
1310 if (intlsz != sz) goto fail; /* inconsisent buffer/internal size */
1311 dv = (void*)((uintptr_t)dv + sizeof(uint32_t));
1312
1313 /* dv points to the start of the format string. Look for nul w/in buf sz */
1314 fmt = (char*)dv;
1315 while ((uintptr_t)dv-(uintptr_t)addr < sz && !found_nul) {
1316 if ( (c = *(char*)dv) == '\0') {
1317 found_nul = 1;
1318 } else if (c == '#') {
1319 num_fxlens++;
1320 }
1321 dv = (void*)((uintptr_t)dv + 1);
1322 }
1323 if (!found_nul) goto fail; /* runaway format string */
1324 fmt_len = (char*)dv - fmt; /* include space for \0 */
1325 fmt_cpy = tpl_hook.malloc(fmt_len);
1326 if (fmt_cpy == NULL) {
1327 fatal_oom();
1328 }
1329 memcpy(fmt_cpy, fmt, fmt_len);
1330
1331 /* retrieve the octothorpic lengths if requested */
1332 if (num_fxlens > 0) {
1333 if (sz < ((uintptr_t)dv + (num_fxlens * sizeof(uint32_t)) - (uintptr_t)addr)) {
1334 goto fail;
1335 }
1336 }
1337 if ((mode & TPL_FXLENS) && (num_fxlens > 0)) {
1338 *fxlens = tpl_hook.malloc(num_fxlens * sizeof(uint32_t));
1339 if (*fxlens == NULL) tpl_hook.fatal("out of memory");
1340 *num_fxlens_out = num_fxlens;
1341 fxlensv = *fxlens;
1342 while(num_fxlens--) {
1343 memcpy(fxlensv,dv,sizeof(uint32_t));
1344 if (xendian) tpl_byteswap(fxlensv, sizeof(uint32_t));
1345 dv = (void*)((uintptr_t)dv + sizeof(uint32_t));
1346 fxlensv++;
1347 }
1348 }
1349 /* if caller requested, peek into the specified data elements */
1350 if (mode & TPL_DATAPEEK) {
1351
1352 first_atom = strspn(fmt, "S()"); /* skip any leading S() */
1353
1354 datapeek_flen = strlen(datapeek_f);
1355 if (strspn(datapeek_f, tpl_datapeek_ok_chars) < datapeek_flen) {
1356 tpl_hook.oops("invalid TPL_DATAPEEK format: %s\n", datapeek_f);
1357 tpl_hook.free(fmt_cpy); fmt_cpy = NULL; /* fail */
1358 goto fail;
1359 }
1360
1361 if (strncmp( &fmt[first_atom], datapeek_f, datapeek_flen) != 0) {
1362 tpl_hook.oops("TPL_DATAPEEK format mismatches tpl iamge\n");
1363 tpl_hook.free(fmt_cpy); fmt_cpy = NULL; /* fail */
1364 goto fail;
1365 }
1366
1367 /* advance to data start, then copy out requested elements */
1368 dv = (void*)((uintptr_t)dv + (num_fxlens * sizeof(uint32_t)));
1369 for(datapeek_c = datapeek_f; datapeek_c != NULL && *datapeek_c != '\0'; datapeek_c++) {
1370 datapeek_p = va_arg(ap, void*);
1371 if (*datapeek_c == 's') { /* special handling for strings */
1372 if ((uintptr_t)dv-(uintptr_t)addr + sizeof(uint32_t) > sz) {
1373 tpl_hook.oops("tpl_peek: tpl has insufficient length\n");
1374 tpl_hook.free(fmt_cpy); fmt_cpy = NULL; /* fail */
1375 goto fail;
1376 }
1377 memcpy(&datapeek_ssz,dv,sizeof(uint32_t)); /* get slen */
1378 if (xendian) tpl_byteswap(&datapeek_ssz, sizeof(uint32_t));
1379 if (old_string_format) datapeek_ssz++;
1380 dv = (void*)((uintptr_t)dv + sizeof(uint32_t)); /* adv. to str */
1381 if (datapeek_ssz == 0) datapeek_s = NULL;
1382 else {
1383 if ((uintptr_t)dv-(uintptr_t)addr + datapeek_ssz-1 > sz) {
1384 tpl_hook.oops("tpl_peek: tpl has insufficient length\n");
1385 tpl_hook.free(fmt_cpy); fmt_cpy = NULL; /* fail */
1386 goto fail;
1387 }
1388 datapeek_s = tpl_hook.malloc(datapeek_ssz);
1389 if (datapeek_s == NULL) fatal_oom();
1390 memcpy(datapeek_s, dv, datapeek_ssz-1);
1391 datapeek_s[datapeek_ssz-1] = '\0';
1392 dv = (void*)((uintptr_t)dv + datapeek_ssz-1);
1393 }
1394 *(char**)datapeek_p = datapeek_s;
1395 } else {
1396 datapeek_csz = tpl_size_for(*datapeek_c);
1397 if ((uintptr_t)dv-(uintptr_t)addr + datapeek_csz > sz) {
1398 tpl_hook.oops("tpl_peek: tpl has insufficient length\n");
1399 tpl_hook.free(fmt_cpy); fmt_cpy = NULL; /* fail */
1400 goto fail;
1401 }
1402 memcpy(datapeek_p, dv, datapeek_csz);
1403 if (xendian) tpl_byteswap(datapeek_p, datapeek_csz);
1404 dv = (void*)((uintptr_t)dv + datapeek_csz);
1405 }
1406 }
1407 }
1408
1409 fail:
1410 va_end(ap);
1411 if ((mode & TPL_FILE) && mr.text != NULL) tpl_unmap_file( &mr );
1412 return fmt_cpy;
1413 }
1414
1415 /* tpl_jot(TPL_FILE, "file.tpl", "si", &s, &i); */
1416 /* tpl_jot(TPL_MEM, &buf, &sz, "si", &s, &i); */
1417 /* tpl_jot(TPL_FD, fd, "si", &s, &i); */
tpl_jot(int mode,...)1418 TPL_API int tpl_jot(int mode, ...) {
1419 va_list ap;
1420 char *filename, *fmt;
1421 size_t *sz;
1422 int fd, rc=0;
1423 void **buf;
1424 tpl_node *tn;
1425
1426 va_start(ap,mode);
1427 if (mode & TPL_FILE) {
1428 filename = va_arg(ap,char*);
1429 fmt = va_arg(ap,char*);
1430 tn = tpl_map_va(fmt, ap);
1431 if (tn == NULL) { rc=-1; goto fail;}
1432 tpl_pack(tn, 0);
1433 rc = tpl_dump(tn, TPL_FILE, filename);
1434 tpl_free(tn);
1435 } else if (mode & TPL_MEM) {
1436 buf = va_arg(ap,void*);
1437 sz = va_arg(ap,size_t*);
1438 fmt = va_arg(ap,char*);
1439 tn = tpl_map_va(fmt,ap);
1440 if (tn == NULL) { rc=-1; goto fail;}
1441 tpl_pack(tn,0);
1442 rc = tpl_dump(tn, TPL_MEM, buf, sz);
1443 tpl_free(tn);
1444 } else if (mode & TPL_FD) {
1445 fd = va_arg(ap,int);
1446 fmt = va_arg(ap,char*);
1447 tn = tpl_map_va(fmt,ap);
1448 if (tn == NULL) { rc=-1; goto fail;}
1449 tpl_pack(tn,0);
1450 rc = tpl_dump(tn, TPL_FD, fd);
1451 tpl_free(tn);
1452 } else {
1453 tpl_hook.fatal("invalid tpl_jot mode\n");
1454 }
1455
1456 fail:
1457 va_end(ap);
1458 return rc;
1459 }
1460
tpl_load(tpl_node * r,int mode,...)1461 TPL_API int tpl_load(tpl_node *r, int mode, ...) {
1462 va_list ap;
1463 int rc=0,fd=0;
1464 char *filename=NULL;
1465 void *addr=NULL;
1466 size_t sz;
1467
1468 va_start(ap,mode);
1469 if (mode & TPL_FILE) filename = va_arg(ap,char *);
1470 else if (mode & TPL_MEM) {
1471 addr = va_arg(ap,void *);
1472 sz = va_arg(ap,size_t);
1473 } else if (mode & TPL_FD) {
1474 fd = va_arg(ap,int);
1475 } else {
1476 va_end(ap);
1477 tpl_hook.oops("unsupported tpl_load mode %d\n", mode);
1478 return -1;
1479 }
1480 va_end(ap);
1481
1482 if (r->type != TPL_TYPE_ROOT) {
1483 tpl_hook.oops("error: tpl_load to non-root node\n");
1484 return -1;
1485 }
1486 if (((tpl_root_data*)(r->data))->flags & (TPL_WRONLY|TPL_RDONLY)) {
1487 /* already packed or loaded, so reset it as if newly mapped */
1488 tpl_free_keep_map(r);
1489 }
1490 if (mode & TPL_FILE) {
1491 if (tpl_mmap_file(filename, &((tpl_root_data*)(r->data))->mmap) != 0) {
1492 tpl_hook.oops("tpl_load failed for file %s\n", filename);
1493 return -1;
1494 }
1495 if ( (rc = tpl_sanity(r, (mode & TPL_EXCESS_OK))) != 0) {
1496 if (rc == ERR_FMT_MISMATCH) {
1497 tpl_hook.oops("%s: format signature mismatch\n", filename);
1498 } else if (rc == ERR_FLEN_MISMATCH) {
1499 tpl_hook.oops("%s: array lengths mismatch\n", filename);
1500 } else {
1501 tpl_hook.oops("%s: not a valid tpl file\n", filename);
1502 }
1503 tpl_unmap_file( &((tpl_root_data*)(r->data))->mmap );
1504 return -1;
1505 }
1506 ((tpl_root_data*)(r->data))->flags = (TPL_FILE | TPL_RDONLY);
1507 } else if (mode & TPL_MEM) {
1508 ((tpl_root_data*)(r->data))->mmap.text = addr;
1509 ((tpl_root_data*)(r->data))->mmap.text_sz = sz;
1510 if ( (rc = tpl_sanity(r, (mode & TPL_EXCESS_OK))) != 0) {
1511 if (rc == ERR_FMT_MISMATCH) {
1512 tpl_hook.oops("format signature mismatch\n");
1513 } else {
1514 tpl_hook.oops("not a valid tpl file\n");
1515 }
1516 return -1;
1517 }
1518 ((tpl_root_data*)(r->data))->flags = (TPL_MEM | TPL_RDONLY);
1519 if (mode & TPL_UFREE) ((tpl_root_data*)(r->data))->flags |= TPL_UFREE;
1520 } else if (mode & TPL_FD) {
1521 /* if fd read succeeds, resulting mem img is used for load */
1522 if (tpl_gather(TPL_GATHER_BLOCKING,fd,&addr,&sz) > 0) {
1523 return tpl_load(r, TPL_MEM|TPL_UFREE, addr, sz);
1524 } else return -1;
1525 } else {
1526 tpl_hook.oops("invalid tpl_load mode %d\n", mode);
1527 return -1;
1528 }
1529 /* this applies to TPL_MEM or TPL_FILE */
1530 if (tpl_needs_endian_swap(((tpl_root_data*)(r->data))->mmap.text))
1531 ((tpl_root_data*)(r->data))->flags |= TPL_XENDIAN;
1532 tpl_unpackA0(r); /* prepare root A nodes for use */
1533 return 0;
1534 }
1535
tpl_Alen(tpl_node * r,int i)1536 TPL_API int tpl_Alen(tpl_node *r, int i) {
1537 tpl_node *n;
1538
1539 n = tpl_find_i(r,i);
1540 if (n == NULL) {
1541 tpl_hook.oops("invalid index %d to tpl_unpack\n", i);
1542 return -1;
1543 }
1544 if (n->type != TPL_TYPE_ARY) return -1;
1545 return ((tpl_atyp*)(n->data))->num;
1546 }
1547
tpl_free_atyp(tpl_node * n,tpl_atyp * atyp)1548 static void tpl_free_atyp(tpl_node *n, tpl_atyp *atyp) {
1549 tpl_backbone *bb,*bbnxt;
1550 tpl_node *c;
1551 void *dv;
1552 tpl_bin *binp;
1553 tpl_atyp *atypp;
1554 char *strp;
1555 size_t itermax;
1556 tpl_pound_data *pd;
1557 int i;
1558
1559 bb = atyp->bb;
1560 while (bb) {
1561 bbnxt = bb->next;
1562 dv = bb->data;
1563 c=n->children;
1564 while (c) {
1565 switch (c->type) {
1566 case TPL_TYPE_BYTE:
1567 case TPL_TYPE_DOUBLE:
1568 case TPL_TYPE_INT32:
1569 case TPL_TYPE_UINT32:
1570 case TPL_TYPE_INT64:
1571 case TPL_TYPE_UINT64:
1572 case TPL_TYPE_INT16:
1573 case TPL_TYPE_UINT16:
1574 dv = (void*)((uintptr_t)dv + tpl_types[c->type].sz*c->num);
1575 break;
1576 case TPL_TYPE_BIN:
1577 memcpy(&binp,dv,sizeof(tpl_bin*)); /* cp to aligned */
1578 if (binp->addr) tpl_hook.free( binp->addr ); /* free buf */
1579 tpl_hook.free(binp); /* free tpl_bin */
1580 dv = (void*)((uintptr_t)dv + sizeof(tpl_bin*));
1581 break;
1582 case TPL_TYPE_STR:
1583 for(i=0; i < c->num; i++) {
1584 memcpy(&strp,dv,sizeof(char*)); /* cp to aligned */
1585 if (strp) tpl_hook.free(strp); /* free string */
1586 dv = (void*)((uintptr_t)dv + sizeof(char*));
1587 }
1588 break;
1589 case TPL_TYPE_POUND:
1590 /* iterate over the preceding nodes */
1591 itermax = c->num;
1592 pd = (tpl_pound_data*)c->data;
1593 if (++(pd->iternum) < itermax) {
1594 c = pd->iter_start_node;
1595 continue;
1596 } else { /* loop complete. */
1597 pd->iternum = 0;
1598 }
1599 break;
1600 case TPL_TYPE_ARY:
1601 memcpy(&atypp,dv,sizeof(tpl_atyp*)); /* cp to aligned */
1602 tpl_free_atyp(c,atypp); /* free atyp */
1603 dv = (void*)((uintptr_t)dv + sizeof(void*));
1604 break;
1605 default:
1606 tpl_hook.fatal("unsupported format character\n");
1607 break;
1608 }
1609 c=c->next;
1610 }
1611 tpl_hook.free(bb);
1612 bb = bbnxt;
1613 }
1614 tpl_hook.free(atyp);
1615 }
1616
1617 /* determine (by walking) byte length of serialized r/A node at address dv
1618 * returns 0 on success, or -1 if the tpl isn't trustworthy (fails consistency)
1619 */
tpl_serlen(tpl_node * r,tpl_node * n,void * dv,size_t * serlen)1620 static int tpl_serlen(tpl_node *r, tpl_node *n, void *dv, size_t *serlen) {
1621 uint32_t slen;
1622 int num=0,fidx;
1623 tpl_node *c;
1624 size_t len=0, alen, buf_past, itermax;
1625 tpl_pound_data *pd;
1626
1627 buf_past = ((uintptr_t)((tpl_root_data*)(r->data))->mmap.text +
1628 ((tpl_root_data*)(r->data))->mmap.text_sz);
1629
1630 if (n->type == TPL_TYPE_ROOT) num = 1;
1631 else if (n->type == TPL_TYPE_ARY) {
1632 if ((uintptr_t)dv + sizeof(uint32_t) > buf_past) return -1;
1633 memcpy(&num,dv,sizeof(uint32_t));
1634 if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN)
1635 tpl_byteswap(&num, sizeof(uint32_t));
1636 dv = (void*)((uintptr_t)dv + sizeof(uint32_t));
1637 len += sizeof(uint32_t);
1638 } else tpl_hook.fatal("internal error in tpl_serlen\n");
1639
1640 while (num-- > 0) {
1641 c=n->children;
1642 while (c) {
1643 switch (c->type) {
1644 case TPL_TYPE_BYTE:
1645 case TPL_TYPE_DOUBLE:
1646 case TPL_TYPE_INT32:
1647 case TPL_TYPE_UINT32:
1648 case TPL_TYPE_INT64:
1649 case TPL_TYPE_UINT64:
1650 case TPL_TYPE_INT16:
1651 case TPL_TYPE_UINT16:
1652 for(fidx=0; fidx < c->num; fidx++) { /* octothorpe support */
1653 if ((uintptr_t)dv + tpl_types[c->type].sz > buf_past) return -1;
1654 dv = (void*)((uintptr_t)dv + tpl_types[c->type].sz);
1655 len += tpl_types[c->type].sz;
1656 }
1657 break;
1658 case TPL_TYPE_BIN:
1659 len += sizeof(uint32_t);
1660 if ((uintptr_t)dv + sizeof(uint32_t) > buf_past) return -1;
1661 memcpy(&slen,dv,sizeof(uint32_t));
1662 if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN)
1663 tpl_byteswap(&slen, sizeof(uint32_t));
1664 len += slen;
1665 dv = (void*)((uintptr_t)dv + sizeof(uint32_t));
1666 if ((uintptr_t)dv + slen > buf_past) return -1;
1667 dv = (void*)((uintptr_t)dv + slen);
1668 break;
1669 case TPL_TYPE_STR:
1670 for(fidx=0; fidx < c->num; fidx++) { /* octothorpe support */
1671 len += sizeof(uint32_t);
1672 if ((uintptr_t)dv + sizeof(uint32_t) > buf_past) return -1;
1673 memcpy(&slen,dv,sizeof(uint32_t));
1674 if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN)
1675 tpl_byteswap(&slen, sizeof(uint32_t));
1676 if (!(((tpl_root_data*)(r->data))->flags & TPL_OLD_STRING_FMT))
1677 slen = (slen>1) ? (slen-1) : 0;
1678 len += slen;
1679 dv = (void*)((uintptr_t)dv + sizeof(uint32_t));
1680 if ((uintptr_t)dv + slen > buf_past) return -1;
1681 dv = (void*)((uintptr_t)dv + slen);
1682 }
1683 break;
1684 case TPL_TYPE_ARY:
1685 if ( tpl_serlen(r,c,dv, &alen) == -1) return -1;
1686 dv = (void*)((uintptr_t)dv + alen);
1687 len += alen;
1688 break;
1689 case TPL_TYPE_POUND:
1690 /* iterate over the preceding nodes */
1691 itermax = c->num;
1692 pd = (tpl_pound_data*)c->data;
1693 if (++(pd->iternum) < itermax) {
1694 c = pd->iter_start_node;
1695 continue;
1696 } else { /* loop complete. */
1697 pd->iternum = 0;
1698 }
1699 break;
1700 default:
1701 tpl_hook.fatal("unsupported format character\n");
1702 break;
1703 }
1704 c=c->next;
1705 }
1706 }
1707 *serlen = len;
1708 return 0;
1709 }
1710
tpl_mmap_output_file(char * filename,size_t sz,void ** text_out)1711 static int tpl_mmap_output_file(char *filename, size_t sz, void **text_out) {
1712 void *text;
1713 int fd,perms;
1714
1715 #ifndef _WIN32
1716 perms = S_IRUSR|S_IWUSR|S_IWGRP|S_IRGRP|S_IROTH; /* ug+w o+r */
1717 fd=open(filename,O_CREAT|O_TRUNC|O_RDWR,perms);
1718 #else
1719 perms = _S_IWRITE;
1720 fd=_open(filename,_O_CREAT|_O_TRUNC|_O_RDWR,perms);
1721 #endif
1722
1723 if ( fd == -1 ) {
1724 tpl_hook.oops("Couldn't open file %s: %s\n", filename, strerror(errno));
1725 return -1;
1726 }
1727
1728 text = mmap(0, sz, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
1729 if (text == MAP_FAILED) {
1730 tpl_hook.oops("Failed to mmap %s: %s\n", filename, strerror(errno));
1731 close(fd);
1732 return -1;
1733 }
1734 if (ftruncate(fd,sz) == -1) {
1735 tpl_hook.oops("ftruncate failed: %s\n", strerror(errno));
1736 (void) munmap( text, sz );
1737 (void) close(fd);
1738 return -1;
1739 }
1740 *text_out = text;
1741 return fd;
1742 }
1743
tpl_mmap_file(char * filename,tpl_mmap_rec * mr)1744 static int tpl_mmap_file(char *filename, tpl_mmap_rec *mr) {
1745 struct stat stat_buf;
1746
1747 if ( (mr->fd = open(filename, O_RDONLY)) == -1 ) {
1748 tpl_hook.oops("Couldn't open file %s: %s\n", filename, strerror(errno));
1749 return -1;
1750 }
1751
1752 if ( fstat(mr->fd, &stat_buf) == -1) {
1753 close(mr->fd);
1754 tpl_hook.oops("Couldn't stat file %s: %s\n", filename, strerror(errno));
1755 return -1;
1756 }
1757
1758 mr->text_sz = (size_t)stat_buf.st_size;
1759 mr->text = mmap(0, mr->text_sz, PROT_READ, MAP_PRIVATE, mr->fd, 0);
1760 if (mr->text == MAP_FAILED) {
1761 close(mr->fd);
1762 tpl_hook.oops("Failed to mmap %s: %s\n", filename, strerror(errno));
1763 return -1;
1764 }
1765
1766 return 0;
1767 }
1768
tpl_pack(tpl_node * r,int i)1769 TPL_API int tpl_pack(tpl_node *r, int i) {
1770 tpl_node *n, *child, *np;
1771 void *datav=NULL;
1772 size_t sz, itermax;
1773 uint32_t slen;
1774 char *str;
1775 tpl_bin *bin;
1776 tpl_pound_data *pd;
1777 int fidx;
1778
1779 n = tpl_find_i(r,i);
1780 if (n == NULL) {
1781 tpl_hook.oops("invalid index %d to tpl_pack\n", i);
1782 return -1;
1783 }
1784
1785 if (((tpl_root_data*)(r->data))->flags & TPL_RDONLY) {
1786 /* convert to an writeable tpl, initially empty */
1787 tpl_free_keep_map(r);
1788 }
1789
1790 ((tpl_root_data*)(r->data))->flags |= TPL_WRONLY;
1791
1792 if (n->type == TPL_TYPE_ARY) datav = tpl_extend_backbone(n);
1793 child = n->children;
1794 while(child) {
1795 switch(child->type) {
1796 case TPL_TYPE_BYTE:
1797 case TPL_TYPE_DOUBLE:
1798 case TPL_TYPE_INT32:
1799 case TPL_TYPE_UINT32:
1800 case TPL_TYPE_INT64:
1801 case TPL_TYPE_UINT64:
1802 case TPL_TYPE_INT16:
1803 case TPL_TYPE_UINT16:
1804 /* no need to use fidx iteration here; we can copy multiple values in one memcpy */
1805 memcpy(child->data,child->addr,tpl_types[child->type].sz * child->num);
1806 if (datav) datav = tpl_cpv(datav,child->data,tpl_types[child->type].sz * child->num);
1807 if (n->type == TPL_TYPE_ARY) n->ser_osz += tpl_types[child->type].sz * child->num;
1808 break;
1809 case TPL_TYPE_BIN:
1810 /* copy the buffer to be packed */
1811 slen = ((tpl_bin*)child->addr)->sz;
1812 if (slen >0) {
1813 str = tpl_hook.malloc(slen);
1814 if (!str) fatal_oom();
1815 memcpy(str,((tpl_bin*)child->addr)->addr,slen);
1816 } else str = NULL;
1817 /* and make a tpl_bin to point to it */
1818 bin = tpl_hook.malloc(sizeof(tpl_bin));
1819 if (!bin) fatal_oom();
1820 bin->addr = str;
1821 bin->sz = slen;
1822 /* now pack its pointer, first deep freeing any pre-existing bin */
1823 if (*(tpl_bin**)(child->data) != NULL) {
1824 if ((*(tpl_bin**)(child->data))->sz != 0) {
1825 tpl_hook.free( (*(tpl_bin**)(child->data))->addr );
1826 }
1827 tpl_hook.free(*(tpl_bin**)(child->data));
1828 }
1829 memcpy(child->data,&bin,sizeof(tpl_bin*));
1830 if (datav) {
1831 datav = tpl_cpv(datav, &bin, sizeof(tpl_bin*));
1832 *(tpl_bin**)(child->data) = NULL;
1833 }
1834 if (n->type == TPL_TYPE_ARY) {
1835 n->ser_osz += sizeof(uint32_t); /* binary buf len word */
1836 n->ser_osz += bin->sz; /* binary buf */
1837 }
1838 break;
1839 case TPL_TYPE_STR:
1840 for(fidx=0; fidx < child->num; fidx++) {
1841 /* copy the string to be packed. slen includes \0. this
1842 block also works if the string pointer is NULL. */
1843 char *caddr = ((char**)child->addr)[fidx];
1844 char **cdata = &((char**)child->data)[fidx];
1845 slen = caddr ? (strlen(caddr) + 1) : 0;
1846 if (slen) {
1847 str = tpl_hook.malloc(slen);
1848 if (!str) fatal_oom();
1849 memcpy(str,caddr,slen); /* include \0 */
1850 } else {
1851 str = NULL;
1852 }
1853 /* now pack its pointer, first freeing any pre-existing string */
1854 if (*cdata != NULL) {
1855 tpl_hook.free(*cdata);
1856 }
1857 memcpy(cdata,&str,sizeof(char*));
1858 if (datav) {
1859 datav = tpl_cpv(datav, &str, sizeof(char*));
1860 *cdata = NULL;
1861 }
1862 if (n->type == TPL_TYPE_ARY) {
1863 n->ser_osz += sizeof(uint32_t); /* string len word */
1864 if (slen>1) n->ser_osz += slen-1;/* string (without nul) */
1865 }
1866 }
1867 break;
1868 case TPL_TYPE_ARY:
1869 /* copy the child's tpl_atype* and reset it to empty */
1870 if (datav) {
1871 sz = ((tpl_atyp*)(child->data))->sz;
1872 datav = tpl_cpv(datav, &child->data, sizeof(void*));
1873 child->data = tpl_hook.malloc(sizeof(tpl_atyp));
1874 if (!child->data) fatal_oom();
1875 ((tpl_atyp*)(child->data))->num = 0;
1876 ((tpl_atyp*)(child->data))->sz = sz;
1877 ((tpl_atyp*)(child->data))->bb = NULL;
1878 ((tpl_atyp*)(child->data))->bbtail = NULL;
1879 }
1880 /* parent is array? then bubble up child array's ser_osz */
1881 if (n->type == TPL_TYPE_ARY) {
1882 n->ser_osz += sizeof(uint32_t); /* array len word */
1883 n->ser_osz += child->ser_osz; /* child array ser_osz */
1884 child->ser_osz = 0; /* reset child array ser_osz */
1885 }
1886 break;
1887
1888 case TPL_TYPE_POUND:
1889 /* we need to iterate n times over preceding nodes in S(...).
1890 * we may be in the midst of an iteration each time or starting. */
1891 pd = (tpl_pound_data*)child->data;
1892 itermax = child->num;
1893
1894 /* itermax is total num of iterations needed */
1895 /* pd->iternum is current iteration index */
1896 /* pd->inter_elt_len is element-to-element len of contiguous structs */
1897 /* pd->iter_start_node is where we jump to at each iteration. */
1898
1899 if (++(pd->iternum) < itermax) {
1900
1901 /* in start or midst of loop. advance addr/data pointers. */
1902 for(np=pd->iter_start_node; np != child; np = np->next) {
1903 np->data = (char*)(np->data) +
1904 (tpl_types[np->type].sz * np->num);
1905 np->addr = (char*)(np->addr) + pd->inter_elt_len;
1906 }
1907 /* do next iteration */
1908 child = pd->iter_start_node;
1909 continue;
1910
1911 } else { /* loop complete. */
1912
1913 /* reset iteration index and addr/data pointers. */
1914 pd->iternum = 0;
1915 for(np=pd->iter_start_node; np != child; np = np->next) {
1916 np->data = (char*)(np->data) - ((itermax-1) *
1917 tpl_types[np->type].sz *
1918 np->num);
1919 np->addr = (char*)(np->addr) - ((itermax-1) * pd->inter_elt_len);
1920 }
1921
1922 }
1923 break;
1924 default:
1925 tpl_hook.fatal("unsupported format character\n");
1926 break;
1927 }
1928 child=child->next;
1929 }
1930 return 0;
1931 }
1932
tpl_unpack(tpl_node * r,int i)1933 TPL_API int tpl_unpack(tpl_node *r, int i) {
1934 tpl_node *n, *c, *np;
1935 uint32_t slen;
1936 int rc=1, fidx;
1937 char *str;
1938 void *dv=NULL, *caddr;
1939 size_t A_bytes, itermax;
1940 tpl_pound_data *pd;
1941 void *img;
1942 size_t sz;
1943
1944
1945 /* handle unusual case of tpl_pack,tpl_unpack without an
1946 * intervening tpl_dump. do a dump/load implicitly. */
1947 if (((tpl_root_data*)(r->data))->flags & TPL_WRONLY) {
1948 if (tpl_dump(r,TPL_MEM,&img,&sz) != 0) return -1;
1949 if (tpl_load(r,TPL_MEM|TPL_UFREE,img,sz) != 0) {
1950 tpl_hook.free(img);
1951 return -1;
1952 };
1953 }
1954
1955 n = tpl_find_i(r,i);
1956 if (n == NULL) {
1957 tpl_hook.oops("invalid index %d to tpl_unpack\n", i);
1958 return -1;
1959 }
1960
1961 /* either root node or an A node */
1962 if (n->type == TPL_TYPE_ROOT) {
1963 dv = tpl_find_data_start( ((tpl_root_data*)(n->data))->mmap.text );
1964 } else if (n->type == TPL_TYPE_ARY) {
1965 if (((tpl_atyp*)(n->data))->num <= 0) return 0; /* array consumed */
1966 else rc = ((tpl_atyp*)(n->data))->num--;
1967 dv = ((tpl_atyp*)(n->data))->cur;
1968 if (!dv) tpl_hook.fatal("must unpack parent of node before node itself\n");
1969 }
1970
1971 c = n->children;
1972 while (c) {
1973 switch (c->type) {
1974 case TPL_TYPE_BYTE:
1975 case TPL_TYPE_DOUBLE:
1976 case TPL_TYPE_INT32:
1977 case TPL_TYPE_UINT32:
1978 case TPL_TYPE_INT64:
1979 case TPL_TYPE_UINT64:
1980 case TPL_TYPE_INT16:
1981 case TPL_TYPE_UINT16:
1982 /* unpack elements of cross-endian octothorpic array individually */
1983 if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN) {
1984 for(fidx=0; fidx < c->num; fidx++) {
1985 caddr = (void*)((uintptr_t)c->addr + (fidx * tpl_types[c->type].sz));
1986 memcpy(caddr,dv,tpl_types[c->type].sz);
1987 tpl_byteswap(caddr, tpl_types[c->type].sz);
1988 dv = (void*)((uintptr_t)dv + tpl_types[c->type].sz);
1989 }
1990 } else {
1991 /* bulk unpack ok if not cross-endian */
1992 memcpy(c->addr, dv, tpl_types[c->type].sz * c->num);
1993 dv = (void*)((uintptr_t)dv + tpl_types[c->type].sz * c->num);
1994 }
1995 break;
1996 case TPL_TYPE_BIN:
1997 memcpy(&slen,dv,sizeof(uint32_t));
1998 if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN)
1999 tpl_byteswap(&slen, sizeof(uint32_t));
2000 if (slen > 0) {
2001 str = (char*)tpl_hook.malloc(slen);
2002 if (!str) fatal_oom();
2003 } else str=NULL;
2004 dv = (void*)((uintptr_t)dv + sizeof(uint32_t));
2005 if (slen>0) memcpy(str,dv,slen);
2006 memcpy(&(((tpl_bin*)c->addr)->addr),&str,sizeof(void*));
2007 memcpy(&(((tpl_bin*)c->addr)->sz),&slen,sizeof(uint32_t));
2008 dv = (void*)((uintptr_t)dv + slen);
2009 break;
2010 case TPL_TYPE_STR:
2011 for(fidx=0; fidx < c->num; fidx++) {
2012 memcpy(&slen,dv,sizeof(uint32_t));
2013 if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN)
2014 tpl_byteswap(&slen, sizeof(uint32_t));
2015 if (((tpl_root_data*)(r->data))->flags & TPL_OLD_STRING_FMT)
2016 slen += 1;
2017 dv = (void*)((uintptr_t)dv + sizeof(uint32_t));
2018 if (slen) { /* slen includes \0 */
2019 str = (char*)tpl_hook.malloc(slen);
2020 if (!str) fatal_oom();
2021 if (slen>1) memcpy(str,dv,slen-1);
2022 str[slen-1] = '\0'; /* nul terminate */
2023 dv = (void*)((uintptr_t)dv + slen-1);
2024 } else str=NULL;
2025 memcpy(&((char**)c->addr)[fidx],&str,sizeof(char*));
2026 }
2027 break;
2028 case TPL_TYPE_POUND:
2029 /* iterate over preceding nodes */
2030 pd = (tpl_pound_data*)c->data;
2031 itermax = c->num;
2032 if (++(pd->iternum) < itermax) {
2033 /* in start or midst of loop. advance addr/data pointers. */
2034 for(np=pd->iter_start_node; np != c; np = np->next) {
2035 np->addr = (char*)(np->addr) + pd->inter_elt_len;
2036 }
2037 /* do next iteration */
2038 c = pd->iter_start_node;
2039 continue;
2040
2041 } else { /* loop complete. */
2042
2043 /* reset iteration index and addr/data pointers. */
2044 pd->iternum = 0;
2045 for(np=pd->iter_start_node; np != c; np = np->next) {
2046 np->addr = (char*)(np->addr) - ((itermax-1) * pd->inter_elt_len);
2047 }
2048
2049 }
2050 break;
2051 case TPL_TYPE_ARY:
2052 if (tpl_serlen(r,c,dv, &A_bytes) == -1)
2053 tpl_hook.fatal("internal error in unpack\n");
2054 memcpy( &((tpl_atyp*)(c->data))->num, dv, sizeof(uint32_t));
2055 if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN)
2056 tpl_byteswap(&((tpl_atyp*)(c->data))->num, sizeof(uint32_t));
2057 ((tpl_atyp*)(c->data))->cur = (void*)((uintptr_t)dv+sizeof(uint32_t));
2058 dv = (void*)((uintptr_t)dv + A_bytes);
2059 break;
2060 default:
2061 tpl_hook.fatal("unsupported format character\n");
2062 break;
2063 }
2064
2065 c = c->next;
2066 }
2067 if (n->type == TPL_TYPE_ARY) ((tpl_atyp*)(n->data))->cur = dv; /* next element */
2068 return rc;
2069 }
2070
2071 /* Specialized function that unpacks only the root's A nodes, after tpl_load */
tpl_unpackA0(tpl_node * r)2072 static int tpl_unpackA0(tpl_node *r) {
2073 tpl_node *n, *c;
2074 uint32_t slen;
2075 int rc=1,fidx,i;
2076 void *dv;
2077 size_t A_bytes, itermax;
2078 tpl_pound_data *pd;
2079
2080 n = r;
2081 dv = tpl_find_data_start( ((tpl_root_data*)(r->data))->mmap.text);
2082
2083 c=n->children;
2084 while (c) {
2085 switch (c->type) {
2086 case TPL_TYPE_BYTE:
2087 case TPL_TYPE_DOUBLE:
2088 case TPL_TYPE_INT32:
2089 case TPL_TYPE_UINT32:
2090 case TPL_TYPE_INT64:
2091 case TPL_TYPE_UINT64:
2092 case TPL_TYPE_INT16:
2093 case TPL_TYPE_UINT16:
2094 for(fidx=0;fidx < c->num; fidx++) {
2095 dv = (void*)((uintptr_t)dv + tpl_types[c->type].sz);
2096 }
2097 break;
2098 case TPL_TYPE_BIN:
2099 memcpy(&slen,dv,sizeof(uint32_t));
2100 if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN)
2101 tpl_byteswap(&slen, sizeof(uint32_t));
2102 dv = (void*)((uintptr_t)dv + sizeof(uint32_t));
2103 dv = (void*)((uintptr_t)dv + slen);
2104 break;
2105 case TPL_TYPE_STR:
2106 for(i=0; i<c->num; i++) {
2107 memcpy(&slen,dv,sizeof(uint32_t));
2108 if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN)
2109 tpl_byteswap(&slen, sizeof(uint32_t));
2110 if (((tpl_root_data*)(r->data))->flags & TPL_OLD_STRING_FMT)
2111 slen += 1;
2112 dv = (void*)((uintptr_t)dv + sizeof(uint32_t));
2113 if (slen>1) dv = (void*)((uintptr_t)dv + slen-1);
2114 }
2115 break;
2116 case TPL_TYPE_POUND:
2117 /* iterate over the preceding nodes */
2118 itermax = c->num;
2119 pd = (tpl_pound_data*)c->data;
2120 if (++(pd->iternum) < itermax) {
2121 c = pd->iter_start_node;
2122 continue;
2123 } else { /* loop complete. */
2124 pd->iternum = 0;
2125 }
2126 break;
2127 case TPL_TYPE_ARY:
2128 if ( tpl_serlen(r,c,dv, &A_bytes) == -1)
2129 tpl_hook.fatal("internal error in unpackA0\n");
2130 memcpy( &((tpl_atyp*)(c->data))->num, dv, sizeof(uint32_t));
2131 if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN)
2132 tpl_byteswap(&((tpl_atyp*)(c->data))->num, sizeof(uint32_t));
2133 ((tpl_atyp*)(c->data))->cur = (void*)((uintptr_t)dv+sizeof(uint32_t));
2134 dv = (void*)((uintptr_t)dv + A_bytes);
2135 break;
2136 default:
2137 tpl_hook.fatal("unsupported format character\n");
2138 break;
2139 }
2140 c=c->next;
2141 }
2142 return rc;
2143 }
2144
2145 /* In-place byte order swapping of a word of length "len" bytes */
tpl_byteswap(void * word,int len)2146 static void tpl_byteswap(void *word, int len) {
2147 int i;
2148 char c, *w;
2149 w = (char*)word;
2150 for(i=0; i<len/2; i++) {
2151 c = w[i];
2152 w[i] = w[len-1-i];
2153 w[len-1-i] = c;
2154 }
2155 }
2156
tpl_fatal(char * fmt,...)2157 void tpl_fatal(char *fmt, ...) {
2158 va_list ap;
2159 char exit_msg[100];
2160
2161 memset(exit_msg, '\0', sizeof(exit_msg));
2162 va_start(ap,fmt);
2163 vsnprintf(exit_msg, sizeof(exit_msg)-1, fmt, ap);
2164 va_end(ap);
2165
2166 tpl_hook.oops("%s", exit_msg);
2167 exit(-1);
2168 }
2169
tpl_gather(int mode,...)2170 TPL_API int tpl_gather(int mode, ...) {
2171 va_list ap;
2172 int fd,rc=0;
2173 size_t *szp,sz;
2174 void **img,*addr,*data;
2175 tpl_gather_t **gs;
2176 tpl_gather_cb *cb;
2177
2178 va_start(ap,mode);
2179 switch (mode) {
2180 case TPL_GATHER_BLOCKING:
2181 fd = va_arg(ap,int);
2182 img = va_arg(ap,void*);
2183 szp = va_arg(ap,size_t*);
2184 rc = tpl_gather_blocking(fd,img,szp);
2185 break;
2186 case TPL_GATHER_NONBLOCKING:
2187 fd = va_arg(ap,int);
2188 gs = (tpl_gather_t**)va_arg(ap,void*);
2189 cb = (tpl_gather_cb*)va_arg(ap,tpl_gather_cb*);
2190 data = va_arg(ap,void*);
2191 rc = tpl_gather_nonblocking(fd,gs,cb,data);
2192 break;
2193 case TPL_GATHER_MEM:
2194 addr = va_arg(ap,void*);
2195 sz = va_arg(ap,size_t);
2196 gs = (tpl_gather_t**)va_arg(ap,void*);
2197 cb = (tpl_gather_cb*)va_arg(ap,tpl_gather_cb*);
2198 data = va_arg(ap,void*);
2199 rc = tpl_gather_mem(addr,sz,gs,cb,data);
2200 break;
2201 default:
2202 tpl_hook.fatal("unsupported tpl_gather mode %d\n",mode);
2203 break;
2204 }
2205 va_end(ap);
2206 return rc;
2207 }
2208
2209 /* dequeue a tpl by reading until one full tpl image is obtained.
2210 * We take care not to read past the end of the tpl.
2211 * This is intended as a blocking call i.e. for use with a blocking fd.
2212 * It can be given a non-blocking fd, but the read spins if we have to wait.
2213 */
tpl_gather_blocking(int fd,void ** img,size_t * sz)2214 static int tpl_gather_blocking(int fd, void **img, size_t *sz) {
2215 char preamble[8];
2216 int i=0, rc;
2217 uint32_t tpllen;
2218
2219 do {
2220 rc = read(fd,&preamble[i],8-i);
2221 i += (rc>0) ? rc : 0;
2222 } while ((rc==-1 && (errno==EINTR||errno==EAGAIN)) || (rc>0 && i<8));
2223
2224 if (rc<0) {
2225 tpl_hook.oops("tpl_gather_fd_blocking failed: %s\n", strerror(errno));
2226 return -1;
2227 } else if (rc == 0) {
2228 /* tpl_hook.oops("tpl_gather_fd_blocking: eof\n"); */
2229 return 0;
2230 } else if (i != 8) {
2231 tpl_hook.oops("internal error\n");
2232 return -1;
2233 }
2234
2235 if (preamble[0] == 't' && preamble[1] == 'p' && preamble[2] == 'l') {
2236 memcpy(&tpllen,&preamble[4],4);
2237 if (tpl_needs_endian_swap(preamble)) tpl_byteswap(&tpllen,4);
2238 } else {
2239 tpl_hook.oops("tpl_gather_fd_blocking: non-tpl input\n");
2240 return -1;
2241 }
2242
2243 /* malloc space for remainder of tpl image (overall length tpllen)
2244 * and read it in
2245 */
2246 if (tpl_hook.gather_max > 0 &&
2247 tpllen > tpl_hook.gather_max) {
2248 tpl_hook.oops("tpl exceeds max length %d\n",
2249 tpl_hook.gather_max);
2250 return -2;
2251 }
2252 *sz = tpllen;
2253 if ( (*img = tpl_hook.malloc(tpllen)) == NULL) {
2254 fatal_oom();
2255 }
2256
2257 memcpy(*img,preamble,8); /* copy preamble to output buffer */
2258 i=8;
2259 do {
2260 rc = read(fd,&((*(char**)img)[i]),tpllen-i);
2261 i += (rc>0) ? rc : 0;
2262 } while ((rc==-1 && (errno==EINTR||errno==EAGAIN)) || (rc>0 && (uint32_t)i<tpllen));
2263
2264 if (rc<0) {
2265 tpl_hook.oops("tpl_gather_fd_blocking failed: %s\n", strerror(errno));
2266 tpl_hook.free(*img);
2267 return -1;
2268 } else if (rc == 0) {
2269 /* tpl_hook.oops("tpl_gather_fd_blocking: eof\n"); */
2270 tpl_hook.free(*img);
2271 return 0;
2272 } else if ((uint32_t)i != tpllen) {
2273 tpl_hook.oops("internal error\n");
2274 tpl_hook.free(*img);
2275 return -1;
2276 }
2277
2278 return 1;
2279 }
2280
2281 /* Used by select()-driven apps which want to gather tpl images piecemeal */
2282 /* the file descriptor must be non-blocking for this functino to work. */
tpl_gather_nonblocking(int fd,tpl_gather_t ** gs,tpl_gather_cb * cb,void * data)2283 static int tpl_gather_nonblocking( int fd, tpl_gather_t **gs, tpl_gather_cb *cb, void *data) {
2284 char buf[TPL_GATHER_BUFLEN], *img, *tpl;
2285 int rc, keep_looping, cbrc=0;
2286 size_t catlen;
2287 uint32_t tpllen;
2288
2289 while (1) {
2290 rc = read(fd,buf,TPL_GATHER_BUFLEN);
2291 if (rc == -1) {
2292 if (errno == EINTR) continue; /* got signal during read, ignore */
2293 if (errno == EAGAIN) return 1; /* nothing to read right now */
2294 else {
2295 tpl_hook.oops("tpl_gather failed: %s\n", strerror(errno));
2296 if (*gs) {
2297 tpl_hook.free((*gs)->img);
2298 tpl_hook.free(*gs);
2299 *gs = NULL;
2300 }
2301 return -1; /* error, caller should close fd */
2302 }
2303 } else if (rc == 0) {
2304 if (*gs) {
2305 tpl_hook.oops("tpl_gather: partial tpl image precedes EOF\n");
2306 tpl_hook.free((*gs)->img);
2307 tpl_hook.free(*gs);
2308 *gs = NULL;
2309 }
2310 return 0; /* EOF, caller should close fd */
2311 } else {
2312 /* concatenate any partial tpl from last read with new buffer */
2313 if (*gs) {
2314 catlen = (*gs)->len + rc;
2315 if (tpl_hook.gather_max > 0 &&
2316 catlen > tpl_hook.gather_max) {
2317 tpl_hook.free( (*gs)->img );
2318 tpl_hook.free( (*gs) );
2319 *gs = NULL;
2320 tpl_hook.oops("tpl exceeds max length %d\n",
2321 tpl_hook.gather_max);
2322 return -2; /* error, caller should close fd */
2323 }
2324 if ( (img = tpl_hook.realloc((*gs)->img, catlen)) == NULL) {
2325 fatal_oom();
2326 }
2327 memcpy(img + (*gs)->len, buf, rc);
2328 tpl_hook.free(*gs);
2329 *gs = NULL;
2330 } else {
2331 img = buf;
2332 catlen = rc;
2333 }
2334 /* isolate any full tpl(s) in img and invoke cb for each */
2335 tpl = img;
2336 keep_looping = (tpl+8 < img+catlen) ? 1 : 0;
2337 while (keep_looping) {
2338 if (strncmp("tpl", tpl, 3) != 0) {
2339 tpl_hook.oops("tpl prefix invalid\n");
2340 if (img != buf) tpl_hook.free(img);
2341 tpl_hook.free(*gs);
2342 *gs = NULL;
2343 return -3; /* error, caller should close fd */
2344 }
2345 memcpy(&tpllen,&tpl[4],4);
2346 if (tpl_needs_endian_swap(tpl)) tpl_byteswap(&tpllen,4);
2347 if (tpl+tpllen <= img+catlen) {
2348 cbrc = (cb)(tpl,tpllen,data); /* invoke cb for tpl image */
2349 tpl += tpllen; /* point to next tpl image */
2350 if (cbrc < 0) keep_looping = 0;
2351 else keep_looping = (tpl+8 < img+catlen) ? 1 : 0;
2352 } else keep_looping=0;
2353 }
2354 /* check if app callback requested closure of tpl source */
2355 if (cbrc < 0) {
2356 tpl_hook.oops("tpl_fd_gather aborted by app callback\n");
2357 if (img != buf) tpl_hook.free(img);
2358 if (*gs) tpl_hook.free(*gs);
2359 *gs = NULL;
2360 return -4;
2361 }
2362 /* store any leftover, partial tpl fragment for next read */
2363 if (tpl == img && img != buf) {
2364 /* consumed nothing from img!=buf */
2365 if ( (*gs = tpl_hook.malloc(sizeof(tpl_gather_t))) == NULL ) {
2366 fatal_oom();
2367 }
2368 (*gs)->img = tpl;
2369 (*gs)->len = catlen;
2370 } else if (tpl < img+catlen) {
2371 /* consumed 1+ tpl(s) from img!=buf or 0 from img==buf */
2372 if ( (*gs = tpl_hook.malloc(sizeof(tpl_gather_t))) == NULL ) {
2373 fatal_oom();
2374 }
2375 if ( ((*gs)->img = tpl_hook.malloc(img+catlen - tpl)) == NULL ) {
2376 fatal_oom();
2377 }
2378 (*gs)->len = img+catlen - tpl;
2379 memcpy( (*gs)->img, tpl, img+catlen - tpl);
2380 /* free partially consumed concat buffer if used */
2381 if (img != buf) tpl_hook.free(img);
2382 } else { /* tpl(s) fully consumed */
2383 /* free consumed concat buffer if used */
2384 if (img != buf) tpl_hook.free(img);
2385 }
2386 }
2387 }
2388 }
2389
2390 /* gather tpl piecemeal from memory buffer (not fd) e.g., from a lower-level api */
tpl_gather_mem(char * buf,size_t len,tpl_gather_t ** gs,tpl_gather_cb * cb,void * data)2391 static int tpl_gather_mem( char *buf, size_t len, tpl_gather_t **gs, tpl_gather_cb *cb, void *data) {
2392 char *img, *tpl;
2393 int keep_looping, cbrc=0;
2394 size_t catlen;
2395 uint32_t tpllen;
2396
2397 /* concatenate any partial tpl from last read with new buffer */
2398 if (*gs) {
2399 catlen = (*gs)->len + len;
2400 if (tpl_hook.gather_max > 0 &&
2401 catlen > tpl_hook.gather_max) {
2402 tpl_hook.free( (*gs)->img );
2403 tpl_hook.free( (*gs) );
2404 *gs = NULL;
2405 tpl_hook.oops("tpl exceeds max length %d\n",
2406 tpl_hook.gather_max);
2407 return -2; /* error, caller should stop accepting input from source*/
2408 }
2409 if ( (img = tpl_hook.realloc((*gs)->img, catlen)) == NULL) {
2410 fatal_oom();
2411 }
2412 memcpy(img + (*gs)->len, buf, len);
2413 tpl_hook.free(*gs);
2414 *gs = NULL;
2415 } else {
2416 img = buf;
2417 catlen = len;
2418 }
2419 /* isolate any full tpl(s) in img and invoke cb for each */
2420 tpl = img;
2421 keep_looping = (tpl+8 < img+catlen) ? 1 : 0;
2422 while (keep_looping) {
2423 if (strncmp("tpl", tpl, 3) != 0) {
2424 tpl_hook.oops("tpl prefix invalid\n");
2425 if (img != buf) tpl_hook.free(img);
2426 tpl_hook.free(*gs);
2427 *gs = NULL;
2428 return -3; /* error, caller should stop accepting input from source*/
2429 }
2430 memcpy(&tpllen,&tpl[4],4);
2431 if (tpl_needs_endian_swap(tpl)) tpl_byteswap(&tpllen,4);
2432 if (tpl+tpllen <= img+catlen) {
2433 cbrc = (cb)(tpl,tpllen,data); /* invoke cb for tpl image */
2434 tpl += tpllen; /* point to next tpl image */
2435 if (cbrc < 0) keep_looping = 0;
2436 else keep_looping = (tpl+8 < img+catlen) ? 1 : 0;
2437 } else keep_looping=0;
2438 }
2439 /* check if app callback requested closure of tpl source */
2440 if (cbrc < 0) {
2441 tpl_hook.oops("tpl_mem_gather aborted by app callback\n");
2442 if (img != buf) tpl_hook.free(img);
2443 if (*gs) tpl_hook.free(*gs);
2444 *gs = NULL;
2445 return -4;
2446 }
2447 /* store any leftover, partial tpl fragment for next read */
2448 if (tpl == img && img != buf) {
2449 /* consumed nothing from img!=buf */
2450 if ( (*gs = tpl_hook.malloc(sizeof(tpl_gather_t))) == NULL ) {
2451 fatal_oom();
2452 }
2453 (*gs)->img = tpl;
2454 (*gs)->len = catlen;
2455 } else if (tpl < img+catlen) {
2456 /* consumed 1+ tpl(s) from img!=buf or 0 from img==buf */
2457 if ( (*gs = tpl_hook.malloc(sizeof(tpl_gather_t))) == NULL ) {
2458 fatal_oom();
2459 }
2460 if ( ((*gs)->img = tpl_hook.malloc(img+catlen - tpl)) == NULL ) {
2461 fatal_oom();
2462 }
2463 (*gs)->len = img+catlen - tpl;
2464 memcpy( (*gs)->img, tpl, img+catlen - tpl);
2465 /* free partially consumed concat buffer if used */
2466 if (img != buf) tpl_hook.free(img);
2467 } else { /* tpl(s) fully consumed */
2468 /* free consumed concat buffer if used */
2469 if (img != buf) tpl_hook.free(img);
2470 }
2471 return 1;
2472 }
2473