source: mainline/uspace/lib/sif/src/sif.c@ ac9b4f2

Last change on this file since ac9b4f2 was ac9b4f2, checked in by Jiri Svoboda <jiri@…>, 16 months ago

Change SIF storage format to be more XML-like

While slightly more difficult to parse than previously, this potentially
allows the use of standard XML utilities to more or less process SIF files.
Also, insert newline after every tag to be more readable / more
accessible to diff/patch.

  • Property mode set to 100644
File size: 20.6 KB
Line 
1/*
2 * Copyright (c) 2024 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 is
36 * structured (and hence extensible).
37 *
38 * SIF is meant to be used as the basis for the storage backend used to
39 * maintain application or configuration data. SIF is *not* a (relational)
40 * database (not even close). The structure of a SIF repository is quite
41 * similar to an XML document that contains just tags with attributes
42 * (but no text).
43 */
44
45#include <adt/list.h>
46#include <adt/odict.h>
47#include <ctype.h>
48#include <errno.h>
49#include <stdbool.h>
50#include <stdio.h>
51#include <stdlib.h>
52#include <str.h>
53#include "../include/sif.h"
54#include "../private/sif.h"
55
56static errno_t sif_export_node(sif_node_t *, FILE *);
57static errno_t sif_import_node(sif_node_t *, FILE *, sif_node_t **, bool *);
58static sif_attr_t *sif_node_first_attr(sif_node_t *);
59static sif_attr_t *sif_node_next_attr(sif_attr_t *);
60static void sif_attr_delete(sif_attr_t *);
61static void *sif_attr_getkey(odlink_t *);
62static int sif_attr_cmp(void *, void *);
63
64/** Determine if character can start a name.
65 *
66 * @param c Character
67 * @return @c true iff the character can start a name
68 */
69static bool sif_is_name_start_char(char c)
70{
71 return isalpha(c) || c == '_';
72}
73
74/** Determine if character can continue a name.
75 *
76 * @param c Character
77 * @return @c true iff the character can continue a name
78 */
79static bool sif_is_name_char(char c)
80{
81 return isalnum(c) || c == '-' || c == '.';
82}
83
84/** Create new SIF node.
85 *
86 * @param parent Parent node
87 * @return Pointer to new node on success or @c NULL if out of memory
88 */
89static sif_node_t *sif_node_new(sif_node_t *parent)
90{
91 sif_node_t *node;
92
93 node = calloc(1, sizeof(sif_node_t));
94 if (node == NULL)
95 return NULL;
96
97 node->parent = parent;
98 odict_initialize(&node->attrs, sif_attr_getkey, sif_attr_cmp);
99 list_initialize(&node->children);
100
101 return node;
102}
103
104/** Delete SIF node.
105 *
106 * Delete a SIF node that has been already unlinked from the tree.
107 * This will also delete any attributes or child nodes.
108 *
109 * @param node Node
110 */
111static void sif_node_delete(sif_node_t *node)
112{
113 sif_attr_t *attr;
114 sif_node_t *child;
115
116 if (node == NULL)
117 return;
118
119 assert(!link_used(&node->lparent));
120
121 if (node->ntype != NULL)
122 free(node->ntype);
123
124 attr = sif_node_first_attr(node);
125 while (attr != NULL) {
126 odict_remove(&attr->lattrs);
127 sif_attr_delete(attr);
128 attr = sif_node_first_attr(node);
129 }
130
131 child = sif_node_first_child(node);
132 while (child != NULL) {
133 list_remove(&child->lparent);
134 sif_node_delete(child);
135 child = sif_node_first_child(node);
136 }
137
138 free(node);
139}
140
141/** Create new SIF attribute.
142 *
143 * @param node Containing node
144 * @return Pointer to new node on success or @c NULL if out of memory
145 */
146static sif_attr_t *sif_attr_new(sif_node_t *node)
147{
148 sif_attr_t *attr;
149
150 attr = calloc(1, sizeof(sif_attr_t));
151 if (attr == NULL)
152 return NULL;
153
154 attr->node = node;
155 return attr;
156}
157
158/** Delete SIF attribute.
159 *
160 * Delete a SIF attribute that has been already unlinked from is node.
161 *
162 * @param attr Attribute
163 */
164static void sif_attr_delete(sif_attr_t *attr)
165{
166 if (attr == NULL)
167 return;
168
169 assert(!odlink_used(&attr->lattrs));
170
171 if (attr->aname != NULL)
172 free(attr->aname);
173 if (attr->avalue != NULL)
174 free(attr->avalue);
175
176 free(attr);
177}
178
179/** Create SIF document.
180 *
181 * @param rdoc Place to store pointer to new document.
182 *
183 * @return EOK on success or error code
184 */
185errno_t sif_new(sif_doc_t **rdoc)
186{
187 sif_doc_t *doc;
188 sif_node_t *root = NULL;
189 errno_t rc;
190
191 doc = calloc(1, sizeof(sif_doc_t));
192 if (doc == NULL)
193 return ENOMEM;
194
195 root = sif_node_new(NULL);
196 if (root == NULL) {
197 rc = ENOMEM;
198 goto error;
199 }
200
201 root->ntype = str_dup("sif");
202 if (root->ntype == NULL) {
203 rc = ENOMEM;
204 goto error;
205 }
206
207 doc->root = root;
208
209 *rdoc = doc;
210 return EOK;
211error:
212 sif_node_delete(root);
213 free(doc);
214 return rc;
215}
216
217/** Load SIF document.
218 *
219 * @param fname File name
220 * @param rdoc Place to store pointer to new document.
221 *
222 * @return EOK on success or error code
223 */
224errno_t sif_load(const char *fname, sif_doc_t **rdoc)
225{
226 sif_doc_t *doc;
227 sif_node_t *root = NULL;
228 errno_t rc;
229 bool endtag;
230 FILE *f;
231
232 doc = calloc(1, sizeof(sif_doc_t));
233 if (doc == NULL)
234 return ENOMEM;
235
236 doc->fname = str_dup(fname);
237 if (doc->fname == NULL) {
238 rc = ENOMEM;
239 goto error;
240 }
241
242 f = fopen(fname, "r");
243 if (f == NULL) {
244 rc = EIO;
245 goto error;
246 }
247
248 rc = sif_import_node(NULL, f, &root, &endtag);
249 if (rc != EOK || endtag == true)
250 goto error;
251
252 if (str_cmp(root->ntype, "sif") != 0) {
253 rc = EIO;
254 goto error;
255 }
256
257 doc->root = root;
258 *rdoc = doc;
259 return EOK;
260error:
261 sif_node_delete(root);
262 free(doc);
263 return rc;
264}
265
266/** Delete SIF document.
267 *
268 * @param doc SIF document
269 * @return EOK on success or error code
270 */
271void sif_delete(sif_doc_t *doc)
272{
273 sif_node_delete(doc->root);
274 free(doc);
275}
276
277/** Return root node.
278 *
279 * @param doc SIF document
280 */
281sif_node_t *sif_get_root(sif_doc_t *doc)
282{
283 return doc->root;
284}
285
286/** Get first child of a node.
287 *
288 * @param parent Parent node
289 * @return First child node or @c NULL if @a parent has no children
290 */
291sif_node_t *sif_node_first_child(sif_node_t *parent)
292{
293 link_t *link;
294
295 link = list_first(&parent->children);
296 if (link == NULL)
297 return NULL;
298
299 return list_get_instance(link, sif_node_t, lparent);
300}
301
302/** Get next child of a node.
303 *
304 * @param current Current node
305 * @return Next child (i.e. next sibling of @a current)
306 */
307sif_node_t *sif_node_next_child(sif_node_t *current)
308{
309 link_t *link;
310
311 link = list_next(&current->lparent, &current->parent->children);
312 if (link == NULL)
313 return NULL;
314
315 return list_get_instance(link, sif_node_t, lparent);
316}
317
318/** Get node type.
319 *
320 * @param node SIF node
321 * @return Pointer to string, valid until next modification.
322 */
323const char *sif_node_get_type(sif_node_t *node)
324{
325 return node->ntype;
326}
327
328/** Get node attribute.
329 *
330 * @param node SIF node
331 * @param aname Attribute name
332 *
333 * @return Attribute value or @c NULL if attribute is not set
334 */
335const char *sif_node_get_attr(sif_node_t *node, const char *aname)
336{
337 odlink_t *link;
338 sif_attr_t *attr;
339
340 link = odict_find_eq(&node->attrs, (void *)aname, NULL);
341 if (link == NULL)
342 return NULL;
343
344 attr = odict_get_instance(link, sif_attr_t, lattrs);
345 return attr->avalue;
346}
347
348/** Save SIF document to file.
349 * *
350 * @param doc SIF document
351 * @param fname File name
352 * @return EOK on success or error code
353 */
354errno_t sif_save(sif_doc_t *doc, const char *fname)
355{
356 FILE *f = NULL;
357 errno_t rc;
358
359 f = fopen(fname, "w");
360 if (f == NULL) {
361 rc = EIO;
362 goto error;
363 }
364
365 rc = sif_export_node(doc->root, f);
366 if (rc != EOK)
367 goto error;
368
369 if (fflush(f) == EOF) {
370 rc = EIO;
371 goto error;
372 }
373
374 return EOK;
375error:
376 if (f != NULL)
377 fclose(f);
378 return rc;
379}
380
381/** Prepend new child.
382 *
383 * Create a new child and prepend it at the beginning of children list of
384 * @a parent.
385 *
386 * @param parent Parent node
387 * @param ctype Child type
388 * @param rchild Place to store pointer to new child
389 *
390 * @return EOK on success or ENOMEM if out of memory
391 */
392errno_t sif_node_prepend_child(sif_node_t *parent, const char *ctype,
393 sif_node_t **rchild)
394{
395 sif_node_t *child;
396
397 child = sif_node_new(parent);
398 if (child == NULL)
399 return ENOMEM;
400
401 child->ntype = str_dup(ctype);
402 if (child->ntype == NULL) {
403 sif_node_delete(child);
404 return ENOMEM;
405 }
406
407 list_prepend(&child->lparent, &parent->children);
408
409 *rchild = child;
410 return EOK;
411}
412
413/** Append new child.
414 *
415 * Create a new child and append it at the end of children list of @a parent.
416 *
417 * @param parent Parent node
418 * @param ctype Child type
419 * @param rchild Place to store pointer to new child
420 *
421 * @return EOK on success or ENOMEM if out of memory
422 */
423errno_t sif_node_append_child(sif_node_t *parent, const char *ctype,
424 sif_node_t **rchild)
425{
426 sif_node_t *child;
427
428 child = sif_node_new(parent);
429 if (child == NULL)
430 return ENOMEM;
431
432 child->ntype = str_dup(ctype);
433 if (child->ntype == NULL) {
434 sif_node_delete(child);
435 return ENOMEM;
436 }
437
438 list_append(&child->lparent, &parent->children);
439
440 *rchild = child;
441 return EOK;
442}
443
444/** Insert new child before existing child.
445 *
446 * Create a new child and insert it before an existing child.
447 *
448 * @param sibling Sibling before which to insert
449 * @param ctype Child type
450 * @param rchild Place to store pointer to new child
451 *
452 * @return EOK on success or ENOMEM if out of memory
453 */
454errno_t sif_node_insert_before(sif_node_t *sibling, const char *ctype,
455 sif_node_t **rchild)
456{
457 sif_node_t *child;
458
459 child = sif_node_new(sibling->parent);
460 if (child == NULL)
461 return ENOMEM;
462
463 child->ntype = str_dup(ctype);
464 if (child->ntype == NULL) {
465 sif_node_delete(child);
466 return ENOMEM;
467 }
468
469 list_insert_before(&child->lparent, &sibling->lparent);
470
471 *rchild = child;
472 return EOK;
473}
474
475/** Insert new child after existing child.
476 *
477 * Create a new child and insert it after an existing child.
478 *
479 * @param sibling Sibling after which to insert
480 * @param ctype Child type
481 * @param rchild Place to store pointer to new child
482 *
483 * @return EOK on success or ENOMEM if out of memory
484 */
485errno_t sif_node_insert_after(sif_node_t *sibling, const char *ctype,
486 sif_node_t **rchild)
487{
488 sif_node_t *child;
489
490 child = sif_node_new(sibling->parent);
491 if (child == NULL)
492 return ENOMEM;
493
494 child->ntype = str_dup(ctype);
495 if (child->ntype == NULL) {
496 sif_node_delete(child);
497 return ENOMEM;
498 }
499
500 list_insert_after(&child->lparent, &sibling->lparent);
501
502 *rchild = child;
503 return EOK;
504}
505
506/** Destroy SIF node.
507 *
508 * @param node Node to destroy
509 */
510void sif_node_destroy(sif_node_t *node)
511{
512 list_remove(&node->lparent);
513 sif_node_delete(node);
514}
515
516/** Set node attribute.
517 *
518 * @param node SIF node
519 * @param aname Attribute name
520 * @param value Attribute value
521 *
522 * @return EOK on success, ENOMEM if out of memory
523 */
524errno_t sif_node_set_attr(sif_node_t *node, const char *aname,
525 const char *avalue)
526{
527 odlink_t *link;
528 sif_attr_t *attr;
529 char *cvalue;
530
531 link = odict_find_eq(&node->attrs, (void *)aname, NULL);
532
533 if (link != NULL) {
534 attr = odict_get_instance(link, sif_attr_t, lattrs);
535 cvalue = str_dup(avalue);
536 if (cvalue == NULL)
537 return ENOMEM;
538
539 free(attr->avalue);
540 attr->avalue = cvalue;
541 } else {
542 attr = sif_attr_new(node);
543 if (attr == NULL)
544 return ENOMEM;
545
546 attr->aname = str_dup(aname);
547 if (attr->aname == NULL) {
548 sif_attr_delete(attr);
549 return ENOMEM;
550 }
551
552 attr->avalue = str_dup(avalue);
553 if (attr->avalue == NULL) {
554 sif_attr_delete(attr);
555 return ENOMEM;
556 }
557
558 odict_insert(&attr->lattrs, &node->attrs, NULL);
559 }
560
561 return EOK;
562}
563
564/** Unset node attribute.
565 *
566 * @param node Node
567 * @param aname Attribute name
568 */
569void sif_node_unset_attr(sif_node_t *node, const char *aname)
570{
571 odlink_t *link;
572 sif_attr_t *attr;
573
574 link = odict_find_eq(&node->attrs, (void *)aname, NULL);
575 if (link == NULL)
576 return;
577
578 attr = odict_get_instance(link, sif_attr_t, lattrs);
579 odict_remove(link);
580 sif_attr_delete(attr);
581}
582
583/** Export node name to file.
584 *
585 * Export node name to file.
586 *
587 * @param str String
588 * @param f File
589 * @return EOK on success, EIO on I/O error
590 */
591static errno_t sif_export_name(const char *str, FILE *f)
592{
593 if (fputs(str, f) == EOF)
594 return EIO;
595
596 return EOK;
597}
598
599/** Export string to file.
600 *
601 * Export string to file (the string is double-quoted and escaped).
602 *
603 * @param str String
604 * @param f File
605 * @return EOK on success, EIO on I/O error
606 */
607static errno_t sif_export_string(const char *str, FILE *f)
608{
609 const char *cp;
610
611 if (fputc('"', f) == EOF)
612 return EIO;
613
614 cp = str;
615 while (*cp != '\0') {
616 if (*cp == '<') {
617 if (fputs("&lt;", f) == EOF)
618 return EIO;
619 } else if (*cp == '"') {
620 if (fputs("&quot;", f) == EOF)
621 return EIO;
622 } else {
623 if (fputc(*cp, f) == EOF)
624 return EIO;
625 }
626
627 ++cp;
628 }
629
630 if (fputc('"', f) == EOF)
631 return EIO;
632
633 return EOK;
634}
635
636/** Read characters from file, make sure they match the specified sequence.
637 *
638 * @param f File
639 * @param chars Expected sequence of characters to be read
640 *
641 * @return EOK on success, EIO on I/O error or mismatch
642 */
643static errno_t sif_get_verify_chars(FILE *f, const char *chars)
644{
645 const char *cp;
646 char c;
647
648 cp = chars;
649 while (*cp != '\0') {
650 c = fgetc(f);
651 if (c != *cp)
652 return EIO;
653 ++cp;
654 }
655
656 return EOK;
657}
658
659/** Import name from file.
660 * *
661 * @param f File
662 * @param rstr Place to store pointer to newly allocated string
663 * @return EOK on success, EIO on I/O error
664 */
665static errno_t sif_import_name(FILE *f, char **rstr)
666{
667 char *str;
668 char *nstr;
669 size_t str_size;
670 size_t sidx;
671 int c;
672 errno_t rc;
673
674 str_size = 1;
675 sidx = 0;
676 str = malloc(str_size + 1);
677 if (str == NULL)
678 return ENOMEM;
679
680 c = fgetc(f);
681 if (!sif_is_name_start_char(c)) {
682 rc = EIO;
683 goto error;
684 }
685
686 while (true) {
687 if (sidx >= str_size) {
688 str_size *= 2;
689 nstr = realloc(str, str_size + 1);
690 if (nstr == NULL) {
691 rc = ENOMEM;
692 goto error;
693 }
694
695 str = nstr;
696 }
697
698 str[sidx++] = c;
699
700 c = fgetc(f);
701 if (!sif_is_name_char(c))
702 break;
703 }
704
705 ungetc(c, f);
706
707 str[sidx] = '\0';
708 *rstr = str;
709 return EOK;
710error:
711 free(str);
712 return rc;
713}
714
715/** Import string from file.
716 *
717 * Import string from file (the string in the file must be
718 * properly quoted and escaped).
719 *
720 * @param f File
721 * @param rstr Place to store pointer to newly allocated string
722 * @return EOK on success, EIO on I/O error
723 */
724static errno_t sif_import_string(FILE *f, char **rstr)
725{
726 char *str;
727 char *nstr;
728 size_t str_size;
729 size_t sidx;
730 int c;
731 errno_t rc;
732
733 str_size = 1;
734 sidx = 0;
735 str = malloc(str_size + 1);
736 if (str == NULL)
737 return ENOMEM;
738
739 c = fgetc(f);
740 if (c != '"') {
741 rc = EIO;
742 goto error;
743 }
744
745 while (true) {
746 c = fgetc(f);
747 if (c == EOF) {
748 rc = EIO;
749 goto error;
750 }
751
752 if (c == '"')
753 break;
754
755 if (c == '&') {
756 c = fgetc(f);
757 if (c == EOF) {
758 rc = EIO;
759 goto error;
760 }
761
762 if (c == 'q') {
763 rc = sif_get_verify_chars(f, "uot;");
764 if (rc != EOK)
765 goto error;
766 } else if (c == 'l') {
767 rc = sif_get_verify_chars(f, "t;");
768 if (rc != EOK)
769 goto error;
770 } else {
771 rc = EIO;
772 goto error;
773 }
774 }
775
776 if (sidx >= str_size) {
777 str_size *= 2;
778 nstr = realloc(str, str_size + 1);
779 if (nstr == NULL) {
780 rc = ENOMEM;
781 goto error;
782 }
783
784 str = nstr;
785 }
786
787 str[sidx++] = c;
788 }
789
790 str[sidx] = '\0';
791 *rstr = str;
792 return EOK;
793error:
794 free(str);
795 return rc;
796}
797
798/** Import SIF attribute from file.
799 *
800 * @param node Node under which attribute shou
801 * @param f File
802 * @param rattr Place to store pointer to imported SIF attribute
803 * @return EOK on success, EIO on I/O error
804 */
805static errno_t sif_import_attr(sif_node_t *node, FILE *f, sif_attr_t **rattr)
806{
807 errno_t rc;
808 char *aname = NULL;
809 char *avalue = NULL;
810 sif_attr_t *attr;
811 int c;
812
813 rc = sif_import_name(f, &aname);
814 if (rc != EOK)
815 goto error;
816
817 c = fgetc(f);
818 if (c != '=') {
819 rc = EIO;
820 goto error;
821 }
822
823 rc = sif_import_string(f, &avalue);
824 if (rc != EOK)
825 goto error;
826
827 attr = sif_attr_new(node);
828 if (attr == NULL) {
829 rc = ENOMEM;
830 goto error;
831 }
832
833 attr->aname = aname;
834 attr->avalue = avalue;
835
836 *rattr = attr;
837 return EOK;
838error:
839 if (aname != NULL)
840 free(aname);
841 if (avalue != NULL)
842 free(avalue);
843 return rc;
844}
845
846/** Export SIF attribute to file.
847 *
848 * @param attr SIF attribute
849 * @param f File
850 * @return EOK on success, EIO on I/O error
851 */
852static errno_t sif_export_attr(sif_attr_t *attr, FILE *f)
853{
854 errno_t rc;
855
856 rc = sif_export_name(attr->aname, f);
857 if (rc != EOK)
858 return rc;
859
860 if (fputc('=', f) == EOF)
861 return EIO;
862
863 rc = sif_export_string(attr->avalue, f);
864 if (rc != EOK)
865 return rc;
866
867 return EOK;
868}
869
870/** Export SIF node to file.
871 *
872 * @param node SIF node
873 * @param f File
874 * @return EOK on success, EIO on I/O error
875 */
876static errno_t sif_export_node(sif_node_t *node, FILE *f)
877{
878 errno_t rc;
879 sif_attr_t *attr;
880 sif_node_t *child;
881
882 if (fputc('<', f) == EOF)
883 return EIO;
884
885 rc = sif_export_name(node->ntype, f);
886 if (rc != EOK)
887 return rc;
888
889 /* Attributes */
890
891 attr = sif_node_first_attr(node);
892 while (attr != NULL) {
893 if (fputc(' ', f) == EOF)
894 return EIO;
895
896 rc = sif_export_attr(attr, f);
897 if (rc != EOK)
898 return rc;
899
900 attr = sif_node_next_attr(attr);
901 }
902
903 if (fputs(">\n", f) == EOF)
904 return EIO;
905
906 /* Child nodes */
907
908 child = sif_node_first_child(node);
909 while (child != NULL) {
910 rc = sif_export_node(child, f);
911 if (rc != EOK)
912 return rc;
913
914 child = sif_node_next_child(child);
915 }
916
917 if (fputs("</", f) == EOF)
918 return EIO;
919
920 rc = sif_export_name(node->ntype, f);
921 if (rc != EOK)
922 return rc;
923
924 if (fputs(">\n", f) == EOF)
925 return EIO;
926
927 return EOK;
928}
929
930/** Import SIF node from file.
931 *
932 * @param parent Parent node
933 * @param f File
934 * @param rnode Place to store pointer to imported node
935 * @param rendtag Place to store @c true iff end tag is encountered
936 * @return EOK on success, EIO on I/O error
937 */
938static errno_t sif_import_node(sif_node_t *parent, FILE *f, sif_node_t **rnode,
939 bool *rendtag)
940{
941 errno_t rc;
942 sif_node_t *node = NULL;
943 sif_node_t *child;
944 sif_attr_t *attr = NULL;
945 bool endtag;
946 bool cendtag;
947 char *ntype;
948 int c;
949
950 node = sif_node_new(parent);
951 if (node == NULL)
952 return ENOMEM;
953
954 c = fgetc(f);
955 while (isspace(c))
956 c = fgetc(f);
957
958 if (c != '<') {
959 rc = EIO;
960 goto error;
961 }
962
963 c = fgetc(f);
964 if (c == '/') {
965 endtag = true;
966 } else {
967 endtag = false;
968 ungetc(c, f);
969 }
970
971 rc = sif_import_name(f, &ntype);
972 if (rc != EOK)
973 goto error;
974
975 node->ntype = ntype;
976
977 /* Attributes */
978
979 c = fgetc(f);
980 if (c == EOF) {
981 rc = EIO;
982 goto error;
983 }
984
985 while (c != '>') {
986 /* End tags cannot have attributes */
987 if (endtag) {
988 rc = EIO;
989 goto error;
990 }
991
992 while (isspace(c))
993 c = fgetc(f);
994 ungetc(c, f);
995
996 rc = sif_import_attr(node, f, &attr);
997 if (rc != EOK)
998 goto error;
999
1000 odict_insert(&attr->lattrs, &node->attrs, NULL);
1001
1002 c = fgetc(f);
1003 if (c == EOF) {
1004 rc = EIO;
1005 goto error;
1006 }
1007 }
1008
1009 /* Child nodes */
1010
1011 if (!endtag) {
1012 while (true) {
1013 rc = sif_import_node(node, f, &child, &cendtag);
1014 if (rc != EOK)
1015 goto error;
1016
1017 if (cendtag) {
1018 sif_node_delete(child);
1019 break;
1020 }
1021
1022 list_append(&child->lparent, &node->children);
1023 }
1024 }
1025
1026 *rnode = node;
1027 *rendtag = endtag;
1028 return EOK;
1029error:
1030 sif_node_delete(node);
1031 return rc;
1032}
1033
1034/** Get first attribute or a node.
1035 *
1036 * @param node SIF node
1037 * @return First attribute or @c NULL if there is none
1038 */
1039static sif_attr_t *sif_node_first_attr(sif_node_t *node)
1040{
1041 odlink_t *link;
1042
1043 link = odict_first(&node->attrs);
1044 if (link == NULL)
1045 return NULL;
1046
1047 return odict_get_instance(link, sif_attr_t, lattrs);
1048}
1049
1050/** Get next attribute or a node.
1051 *
1052 * @param cur Current attribute
1053 * @return Next attribute or @c NULL if there is none
1054 */
1055static sif_attr_t *sif_node_next_attr(sif_attr_t *cur)
1056{
1057 odlink_t *link;
1058
1059 link = odict_next(&cur->lattrs, &cur->node->attrs);
1060 if (link == NULL)
1061 return NULL;
1062
1063 return odict_get_instance(link, sif_attr_t, lattrs);
1064}
1065
1066/** Get key callback for ordered dictionary of node attributes.
1067 *
1068 * @param link Ordered dictionary link of attribute
1069 * @return Pointer to attribute name
1070 */
1071static void *sif_attr_getkey(odlink_t *link)
1072{
1073 return (void *)odict_get_instance(link, sif_attr_t, lattrs)->aname;
1074}
1075
1076/** Comparison callback for ordered dictionary of node attributes.
1077 *
1078 * @param a Name of first attribute
1079 * @param b Name of second attribute
1080 * @return Less than zero, zero or greater than zero, if a < b, a == b, a > b,
1081 * respectively.
1082 */
1083static int sif_attr_cmp(void *a, void *b)
1084{
1085 char *ca, *cb;
1086
1087 ca = (char *)a;
1088 cb = (char *)b;
1089
1090 return str_cmp(ca, cb);
1091}
1092
1093/** @}
1094 */
Note: See TracBrowser for help on using the repository browser.