source: mainline/uspace/lib/sif/src/sif.c@ 153dd3b

ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 153dd3b was 153dd3b, checked in by Jiri Svoboda <jiri@…>, 22 months ago

Do not leak memory when realloc() fails

  • Property mode set to 100644
File size: 20.5 KB
RevLine 
[b79903b]1/*
[153dd3b]2 * Copyright (c) 2023 Jiri Svoboda
[b79903b]3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28/** @addtogroup libsif
29 * @{
30 */
31/**
32 * @file Structured Information Format
33 *
34 * Structured Information Format (SIF) is an API that allows an application
35 * to maintain data in a persistent repository in a format that
36 *
37 * - is structured (and hence extensible)
38 * - allows atomic (transactional) updates
39 * - allows efficient updates
40 *
41 * SIF is meant to be used as the basis for the storage backend used to
42 * maintain application or configuration data. SIF is *not* a (relational)
43 * database (not even close). The structure of a SIF repository is quite
44 * similar to an XML document that contains just tags with attributes
45 * (but no text).
46 *
47 * SIF can thus naturally express ordered lists (unlike a relational database).
48 * When contrasted to a (relational) database, SIF is much more primitive.
49 *
50 * In particular, SIF
51 *
52 * - does not run on a separate server
53 * - des not handle concurrency
54 * - does not have a notion of types or data validation
55 * - does not understand relations
56 * - does not implement any kind of search/queries
57 * - does not deal with data sets large enough not to fit in primary memory
58 *
59 * any kind of structure data validation is left up to the application.
60 */
61
[7b87e1d]62#include <adt/list.h>
63#include <adt/odict.h>
[b79903b]64#include <errno.h>
65#include <stdio.h>
66#include <stdlib.h>
67#include <str.h>
68#include "../include/sif.h"
69#include "../private/sif.h"
70
71static errno_t sif_export_node(sif_node_t *, FILE *);
[9c5e3a5]72static errno_t sif_import_node(sif_node_t *, FILE *, sif_node_t **);
[7b87e1d]73static sif_attr_t *sif_node_first_attr(sif_node_t *);
74static sif_attr_t *sif_node_next_attr(sif_attr_t *);
75static void sif_attr_delete(sif_attr_t *);
76static void *sif_attr_getkey(odlink_t *);
77static int sif_attr_cmp(void *, void *);
[b79903b]78
79/** Create new SIF node.
80 *
[7b87e1d]81 * @param parent Parent node
[b79903b]82 * @return Pointer to new node on success or @c NULL if out of memory
83 */
84static sif_node_t *sif_node_new(sif_node_t *parent)
85{
86 sif_node_t *node;
87
88 node = calloc(1, sizeof(sif_node_t));
89 if (node == NULL)
90 return NULL;
91
92 node->parent = parent;
[7b87e1d]93 odict_initialize(&node->attrs, sif_attr_getkey, sif_attr_cmp);
[b79903b]94 list_initialize(&node->children);
95
96 return node;
97}
98
99/** Delete SIF node.
100 *
101 * Delete a SIF node that has been already unlinked from the tree.
[7b87e1d]102 * This will also delete any attributes or child nodes.
[b79903b]103 *
104 * @param node Node
105 */
106static void sif_node_delete(sif_node_t *node)
107{
[7b87e1d]108 sif_attr_t *attr;
109 sif_node_t *child;
110
[b79903b]111 if (node == NULL)
112 return;
113
[7b87e1d]114 assert(!link_used(&node->lparent));
115
[b79903b]116 if (node->ntype != NULL)
117 free(node->ntype);
118
[7b87e1d]119 attr = sif_node_first_attr(node);
120 while (attr != NULL) {
121 odict_remove(&attr->lattrs);
122 sif_attr_delete(attr);
123 attr = sif_node_first_attr(node);
124 }
125
126 child = sif_node_first_child(node);
127 while (child != NULL) {
128 list_remove(&child->lparent);
129 sif_node_delete(child);
130 child = sif_node_first_child(node);
131 }
132
[b79903b]133 free(node);
134}
135
[7b87e1d]136/** Create new SIF attribute.
137 *
138 * @param node Containing node
139 * @return Pointer to new node on success or @c NULL if out of memory
140 */
141static sif_attr_t *sif_attr_new(sif_node_t *node)
142{
143 sif_attr_t *attr;
144
145 attr = calloc(1, sizeof(sif_attr_t));
146 if (attr == NULL)
147 return NULL;
148
149 attr->node = node;
150 return attr;
151}
152
153/** Delete SIF attribute.
154 *
155 * Delete a SIF attribute that has been already unlinked from is node.
156 *
157 * @param attr Attribute
158 */
159static void sif_attr_delete(sif_attr_t *attr)
160{
161 if (attr == NULL)
162 return;
163
164 assert(!odlink_used(&attr->lattrs));
165
166 if (attr->aname != NULL)
167 free(attr->aname);
168 if (attr->avalue != NULL)
169 free(attr->avalue);
170
171 free(attr);
172}
173
[b79903b]174/** Create and open a SIF repository.
175 *
176 * @param fname File name
177 * @param rsess Place to store pointer to new session.
178 *
179 * @return EOK on success or error code
180 */
181errno_t sif_create(const char *fname, sif_sess_t **rsess)
182{
183 sif_sess_t *sess;
184 sif_node_t *root = NULL;
[7b87e1d]185 sif_trans_t *trans = NULL;
[b79903b]186 errno_t rc;
187 FILE *f;
188
189 sess = calloc(1, sizeof(sif_sess_t));
190 if (sess == NULL)
191 return ENOMEM;
192
[1dcba91]193 sess->fname = str_dup(fname);
194 if (sess->fname == NULL) {
195 rc = ENOMEM;
196 goto error;
197 }
198
[b79903b]199 root = sif_node_new(NULL);
200 if (root == NULL) {
201 rc = ENOMEM;
202 goto error;
203 }
204
205 root->ntype = str_dup("sif");
206 if (root->ntype == NULL) {
207 rc = ENOMEM;
208 goto error;
209 }
210
[7ab7075f]211 f = fopen(fname, "wx");
[b79903b]212 if (f == NULL) {
213 rc = EIO;
214 goto error;
215 }
216
217 sess->f = f;
218 sess->root = root;
[7b87e1d]219
220 /* Run a dummy trasaction to marshall initial repo state to file */
221 rc = sif_trans_begin(sess, &trans);
222 if (rc != EOK)
223 goto error;
224
225 rc = sif_trans_end(trans);
226 if (rc != EOK)
227 goto error;
228
[b79903b]229 *rsess = sess;
230 return EOK;
231error:
[7b87e1d]232 if (trans != NULL)
233 sif_trans_abort(trans);
[b79903b]234 sif_node_delete(root);
[1dcba91]235 if (sess->fname != NULL)
236 free(sess->fname);
[b79903b]237 free(sess);
238 return rc;
239}
240
241/** Open an existing SIF repository.
242 *
243 * @param fname File name
244 * @param rsess Place to store pointer to new session.
245 *
246 * @return EOK on success or error code
247 */
248errno_t sif_open(const char *fname, sif_sess_t **rsess)
249{
250 sif_sess_t *sess;
251 sif_node_t *root = NULL;
252 errno_t rc;
253 FILE *f;
254
255 sess = calloc(1, sizeof(sif_sess_t));
256 if (sess == NULL)
257 return ENOMEM;
258
[1dcba91]259 sess->fname = str_dup(fname);
260 if (sess->fname == NULL) {
261 rc = ENOMEM;
262 goto error;
263 }
264
265 f = fopen(fname, "r");
[9c5e3a5]266 if (f == NULL) {
267 rc = EIO;
[b79903b]268 goto error;
269 }
270
[9c5e3a5]271 rc = sif_import_node(NULL, f, &root);
272 if (rc != EOK)
[b79903b]273 goto error;
274
[9c5e3a5]275 if (str_cmp(root->ntype, "sif") != 0) {
[b79903b]276 rc = EIO;
277 goto error;
278 }
279
[9c5e3a5]280 sess->root = root;
281
[b79903b]282 sess->f = f;
283 sess->root = root;
284 *rsess = sess;
285 return EOK;
286error:
287 sif_node_delete(root);
[1dcba91]288 if (sess->fname != NULL)
289 free(sess->fname);
[b79903b]290 free(sess);
291 return rc;
292}
293
294/** Close SIF session.
295 *
296 * @param sess SIF session
297 * @return EOK on success or error code
298 */
299errno_t sif_close(sif_sess_t *sess)
300{
301 sif_node_delete(sess->root);
302
303 if (fclose(sess->f) < 0) {
304 free(sess);
305 return EIO;
306 }
307
[1dcba91]308 if (sess->fname != NULL)
309 free(sess->fname);
310 free(sess);
[b79903b]311 return EOK;
312}
313
314/** Return root node.
315 *
316 * @param sess SIF session
317 */
318sif_node_t *sif_get_root(sif_sess_t *sess)
319{
320 return sess->root;
321}
322
323/** Get first child of a node.
324 *
325 * @param parent Parent node
326 * @return First child node or @c NULL if @a parent has no children
327 */
328sif_node_t *sif_node_first_child(sif_node_t *parent)
329{
330 link_t *link;
331
332 link = list_first(&parent->children);
333 if (link == NULL)
334 return NULL;
335
336 return list_get_instance(link, sif_node_t, lparent);
337}
338
339/** Get next child of a node.
340 *
341 * @param current Current node
342 * @return Next child (i.e. next sibling of @a current)
343 */
344sif_node_t *sif_node_next_child(sif_node_t *current)
345{
346 link_t *link;
347
348 link = list_next(&current->lparent, &current->parent->children);
349 if (link == NULL)
350 return NULL;
351
352 return list_get_instance(link, sif_node_t, lparent);
353}
354
355/** Get node type.
356 *
357 * @param node SIF node
358 * @return Pointer to string, valid until next modification.
359 */
360const char *sif_node_get_type(sif_node_t *node)
361{
362 return node->ntype;
363}
364
365/** Get node attribute.
366 *
367 * @param node SIF node
368 * @param aname Attribute name
369 *
370 * @return Attribute value or @c NULL if attribute is not set
371 */
372const char *sif_node_get_attr(sif_node_t *node, const char *aname)
373{
[7b87e1d]374 odlink_t *link;
375 sif_attr_t *attr;
376
377 link = odict_find_eq(&node->attrs, (void *)aname, NULL);
378 if (link == NULL)
379 return NULL;
380
381 attr = odict_get_instance(link, sif_attr_t, lattrs);
382 return attr->avalue;
[b79903b]383}
384
385/** Begin SIF transaction.
386 *
387 * @param trans Transaction
388 * @return EOK on success or error code
389 */
390errno_t sif_trans_begin(sif_sess_t *sess, sif_trans_t **rtrans)
391{
392 sif_trans_t *trans;
393
394 trans = calloc(1, sizeof(sif_trans_t));
395 if (trans == NULL)
396 return ENOMEM;
397
398 trans->sess = sess;
399 *rtrans = trans;
400 return EOK;
401}
402
403/** Commit SIF transaction.
[7b87e1d]404 *
405 * Commit and free the transaction. If an error is returned, that means
406 * the transaction has not been freed (and sif_trans_abort() must be used).
[b79903b]407 *
408 * @param trans Transaction
409 * @return EOK on success or error code
410 */
411errno_t sif_trans_end(sif_trans_t *trans)
412{
413 errno_t rc;
414
[1dcba91]415 (void) fclose(trans->sess->f);
416
417 trans->sess->f = fopen(trans->sess->fname, "w");
418 if (trans->sess->f == NULL)
419 return EIO;
[b79903b]420
421 rc = sif_export_node(trans->sess->root, trans->sess->f);
422 if (rc != EOK)
423 return rc;
424
[1dcba91]425 if (fputc('\n', trans->sess->f) == EOF)
426 return EIO;
427
428 if (fflush(trans->sess->f) == EOF)
429 return EIO;
430
[b79903b]431 free(trans);
432 return EOK;
433}
434
435/** Abort SIF transaction.
436 *
437 * @param trans Transaction
438 */
439void sif_trans_abort(sif_trans_t *trans)
440{
[7b87e1d]441 free(trans);
[b79903b]442}
443
444/** Prepend new child.
445 *
446 * Create a new child and prepend it at the beginning of children list of
447 * @a parent.
448 *
449 * @param trans Transaction
450 * @param parent Parent node
451 * @param ctype Child type
452 * @param rchild Place to store pointer to new child
453 *
454 * @return EOK on success or ENOMEM if out of memory
455 */
456errno_t sif_node_prepend_child(sif_trans_t *trans, sif_node_t *parent,
457 const char *ctype, sif_node_t **rchild)
458{
459 sif_node_t *child;
460
461 child = sif_node_new(parent);
462 if (child == NULL)
463 return ENOMEM;
464
465 child->ntype = str_dup(ctype);
466 if (child->ntype == NULL) {
467 sif_node_delete(child);
468 return ENOMEM;
469 }
470
471 list_prepend(&child->lparent, &parent->children);
472
473 *rchild = child;
474 return EOK;
475}
476
477/** Append new child.
478 *
479 * Create a new child and append it at the end of children list of @a parent.
480 *
481 * @param trans Transaction
482 * @param parent Parent node
483 * @param ctype Child type
484 * @param rchild Place to store pointer to new child
485 *
486 * @return EOK on success or ENOMEM if out of memory
487 */
488errno_t sif_node_append_child(sif_trans_t *trans, sif_node_t *parent,
489 const char *ctype, sif_node_t **rchild)
490{
491 sif_node_t *child;
492
493 child = sif_node_new(parent);
494 if (child == NULL)
495 return ENOMEM;
496
497 child->ntype = str_dup(ctype);
498 if (child->ntype == NULL) {
499 sif_node_delete(child);
500 return ENOMEM;
501 }
502
503 list_append(&child->lparent, &parent->children);
504
505 *rchild = child;
506 return EOK;
507}
508
509/** Insert new child before existing child.
510 *
511 * Create a new child and insert it before an existing child.
512 *
513 * @param trans Transaction
514 * @param sibling Sibling before which to insert
515 * @param ctype Child type
516 * @param rchild Place to store pointer to new child
517 *
518 * @return EOK on success or ENOMEM if out of memory
519 */
520errno_t sif_node_insert_before(sif_trans_t *trans, sif_node_t *sibling,
521 const char *ctype, sif_node_t **rchild)
522{
523 sif_node_t *child;
524
525 child = sif_node_new(sibling->parent);
526 if (child == NULL)
527 return ENOMEM;
528
529 child->ntype = str_dup(ctype);
530 if (child->ntype == NULL) {
531 sif_node_delete(child);
532 return ENOMEM;
533 }
534
535 list_insert_before(&child->lparent, &sibling->lparent);
536
537 *rchild = child;
538 return EOK;
539}
540
541/** Insert new child after existing child.
542 *
543 * Create a new child and insert it after an existing child.
544 *
545 * @param trans Transaction
546 * @param sibling Sibling after which to insert
547 * @param ctype Child type
548 * @param rchild Place to store pointer to new child
549 *
550 * @return EOK on success or ENOMEM if out of memory
551 */
552errno_t sif_node_insert_after(sif_trans_t *trans, sif_node_t *sibling,
553 const char *ctype, sif_node_t **rchild)
554{
555 sif_node_t *child;
556
557 child = sif_node_new(sibling->parent);
558 if (child == NULL)
559 return ENOMEM;
560
561 child->ntype = str_dup(ctype);
562 if (child->ntype == NULL) {
563 sif_node_delete(child);
564 return ENOMEM;
565 }
566
567 list_insert_after(&child->lparent, &sibling->lparent);
568
569 *rchild = child;
570 return EOK;
571}
572
573/** Destroy SIF node.
574 *
575 * This function does not return an error, but the transaction may still
576 * fail to complete.
577 *
578 * @param trans Transaction
579 * @param node Node to destroy
580 */
581void sif_node_destroy(sif_trans_t *trans, sif_node_t *node)
582{
583 list_remove(&node->lparent);
584 sif_node_delete(node);
585}
586
587/** Set node attribute.
588 *
589 * @param trans Transaction
590 * @param node SIF node
591 * @param aname Attribute name
592 * @param value Attribute value
593 *
594 * @return EOK on success, ENOMEM if out of memory
595 */
596errno_t sif_node_set_attr(sif_trans_t *trans, sif_node_t *node,
[7b87e1d]597 const char *aname, const char *avalue)
[b79903b]598{
[7b87e1d]599 odlink_t *link;
600 sif_attr_t *attr;
601 char *cvalue;
602
603 link = odict_find_eq(&node->attrs, (void *)aname, NULL);
604
605 if (link != NULL) {
606 attr = odict_get_instance(link, sif_attr_t, lattrs);
607 cvalue = str_dup(avalue);
608 if (cvalue == NULL)
609 return ENOMEM;
610
611 free(attr->avalue);
612 attr->avalue = cvalue;
613 } else {
614 attr = sif_attr_new(node);
615 if (attr == NULL)
616 return ENOMEM;
617
618 attr->aname = str_dup(aname);
619 if (attr->aname == NULL) {
620 sif_attr_delete(attr);
621 return ENOMEM;
622 }
623
624 attr->avalue = str_dup(avalue);
625 if (attr->avalue == NULL) {
626 sif_attr_delete(attr);
627 return ENOMEM;
628 }
629
630 odict_insert(&attr->lattrs, &node->attrs, NULL);
631 }
632
[b79903b]633 return EOK;
634}
635
636/** Unset node attribute.
637 *
638 * This function does not return an error, but the transaction may still
639 * fail to complete.
640 *
641 * @param trans Transaction
642 * @param node Node
643 * @param aname Attribute name
644 */
645void sif_node_unset_attr(sif_trans_t *trans, sif_node_t *node,
646 const char *aname)
647{
[7b87e1d]648 odlink_t *link;
649 sif_attr_t *attr;
650
651 link = odict_find_eq(&node->attrs, (void *)aname, NULL);
652 if (link == NULL)
653 return;
654
655 attr = odict_get_instance(link, sif_attr_t, lattrs);
656 odict_remove(link);
657 sif_attr_delete(attr);
[b79903b]658}
659
660/** Export string to file.
661 *
662 * Export string to file (the string is bracketed and escaped).
663 *
664 * @param str String
665 * @param f File
666 * @return EOK on success, EIO on I/O error
667 */
668static errno_t sif_export_string(const char *str, FILE *f)
669{
670 const char *cp;
671
672 if (fputc('[', f) == EOF)
673 return EIO;
674
675 cp = str;
676 while (*cp != '\0') {
677 if (*cp == ']' || *cp == '\\') {
678 if (fputc('\\', f) == EOF)
679 return EIO;
680 }
681 if (fputc(*cp, f) == EOF)
682 return EIO;
683 ++cp;
684 }
685
686 if (fputc(']', f) == EOF)
687 return EIO;
688
689 return EOK;
690}
691
[9c5e3a5]692/** Import string from file.
693 *
694 * Import string from file (the string in the file must be
695 * properly bracketed and escaped).
696 *
697 * @param f File
698 * @param rstr Place to store pointer to newly allocated string
699 * @return EOK on success, EIO on I/O error
700 */
701static errno_t sif_import_string(FILE *f, char **rstr)
702{
703 char *str;
[153dd3b]704 char *nstr;
[9c5e3a5]705 size_t str_size;
706 size_t sidx;
[1c398db2]707 int c;
[9c5e3a5]708 errno_t rc;
709
710 str_size = 1;
711 sidx = 0;
712 str = malloc(str_size + 1);
713 if (str == NULL)
714 return ENOMEM;
715
716 c = fgetc(f);
717 if (c != '[') {
718 rc = EIO;
719 goto error;
720 }
721
722 while (true) {
723 c = fgetc(f);
724 if (c == EOF) {
725 rc = EIO;
726 goto error;
727 }
728
729 if (c == ']')
730 break;
731
732 if (c == '\\') {
733 c = fgetc(f);
734 if (c == EOF) {
735 rc = EIO;
736 goto error;
737 }
738 }
739
740 if (sidx >= str_size) {
741 str_size *= 2;
[153dd3b]742 nstr = realloc(str, str_size + 1);
743 if (nstr == NULL) {
[9c5e3a5]744 rc = ENOMEM;
745 goto error;
746 }
[153dd3b]747
748 str = nstr;
[9c5e3a5]749 }
750
751 str[sidx++] = c;
752 }
753
754 str[sidx] = '\0';
755 *rstr = str;
756 return EOK;
757error:
758 free(str);
759 return rc;
760}
761
[7b87e1d]762/** Import SIF attribute from file.
763 *
764 * @param node Node under which attribute shou
765 * @param f File
766 * @param rattr Place to store pointer to imported SIF attribute
767 * @return EOK on success, EIO on I/O error
768 */
769static errno_t sif_import_attr(sif_node_t *node, FILE *f, sif_attr_t **rattr)
770{
771 errno_t rc;
772 char *aname = NULL;
773 char *avalue = NULL;
774 sif_attr_t *attr;
775 int c;
776
777 rc = sif_import_string(f, &aname);
778 if (rc != EOK)
779 goto error;
780
781 c = fgetc(f);
782 if (c != '=') {
783 rc = EIO;
784 goto error;
785 }
786
787 rc = sif_import_string(f, &avalue);
788 if (rc != EOK)
789 goto error;
790
791 attr = sif_attr_new(node);
792 if (attr == NULL) {
793 rc = ENOMEM;
794 goto error;
795 }
796
797 attr->aname = aname;
798 attr->avalue = avalue;
799
800 *rattr = attr;
801 return EOK;
802error:
803 if (aname != NULL)
804 free(aname);
805 if (avalue != NULL)
806 free(avalue);
807 return rc;
808}
809
810/** Export SIF attribute to file.
811 *
812 * @param attr SIF attribute
813 * @param f File
814 * @return EOK on success, EIO on I/O error
815 */
816static errno_t sif_export_attr(sif_attr_t *attr, FILE *f)
817{
818 errno_t rc;
819
820 rc = sif_export_string(attr->aname, f);
821 if (rc != EOK)
822 return rc;
823
824 if (fputc('=', f) == EOF)
825 return EIO;
826
827 rc = sif_export_string(attr->avalue, f);
828 if (rc != EOK)
829 return rc;
830
831 return EOK;
832}
833
[b79903b]834/** Export SIF node to file.
835 *
836 * @param node SIF node
837 * @param f File
838 * @return EOK on success, EIO on I/O error
839 */
840static errno_t sif_export_node(sif_node_t *node, FILE *f)
841{
842 errno_t rc;
[7b87e1d]843 sif_attr_t *attr;
[b79903b]844 sif_node_t *child;
845
846 rc = sif_export_string(node->ntype, f);
847 if (rc != EOK)
848 return rc;
849
[7b87e1d]850 /* Attributes */
851
852 if (fputc('(', f) == EOF)
853 return EIO;
854
855 attr = sif_node_first_attr(node);
856 while (attr != NULL) {
857 rc = sif_export_attr(attr, f);
858 if (rc != EOK)
859 return rc;
860
861 attr = sif_node_next_attr(attr);
862 }
863
864 if (fputc(')', f) == EOF)
865 return EIO;
866
867 /* Child nodes */
868
[b79903b]869 if (fputc('{', f) == EOF)
870 return EIO;
871
872 child = sif_node_first_child(node);
873 while (child != NULL) {
874 rc = sif_export_node(child, f);
875 if (rc != EOK)
876 return rc;
877
878 child = sif_node_next_child(child);
879 }
880
881 if (fputc('}', f) == EOF)
882 return EIO;
883
884 return EOK;
885}
886
[9c5e3a5]887/** Import SIF node from file.
888 *
889 * @param parent Parent node
890 * @param f File
891 * @param rnode Place to store pointer to imported node
892 * @return EOK on success, EIO on I/O error
893 */
894static errno_t sif_import_node(sif_node_t *parent, FILE *f, sif_node_t **rnode)
895{
896 errno_t rc;
897 sif_node_t *node = NULL;
898 sif_node_t *child;
[ee8d4d6]899 sif_attr_t *attr = NULL;
[9c5e3a5]900 char *ntype;
[1c398db2]901 int c;
[9c5e3a5]902
903 node = sif_node_new(parent);
904 if (node == NULL)
905 return ENOMEM;
906
907 rc = sif_import_string(f, &ntype);
908 if (rc != EOK)
909 goto error;
910
911 node->ntype = ntype;
912
[7b87e1d]913 /* Attributes */
914
915 c = fgetc(f);
916 if (c != '(') {
917 rc = EIO;
918 goto error;
919 }
920
921 c = fgetc(f);
922 if (c == EOF) {
923 rc = EIO;
924 goto error;
925 }
926
927 while (c != ')') {
928 ungetc(c, f);
929
930 rc = sif_import_attr(node, f, &attr);
931 if (rc != EOK)
932 goto error;
933
934 odict_insert(&attr->lattrs, &node->attrs, NULL);
935
936 c = fgetc(f);
937 if (c == EOF) {
938 rc = EIO;
939 goto error;
940 }
941 }
942
943 /* Child nodes */
944
[9c5e3a5]945 c = fgetc(f);
946 if (c != '{') {
947 rc = EIO;
948 goto error;
949 }
950
951 c = fgetc(f);
952 if (c == EOF) {
953 rc = EIO;
954 goto error;
955 }
956
957 while (c != '}') {
958 ungetc(c, f);
959
960 rc = sif_import_node(node, f, &child);
961 if (rc != EOK)
962 goto error;
963
964 list_append(&child->lparent, &node->children);
965
966 c = fgetc(f);
967 if (c == EOF) {
968 rc = EIO;
969 goto error;
970 }
971 }
972
973 *rnode = node;
974 return EOK;
975error:
976 sif_node_delete(node);
977 return rc;
978}
979
[7b87e1d]980/** Get first attribute or a node.
981 *
982 * @param node SIF node
983 * @return First attribute or @c NULL if there is none
984 */
985static sif_attr_t *sif_node_first_attr(sif_node_t *node)
986{
987 odlink_t *link;
988
989 link = odict_first(&node->attrs);
990 if (link == NULL)
991 return NULL;
992
993 return odict_get_instance(link, sif_attr_t, lattrs);
994}
995
996/** Get next attribute or a node.
997 *
998 * @param cur Current attribute
999 * @return Next attribute or @c NULL if there is none
1000 */
1001static sif_attr_t *sif_node_next_attr(sif_attr_t *cur)
1002{
1003 odlink_t *link;
1004
1005 link = odict_next(&cur->lattrs, &cur->node->attrs);
1006 if (link == NULL)
1007 return NULL;
1008
1009 return odict_get_instance(link, sif_attr_t, lattrs);
1010}
1011
1012/** Get key callback for ordered dictionary of node attributes.
1013 *
1014 * @param link Ordered dictionary link of attribute
1015 * @return Pointer to attribute name
1016 */
1017static void *sif_attr_getkey(odlink_t *link)
1018{
1019 return (void *)odict_get_instance(link, sif_attr_t, lattrs)->aname;
1020}
1021
1022/** Comparison callback for ordered dictionary of node attributes.
1023 *
1024 * @param a Name of first attribute
1025 * @param b Name of second attribute
1026 * @return Less than zero, zero or greater than zero, if a < b, a == b, a > b,
1027 * respectively.
1028 */
1029static int sif_attr_cmp(void *a, void *b)
1030{
1031 char *ca, *cb;
1032
1033 ca = (char *)a;
1034 cb = (char *)b;
1035
1036 return str_cmp(ca, cb);
1037}
1038
[b79903b]1039/** @}
1040 */
Note: See TracBrowser for help on using the repository browser.