source: mainline/uspace/lib/sif/src/sif.c@ 7ab7075f

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 7ab7075f was 7ab7075f, checked in by Jiri Svoboda <jiri@…>, 7 years ago

Add support for 'x' fopen file mode modifier from C11.

  • Property mode set to 100644
File size: 19.9 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 root = sif_node_new(NULL);
194 if (root == NULL) {
195 rc = ENOMEM;
196 goto error;
197 }
198
199 root->ntype = str_dup("sif");
200 if (root->ntype == NULL) {
201 rc = ENOMEM;
202 goto error;
203 }
204
205 f = fopen(fname, "wx");
206 if (f == NULL) {
207 rc = EIO;
208 goto error;
209 }
210
211 sess->f = f;
212 sess->root = root;
213
214 /* Run a dummy trasaction to marshall initial repo state to file */
215 rc = sif_trans_begin(sess, &trans);
216 if (rc != EOK)
217 goto error;
218
219 rc = sif_trans_end(trans);
220 if (rc != EOK)
221 goto error;
222
223 *rsess = sess;
224 return EOK;
225error:
226 if (trans != NULL)
227 sif_trans_abort(trans);
228 sif_node_delete(root);
229 free(sess);
230 return rc;
231}
232
233/** Open an existing SIF repository.
234 *
235 * @param fname File name
236 * @param rsess Place to store pointer to new session.
237 *
238 * @return EOK on success or error code
239 */
240errno_t sif_open(const char *fname, sif_sess_t **rsess)
241{
242 sif_sess_t *sess;
243 sif_node_t *root = NULL;
244 errno_t rc;
245 FILE *f;
246
247 sess = calloc(1, sizeof(sif_sess_t));
248 if (sess == NULL)
249 return ENOMEM;
250
251 f = fopen(fname, "r+");
252 if (f == NULL) {
253 rc = EIO;
254 goto error;
255 }
256
257 rc = sif_import_node(NULL, f, &root);
258 if (rc != EOK)
259 goto error;
260
261 if (str_cmp(root->ntype, "sif") != 0) {
262 rc = EIO;
263 goto error;
264 }
265
266 sess->root = root;
267
268 sess->f = f;
269 sess->root = root;
270 *rsess = sess;
271 return EOK;
272error:
273 sif_node_delete(root);
274 free(sess);
275 return rc;
276}
277
278/** Close SIF session.
279 *
280 * @param sess SIF session
281 * @return EOK on success or error code
282 */
283errno_t sif_close(sif_sess_t *sess)
284{
285 sif_node_delete(sess->root);
286
287 if (fclose(sess->f) < 0) {
288 free(sess);
289 return EIO;
290 }
291
292 return EOK;
293}
294
295/** Return root node.
296 *
297 * @param sess SIF session
298 */
299sif_node_t *sif_get_root(sif_sess_t *sess)
300{
301 return sess->root;
302}
303
304/** Get first child of a node.
305 *
306 * @param parent Parent node
307 * @return First child node or @c NULL if @a parent has no children
308 */
309sif_node_t *sif_node_first_child(sif_node_t *parent)
310{
311 link_t *link;
312
313 link = list_first(&parent->children);
314 if (link == NULL)
315 return NULL;
316
317 return list_get_instance(link, sif_node_t, lparent);
318}
319
320/** Get next child of a node.
321 *
322 * @param current Current node
323 * @return Next child (i.e. next sibling of @a current)
324 */
325sif_node_t *sif_node_next_child(sif_node_t *current)
326{
327 link_t *link;
328
329 link = list_next(&current->lparent, &current->parent->children);
330 if (link == NULL)
331 return NULL;
332
333 return list_get_instance(link, sif_node_t, lparent);
334}
335
336/** Get node type.
337 *
338 * @param node SIF node
339 * @return Pointer to string, valid until next modification.
340 */
341const char *sif_node_get_type(sif_node_t *node)
342{
343 return node->ntype;
344}
345
346/** Get node attribute.
347 *
348 * @param node SIF node
349 * @param aname Attribute name
350 *
351 * @return Attribute value or @c NULL if attribute is not set
352 */
353const char *sif_node_get_attr(sif_node_t *node, const char *aname)
354{
355 odlink_t *link;
356 sif_attr_t *attr;
357
358 link = odict_find_eq(&node->attrs, (void *)aname, NULL);
359 if (link == NULL)
360 return NULL;
361
362 attr = odict_get_instance(link, sif_attr_t, lattrs);
363 return attr->avalue;
364}
365
366/** Begin SIF transaction.
367 *
368 * @param trans Transaction
369 * @return EOK on success or error code
370 */
371errno_t sif_trans_begin(sif_sess_t *sess, sif_trans_t **rtrans)
372{
373 sif_trans_t *trans;
374
375 trans = calloc(1, sizeof(sif_trans_t));
376 if (trans == NULL)
377 return ENOMEM;
378
379 trans->sess = sess;
380 *rtrans = trans;
381 return EOK;
382}
383
384/** Commit SIF transaction.
385 *
386 * Commit and free the transaction. If an error is returned, that means
387 * the transaction has not been freed (and sif_trans_abort() must be used).
388 *
389 * @param trans Transaction
390 * @return EOK on success or error code
391 */
392errno_t sif_trans_end(sif_trans_t *trans)
393{
394 errno_t rc;
395
396 rewind(trans->sess->f);
397
398 rc = sif_export_node(trans->sess->root, trans->sess->f);
399 if (rc != EOK)
400 return rc;
401
402 free(trans);
403 return EOK;
404}
405
406/** Abort SIF transaction.
407 *
408 * @param trans Transaction
409 */
410void sif_trans_abort(sif_trans_t *trans)
411{
412 free(trans);
413}
414
415/** Prepend new child.
416 *
417 * Create a new child and prepend it at the beginning of children list of
418 * @a parent.
419 *
420 * @param trans Transaction
421 * @param parent Parent node
422 * @param ctype Child type
423 * @param rchild Place to store pointer to new child
424 *
425 * @return EOK on success or ENOMEM if out of memory
426 */
427errno_t sif_node_prepend_child(sif_trans_t *trans, sif_node_t *parent,
428 const char *ctype, sif_node_t **rchild)
429{
430 sif_node_t *child;
431
432 child = sif_node_new(parent);
433 if (child == NULL)
434 return ENOMEM;
435
436 child->ntype = str_dup(ctype);
437 if (child->ntype == NULL) {
438 sif_node_delete(child);
439 return ENOMEM;
440 }
441
442 list_prepend(&child->lparent, &parent->children);
443
444 *rchild = child;
445 return EOK;
446}
447
448/** Append new child.
449 *
450 * Create a new child and append it at the end of children list of @a parent.
451 *
452 * @param trans Transaction
453 * @param parent Parent node
454 * @param ctype Child type
455 * @param rchild Place to store pointer to new child
456 *
457 * @return EOK on success or ENOMEM if out of memory
458 */
459errno_t sif_node_append_child(sif_trans_t *trans, sif_node_t *parent,
460 const char *ctype, sif_node_t **rchild)
461{
462 sif_node_t *child;
463
464 child = sif_node_new(parent);
465 if (child == NULL)
466 return ENOMEM;
467
468 child->ntype = str_dup(ctype);
469 if (child->ntype == NULL) {
470 sif_node_delete(child);
471 return ENOMEM;
472 }
473
474 list_append(&child->lparent, &parent->children);
475
476 *rchild = child;
477 return EOK;
478}
479
480/** Insert new child before existing child.
481 *
482 * Create a new child and insert it before an existing child.
483 *
484 * @param trans Transaction
485 * @param sibling Sibling before which to insert
486 * @param ctype Child type
487 * @param rchild Place to store pointer to new child
488 *
489 * @return EOK on success or ENOMEM if out of memory
490 */
491errno_t sif_node_insert_before(sif_trans_t *trans, sif_node_t *sibling,
492 const char *ctype, sif_node_t **rchild)
493{
494 sif_node_t *child;
495
496 child = sif_node_new(sibling->parent);
497 if (child == NULL)
498 return ENOMEM;
499
500 child->ntype = str_dup(ctype);
501 if (child->ntype == NULL) {
502 sif_node_delete(child);
503 return ENOMEM;
504 }
505
506 list_insert_before(&child->lparent, &sibling->lparent);
507
508 *rchild = child;
509 return EOK;
510}
511
512/** Insert new child after existing child.
513 *
514 * Create a new child and insert it after an existing child.
515 *
516 * @param trans Transaction
517 * @param sibling Sibling after which to insert
518 * @param ctype Child type
519 * @param rchild Place to store pointer to new child
520 *
521 * @return EOK on success or ENOMEM if out of memory
522 */
523errno_t sif_node_insert_after(sif_trans_t *trans, sif_node_t *sibling,
524 const char *ctype, sif_node_t **rchild)
525{
526 sif_node_t *child;
527
528 child = sif_node_new(sibling->parent);
529 if (child == NULL)
530 return ENOMEM;
531
532 child->ntype = str_dup(ctype);
533 if (child->ntype == NULL) {
534 sif_node_delete(child);
535 return ENOMEM;
536 }
537
538 list_insert_after(&child->lparent, &sibling->lparent);
539
540 *rchild = child;
541 return EOK;
542}
543
544/** Destroy SIF node.
545 *
546 * This function does not return an error, but the transaction may still
547 * fail to complete.
548 *
549 * @param trans Transaction
550 * @param node Node to destroy
551 */
552void sif_node_destroy(sif_trans_t *trans, sif_node_t *node)
553{
554 list_remove(&node->lparent);
555 sif_node_delete(node);
556}
557
558/** Set node attribute.
559 *
560 * @param trans Transaction
561 * @param node SIF node
562 * @param aname Attribute name
563 * @param value Attribute value
564 *
565 * @return EOK on success, ENOMEM if out of memory
566 */
567errno_t sif_node_set_attr(sif_trans_t *trans, sif_node_t *node,
568 const char *aname, const char *avalue)
569{
570 odlink_t *link;
571 sif_attr_t *attr;
572 char *cvalue;
573
574 link = odict_find_eq(&node->attrs, (void *)aname, NULL);
575
576 if (link != NULL) {
577 attr = odict_get_instance(link, sif_attr_t, lattrs);
578 cvalue = str_dup(avalue);
579 if (cvalue == NULL)
580 return ENOMEM;
581
582 free(attr->avalue);
583 attr->avalue = cvalue;
584 } else {
585 attr = sif_attr_new(node);
586 if (attr == NULL)
587 return ENOMEM;
588
589 attr->aname = str_dup(aname);
590 if (attr->aname == NULL) {
591 sif_attr_delete(attr);
592 return ENOMEM;
593 }
594
595 attr->avalue = str_dup(avalue);
596 if (attr->avalue == NULL) {
597 sif_attr_delete(attr);
598 return ENOMEM;
599 }
600
601 odict_insert(&attr->lattrs, &node->attrs, NULL);
602 }
603
604 return EOK;
605}
606
607/** Unset node attribute.
608 *
609 * This function does not return an error, but the transaction may still
610 * fail to complete.
611 *
612 * @param trans Transaction
613 * @param node Node
614 * @param aname Attribute name
615 */
616void sif_node_unset_attr(sif_trans_t *trans, sif_node_t *node,
617 const char *aname)
618{
619 odlink_t *link;
620 sif_attr_t *attr;
621
622 link = odict_find_eq(&node->attrs, (void *)aname, NULL);
623 if (link == NULL)
624 return;
625
626 attr = odict_get_instance(link, sif_attr_t, lattrs);
627 odict_remove(link);
628 sif_attr_delete(attr);
629}
630
631/** Export string to file.
632 *
633 * Export string to file (the string is bracketed and escaped).
634 *
635 * @param str String
636 * @param f File
637 * @return EOK on success, EIO on I/O error
638 */
639static errno_t sif_export_string(const char *str, FILE *f)
640{
641 const char *cp;
642
643 if (fputc('[', f) == EOF)
644 return EIO;
645
646 cp = str;
647 while (*cp != '\0') {
648 if (*cp == ']' || *cp == '\\') {
649 if (fputc('\\', f) == EOF)
650 return EIO;
651 }
652 if (fputc(*cp, f) == EOF)
653 return EIO;
654 ++cp;
655 }
656
657 if (fputc(']', f) == EOF)
658 return EIO;
659
660 return EOK;
661}
662
663/** Import string from file.
664 *
665 * Import string from file (the string in the file must be
666 * properly bracketed and escaped).
667 *
668 * @param f File
669 * @param rstr Place to store pointer to newly allocated string
670 * @return EOK on success, EIO on I/O error
671 */
672static errno_t sif_import_string(FILE *f, char **rstr)
673{
674 char *str;
675 size_t str_size;
676 size_t sidx;
677 int c;
678 errno_t rc;
679
680 str_size = 1;
681 sidx = 0;
682 str = malloc(str_size + 1);
683 if (str == NULL)
684 return ENOMEM;
685
686 c = fgetc(f);
687 if (c != '[') {
688 rc = EIO;
689 goto error;
690 }
691
692 while (true) {
693 c = fgetc(f);
694 if (c == EOF) {
695 rc = EIO;
696 goto error;
697 }
698
699 if (c == ']')
700 break;
701
702 if (c == '\\') {
703 c = fgetc(f);
704 if (c == EOF) {
705 rc = EIO;
706 goto error;
707 }
708 }
709
710 if (sidx >= str_size) {
711 str_size *= 2;
712 str = realloc(str, str_size + 1);
713 if (str == NULL) {
714 rc = ENOMEM;
715 goto error;
716 }
717 }
718
719 str[sidx++] = c;
720 }
721
722 str[sidx] = '\0';
723 *rstr = str;
724 return EOK;
725error:
726 free(str);
727 return rc;
728}
729
730/** Import SIF attribute from file.
731 *
732 * @param node Node under which attribute shou
733 * @param f File
734 * @param rattr Place to store pointer to imported SIF attribute
735 * @return EOK on success, EIO on I/O error
736 */
737static errno_t sif_import_attr(sif_node_t *node, FILE *f, sif_attr_t **rattr)
738{
739 errno_t rc;
740 char *aname = NULL;
741 char *avalue = NULL;
742 sif_attr_t *attr;
743 int c;
744
745 rc = sif_import_string(f, &aname);
746 if (rc != EOK)
747 goto error;
748
749 c = fgetc(f);
750 if (c != '=') {
751 rc = EIO;
752 goto error;
753 }
754
755 rc = sif_import_string(f, &avalue);
756 if (rc != EOK)
757 goto error;
758
759 attr = sif_attr_new(node);
760 if (attr == NULL) {
761 rc = ENOMEM;
762 goto error;
763 }
764
765 attr->aname = aname;
766 attr->avalue = avalue;
767
768 *rattr = attr;
769 return EOK;
770error:
771 if (aname != NULL)
772 free(aname);
773 if (avalue != NULL)
774 free(avalue);
775 return rc;
776}
777
778
779/** Export SIF attribute to file.
780 *
781 * @param attr SIF attribute
782 * @param f File
783 * @return EOK on success, EIO on I/O error
784 */
785static errno_t sif_export_attr(sif_attr_t *attr, FILE *f)
786{
787 errno_t rc;
788
789 rc = sif_export_string(attr->aname, f);
790 if (rc != EOK)
791 return rc;
792
793 if (fputc('=', f) == EOF)
794 return EIO;
795
796 rc = sif_export_string(attr->avalue, f);
797 if (rc != EOK)
798 return rc;
799
800 return EOK;
801}
802
803/** Export SIF node to file.
804 *
805 * @param node SIF node
806 * @param f File
807 * @return EOK on success, EIO on I/O error
808 */
809static errno_t sif_export_node(sif_node_t *node, FILE *f)
810{
811 errno_t rc;
812 sif_attr_t *attr;
813 sif_node_t *child;
814
815 rc = sif_export_string(node->ntype, f);
816 if (rc != EOK)
817 return rc;
818
819 /* Attributes */
820
821 if (fputc('(', f) == EOF)
822 return EIO;
823
824 attr = sif_node_first_attr(node);
825 while (attr != NULL) {
826 rc = sif_export_attr(attr, f);
827 if (rc != EOK)
828 return rc;
829
830 attr = sif_node_next_attr(attr);
831 }
832
833 if (fputc(')', f) == EOF)
834 return EIO;
835
836 /* Child nodes */
837
838 if (fputc('{', f) == EOF)
839 return EIO;
840
841 child = sif_node_first_child(node);
842 while (child != NULL) {
843 rc = sif_export_node(child, f);
844 if (rc != EOK)
845 return rc;
846
847 child = sif_node_next_child(child);
848 }
849
850 if (fputc('}', f) == EOF)
851 return EIO;
852
853 return EOK;
854}
855
856/** Import SIF node from file.
857 *
858 * @param parent Parent node
859 * @param f File
860 * @param rnode Place to store pointer to imported node
861 * @return EOK on success, EIO on I/O error
862 */
863static errno_t sif_import_node(sif_node_t *parent, FILE *f, sif_node_t **rnode)
864{
865 errno_t rc;
866 sif_node_t *node = NULL;
867 sif_node_t *child;
868 sif_attr_t *attr;
869 char *ntype;
870 int c;
871
872 node = sif_node_new(parent);
873 if (node == NULL)
874 return ENOMEM;
875
876 rc = sif_import_string(f, &ntype);
877 if (rc != EOK)
878 goto error;
879
880 node->ntype = ntype;
881
882 /* Attributes */
883
884 c = fgetc(f);
885 if (c != '(') {
886 rc = EIO;
887 goto error;
888 }
889
890 c = fgetc(f);
891 if (c == EOF) {
892 rc = EIO;
893 goto error;
894 }
895
896 while (c != ')') {
897 ungetc(c, f);
898
899 rc = sif_import_attr(node, f, &attr);
900 if (rc != EOK)
901 goto error;
902
903 odict_insert(&attr->lattrs, &node->attrs, NULL);
904
905 c = fgetc(f);
906 if (c == EOF) {
907 rc = EIO;
908 goto error;
909 }
910 }
911
912 /* Child nodes */
913
914 c = fgetc(f);
915 if (c != '{') {
916 rc = EIO;
917 goto error;
918 }
919
920 c = fgetc(f);
921 if (c == EOF) {
922 rc = EIO;
923 goto error;
924 }
925
926 while (c != '}') {
927 ungetc(c, f);
928
929 rc = sif_import_node(node, f, &child);
930 if (rc != EOK)
931 goto error;
932
933 list_append(&child->lparent, &node->children);
934
935 c = fgetc(f);
936 if (c == EOF) {
937 rc = EIO;
938 goto error;
939 }
940 }
941
942 *rnode = node;
943 return EOK;
944error:
945 sif_node_delete(node);
946 return rc;
947}
948
949/** Get first attribute or a node.
950 *
951 * @param node SIF node
952 * @return First attribute or @c NULL if there is none
953 */
954static sif_attr_t *sif_node_first_attr(sif_node_t *node)
955{
956 odlink_t *link;
957
958 link = odict_first(&node->attrs);
959 if (link == NULL)
960 return NULL;
961
962 return odict_get_instance(link, sif_attr_t, lattrs);
963}
964
965/** Get next attribute or a node.
966 *
967 * @param cur Current attribute
968 * @return Next attribute or @c NULL if there is none
969 */
970static sif_attr_t *sif_node_next_attr(sif_attr_t *cur)
971{
972 odlink_t *link;
973
974 link = odict_next(&cur->lattrs, &cur->node->attrs);
975 if (link == NULL)
976 return NULL;
977
978 return odict_get_instance(link, sif_attr_t, lattrs);
979}
980
981/** Get key callback for ordered dictionary of node attributes.
982 *
983 * @param link Ordered dictionary link of attribute
984 * @return Pointer to attribute name
985 */
986static void *sif_attr_getkey(odlink_t *link)
987{
988 return (void *)odict_get_instance(link, sif_attr_t, lattrs)->aname;
989}
990
991/** Comparison callback for ordered dictionary of node attributes.
992 *
993 * @param a Name of first attribute
994 * @param b Name of second attribute
995 * @return Less than zero, zero or greater than zero, if a < b, a == b, a > b,
996 * respectively.
997 */
998static int sif_attr_cmp(void *a, void *b)
999{
1000 char *ca, *cb;
1001
1002 ca = (char *)a;
1003 cb = (char *)b;
1004
1005 return str_cmp(ca, cb);
1006}
1007
1008/** @}
1009 */
Note: See TracBrowser for help on using the repository browser.