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 : }
|