LCOV - code coverage report
Current view: top level - libsd - xml.c (source / functions) Hit Total Coverage
Test: app.info Lines: 338 358 94.4 %
Date: 2015-08-29 Functions: 29 29 100.0 %

          Line data    Source code
       1             : // Copyright 2014 Bobby Powers. All rights reserved.
       2             : // Use of this source code is governed by a BSD-style
       3             : // license that can be found in the LICENSE file.
       4             : 
       5             : #include <errno.h>
       6             : #include <stdio.h>
       7             : #include <stdlib.h>
       8             : #include <string.h>
       9             : #include <stdbool.h>
      10             : 
      11             : #define _GNU_SOURCE // non-global hash table
      12             : #include <search.h>
      13             : #undef _GNU_SOURCE
      14             : 
      15             : #define XML_LARGE_SIZE
      16             : #include <expat.h>
      17             : 
      18             : #include "sd.h"
      19             : #include "sd_internal.h"
      20             : 
      21             : 
      22             : // FIXME(bp) remove max stack depth limit
      23             : #define STACKLEN 32
      24             : 
      25             : 
      26             : typedef struct BuilderOps_s BuilderOps;
      27             : 
      28             : typedef struct {
      29             :         const BuilderOps *ops;
      30             :         int refcount;
      31             : } Builder;
      32             : 
      33             : typedef struct NodeBuilder_s {
      34             :         Builder b;
      35             :         char *name;
      36             :         char *content;
      37             :         size_t content_len;
      38             :         struct hsearch_data *attr_map;
      39             :         Slice attrs;
      40             :         Slice children;
      41             : } NodeBuilder;
      42             : 
      43             : typedef struct {
      44             :         Builder b;
      45             :         File *file; // XmileBuilder doesn't own this
      46             : } XmileBuilder;
      47             : 
      48             : struct BuilderOps_s {
      49             :         void (*ref)(void *data);
      50             :         void (*unref)(void *data);
      51             :         void (*characters)(void *data, const char *contents, int len);
      52             :         Builder *(*start_child)(void *data, const char *tag_name, const char **attrs);
      53             :         void (*end_child)(void *data, const char *tag_name, Builder *child);
      54             :         void (*end_element)(void *data);
      55             : };
      56             : 
      57             : typedef struct {
      58             :         Builder **stack;
      59             :         int stack_top;
      60             :         File *file;
      61             : } BuilderStack;
      62             : 
      63             : static void builder_stack_start_element(void *data, const char *name, const char **attrs);
      64             : static void builder_stack_end_element(void *data, const char *name);
      65             : static void builder_stack_characters(void *data, const char *s, int len);
      66             : 
      67             : static void builder_ref(Builder *b);
      68             : static void builder_unref(Builder *b);
      69             : static void builder_characters(Builder *b, const char *contents, int len);
      70             : static Builder *builder_start_child(Builder *b, const char *tag_name, const char **attrs);
      71             : static void builder_end_child(Builder *b, const char *tag_name, Builder *child);
      72             : static void builder_end_element(Builder *b);
      73             : 
      74             : static NodeBuilder *node_builder_new(const char *name, const char **attrs);
      75             : static void node_builder_ref(void *data);
      76             : static void node_builder_unref(void *data);
      77             : static void node_builder_characters(void *data, const char *contents, int len);
      78             : static Builder *node_builder_start_child(void *data, const char *tag_name, const char **attrs);
      79             : static void node_builder_end_child(void *data, const char *tag_name, Builder *child);
      80             : static void node_builder_end_element(void *data);
      81             : static NodeBuilder *node_builder_get_first_child(NodeBuilder *nb, const char *name);
      82             : static const char *node_builder_get_attr(NodeBuilder *nb, const char *name);
      83             : 
      84             : static XmileBuilder *xmile_builder_new(void);
      85             : static void xmile_builder_ref(void *data);
      86             : static void xmile_builder_unref(void *data);
      87             : static Builder *xmile_builder_start_child(void *data, const char *tag_name, const char **attrs);
      88             : static void xmile_builder_end_child(void *data, const char *tag_name, Builder *child);
      89             : static void xmile_builder_end_element(void *data);
      90             : 
      91             : static SDModel *model_from_node_builder(NodeBuilder *b);
      92             : static Var *var_from_node_builder(NodeBuilder *b);
      93             : static Table *table_from_node_builder(NodeBuilder *b);
      94             : static Var *ref_from_node_builder(NodeBuilder *b);
      95             : 
      96             : static const BuilderOps NODE_BUILDER_OPS = {
      97             :         .ref = node_builder_ref,
      98             :         .unref = node_builder_unref,
      99             :         .characters = node_builder_characters,
     100             :         .start_child = node_builder_start_child,
     101             :         .end_child = node_builder_end_child,
     102             :         .end_element = node_builder_end_element,
     103             : };
     104             : 
     105             : static const BuilderOps XMILE_BUILDER_OPS = {
     106             :         .ref = xmile_builder_ref,
     107             :         .unref = xmile_builder_unref,
     108             :         .characters = NULL,
     109             :         .start_child = xmile_builder_start_child,
     110             :         .end_child = xmile_builder_end_child,
     111             :         .end_element = xmile_builder_end_element,
     112             : };
     113             : 
     114             : 
     115             : int
     116          18 : project_parse_file(SDProject *p, FILE *f)
     117             : {
     118          18 :         char *buf = calloc(BUFSIZ, sizeof(char));
     119          18 :         File *sdf = calloc(1, sizeof(File));
     120             :         BuilderStack bs;
     121          18 :         XML_Parser parser = NULL;
     122          18 :         int err = SD_ERR_NO_ERROR;
     123          18 :         bool done = false;
     124             : 
     125          18 :         if (!buf || !sdf) {
     126           0 :                 err = SD_ERR_NOMEM;
     127           0 :                 goto error;
     128             :         }
     129             : 
     130          18 :         sdf->project = p;
     131             : 
     132          18 :         memset(&bs, 0, sizeof(bs));
     133          18 :         bs.file = sdf;
     134          18 :         bs.stack_top = -1;
     135          18 :         bs.stack = calloc(STACKLEN, sizeof(Builder*));
     136             : 
     137             :         // TODO(bp) ParserCreateNS
     138          18 :         parser = XML_ParserCreate(NULL);
     139          18 :         XML_SetUserData(parser, &bs);
     140          18 :         XML_SetElementHandler(parser, builder_stack_start_element,
     141             :                               builder_stack_end_element);
     142          18 :         XML_SetCharacterDataHandler(parser, builder_stack_characters);
     143             : 
     144          18 :         while (!done) {
     145         117 :                 size_t len = fread(buf, 1, BUFSIZ, f);
     146         117 :                 done = len < BUFSIZ;
     147         117 :                 if (XML_Parse(parser, buf, len, done) == XML_STATUS_ERROR) {
     148           1 :                         err = SD_ERR_BAD_XML;
     149             :                         //fprintf(stderr,
     150             :                         //      "%s at line %llu\n",
     151             :                         //      XML_ErrorString(XML_GetErrorCode(parser)),
     152             :                         //      XML_GetCurrentLineNumber(parser));
     153           1 :                         goto error;
     154             :                 }
     155             :         }
     156             : 
     157          17 :         project_add_file(p, sdf);
     158             : 
     159          17 :         XML_ParserFree(parser);
     160          17 :         free(bs.stack);
     161          17 :         free(buf);
     162          17 :         return SD_ERR_NO_ERROR;
     163             : error:
     164           1 :         XML_ParserFree(parser);
     165           1 :         free(sdf);
     166           3 :         for (int i = bs.stack_top; i >= 0; i--) {
     167           2 :                 builder_unref(bs.stack[i]);
     168           2 :                 bs.stack[i] = NULL;
     169             :         }
     170           1 :         free(bs.stack);
     171           1 :         free(buf);
     172           1 :         return err;
     173             : }
     174             : 
     175             : void
     176        1484 : builder_stack_start_element(void *data, const char *name, const char **attrs)
     177             : {
     178        1484 :         BuilderStack *bs = data;
     179             : 
     180        1484 :         if (bs->stack_top < 0) {
     181             :                 // TODO(bp) check/handle non-xmile top level tag
     182          18 :                 XmileBuilder *xb = xmile_builder_new();
     183          18 :                 if (!xb) {
     184             :                         // TODO(bp) handle ENOMEM
     185           0 :                         return;
     186             :                 }
     187          18 :                 xb->file = bs->file;
     188             : 
     189          18 :                 bs->stack[0] = &xb->b;
     190          18 :                 bs->stack_top = 0;
     191             :         } else {
     192        1466 :                 Builder *b = builder_start_child(bs->stack[bs->stack_top], name, attrs);
     193        1466 :                 if (!b) {
     194         100 :                         NodeBuilder *nb = node_builder_new(name, attrs);
     195         100 :                         if (!nb) {
     196             :                                 // TODO(bp) handle ENOMEM
     197           0 :                                 return;
     198             :                         }
     199         100 :                         b = &nb->b;
     200             :                 }
     201        1466 :                 bs->stack[++bs->stack_top] = b;
     202             :         }
     203             : }
     204             : 
     205             : void
     206        1482 : builder_stack_end_element(void *data, const char *name)
     207             : {
     208        1482 :         BuilderStack *bs = data;
     209        1482 :         Builder *child = NULL;
     210             : 
     211             :         // TODO(bp) remove or add logging, this should never happen
     212        1482 :         if (bs->stack_top < 0)
     213           0 :                 return;
     214             : 
     215        1482 :         child = bs->stack[bs->stack_top];
     216        1482 :         bs->stack[bs->stack_top--] = NULL;
     217             : 
     218        1482 :         builder_end_element(child);
     219             : 
     220        1482 :         if (bs->stack_top < 0) {
     221             :                 // TODO(bp) free xmile_builder
     222             :         } else {
     223        1465 :                 builder_end_child(bs->stack[bs->stack_top], name, child);
     224             :         }
     225        1482 :         builder_unref(child);
     226             : }
     227             : 
     228             : void
     229        4952 : builder_stack_characters(void *data, const char *s, int len)
     230             : {
     231        4952 :         BuilderStack *bs = data;
     232        4952 :         Builder *b = bs->stack[bs->stack_top];
     233             : 
     234        4952 :         len = strtrim(&s, len);
     235        4952 :         if (len)
     236         881 :                 builder_characters(b, s, len);
     237        4952 : }
     238             : 
     239             : void
     240        2850 : builder_ref(Builder *b)
     241             : {
     242        2850 :         b->ops->ref(b);
     243        2850 : }
     244             : 
     245             : void
     246        2850 : builder_unref(Builder *b)
     247             : {
     248        2850 :         b->ops->unref(b);
     249        2850 : }
     250             : 
     251             : void
     252         881 : builder_characters(Builder *b, const char *c, int len)
     253             : {
     254         881 :         if (b->ops->characters)
     255         881 :                 b->ops->characters(b, c, len);
     256         881 : }
     257             : 
     258             : Builder *
     259        1466 : builder_start_child(Builder *b, const char *tag_name, const char **attrs)
     260             : {
     261        1466 :         return b->ops->start_child(b, tag_name, attrs);
     262             : }
     263             : 
     264             : void
     265        1465 : builder_end_child(Builder *b, const char *tag_name, Builder *child)
     266             : {
     267        1465 :         b->ops->end_child(b, tag_name, child);
     268        1465 : }
     269             : 
     270             : void
     271        1482 : builder_end_element(Builder *b)
     272             : {
     273        1482 :         b->ops->end_element(b);
     274        1482 : }
     275             : 
     276             : NodeBuilder *
     277        1466 : node_builder_new(const char *name, const char **attrs)
     278             : {
     279        1466 :         NodeBuilder *nb = calloc(1, sizeof(NodeBuilder));
     280        1466 :         if (!nb)
     281           0 :                 return NULL;
     282        1466 :         nb->b.ops = &NODE_BUILDER_OPS;
     283        1466 :         builder_ref(&nb->b);
     284        1466 :         nb->name = strdup(name);
     285        1466 :         slice_make(&nb->attrs, 0, 0);
     286        1466 :         slice_make(&nb->children, 0, 0);
     287        4150 :         for (size_t i = 0; attrs[i]; i += 2) {
     288             :                 //printf("%s\t%s\n", name, attrs[i]);
     289        2684 :                 ENTRY *e = calloc(1, sizeof(ENTRY));
     290        2684 :                 e->key = strdup(attrs[i]);
     291        2684 :                 e->data = strdup(attrs[i+1]);
     292        2684 :                 slice_append(&nb->attrs, e);
     293             :         }
     294             : 
     295        1466 :         return nb;
     296             : }
     297             : 
     298             : void
     299        2832 : node_builder_ref(void *data)
     300             : {
     301        2832 :         NodeBuilder *nb = data;
     302        2832 :         __sync_fetch_and_add(&nb->b.refcount, 1);
     303        2832 : }
     304             : 
     305             : void
     306        2832 : node_builder_unref(void *data)
     307             : {
     308        2832 :         NodeBuilder *nb = data;
     309        2832 :         if (!nb || __sync_sub_and_fetch(&nb->b.refcount, 1) != 0)
     310        1366 :                 return;
     311             : 
     312        1466 :         free(nb->name);
     313        1466 :         free(nb->content);
     314        4150 :         for (size_t i = 0; i < nb->attrs.len; i++) {
     315        2684 :                 ENTRY *e = nb->attrs.elems[i];
     316        2684 :                 free(e->key);
     317        2684 :                 free(e->data);
     318        2684 :                 free(e);
     319             :         }
     320        1466 :         free(nb->attrs.elems);
     321        2832 :         for (size_t i = 0; i < nb->children.len; i++) {
     322        1366 :                 NodeBuilder *child = nb->children.elems[i];
     323        1366 :                 builder_unref(&child->b);
     324             :         }
     325        1466 :         free(nb->children.elems);
     326        1466 :         free(nb);
     327             : }
     328             : 
     329             : void
     330         881 : node_builder_characters(void *data, const char *contents, int len)
     331             : {
     332         881 :         NodeBuilder *nb = data;
     333         881 :         if (!nb->content) {
     334         654 :                 char *s = malloc(len+1);
     335         654 :                 s[len] = '\0';
     336         654 :                 memcpy(s, contents, len);
     337         654 :                 nb->content = s;
     338         654 :                 nb->content_len = len;
     339             :         } else {
     340         227 :                 size_t content_len = nb->content_len;
     341             :                 // +2 for ' ' and trailing NULL
     342         227 :                 nb->content = realloc(nb->content, content_len + len + 1);
     343         227 :                 if (!nb->content) {
     344             :                         // FIXME(bp) handle ENOMEM
     345           0 :                         return;
     346             :                 }
     347         227 :                 memcpy(&nb->content[content_len], contents, len);
     348         227 :                 nb->content[content_len+len] = '\0';
     349         227 :                 nb->content_len += len;
     350             :         }
     351             : }
     352             : 
     353             : Builder *
     354        1366 : node_builder_start_child(void *data, const char *tag_name, const char **attrs)
     355             : {
     356        1366 :         NodeBuilder *child = node_builder_new(tag_name, attrs);
     357        1366 :         if (!child) {
     358             :                 // TODO(bp) handle ENOMEM
     359           0 :                 return NULL;
     360             :         }
     361        1366 :         return &child->b;
     362             : }
     363             : 
     364             : void
     365        1366 : node_builder_end_child(void *data, const char *tag_name, Builder *child)
     366             : {
     367        1366 :         NodeBuilder *nb = data;
     368        1366 :         builder_ref(child);
     369        1366 :         slice_append(&nb->children, (NodeBuilder*)child);
     370        1366 : }
     371             : 
     372             : void
     373        1465 : node_builder_end_element(void *data)
     374             : {
     375        1465 : }
     376             : 
     377             : NodeBuilder *
     378         318 : node_builder_get_first_child(NodeBuilder *nb, const char *name)
     379             : {
     380         662 :         for (size_t i = 0; i < nb->children.len; i++) {
     381         633 :                 NodeBuilder *child = nb->children.elems[i];
     382         633 :                 if (strcmp(child->name, name) == 0)
     383         289 :                         return child;
     384             :         }
     385          29 :         return NULL;
     386             : }
     387             : 
     388             : const char *
     389         262 : node_builder_get_attr(NodeBuilder *nb, const char *name)
     390             : {
     391         324 :         for (size_t i = 0; i < nb->attrs.len; i++) {
     392         265 :                 ENTRY *e = nb->attrs.elems[i];
     393         265 :                 if (strcmp(e->key, name) == 0)
     394         203 :                         return (const char *)e->data;
     395             :         }
     396             : 
     397          59 :         return NULL;
     398             : }
     399             : 
     400             : XmileBuilder *
     401          18 : xmile_builder_new(void)
     402             : {
     403          18 :         XmileBuilder *xb = calloc(1, sizeof(XmileBuilder));
     404          18 :         if (!xb)
     405           0 :                 return NULL;
     406          18 :         xb->b.ops = &XMILE_BUILDER_OPS;
     407          18 :         builder_ref(&xb->b);
     408          18 :         return xb;
     409             : }
     410             : 
     411             : void
     412          18 : xmile_builder_ref(void *data)
     413             : {
     414          18 :         XmileBuilder *xb = data;
     415          18 :         __sync_fetch_and_add(&xb->b.refcount, 1);
     416          18 : }
     417             : 
     418             : void
     419          18 : xmile_builder_unref(void *data)
     420             : {
     421          18 :         XmileBuilder *xb = data;
     422          18 :         if (!xb || __sync_sub_and_fetch(&xb->b.refcount, 1) != 0)
     423           0 :                 return;
     424          18 :         free(xb);
     425             : }
     426             : 
     427             : Builder *
     428         100 : xmile_builder_start_child(void *data, const char *tag_name, const char **attrs)
     429             : {
     430         100 :         return NULL;
     431             : }
     432             : 
     433             : void
     434          99 : xmile_builder_end_child(void *data, const char *tag_name, Builder *child)
     435             : {
     436             :         const char *val;
     437             :         NodeBuilder *nbchild;
     438          99 :         XmileBuilder *xb = data;
     439             : 
     440          99 :         if (strcmp(tag_name, "header") == 0) {
     441          17 :                 Header *header = &xb->file->header;
     442          17 :                 NodeBuilder *nbheader = (NodeBuilder *)child;
     443          17 :                 nbchild = node_builder_get_first_child(nbheader, "smile");
     444          17 :                 if (nbchild) {
     445          16 :                         val = node_builder_get_attr(nbchild, "version");
     446          16 :                         if (val)
     447          16 :                                 header->smile_version = strdup(val);
     448          16 :                         val = node_builder_get_attr(nbchild, "namespace");
     449          16 :                         if (val)
     450           2 :                                 header->smile_namespace = strdup(val);
     451             :                         // TODO(bp) uses_* flags
     452             :                 }
     453          17 :                 nbchild = node_builder_get_first_child(nbheader, "name");
     454          17 :                 if (nbchild && nbchild->content)
     455          16 :                         header->name = strdup(nbchild->content);
     456          17 :                 nbchild = node_builder_get_first_child(nbheader, "uuid");
     457          17 :                 if (nbchild && nbchild->content)
     458          16 :                         header->uuid = strdup(nbchild->content);
     459          17 :                 nbchild = node_builder_get_first_child(nbheader, "vendor");
     460          17 :                 if (nbchild && nbchild->content)
     461          17 :                         header->vendor = strdup(nbchild->content);
     462          17 :                 nbchild = node_builder_get_first_child(nbheader, "product");
     463          17 :                 if (nbchild) {
     464          17 :                         if (nbchild->content)
     465          17 :                                 header->product.name = strdup(nbchild->content);
     466          17 :                         val = node_builder_get_attr(nbchild, "version");
     467          17 :                         if (val)
     468          17 :                                 header->product.version = strdup(val);
     469          17 :                         val = node_builder_get_attr(nbchild, "lang");
     470          17 :                         if (val)
     471           5 :                                 header->product.lang = strdup(val);
     472             :                 }
     473          82 :         } else if (strcmp(tag_name, "sim_specs") == 0) {
     474          17 :                 SimSpec *specs = &xb->file->sim_specs;
     475          17 :                 NodeBuilder *nbspecs = (NodeBuilder *)child;
     476          17 :                 val = node_builder_get_attr(nbspecs, "method");
     477          17 :                 if (val)
     478           2 :                         specs->method = strdup(val);
     479          17 :                 val = node_builder_get_attr(nbspecs, "time_units");
     480          17 :                 if (val)
     481          16 :                         specs->time_units = strdup(val);
     482             :                 // TODO(bp) better error handling
     483          17 :                 nbchild = node_builder_get_first_child(nbspecs, "start");
     484          17 :                 if (nbchild && nbchild->content)
     485          17 :                         specs->start = strtod(nbchild->content, NULL);
     486          17 :                 nbchild = node_builder_get_first_child(nbspecs, "stop");
     487          17 :                 if (nbchild && nbchild->content)
     488          17 :                         specs->stop = strtod(nbchild->content, NULL);
     489          17 :                 nbchild = node_builder_get_first_child(nbspecs, "dt");
     490          17 :                 if (nbchild && nbchild->content)
     491          17 :                         specs->dt = strtod(nbchild->content, NULL);
     492          17 :                 nbchild = node_builder_get_first_child(nbspecs, "savestep");
     493          17 :                 if (nbchild && nbchild->content)
     494           1 :                         specs->savestep = strtod(nbchild->content, NULL);
     495             :                 else
     496          16 :                         specs->savestep = specs->dt;
     497          65 :         } else if (strcmp(tag_name, "model") == 0) {
     498          21 :                 SDModel *m = model_from_node_builder((NodeBuilder *)child);
     499          21 :                 if (m) {
     500          21 :                         m->file = xb->file;
     501          21 :                         slice_append(&xb->file->models, m);
     502             :                 }
     503             :         }
     504          99 : }
     505             : 
     506             : void
     507          17 : xmile_builder_end_element(void *data)
     508             : {
     509          17 : }
     510             : 
     511             : SDModel *
     512          21 : model_from_node_builder(NodeBuilder *nb)
     513             : {
     514             :         const char *val;
     515          21 :         SDModel *m = calloc(1, sizeof(SDModel));
     516          21 :         if (!m)
     517           0 :                 return NULL; // TODO(bp) handle ENOMEM
     518          21 :         sd_model_ref(m);
     519             : 
     520          21 :         val = node_builder_get_attr(nb, "name");
     521          21 :         if (val)
     522           4 :                 m->name = strdup(val);
     523             : 
     524          21 :         NodeBuilder *nbvars = node_builder_get_first_child(nb, "variables");;
     525          21 :         if (nbvars) {
     526         136 :                 for (size_t i = 0; i < nbvars->children.len; i++) {
     527         115 :                         NodeBuilder *nbvar = nbvars->children.elems[i];
     528         115 :                         Var *v = var_from_node_builder(nbvar);
     529         115 :                         if (v)
     530         115 :                                 slice_append(&m->vars, v);
     531             :                 }
     532             :         }
     533             : 
     534          21 :         return m;
     535             : }
     536             : 
     537             : Var *
     538         115 : var_from_node_builder(NodeBuilder *nb)
     539             : {
     540             :         const char *val;
     541             :         Var *v;
     542             :         NodeBuilder *nbeqn;
     543         115 :         VarType ty = 0;
     544             : 
     545         115 :         if (strcmp(nb->name, "aux") == 0)
     546          68 :                 ty = VAR_AUX;
     547          47 :         else if (strcmp(nb->name, "stock") == 0)
     548          17 :                 ty = VAR_STOCK;
     549          30 :         else if (strcmp(nb->name, "flow") == 0)
     550          26 :                 ty = VAR_FLOW;
     551           4 :         else if (strcmp(nb->name, "module") == 0)
     552           4 :                 ty = VAR_MODULE;
     553             :         else
     554           0 :                 return NULL; // TODO(bp) record error
     555             : 
     556         115 :         v = calloc(1, sizeof(Var));
     557         115 :         if (!v)
     558           0 :                 return NULL; // TODO(bp) handle ENOMEM
     559             : 
     560         115 :         v->type = ty;
     561             : 
     562         115 :         val = node_builder_get_attr(nb, "name");
     563         115 :         if (val) {
     564         115 :                 v->name = normalize_name(val);
     565             :         }
     566         115 :         nbeqn = node_builder_get_first_child(nb, "eqn");
     567         115 :         if (nbeqn && nbeqn->content)
     568         111 :                 v->eqn = strdup(nbeqn->content);
     569             : 
     570         345 :         for (size_t i = 0; i < nb->children.len; i++) {
     571         230 :                 NodeBuilder *child = nb->children.elems[i];
     572         230 :                 if (strcmp(child->name, "inflow") == 0 && child->content)
     573          12 :                         slice_append(&v->inflows, normalize_name(child->content));
     574         230 :                 if (strcmp(child->name, "outflow") == 0 && child->content)
     575          16 :                         slice_append(&v->outflows, normalize_name(child->content));
     576         230 :                 if (strcmp(child->name, "non_negative") == 0)
     577          28 :                         v->is_nonneg = true;
     578         230 :                 if (strcmp(child->name, "gf") == 0)
     579          13 :                         v->gf = table_from_node_builder(child);
     580         230 :                 if (strcmp(child->name, "connect") == 0) {
     581           8 :                         Var *ref = ref_from_node_builder(child);
     582           8 :                         if (ref)
     583           8 :                                 slice_append(&v->conns, ref);
     584             :                 }
     585             :         }
     586             : 
     587         115 :         return v;
     588             : }
     589             : 
     590             : Table *
     591          13 : table_from_node_builder(NodeBuilder *nb)
     592             : {
     593          13 :         void *mem = NULL;
     594          13 :         Table *t = NULL;
     595          13 :         char *pts = NULL;
     596             : 
     597          13 :         NodeBuilder *ypts = node_builder_get_first_child(nb, "ypts");
     598          13 :         if (!ypts || !ypts->content)
     599             :                 goto error;
     600             : 
     601          11 :         NodeBuilder *xpts = node_builder_get_first_child(nb, "xpts");
     602             : 
     603          11 :         size_t n = 1;
     604          11 :         size_t yptslen = strlen(ypts->content);
     605        1270 :         for (size_t i = 0; i < yptslen; i++) {
     606        1259 :                 if (ypts->content[i] == ',')
     607         109 :                         n++;
     608             :         }
     609             : 
     610             :         // round_up ensures following arrays aren't under-aligned.
     611             :         // obtain all the memory for the table in a single allocation.
     612          11 :         mem = calloc(1, round_up(sizeof(Table), 8) + 2*n*sizeof(double));
     613          11 :         if (!mem)
     614           0 :                 return NULL; // TODO(bp) handle ENOMEM
     615             : 
     616          11 :         t = mem;
     617          11 :         t->len = n;
     618          11 :         t->x = (double *)((char *)mem + round_up(sizeof(Table), 8));
     619          11 :         t->y = (double *)((char *)mem + round_up(sizeof(Table), 8) + n*sizeof(double));
     620             : 
     621          11 :         errno = 0;
     622          11 :         pts = ypts->content;
     623         131 :         for (size_t i = 0; i < t->len; i++) {
     624         120 :                 t->y[i] = strtod(pts, &pts);
     625         120 :                 if (errno == ERANGE || (*pts && *pts != ','))
     626             :                         goto error;
     627         120 :                 pts++;
     628             :         }
     629             : 
     630          11 :         if (xpts) {
     631           6 :                 errno = 0;
     632           6 :                 pts = xpts->content;
     633          72 :                 for (size_t i = 0; i < t->len; i++) {
     634          66 :                         t->x[i] = strtod(pts, &pts);
     635          66 :                         if (errno == ERANGE || (*pts && *pts != ','))
     636             :                                 goto error;
     637          66 :                         pts++;
     638             :                 }
     639             :         } else {
     640           5 :                 double xmin = 0, xmax = 0;
     641             :                 const char *attr;
     642             : 
     643           5 :                 NodeBuilder *xscale = node_builder_get_first_child(nb, "xscale");
     644           5 :                 if (!xscale)
     645           0 :                         goto error;
     646             : 
     647           5 :                 attr = node_builder_get_attr(xscale, "min");
     648           5 :                 if (attr)
     649           5 :                         xmin = atof(attr);
     650           5 :                 attr = node_builder_get_attr(xscale, "max");
     651           5 :                 if (attr)
     652           5 :                         xmax = atof(attr);
     653             : 
     654          59 :                 for (size_t i = 0; i < t->len; i++)
     655          54 :                         t->x[i] = ((double)i/(t->len-1))*(xmax-xmin) + xmin;
     656             :         }
     657             : 
     658          11 :         return t;
     659             : error:
     660           2 :         free(mem);
     661           2 :         return NULL;
     662             : }
     663             : 
     664             : Var *
     665           8 : ref_from_node_builder(NodeBuilder *nb)
     666             : {
     667             :         Var *ref;
     668             :         const char *src, *dst;
     669             : 
     670           8 :         ref = calloc(1, sizeof(*ref));
     671           8 :         src = node_builder_get_attr(nb, "from");
     672           8 :         dst = node_builder_get_attr(nb, "to");
     673             : 
     674           8 :         if (!ref || !src || !dst)
     675           0 :                 return NULL;
     676             : 
     677           8 :         ref->type = VAR_REF;
     678           8 :         ref->src = normalize_name(src);
     679           8 :         ref->name = normalize_name(dst);
     680             : 
     681           8 :         if (!ref->src || !ref->name) {
     682           0 :                 free(ref->src);
     683           0 :                 free(ref->name);
     684           0 :                 free(ref);
     685             :                 // ENOMEM
     686           0 :                 return NULL;
     687             :         }
     688             : 
     689           8 :         return ref;
     690             : }

Generated by: LCOV version 1.10