1 /* This files solves the problems with instore macros.
2  * While parsing a special tree of type tnode is created
3  * and fold together with containing strings to one big
4  * beast (called a tinned tree, american: canned heat :-) ).
5  * Then we have two objects: a special tree and the tinned tree.
6  * There is a one to one releationship between them.
7  * The tinned tree may be expanded to a tree by ExpandTinnedTree().
8  * The tree is tinned by TinTree() which should only be called from
9  * the lexical analyser.
10  * We are protected against multithreading while normal parsing.
11  * Destroying the tree and tinning/expanding is done in
12  * multithreaded mode.
13  */
14 
15 #include "rexx.h"
16 #include "rxiface.h"
17 #include <assert.h>
18 
19 /* We begin with some odd tricks. We want a fast but memory
20  * saving routine to build the tree. We don't want to copy
21  * tree leaves more than one. Thus we pack them into buckets.
22  * This allows a very fast kind of searching. Keep in mind
23  * that we must create a pointerless list which is included
24  * in the tinned tree.
25  *
26  * This structure is called ttree, have a look at regina_t.h.
27  *
28  * DEFAULT_TTREE_ELEMS defines the normal number of elements in the ttree.
29  * The more elements the better is the performance in searching. The less
30  * elements the better is the memory waste overhead. We want some more space
31  * than MAX_INTERNAL_SIZE in memory.c. This leads to "normal" memory
32  * allocation and can be freed without problems if we parse a nested
33  * Rexx program. This is currently 2048. We allocate nearly 8K. This
34  * will give a reasonable performance and a slight overhead for
35  * external(!) functions, 4K in the middle.
36  */
37 #define DEFAULT_TTREE_ELEMS (8192 / sizeof(treenode))
38 
39 /* Same as before but for offsrclines: */
40 #define DEFAULT_OTREE_ELEMS (4096 / sizeof(offsrcline))
41 
42 static ttree *CurrentT = NULL;
43 static otree *CurrentO = NULL;
44 static nodeptr Reused = NULL;
45 static const char MagicHeader[] = MAGIC;
46 
47 /* NewProg indicates the start of a new parsing sequence. We don't allow
48  * a nesting call. In case of an error is is the job of fetch_... to do the
49  * cleanup.
50  */
NewProg(void)51 void NewProg(void)
52 {
53    CurrentT = NULL; /* Trivial */
54    CurrentO = NULL; /* Trivial */
55    Reused = NULL; /* Can't reuse stuff of another parsing process */
56 }
57 
58 /* EndProg is called at the end of the parsing process. Start is the
59  * starting element of the later interpreter tree.
60  * parser_data.root is assigned.
61  */
EndProg(nodeptr Start)62 void EndProg(nodeptr Start)
63 {
64    parser_data.root = Start; /* trivial, too */
65    CurrentT = NULL;
66    CurrentO = NULL;
67    Reused = NULL; /* Can't reuse stuff of another parsing process */
68 }
69 
70 /* FreshNode returns a new ttree element. Call only within a NewProg/EndProg
71  * calling sequence.
72  * If you don't want the returned node WHILE PARSING because you want to do
73  * some tricks the the node at a later time, you are allowed to call
74  * RejectNode(). Rejected nodes are tried to be passed back to the used
75  * nodes.
76  */
FreshNode(void)77 nodeptr FreshNode(void)
78 {
79    nodeptr h;
80 
81    if (Reused != NULL) /* This should be put back first */
82    {
83       h = Reused;
84       Reused = Reused->next;
85       h->next = NULL; /* Everything except nodeindex is 0 now */
86       return(h);
87    }
88 
89    if (CurrentT && (CurrentT->num < CurrentT->max)) /* bucket not full */
90    {
91       memset(CurrentT->elems + CurrentT->num, 0, sizeof(treenode));
92       CurrentT->elems[CurrentT->num].nodeindex = CurrentT->sum + CurrentT->num;
93       return(CurrentT->elems + CurrentT->num++);
94    }
95 
96    if (CurrentT == NULL) /* First call */
97    {
98       parser_data.nodes = (ttree *)Malloc_TSD(parser_data.TSD, sizeof(ttree));
99       CurrentT = parser_data.nodes;
100       CurrentT->sum = 0;
101    }
102    else /* current bucket is full */
103    {
104       CurrentT->next = (ttree *)Malloc_TSD(parser_data.TSD, sizeof(ttree));
105       CurrentT->next->sum = CurrentT->sum + CurrentT->num;
106       CurrentT = CurrentT->next;
107    }
108 
109    /* always */
110    CurrentT->next = NULL;
111    CurrentT->max = DEFAULT_TTREE_ELEMS;
112    CurrentT->num = 1;
113    CurrentT->elems = (treenode *)Malloc_TSD(parser_data.TSD,
114                                 CurrentT->max * sizeof(treenode));
115 
116    memset(CurrentT->elems, 0, sizeof(treenode));
117    CurrentT->elems[0].nodeindex = CurrentT->sum;
118    return(CurrentT->elems);
119 }
120 
121 /* RejectNode gives the argument back to the pool of unused treenode entries
122  * which are managed and passed back by FreshNode().
123  * You should use the function ONLY IF YOU ARE WITHING THE PARSING PROCESS!
124  * It is not guaranteed that the memory of the entry is freed. It can only
125  * be reused.
126  * Note that the content of the entry is NOT FREED in any kind.
127  */
RejectNode(nodeptr NoLongerUsed)128 void RejectNode(nodeptr NoLongerUsed)
129 {
130    unsigned long idx;
131 
132    assert(CurrentT != NULL);
133    /* CurrentT == NULL can't happen, since CurrentT is only set within the
134     * parsing process while at least one treenode has been returned.
135     */
136    if (CurrentT == NULL) /* In case of no assertion we return simply */
137       return;
138 
139    /* Save exactly the nodeindex and destroy everything else */
140    idx = NoLongerUsed->nodeindex;
141    memset(NoLongerUsed, 0, sizeof(treenode)); /* Clean it up */
142    NoLongerUsed->nodeindex = idx;
143 
144    NoLongerUsed->next = Reused;
145    Reused = NoLongerUsed;
146 }
147 
148 /* FreshLine returns a new otree element. Call only within a NewProg/EndProg
149  * calling sequence.
150  */
FreshLine(void)151 offsrcline *FreshLine(void)
152 {
153    if (CurrentO && (CurrentO->num < CurrentO->max)) /* bucket not full */
154    {
155       memset(CurrentO->elems + CurrentO->num, 0, sizeof(offsrcline));
156       return(CurrentO->elems + CurrentO->num++);
157    }
158 
159    if (CurrentO == NULL) /* First call */
160    {
161       parser_data.srclines = (otree *)Malloc_TSD(parser_data.TSD, sizeof(otree));
162       CurrentO = parser_data.srclines;
163       CurrentO->sum = 0;
164    }
165    else /* current bucket is full */
166    {
167       CurrentO->next = (otree *)Malloc_TSD(parser_data.TSD, sizeof(otree));
168       CurrentO->next->sum = CurrentO->sum + CurrentO->num;
169       CurrentO = CurrentO->next;
170    }
171 
172    /* always */
173    CurrentO->next = NULL;
174    CurrentO->max = DEFAULT_OTREE_ELEMS;
175    CurrentO->num = 1;
176    CurrentO->elems = (offsrcline *)Malloc_TSD(parser_data.TSD,
177                                 CurrentO->max * sizeof(offsrcline));
178 
179    memset(CurrentO->elems, 0, sizeof(offsrcline));
180    return(CurrentO->elems);
181 }
182 
183 /*****************************************************************************
184  *****************************************************************************
185  * start of the multithreaded part *******************************************
186  *****************************************************************************
187  *****************************************************************************/
188 
189 
190 /* DestroyNode kills all allocated elements within a nodeptr
191  * without freeing the node itself.
192  */
DestroyNode(const tsd_t * TSD,nodeptr p)193 static void DestroyNode(const tsd_t *TSD, nodeptr p)
194 {
195    int type ;
196 
197    if (p->name)
198       Free_stringTSD( p->name ) ;
199 
200    if (p->now)
201       FreeTSD( p->now ) ;
202 
203    type = p->type ;
204    if (type == X_CON_SYMBOL || type == X_STRING)
205    {
206       if (p->u.number)
207       {
208          FreeTSD( p->u.number->num ) ;
209          FreeTSD( p->u.number ) ;
210       }
211    }
212    if (type==X_SIM_SYMBOL || type==X_STEM_SYMBOL || type==X_HEAD_SYMBOL ||
213        type==X_CTAIL_SYMBOL || type==X_VTAIL_SYMBOL )
214    {
215       if (p->u.varbx)
216       {
217          detach( TSD, p->u.varbx ) ;
218       }
219    }
220 
221    if (type == X_CEXPRLIST)
222    {
223       if (p->u.strng)
224          Free_stringTSD( p->u.strng ) ;
225    }
226 }
227 
228 /* DestroyInternalParsingTree frees all allocated memory used by a parsing
229  * tree. The structure itself is not freed.
230  */
DestroyInternalParsingTree(const tsd_t * TSD,internal_parser_type * ipt)231 void DestroyInternalParsingTree(const tsd_t *TSD, internal_parser_type *ipt)
232 {
233    ttree *tr, *th;
234    otree *otr, *oh;
235    lineboxptr lr, lh;
236    labelboxptr ar, ah;
237    unsigned long i;
238 
239    if (!ipt)
240       return;
241 
242    /* Cleanup all the nodes */
243    if (ipt->nodes != NULL)
244    {
245       tr = ipt->nodes;
246 
247       while (tr)
248       {
249          for (i = 0; i < tr->num; i++)
250             DestroyNode(TSD, tr->elems + i);
251          th = tr->next;
252          FreeTSD(tr->elems);
253          FreeTSD(tr);
254          tr = th;
255       }
256 
257       ipt->nodes = NULL;
258    }
259    ipt->root = NULL; /* not really needed */
260 
261    /* Cleanup all the lineboxes */
262    if (ipt->first_source_line != NULL)
263    {
264       lr = ipt->first_source_line;
265 
266       while (lr)
267       {
268          lh = lr->next;
269          Free_stringTSD(lr->line);
270          FreeTSD(lr);
271          lr = lh;
272       }
273 
274       ipt->first_source_line = ipt->last_source_line = NULL;
275    }
276 
277    /* Cleanup all the labelboxes */
278    if (ipt->first_label != NULL)
279    {
280       ar = ipt->first_label;
281 
282       while (ar)
283       {
284          ah = ar->next;
285          FreeTSD(ar);
286          ar = ah;
287       }
288 
289       ipt->first_label = ipt->last_label = NULL;
290    }
291 
292    if (ipt->sort_labels != NULL)
293    {
294       FreeTSD(ipt->sort_labels);
295 
296       ipt->sort_labels = NULL;
297    }
298 
299    /* Cleanup the incore sourceline informations */
300    /* Cleanup all the nodes */
301    if (ipt->srclines != NULL)
302    {
303       otr = ipt->srclines;
304 
305       while (otr)
306       {
307          oh = otr->next;
308          FreeTSD(otr->elems);
309          FreeTSD(otr);
310          otr = oh;
311       }
312 
313       ipt->srclines = NULL;
314    }
315    if (ipt->kill)
316       Free_stringTSD(ipt->kill);
317    ipt->kill = NULL;
318 }
319 
320 /* ExpandTinnedTree expands the external tree from a former parsing operation
321  * to a fully usable tree. All allocations and relacations are done to fake
322  * a normal parsing operation.
323  * The external tree won't be used any longer after this operation but the
324  * external tree must have been checked before this operation.
325  * The freshly allocated tree is returned.
326  */
ExpandTinnedTree(const tsd_t * TSD,const external_parser_type * ept,unsigned long size,const char * incore_source,unsigned long incore_source_length)327 internal_parser_type ExpandTinnedTree(const tsd_t *TSD,
328                                       const external_parser_type *ept,
329                                       unsigned long size,
330                                       const char *incore_source,
331                                       unsigned long incore_source_length)
332 {
333    internal_parser_type ipt;
334    unsigned long i,j;
335    const extstring *es;
336    const offsrcline *lastsrcline;
337    nodeptr thisptr;
338 
339    memset(&ipt, 0, sizeof(ipt));
340 
341    /* We build the sourcelines first *****************************************/
342    if (incore_source_length == 0)
343       incore_source = NULL;
344    if (ept->NumberOfSourceLines == 0)
345       incore_source = NULL;
346    if (incore_source) /* Its worth to check exactly */
347    {
348       lastsrcline = (const offsrcline *) ((char *) ept + ept->source);
349       lastsrcline += ept->NumberOfSourceLines - 1;
350       j = lastsrcline->length + lastsrcline->offset;
351       /* j shall be very close to the end of the source string. It may
352        * follow a linefeed (or carriage return/linefeed) and probably a
353        * ^Z for CP/M descendents which includes Microsoft products. It's
354        * fais to assume the following check:
355        */
356       if ((j > incore_source_length) ||
357           (j + 3 < incore_source_length))
358       incore_source = NULL;
359    }
360    if (incore_source) /* We are sure enough to use the source string */
361    {
362       ipt.incore_source = incore_source;
363       ipt.srclines = (otree *)MallocTSD(sizeof(otree));
364       ipt.srclines->sum = 0;
365       ipt.srclines->next = NULL;
366       ipt.srclines->max = ept->NumberOfSourceLines;
367       ipt.srclines->num = ipt.srclines->max;
368       ipt.srclines->elems = (offsrcline *)MallocTSD(ipt.srclines->num * sizeof(offsrcline));
369       memcpy(ipt.srclines->elems,
370              (char *) ept + ept->source,
371              ipt.srclines->num * sizeof(offsrcline));
372    }
373    /**************************************************************************/
374 
375    ipt.tline = -1; /* unused */
376    ipt.tstart = -1; /* unused */
377    ipt.result = 0; /* unused */
378    ipt.first_label = ipt.last_label = NULL; /* initialize it for newlabel() */
379    ipt.numlabels = 0; /* initialize it for newlabel() */
380    ipt.sort_labels = NULL; /* initialize it for newlabel() */
381 
382    ipt.nodes = (ttree *)MallocTSD(sizeof(ttree));
383    ipt.nodes->sum = 0;
384    ipt.nodes->next = NULL;
385    ipt.nodes->max = ept->NumberOfTreeElements;
386    ipt.nodes->num = ipt.nodes->max;
387    ipt.nodes->elems = (treenode *)MallocTSD(ipt.nodes->num * sizeof(treenode));
388 
389    memcpy(ipt.nodes->elems,
390           (char *) ept + ept->tree,
391           ipt.nodes->num * sizeof(treenode));
392    ipt.root = ipt.nodes->elems + ept->TreeStart;
393 
394    /* Everything is ready for a relocation step. Don't forget to *************
395     * create the labelboxes as necessary.
396     */
397    for (i = 0;i < ept->NumberOfTreeElements;i++)
398    {
399       thisptr = ipt.nodes->elems + i;
400       if (thisptr->name)
401       {
402          es = (extstring *) ((char *) ept + (unsigned long) thisptr->name);
403          thisptr->name = Str_makeTSD(es->length);
404          thisptr->name->len = es->length;
405          memcpy(thisptr->name->value,
406                 es + 1 /* position of string content */,
407                 es->length);
408       }
409 
410       /*
411        * Do things the parsing step would have do. Simple values in thisptr->u
412        * are copied already.
413        */
414 
415       /*
416        * See also several places in this file and in debug.c where this
417        * switch list must be changed. Seek for X_CEXPRLIST.
418        */
419       switch ( thisptr->type )
420       {
421          case X_CEXPRLIST:
422             if ( thisptr->u.strng )
423             {
424                es = (extstring *) ((char *) ept + (unsigned long) thisptr->u.strng);
425                thisptr->u.strng = Str_makeTSD( es->length );
426                thisptr->u.strng->len = es->length;
427                memcpy( thisptr->u.strng->value,
428                        es + 1 /* position of string content */,
429                        es->length);
430             }
431             break;
432 
433          case X_LABEL:
434             newlabel(TSD, &ipt, thisptr);
435             break;
436 
437          default:
438             break;
439       }
440 
441       if (thisptr->next == (nodeptr) (unsigned long) -1)
442          thisptr->next = NULL;
443       else
444          thisptr->next = ipt.nodes->elems + (unsigned long) thisptr->next;
445       for (j = 0;j < sizeof(thisptr->p) / sizeof(thisptr->p[0]);j++)
446       {
447          if (thisptr->p[j] == (nodeptr) (unsigned long) -1)
448             thisptr->p[j] = NULL;
449          else
450             thisptr->p[j] = ipt.nodes->elems + (unsigned long) thisptr->p[j];
451       }
452    }
453    return(ipt);
454 }
455 
456 /* We must take care of the alignment of structure. We may get a SIGBUS in
457  * the following if we don't do it. We assume that an alignment for an
458  * unsigned long is sufficient for all types including structures. We also
459  * assume a power of two for an unsigned's size.
460  */
461 #define USIZ sizeof(unsigned long)
462 #define USIZ_1 (USIZ-1)
463 /* Wastes one byte in average but is much faster */
464 #define StringSize(s) (((sizeof(extstring)+s->len)|USIZ_1)+1)
465 
ComputeExternalSize(const internal_parser_type * ipt,unsigned long * SourceLines,unsigned long * Nodes)466 static unsigned long ComputeExternalSize(const internal_parser_type *ipt,
467                                          unsigned long *SourceLines,
468                                          unsigned long *Nodes)
469 {
470    otree *otp;
471    ttree *ttp;
472    nodeptr np;
473    unsigned long size = sizeof(external_parser_type);
474    unsigned long i, elems, bufchars;
475 
476    /* sourceline table */
477    elems = 0;
478    if ((otp = ipt->srclines) == NULL)
479    {
480       if (ipt->last_source_line)
481       {
482          elems = ipt->last_source_line->lineno;
483       }
484    }
485    else
486    {
487       while (otp->next)
488          otp = otp->next;
489       elems = otp->sum + otp->num;
490    }
491    *SourceLines = elems;
492    size += elems * sizeof(offsrcline); /* the table */
493 
494    /* nodetable */
495    elems = bufchars = 0;
496    ttp = ipt->nodes;
497    while (ttp)
498    {
499       for (i = 0;i < ttp->num;i++)
500       {
501          elems++;
502          np = ttp->elems + i;
503          if (np->name)
504             bufchars += StringSize(np->name);
505 
506          /*
507           * Add all sizes of strings that have been generated at the parsing
508           * step.
509           */
510 
511          /*
512           * See also several places in this file and in debug.c where this
513           * switch list must be changed. Seek for X_CEXPRLIST.
514           */
515          switch ( np->type )
516          {
517             case X_CEXPRLIST:
518                if ( np->u.strng )
519                   bufchars += StringSize( np->u.strng );
520             break;
521 
522             default:
523                break;
524          }
525       }
526       ttp = ttp->next;
527    }
528    *Nodes = elems;
529    size += elems * sizeof(treenode);
530    size += bufchars;
531 
532    size += sizeof(((external_parser_type *)0)->Magic);
533    return(size);
534 }
535 
536 /* FillStrings copies all offsrclines from the otree to base+start
537  * consecutively.
538  * The index just beyond the last copied byte is returned.
539  */
FillStrings(char * base,unsigned long start,const otree * otp)540 static unsigned long FillStrings(char *base, unsigned long start,
541                                  const otree *otp)
542 {
543    if (otp != NULL)
544    {
545       while (otp != NULL)
546       {
547          memcpy(base + start, otp->elems, otp->num * sizeof(offsrcline));
548          start += otp->num * sizeof(offsrcline);
549          otp = otp->next;
550       }
551    }
552    return(start);
553 }
554 
555 /* FillTree copies all treenodes of the ttree to base+buf in a relocatable
556  * manner. Look at ExpandTinnedTree() or regina_t.h for a description.
557  * Each treenode is copied to the table and the containing strings are copied
558  * as extstrings to base+start which is incremented.
559  * The table must be large enough.
560  * The index just beyond the last copied character is returned.
561  */
FillTree(treenode * table,char * base,unsigned long start,const ttree * ttp)562 static unsigned long FillTree(treenode *table, char *base, unsigned long start,
563                               const ttree *ttp)
564 {
565    cnodeptr np;
566    unsigned long i,j;
567    extstring *e;
568 
569    while (ttp)
570    {
571       for (i = 0;i < ttp->num;i++)
572       {
573          np = (cnodeptr) (ttp->elems + i);
574          *table = *np; /* Full copy includes unnecessary stuff but is fast */
575 
576          if (np->name)
577          {
578             table->name = (streng *) start;
579             e = (extstring *) (base + start);
580             e->length = np->name->len;
581             memcpy(e + 1 /* just beyond the head */, np->name->value, e->length);
582             start += StringSize(np->name);
583          }
584 
585          /*
586           * Remove all "flags" from the target and copy only approved values
587           * the parser computes already.
588           */
589          memset( &table->u, 0, sizeof( table->u ) );
590 
591          /*
592           * See also several places in this file and in debug.c where this
593           * switch list must be changed. Seek for X_CEXPRLIST.
594           */
595          switch ( np->type )
596          {
597             case X_EQUAL:
598             case X_DIFF:
599             case X_GT:
600             case X_GTE:
601             case X_LT:
602             case X_LTE:
603                table->u.flags = np->u.flags;
604                break;
605 
606             case X_PARSE:
607                /*
608                 * fixes 972850
609                 */
610                table->u.parseflags = np->u.parseflags;
611                break;
612 
613             case X_ADDR_V:
614                table->u.nonansi = np->u.nonansi;
615                break;
616 
617             case X_CEXPRLIST:
618                if ( np->u.strng )
619                {
620                   table->u.strng = (streng *) start;
621                   e = (extstring *) (base + start);
622                   e->length = np->u.strng->len;
623                   memcpy(e + 1, np->u.strng->value, e->length);
624                   start += StringSize(np->u.strng);
625                }
626             break;
627 
628             case X_LABEL:
629                table->u.trace_only = np->u.trace_only;
630                break;
631 
632             case X_ADDR_WITH:
633                if ( !np->p[0] && !np->p[1] && !np->p[2] )
634                   table->u.of = np->u.of;
635                break;
636 
637             default:
638                break;
639          }
640 
641          if (table->next == NULL)
642             table->next = (nodeptr) (unsigned long) -1;
643          else
644             table->next = (nodeptr) np->next->nodeindex;
645          for (j = 0;j < sizeof(np->p) / sizeof(np->p[0]);j++)
646          {
647             if (table->p[j] == NULL)
648                table->p[j] = (nodeptr) (unsigned long) -1;
649             else
650                table->p[j] = (nodeptr) np->p[j]->nodeindex;
651          }
652          table++;
653       }
654       ttp = ttp->next;
655    }
656 
657    return(start);
658 }
659 
660 /* TinTree "tins" a tree into an external structure. The complete structure
661  * is allocated by one call to IfcAllocateMemory. The returned value shall
662  * be used as an instore macro for RexxStart.
663  * *length is set to the allocated size of the memory block on return.
664  * ExpandedTinnedTree can expand the returned value and IsValidTin checks it.
665  */
TinTree(const tsd_t * TSD,const internal_parser_type * ipt,unsigned long * length)666 external_parser_type *TinTree(const tsd_t *TSD,
667                               const internal_parser_type *ipt,
668                               unsigned long *length)
669 {
670    external_parser_type *retval;
671    unsigned long srclines, nodecount, len;
672 
673    *length = ComputeExternalSize(ipt, &srclines, &nodecount);
674 
675    retval = (external_parser_type *)IfcAllocateMemory(*length);
676    if (retval == NULL)
677       return(NULL);
678    memset(retval, 0, sizeof(external_parser_type));
679 
680    /* Build the envelope */
681    len = sizeof(MagicHeader); /* includes a terminating 0 */
682    if (len > sizeof(retval->Magic))
683       len = sizeof(retval->Magic);
684    memcpy(retval->Magic, MagicHeader, len);
685    len = sizeof(PARSE_VERSION_STRING);
686    if (len > sizeof(retval->ReginaVersion))
687       len = sizeof(retval->ReginaVersion);
688    memcpy(retval->ReginaVersion, PARSE_VERSION_STRING, len);
689 
690    retval->arch_detector.s.one = 1;
691    retval->arch_detector.s.two = 2;
692    retval->arch_detector.s.ptr3 = (void *)3;
693    retval->arch_detector.s.ptr4 = (void *)4;
694    retval->OverallSize = (unsigned long) *length;
695    retval->NumberOfSourceLines = srclines;
696    retval->version = INSTORE_VERSION;
697    retval->NumberOfTreeElements = nodecount;
698 
699    retval->source = sizeof(external_parser_type);
700    len = FillStrings((char *) retval,
701                      sizeof(external_parser_type),
702                      ipt->srclines);
703 
704    retval->tree = len;
705    retval->TreeStart = ipt->root->nodeindex;
706    len = FillTree((treenode *) ((char *) retval + len),
707                   (char *) retval,
708                   len + nodecount*sizeof(treenode),
709                   ipt->nodes);
710 
711    memcpy((char *) retval + len, retval->Magic, sizeof(retval->Magic));
712 
713    assert((unsigned long) len + sizeof(retval->Magic) == *length);
714 
715    /* DEBUGGING: return NULL if you don't want tinned trees */
716    return(retval);
717 }
718 
719 /* IsValidTin returns 1 if the structure ept if of length eptlen and seems
720  * to contain a valid parsing tree. 0 is returned if this is not the case.
721  */
IsValidTin(const external_parser_type * ept,unsigned long eptlen)722 int IsValidTin(const external_parser_type *ept, unsigned long eptlen)
723 {
724    char Magic[sizeof(((external_parser_type *)0)->Magic)];
725    unsigned long len;
726 
727    /* Some paranoia tests first: */
728    if ((ept == NULL) || (eptlen < sizeof(external_parser_type)))
729       return(0);
730 
731    /* Be sure to fill Magic as described */
732    memset(Magic, 0, sizeof(Magic));
733    len = sizeof(MagicHeader); /* includes a terminating 0 */
734    if (len > sizeof(ept->Magic))
735       len = sizeof(ept->Magic);
736    memcpy(Magic, MagicHeader, len);
737 
738    if (memcmp(Magic, ept->Magic, sizeof(Magic)) != 0)
739       return(0);
740 
741    if ((ept->arch_detector.s.one != 1) ||
742        (ept->arch_detector.s.two != 2) ||
743        (ept->arch_detector.s.ptr3 != (void *)3) ||
744        (ept->arch_detector.s.ptr4 != (void *)4))
745       return(0);
746 
747    if (ept->OverallSize != eptlen)
748       return(0);
749 
750    if (ept->version != INSTORE_VERSION)
751       return(0);
752 
753    if (memcmp(Magic,
754               (char *) ept + eptlen - sizeof(Magic),
755               sizeof(Magic)) != 0)
756       return(0);
757 
758    return(1);
759 }
760