libtreestore

annotate src/treestore.c @ 5:f3ade599cfbb

ts_free*/ts_destroy* functions shouldn't bork when passed a null pointer
author John Tsiombikas <nuclear@member.fsf.org>
date Sun, 13 Nov 2016 20:40:07 +0200
parents bb873449cf59
children
rev   line source
nuclear@0 1 #include <stdio.h>
nuclear@0 2 #include <stdlib.h>
nuclear@0 3 #include <string.h>
nuclear@3 4 #include <errno.h>
nuclear@0 5 #include "treestore.h"
nuclear@0 6
nuclear@3 7 struct ts_node *ts_text_load(FILE *fp);
nuclear@3 8 int ts_text_save(struct ts_node *tree, FILE *fp);
nuclear@3 9
nuclear@0 10 /* ---- ts_value implementation ---- */
nuclear@0 11
nuclear@0 12 int ts_init_value(struct ts_value *tsv)
nuclear@0 13 {
nuclear@0 14 memset(tsv, 0, sizeof *tsv);
nuclear@0 15 return 0;
nuclear@0 16 }
nuclear@0 17
nuclear@0 18 void ts_destroy_value(struct ts_value *tsv)
nuclear@0 19 {
nuclear@0 20 int i;
nuclear@0 21
nuclear@0 22 free(tsv->str);
nuclear@0 23 free(tsv->vec);
nuclear@0 24
nuclear@0 25 for(i=0; i<tsv->array_size; i++) {
nuclear@0 26 ts_destroy_value(tsv->array + i);
nuclear@0 27 }
nuclear@0 28 }
nuclear@0 29
nuclear@0 30
nuclear@0 31 struct ts_value *ts_alloc_value(void)
nuclear@0 32 {
nuclear@0 33 struct ts_value *v = malloc(sizeof *v);
nuclear@0 34 if(!v || ts_init_value(v) == -1) {
nuclear@0 35 free(v);
nuclear@0 36 return 0;
nuclear@0 37 }
nuclear@0 38 return v;
nuclear@0 39 }
nuclear@0 40
nuclear@0 41 void ts_free_value(struct ts_value *tsv)
nuclear@0 42 {
nuclear@0 43 ts_destroy_value(tsv);
nuclear@0 44 free(tsv);
nuclear@0 45 }
nuclear@0 46
nuclear@0 47
nuclear@0 48 int ts_copy_value(struct ts_value *dest, struct ts_value *src)
nuclear@0 49 {
nuclear@0 50 int i;
nuclear@0 51
nuclear@0 52 if(dest == src) return 0;
nuclear@0 53
nuclear@0 54 *dest = *src;
nuclear@0 55
nuclear@0 56 dest->str = 0;
nuclear@0 57 dest->vec = 0;
nuclear@0 58 dest->array = 0;
nuclear@0 59
nuclear@0 60 if(src->str) {
nuclear@0 61 if(!(dest->str = malloc(strlen(src->str) + 1))) {
nuclear@0 62 goto fail;
nuclear@0 63 }
nuclear@0 64 strcpy(dest->str, src->str);
nuclear@0 65 }
nuclear@0 66 if(src->vec && src->vec_size > 0) {
nuclear@0 67 if(!(dest->vec = malloc(src->vec_size * sizeof *src->vec))) {
nuclear@0 68 goto fail;
nuclear@0 69 }
nuclear@0 70 memcpy(dest->vec, src->vec, src->vec_size * sizeof *src->vec);
nuclear@0 71 }
nuclear@0 72 if(src->array && src->array_size > 0) {
nuclear@0 73 if(!(dest->array = calloc(src->array_size, sizeof *src->array))) {
nuclear@0 74 goto fail;
nuclear@0 75 }
nuclear@0 76 for(i=0; i<src->array_size; i++) {
nuclear@0 77 if(ts_copy_value(dest->array + i, src->array + i) == -1) {
nuclear@0 78 goto fail;
nuclear@0 79 }
nuclear@0 80 }
nuclear@0 81 }
nuclear@0 82 return 0;
nuclear@0 83
nuclear@0 84 fail:
nuclear@0 85 free(dest->str);
nuclear@0 86 free(dest->vec);
nuclear@0 87 if(dest->array) {
nuclear@0 88 for(i=0; i<dest->array_size; i++) {
nuclear@0 89 ts_destroy_value(dest->array + i);
nuclear@0 90 }
nuclear@0 91 free(dest->array);
nuclear@0 92 }
nuclear@0 93 return -1;
nuclear@0 94 }
nuclear@0 95
nuclear@4 96 struct val_list_node {
nuclear@4 97 struct ts_value val;
nuclear@4 98 struct val_list_node *next;
nuclear@4 99 };
nuclear@0 100
nuclear@0 101 int ts_set_value(struct ts_value *tsv, const char *str)
nuclear@0 102 {
nuclear@4 103 char *endp;
nuclear@4 104
nuclear@0 105 if(tsv->str) {
nuclear@0 106 ts_destroy_value(tsv);
nuclear@0 107 if(ts_init_value(tsv) == -1) {
nuclear@0 108 return -1;
nuclear@0 109 }
nuclear@0 110 }
nuclear@0 111
nuclear@4 112 tsv->type = TS_STRING;
nuclear@0 113 if(!(tsv->str = malloc(strlen(str) + 1))) {
nuclear@0 114 return -1;
nuclear@0 115 }
nuclear@0 116 strcpy(tsv->str, str);
nuclear@4 117
nuclear@4 118 /* try to parse the string and see if it fits any of the value types */
nuclear@4 119 if(*str == '[' || *str == '{') {
nuclear@4 120 /* try to parse as a vector */
nuclear@4 121 struct val_list_node *list = 0, *tail = 0, *node;
nuclear@4 122 int nelem = 0;
nuclear@4 123 char endsym = *str++ + 2; /* ']' is '[' + 2 and '}' is '{' + 2 */
nuclear@4 124
nuclear@4 125 while(*str && *str != endsym) {
nuclear@4 126 float val = strtod(str, &endp);
nuclear@4 127 if(endp == str || !(node = malloc(sizeof *node))) {
nuclear@4 128 break;
nuclear@4 129 }
nuclear@4 130 ts_init_value(&node->val);
nuclear@4 131 ts_set_valuef(&node->val, val);
nuclear@4 132 node->next = 0;
nuclear@4 133
nuclear@4 134 if(list) {
nuclear@4 135 tail->next = node;
nuclear@4 136 tail = node;
nuclear@4 137 } else {
nuclear@4 138 list = tail = node;
nuclear@4 139 }
nuclear@4 140 ++nelem;
nuclear@4 141 str = endp;
nuclear@4 142 }
nuclear@4 143
nuclear@4 144 if(nelem && (tsv->array = malloc(nelem * sizeof *tsv->array)) &&
nuclear@4 145 (tsv->vec = malloc(nelem * sizeof *tsv->vec))) {
nuclear@4 146 int idx = 0;
nuclear@4 147 while(list) {
nuclear@4 148 node = list;
nuclear@4 149 list = list->next;
nuclear@4 150
nuclear@4 151 tsv->array[idx] = node->val;
nuclear@4 152 tsv->vec[idx] = node->val.fnum;
nuclear@4 153 ++idx;
nuclear@4 154 free(node);
nuclear@4 155 }
nuclear@4 156 tsv->type = TS_VECTOR;
nuclear@4 157 }
nuclear@4 158
nuclear@4 159 } else if((tsv->fnum = strtod(str, &endp)), endp != str) {
nuclear@4 160 /* it's a number I guess... */
nuclear@4 161 tsv->type = TS_NUMBER;
nuclear@4 162 }
nuclear@4 163
nuclear@0 164 return 0;
nuclear@0 165 }
nuclear@0 166
nuclear@0 167 int ts_set_valueiv(struct ts_value *tsv, int count, ...)
nuclear@0 168 {
nuclear@0 169 int res;
nuclear@0 170 va_list ap;
nuclear@0 171 va_start(ap, count);
nuclear@0 172 res = ts_set_valueiv_va(tsv, count, ap);
nuclear@0 173 va_end(ap);
nuclear@0 174 return res;
nuclear@0 175 }
nuclear@0 176
nuclear@0 177 #define MAKE_NUMSTR_FUNC(typestr, fmt) \
nuclear@0 178 static char *make_##typestr##str(int x) \
nuclear@0 179 { \
nuclear@0 180 static char scrap[128]; \
nuclear@0 181 char *str; \
nuclear@0 182 int sz = snprintf(scrap, sizeof scrap, fmt, x); \
nuclear@0 183 if(!(str = malloc(sz + 1))) return 0; \
nuclear@0 184 sprintf(str, fmt, x); \
nuclear@0 185 return str; \
nuclear@0 186 }
nuclear@0 187
nuclear@0 188 MAKE_NUMSTR_FUNC(int, "%d")
nuclear@0 189 MAKE_NUMSTR_FUNC(float, "%d")
nuclear@0 190
nuclear@0 191 #define ARGS_ARE_INT ((enum ts_value_type)42)
nuclear@0 192
nuclear@0 193 int ts_set_valueiv_va(struct ts_value *tsv, int count, va_list ap)
nuclear@0 194 {
nuclear@0 195 if(count < 1) return -1;
nuclear@0 196 if(count == 1) {
nuclear@0 197 int num = va_arg(ap, int);
nuclear@0 198 if(!(tsv->str = make_intstr(tsv->inum))) {
nuclear@0 199 return -1;
nuclear@0 200 }
nuclear@0 201
nuclear@0 202 tsv->type = TS_NUMBER;
nuclear@0 203 tsv->inum = num;
nuclear@0 204 tsv->fnum = (float)num;
nuclear@0 205 return 0;
nuclear@0 206 }
nuclear@0 207
nuclear@0 208 /* otherwise it's an array, let ts_set_valuefv_va handle it */
nuclear@0 209 /* XXX: va_arg will need to be called with int instead of float. set a special
nuclear@0 210 * value to the type field before calling this, to signify that.
nuclear@0 211 */
nuclear@0 212 tsv->type = ARGS_ARE_INT;
nuclear@0 213 return ts_set_valuefv_va(tsv, count, ap);
nuclear@0 214 }
nuclear@0 215
nuclear@0 216 int ts_set_valuei(struct ts_value *tsv, int inum)
nuclear@0 217 {
nuclear@0 218 return ts_set_valueiv(tsv, 1, inum);
nuclear@0 219 }
nuclear@0 220
nuclear@0 221
nuclear@0 222 int ts_set_valuefv(struct ts_value *tsv, int count, ...)
nuclear@0 223 {
nuclear@0 224 int res;
nuclear@0 225 va_list ap;
nuclear@0 226 va_start(ap, count);
nuclear@0 227 res = ts_set_valuefv_va(tsv, count, ap);
nuclear@0 228 va_end(ap);
nuclear@0 229 return res;
nuclear@0 230 }
nuclear@0 231
nuclear@0 232 int ts_set_valuefv_va(struct ts_value *tsv, int count, va_list ap)
nuclear@0 233 {
nuclear@0 234 int i;
nuclear@0 235
nuclear@0 236 if(count < 1) return -1;
nuclear@0 237 if(count == 1) {
nuclear@0 238 int num = va_arg(ap, int);
nuclear@0 239 if(!(tsv->str = make_floatstr(tsv->inum))) {
nuclear@0 240 return -1;
nuclear@0 241 }
nuclear@0 242
nuclear@0 243 tsv->type = TS_NUMBER;
nuclear@0 244 tsv->inum = num;
nuclear@0 245 tsv->fnum = (float)num;
nuclear@0 246 return 0;
nuclear@0 247 }
nuclear@0 248
nuclear@0 249 /* otherwise it's an array, we need to create the ts_value array, and
nuclear@0 250 * the simplified vector
nuclear@0 251 */
nuclear@0 252 if(!(tsv->vec = malloc(count * sizeof *tsv->vec))) {
nuclear@0 253 return -1;
nuclear@0 254 }
nuclear@0 255 tsv->vec_size = count;
nuclear@0 256
nuclear@0 257 for(i=0; i<count; i++) {
nuclear@0 258 if(tsv->type == ARGS_ARE_INT) { /* only when called by ts_set_valueiv_va */
nuclear@0 259 tsv->vec[i] = (float)va_arg(ap, int);
nuclear@0 260 } else {
nuclear@0 261 tsv->vec[i] = va_arg(ap, double);
nuclear@0 262 }
nuclear@0 263 }
nuclear@0 264
nuclear@0 265 if(!(tsv->array = malloc(count * sizeof *tsv->array))) {
nuclear@0 266 free(tsv->vec);
nuclear@0 267 }
nuclear@0 268 tsv->array_size = count;
nuclear@0 269
nuclear@0 270 for(i=0; i<count; i++) {
nuclear@0 271 ts_init_value(tsv->array + i);
nuclear@0 272 if(tsv->type == ARGS_ARE_INT) { /* only when called by ts_set_valueiv_va */
nuclear@0 273 ts_set_valuei(tsv->array + i, (int)tsv->vec[i]);
nuclear@0 274 } else {
nuclear@0 275 ts_set_valuef(tsv->array + i, tsv->vec[i]);
nuclear@0 276 }
nuclear@0 277 }
nuclear@0 278
nuclear@0 279 tsv->type = TS_VECTOR;
nuclear@0 280 return 0;
nuclear@0 281 }
nuclear@0 282
nuclear@4 283 int ts_set_valuef(struct ts_value *tsv, float fnum)
nuclear@0 284 {
nuclear@0 285 return ts_set_valuefv(tsv, 1, fnum);
nuclear@0 286 }
nuclear@0 287
nuclear@0 288
nuclear@0 289 int ts_set_valuev(struct ts_value *tsv, int count, ...)
nuclear@0 290 {
nuclear@0 291 int res;
nuclear@0 292 va_list ap;
nuclear@0 293 va_start(ap, count);
nuclear@0 294 res = ts_set_valuev_va(tsv, count, ap);
nuclear@0 295 va_end(ap);
nuclear@0 296 return res;
nuclear@0 297 }
nuclear@0 298
nuclear@0 299 int ts_set_valuev_va(struct ts_value *tsv, int count, va_list ap)
nuclear@0 300 {
nuclear@1 301 int i;
nuclear@1 302
nuclear@0 303 if(count <= 1) return -1;
nuclear@1 304
nuclear@1 305 if((tsv->array = malloc(count * sizeof *tsv->array))) {
nuclear@1 306 return -1;
nuclear@1 307 }
nuclear@1 308 tsv->array_size = count;
nuclear@1 309
nuclear@1 310 for(i=0; i<count; i++) {
nuclear@1 311 struct ts_value *src = va_arg(ap, struct ts_value*);
nuclear@1 312 ts_copy_value(tsv->array + i, src);
nuclear@1 313 }
nuclear@1 314 return 0;
nuclear@0 315 }
nuclear@1 316
nuclear@1 317
nuclear@1 318 /* ---- ts_attr implementation ---- */
nuclear@1 319
nuclear@1 320 int ts_init_attr(struct ts_attr *attr)
nuclear@1 321 {
nuclear@1 322 memset(attr, 0, sizeof *attr);
nuclear@1 323 return ts_init_value(&attr->val);
nuclear@1 324 }
nuclear@1 325
nuclear@1 326 void ts_destroy_attr(struct ts_attr *attr)
nuclear@1 327 {
nuclear@1 328 free(attr->name);
nuclear@1 329 ts_destroy_value(&attr->val);
nuclear@1 330 }
nuclear@1 331
nuclear@1 332 struct ts_attr *ts_alloc_attr(void)
nuclear@1 333 {
nuclear@1 334 struct ts_attr *attr = malloc(sizeof *attr);
nuclear@1 335 if(!attr || ts_init_attr(attr) == -1) {
nuclear@1 336 free(attr);
nuclear@1 337 return 0;
nuclear@1 338 }
nuclear@1 339 return attr;
nuclear@1 340 }
nuclear@1 341
nuclear@1 342 void ts_free_attr(struct ts_attr *attr)
nuclear@1 343 {
nuclear@1 344 ts_destroy_attr(attr);
nuclear@1 345 free(attr);
nuclear@1 346 }
nuclear@1 347
nuclear@1 348 int ts_copy_attr(struct ts_attr *dest, struct ts_attr *src)
nuclear@1 349 {
nuclear@1 350 if(dest == src) return 0;
nuclear@1 351
nuclear@1 352 if(ts_set_attr_name(dest, src->name) == -1) {
nuclear@1 353 return -1;
nuclear@1 354 }
nuclear@1 355
nuclear@1 356 if(ts_copy_value(&dest->val, &src->val) == -1) {
nuclear@1 357 ts_destroy_attr(dest);
nuclear@1 358 return -1;
nuclear@1 359 }
nuclear@1 360 return 0;
nuclear@1 361 }
nuclear@1 362
nuclear@1 363 int ts_set_attr_name(struct ts_attr *attr, const char *name)
nuclear@1 364 {
nuclear@1 365 char *n = malloc(strlen(name) + 1);
nuclear@1 366 if(!n) return -1;
nuclear@1 367 strcpy(n, name);
nuclear@1 368
nuclear@1 369 free(attr->name);
nuclear@1 370 attr->name = n;
nuclear@1 371 return 0;
nuclear@1 372 }
nuclear@1 373
nuclear@1 374
nuclear@1 375 /* ---- ts_node implementation ---- */
nuclear@1 376
nuclear@1 377 int ts_init_node(struct ts_node *node)
nuclear@1 378 {
nuclear@1 379 memset(node, 0, sizeof *node);
nuclear@1 380 return 0;
nuclear@1 381 }
nuclear@1 382
nuclear@1 383 void ts_destroy_node(struct ts_node *node)
nuclear@1 384 {
nuclear@5 385 if(!node) return;
nuclear@5 386
nuclear@1 387 free(node->name);
nuclear@1 388
nuclear@1 389 while(node->attr_list) {
nuclear@1 390 struct ts_attr *attr = node->attr_list;
nuclear@1 391 node->attr_list = node->attr_list->next;
nuclear@1 392 ts_free_attr(attr);
nuclear@1 393 }
nuclear@1 394 }
nuclear@1 395
nuclear@1 396 struct ts_node *ts_alloc_node(void)
nuclear@1 397 {
nuclear@1 398 struct ts_node *node = malloc(sizeof *node);
nuclear@1 399 if(!node || ts_init_node(node) == -1) {
nuclear@1 400 free(node);
nuclear@1 401 return 0;
nuclear@1 402 }
nuclear@1 403 return node;
nuclear@1 404 }
nuclear@1 405
nuclear@1 406 void ts_free_node(struct ts_node *node)
nuclear@1 407 {
nuclear@1 408 ts_destroy_node(node);
nuclear@1 409 free(node);
nuclear@1 410 }
nuclear@1 411
nuclear@1 412 void ts_free_tree(struct ts_node *tree)
nuclear@1 413 {
nuclear@5 414 if(!tree) return;
nuclear@5 415
nuclear@1 416 while(tree->child_list) {
nuclear@2 417 struct ts_node *child = tree->child_list;
nuclear@1 418 tree->child_list = tree->child_list->next;
nuclear@1 419 ts_free_tree(child);
nuclear@1 420 }
nuclear@1 421
nuclear@1 422 ts_free_node(tree);
nuclear@1 423 }
nuclear@3 424
nuclear@3 425 void ts_add_attr(struct ts_node *node, struct ts_attr *attr)
nuclear@3 426 {
nuclear@3 427 attr->next = 0;
nuclear@3 428 if(node->attr_list) {
nuclear@3 429 node->attr_tail->next = attr;
nuclear@3 430 node->attr_tail = attr;
nuclear@3 431 } else {
nuclear@3 432 node->attr_list = node->attr_tail = attr;
nuclear@3 433 }
nuclear@3 434 }
nuclear@3 435
nuclear@3 436 struct ts_attr *ts_get_attr(struct ts_node *node, const char *name)
nuclear@3 437 {
nuclear@3 438 struct ts_attr *attr = node->attr_list;
nuclear@3 439 while(attr) {
nuclear@3 440 if(strcmp(attr->name, name) == 0) {
nuclear@3 441 return attr;
nuclear@3 442 }
nuclear@3 443 attr = attr->next;
nuclear@3 444 }
nuclear@3 445 return 0;
nuclear@3 446 }
nuclear@3 447
nuclear@4 448 const char *ts_get_attr_str(struct ts_node *node, const char *aname, const char *def_val)
nuclear@4 449 {
nuclear@4 450 struct ts_attr *attr = ts_get_attr(node, aname);
nuclear@4 451 if(!attr || !attr->val.str) {
nuclear@4 452 return def_val;
nuclear@4 453 }
nuclear@4 454 return attr->val.str;
nuclear@4 455 }
nuclear@4 456
nuclear@4 457 float ts_get_attr_num(struct ts_node *node, const char *aname, float def_val)
nuclear@4 458 {
nuclear@4 459 struct ts_attr *attr = ts_get_attr(node, aname);
nuclear@4 460 if(!attr || attr->val.type != TS_NUMBER) {
nuclear@4 461 return def_val;
nuclear@4 462 }
nuclear@4 463 return attr->val.fnum;
nuclear@4 464 }
nuclear@4 465
nuclear@4 466 int ts_get_attr_int(struct ts_node *node, const char *aname, int def_val)
nuclear@4 467 {
nuclear@4 468 struct ts_attr *attr = ts_get_attr(node, aname);
nuclear@4 469 if(!attr || attr->val.type != TS_NUMBER) {
nuclear@4 470 return def_val;
nuclear@4 471 }
nuclear@4 472 return attr->val.inum;
nuclear@4 473 }
nuclear@4 474
nuclear@4 475 float *ts_get_attr_vec(struct ts_node *node, const char *aname, float *def_val)
nuclear@4 476 {
nuclear@4 477 struct ts_attr *attr = ts_get_attr(node, aname);
nuclear@4 478 if(!attr || !attr->val.vec) {
nuclear@4 479 return def_val;
nuclear@4 480 }
nuclear@4 481 return attr->val.vec;
nuclear@4 482 }
nuclear@4 483
nuclear@4 484 struct ts_value *ts_get_attr_array(struct ts_node *node, const char *aname, struct ts_value *def_val)
nuclear@4 485 {
nuclear@4 486 struct ts_attr *attr = ts_get_attr(node, aname);
nuclear@4 487 if(!attr || !attr->val.array) {
nuclear@4 488 return def_val;
nuclear@4 489 }
nuclear@4 490 return attr->val.array;
nuclear@4 491 }
nuclear@4 492
nuclear@3 493 void ts_add_child(struct ts_node *node, struct ts_node *child)
nuclear@3 494 {
nuclear@3 495 if(child->parent) {
nuclear@3 496 if(child->parent == node) return;
nuclear@3 497 ts_remove_child(child->parent, child);
nuclear@3 498 }
nuclear@3 499 child->parent = node;
nuclear@3 500 child->next = 0;
nuclear@3 501
nuclear@3 502 if(node->child_list) {
nuclear@3 503 node->child_tail->next = child;
nuclear@3 504 node->child_tail = child;
nuclear@3 505 } else {
nuclear@3 506 node->child_list = node->child_tail = child;
nuclear@3 507 }
nuclear@3 508 }
nuclear@3 509
nuclear@3 510 int ts_remove_child(struct ts_node *node, struct ts_node *child)
nuclear@3 511 {
nuclear@3 512 struct ts_node dummy, *iter = &dummy;
nuclear@3 513 dummy.next = node->child_list;
nuclear@3 514
nuclear@3 515 while(iter->next && iter->next != child) {
nuclear@3 516 iter = iter->next;
nuclear@3 517 }
nuclear@3 518 if(!iter->next) {
nuclear@3 519 return -1;
nuclear@3 520 }
nuclear@3 521
nuclear@3 522 child->parent = 0;
nuclear@3 523
nuclear@3 524 iter->next = child->next;
nuclear@3 525 if(!iter->next) {
nuclear@3 526 node->child_tail = iter;
nuclear@3 527 }
nuclear@3 528 node->child_list = dummy.next;
nuclear@3 529 return 0;
nuclear@3 530 }
nuclear@3 531
nuclear@3 532 struct ts_node *ts_get_child(struct ts_node *node, const char *name)
nuclear@3 533 {
nuclear@3 534 struct ts_node *res = node->child_list;
nuclear@3 535 while(res) {
nuclear@3 536 if(strcmp(res->name, name) == 0) {
nuclear@3 537 return res;
nuclear@3 538 }
nuclear@3 539 res = res->next;
nuclear@3 540 }
nuclear@3 541 return 0;
nuclear@3 542 }
nuclear@3 543
nuclear@3 544 struct ts_node *ts_load(const char *fname)
nuclear@3 545 {
nuclear@3 546 FILE *fp;
nuclear@3 547 struct ts_node *root;
nuclear@3 548
nuclear@3 549 if(!(fp = fopen(fname, "rb"))) {
nuclear@3 550 fprintf(stderr, "ts_load: failed to open file: %s: %s\n", fname, strerror(errno));
nuclear@3 551 return 0;
nuclear@3 552 }
nuclear@3 553
nuclear@3 554 root = ts_text_load(fp);
nuclear@3 555 fclose(fp);
nuclear@3 556 return root;
nuclear@3 557 }
nuclear@3 558
nuclear@3 559 int ts_save(struct ts_node *tree, const char *fname)
nuclear@3 560 {
nuclear@3 561 FILE *fp;
nuclear@3 562 int res;
nuclear@3 563
nuclear@3 564 if(!(fp = fopen(fname, "wb"))) {
nuclear@3 565 fprintf(stderr, "ts_save: failed to open file: %s: %s\n", fname, strerror(errno));
nuclear@3 566 return 0;
nuclear@3 567 }
nuclear@3 568 res = ts_text_save(tree, fp);
nuclear@3 569 fclose(fp);
nuclear@3 570 return res;
nuclear@3 571 }