source: mainline/uspace/lib/sif/src/sif.c@ 08e103d4

Last change on this file since 08e103d4 was 08e103d4, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 7 years ago

Use clearer naming for string length functions

This and the following commit change the names of functions, as well as
their documentation, to use unambiguous terms "bytes" and "code points"
instead of ambiguous terms "size", "length", and "characters".

  • 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_bytes;
705 size_t sidx;
706 int c;
707 errno_t rc;
708
709 str_bytes = 1;
710 sidx = 0;
711 str = malloc(str_bytes + 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_bytes) {
740 str_bytes *= 2;
741 str = realloc(str, str_bytes + 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;
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.