source: mainline/uspace/lib/sif/src/sif.c@ 1e4a937

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 1e4a937 was ee8d4d6, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 6 years ago

Fix build with -O1

  • Property mode set to 100644
File size: 20.4 KB
Line 
1/*
2 * Copyright (c) 2018 Jiri Svoboda
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
62#include <adt/list.h>
63#include <adt/odict.h>
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 *);
72static errno_t sif_import_node(sif_node_t *, FILE *, sif_node_t **);
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 *);
78
79/** Create new SIF node.
80 *
81 * @param parent Parent node
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;
93 odict_initialize(&node->attrs, sif_attr_getkey, sif_attr_cmp);
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.
102 * This will also delete any attributes or child nodes.
103 *
104 * @param node Node
105 */
106static void sif_node_delete(sif_node_t *node)
107{
108 sif_attr_t *attr;
109 sif_node_t *child;
110
111 if (node == NULL)
112 return;
113
114 assert(!link_used(&node->lparent));
115
116 if (node->ntype != NULL)
117 free(node->ntype);
118
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
133 free(node);
134}
135
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
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;
185 sif_trans_t *trans = NULL;
186 errno_t rc;
187 FILE *f;
188
189 sess = calloc(1, sizeof(sif_sess_t));
190 if (sess == NULL)
191 return ENOMEM;
192
193 sess->fname = str_dup(fname);
194 if (sess->fname == NULL) {
195 rc = ENOMEM;
196 goto error;
197 }
198
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
211 f = fopen(fname, "wx");
212 if (f == NULL) {
213 rc = EIO;
214 goto error;
215 }
216
217 sess->f = f;
218 sess->root = root;
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
229 *rsess = sess;
230 return EOK;
231error:
232 if (trans != NULL)
233 sif_trans_abort(trans);
234 sif_node_delete(root);
235 if (sess->fname != NULL)
236 free(sess->fname);
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
259 sess->fname = str_dup(fname);
260 if (sess->fname == NULL) {
261 rc = ENOMEM;
262 goto error;
263 }
264
265 f = fopen(fname, "r");
266 if (f == NULL) {
267 rc = EIO;
268 goto error;
269 }
270
271 rc = sif_import_node(NULL, f, &root);
272 if (rc != EOK)
273 goto error;
274
275 if (str_cmp(root->ntype, "sif") != 0) {
276 rc = EIO;
277 goto error;
278 }
279
280 sess->root = root;
281
282 sess->f = f;
283 sess->root = root;
284 *rsess = sess;
285 return EOK;
286error:
287 sif_node_delete(root);
288 if (sess->fname != NULL)
289 free(sess->fname);
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
308 if (sess->fname != NULL)
309 free(sess->fname);
310 free(sess);
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{
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;
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.
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).
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
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;
420
421 rc = sif_export_node(trans->sess->root, trans->sess->f);
422 if (rc != EOK)
423 return rc;
424
425 if (fputc('\n', trans->sess->f) == EOF)
426 return EIO;
427
428 if (fflush(trans->sess->f) == EOF)
429 return EIO;
430
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{
441 free(trans);
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,
597 const char *aname, const char *avalue)
598{
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
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{
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);
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
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;
704 size_t str_size;
705 size_t sidx;
706 int c;
707 errno_t rc;
708
709 str_size = 1;
710 sidx = 0;
711 str = malloc(str_size + 1);
712 if (str == NULL)
713 return ENOMEM;
714
715 c = fgetc(f);
716 if (c != '[') {
717 rc = EIO;
718 goto error;
719 }
720
721 while (true) {
722 c = fgetc(f);
723 if (c == EOF) {
724 rc = EIO;
725 goto error;
726 }
727
728 if (c == ']')
729 break;
730
731 if (c == '\\') {
732 c = fgetc(f);
733 if (c == EOF) {
734 rc = EIO;
735 goto error;
736 }
737 }
738
739 if (sidx >= str_size) {
740 str_size *= 2;
741 str = realloc(str, str_size + 1);
742 if (str == NULL) {
743 rc = ENOMEM;
744 goto error;
745 }
746 }
747
748 str[sidx++] = c;
749 }
750
751 str[sidx] = '\0';
752 *rstr = str;
753 return EOK;
754error:
755 free(str);
756 return rc;
757}
758
759/** Import SIF attribute from file.
760 *
761 * @param node Node under which attribute shou
762 * @param f File
763 * @param rattr Place to store pointer to imported SIF attribute
764 * @return EOK on success, EIO on I/O error
765 */
766static errno_t sif_import_attr(sif_node_t *node, FILE *f, sif_attr_t **rattr)
767{
768 errno_t rc;
769 char *aname = NULL;
770 char *avalue = NULL;
771 sif_attr_t *attr;
772 int c;
773
774 rc = sif_import_string(f, &aname);
775 if (rc != EOK)
776 goto error;
777
778 c = fgetc(f);
779 if (c != '=') {
780 rc = EIO;
781 goto error;
782 }
783
784 rc = sif_import_string(f, &avalue);
785 if (rc != EOK)
786 goto error;
787
788 attr = sif_attr_new(node);
789 if (attr == NULL) {
790 rc = ENOMEM;
791 goto error;
792 }
793
794 attr->aname = aname;
795 attr->avalue = avalue;
796
797 *rattr = attr;
798 return EOK;
799error:
800 if (aname != NULL)
801 free(aname);
802 if (avalue != NULL)
803 free(avalue);
804 return rc;
805}
806
807/** Export SIF attribute to file.
808 *
809 * @param attr SIF attribute
810 * @param f File
811 * @return EOK on success, EIO on I/O error
812 */
813static errno_t sif_export_attr(sif_attr_t *attr, FILE *f)
814{
815 errno_t rc;
816
817 rc = sif_export_string(attr->aname, f);
818 if (rc != EOK)
819 return rc;
820
821 if (fputc('=', f) == EOF)
822 return EIO;
823
824 rc = sif_export_string(attr->avalue, f);
825 if (rc != EOK)
826 return rc;
827
828 return EOK;
829}
830
831/** Export SIF node to file.
832 *
833 * @param node SIF node
834 * @param f File
835 * @return EOK on success, EIO on I/O error
836 */
837static errno_t sif_export_node(sif_node_t *node, FILE *f)
838{
839 errno_t rc;
840 sif_attr_t *attr;
841 sif_node_t *child;
842
843 rc = sif_export_string(node->ntype, f);
844 if (rc != EOK)
845 return rc;
846
847 /* Attributes */
848
849 if (fputc('(', f) == EOF)
850 return EIO;
851
852 attr = sif_node_first_attr(node);
853 while (attr != NULL) {
854 rc = sif_export_attr(attr, f);
855 if (rc != EOK)
856 return rc;
857
858 attr = sif_node_next_attr(attr);
859 }
860
861 if (fputc(')', f) == EOF)
862 return EIO;
863
864 /* Child nodes */
865
866 if (fputc('{', f) == EOF)
867 return EIO;
868
869 child = sif_node_first_child(node);
870 while (child != NULL) {
871 rc = sif_export_node(child, f);
872 if (rc != EOK)
873 return rc;
874
875 child = sif_node_next_child(child);
876 }
877
878 if (fputc('}', f) == EOF)
879 return EIO;
880
881 return EOK;
882}
883
884/** Import SIF node from file.
885 *
886 * @param parent Parent node
887 * @param f File
888 * @param rnode Place to store pointer to imported node
889 * @return EOK on success, EIO on I/O error
890 */
891static errno_t sif_import_node(sif_node_t *parent, FILE *f, sif_node_t **rnode)
892{
893 errno_t rc;
894 sif_node_t *node = NULL;
895 sif_node_t *child;
896 sif_attr_t *attr = NULL;
897 char *ntype;
898 int c;
899
900 node = sif_node_new(parent);
901 if (node == NULL)
902 return ENOMEM;
903
904 rc = sif_import_string(f, &ntype);
905 if (rc != EOK)
906 goto error;
907
908 node->ntype = ntype;
909
910 /* Attributes */
911
912 c = fgetc(f);
913 if (c != '(') {
914 rc = EIO;
915 goto error;
916 }
917
918 c = fgetc(f);
919 if (c == EOF) {
920 rc = EIO;
921 goto error;
922 }
923
924 while (c != ')') {
925 ungetc(c, f);
926
927 rc = sif_import_attr(node, f, &attr);
928 if (rc != EOK)
929 goto error;
930
931 odict_insert(&attr->lattrs, &node->attrs, NULL);
932
933 c = fgetc(f);
934 if (c == EOF) {
935 rc = EIO;
936 goto error;
937 }
938 }
939
940 /* Child nodes */
941
942 c = fgetc(f);
943 if (c != '{') {
944 rc = EIO;
945 goto error;
946 }
947
948 c = fgetc(f);
949 if (c == EOF) {
950 rc = EIO;
951 goto error;
952 }
953
954 while (c != '}') {
955 ungetc(c, f);
956
957 rc = sif_import_node(node, f, &child);
958 if (rc != EOK)
959 goto error;
960
961 list_append(&child->lparent, &node->children);
962
963 c = fgetc(f);
964 if (c == EOF) {
965 rc = EIO;
966 goto error;
967 }
968 }
969
970 *rnode = node;
971 return EOK;
972error:
973 sif_node_delete(node);
974 return rc;
975}
976
977/** Get first attribute or a node.
978 *
979 * @param node SIF node
980 * @return First attribute or @c NULL if there is none
981 */
982static sif_attr_t *sif_node_first_attr(sif_node_t *node)
983{
984 odlink_t *link;
985
986 link = odict_first(&node->attrs);
987 if (link == NULL)
988 return NULL;
989
990 return odict_get_instance(link, sif_attr_t, lattrs);
991}
992
993/** Get next attribute or a node.
994 *
995 * @param cur Current attribute
996 * @return Next attribute or @c NULL if there is none
997 */
998static sif_attr_t *sif_node_next_attr(sif_attr_t *cur)
999{
1000 odlink_t *link;
1001
1002 link = odict_next(&cur->lattrs, &cur->node->attrs);
1003 if (link == NULL)
1004 return NULL;
1005
1006 return odict_get_instance(link, sif_attr_t, lattrs);
1007}
1008
1009/** Get key callback for ordered dictionary of node attributes.
1010 *
1011 * @param link Ordered dictionary link of attribute
1012 * @return Pointer to attribute name
1013 */
1014static void *sif_attr_getkey(odlink_t *link)
1015{
1016 return (void *)odict_get_instance(link, sif_attr_t, lattrs)->aname;
1017}
1018
1019/** Comparison callback for ordered dictionary of node attributes.
1020 *
1021 * @param a Name of first attribute
1022 * @param b Name of second attribute
1023 * @return Less than zero, zero or greater than zero, if a < b, a == b, a > b,
1024 * respectively.
1025 */
1026static int sif_attr_cmp(void *a, void *b)
1027{
1028 char *ca, *cb;
1029
1030 ca = (char *)a;
1031 cb = (char *)b;
1032
1033 return str_cmp(ca, cb);
1034}
1035
1036/** @}
1037 */
Note: See TracBrowser for help on using the repository browser.