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