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

Last change on this file was ca95ccd, checked in by Jiri Svoboda <jiri@…>, 10 months ago

Fix file closing, volume config sync.

  • Property mode set to 100644
File size: 20.6 KB
RevLine 
[b79903b]1/*
[bff8619]2 * Copyright (c) 2024 Jiri Svoboda
[b79903b]3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28/** @addtogroup libsif
29 * @{
30 */
31/**
32 * @file Structured Information Format
33 *
34 * Structured Information Format (SIF) is an API that allows an application
[bff8619]35 * to maintain data in a persistent repository in a format that is
36 * structured (and hence extensible).
[b79903b]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
[7b87e1d]45#include <adt/list.h>
46#include <adt/odict.h>
[ac9b4f2]47#include <ctype.h>
[b79903b]48#include <errno.h>
[ac9b4f2]49#include <stdbool.h>
[b79903b]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 *);
[ac9b4f2]57static errno_t sif_import_node(sif_node_t *, FILE *, sif_node_t **, bool *);
[7b87e1d]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 *);
[b79903b]63
[ac9b4f2]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
[b79903b]84/** Create new SIF node.
85 *
[7b87e1d]86 * @param parent Parent node
[b79903b]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;
[7b87e1d]98 odict_initialize(&node->attrs, sif_attr_getkey, sif_attr_cmp);
[b79903b]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.
[7b87e1d]107 * This will also delete any attributes or child nodes.
[b79903b]108 *
109 * @param node Node
110 */
111static void sif_node_delete(sif_node_t *node)
112{
[7b87e1d]113 sif_attr_t *attr;
114 sif_node_t *child;
115
[b79903b]116 if (node == NULL)
117 return;
118
[7b87e1d]119 assert(!link_used(&node->lparent));
120
[b79903b]121 if (node->ntype != NULL)
122 free(node->ntype);
123
[7b87e1d]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
[b79903b]138 free(node);
139}
140
[7b87e1d]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
[bff8619]179/** Create SIF document.
[b79903b]180 *
[bff8619]181 * @param rdoc Place to store pointer to new document.
[b79903b]182 *
183 * @return EOK on success or error code
184 */
[bff8619]185errno_t sif_new(sif_doc_t **rdoc)
[b79903b]186{
[bff8619]187 sif_doc_t *doc;
[b79903b]188 sif_node_t *root = NULL;
189 errno_t rc;
190
[bff8619]191 doc = calloc(1, sizeof(sif_doc_t));
192 if (doc == NULL)
[b79903b]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
[bff8619]207 doc->root = root;
[7b87e1d]208
[bff8619]209 *rdoc = doc;
[b79903b]210 return EOK;
211error:
212 sif_node_delete(root);
[bff8619]213 free(doc);
[b79903b]214 return rc;
215}
216
[bff8619]217/** Load SIF document.
[b79903b]218 *
219 * @param fname File name
[bff8619]220 * @param rdoc Place to store pointer to new document.
[b79903b]221 *
222 * @return EOK on success or error code
223 */
[bff8619]224errno_t sif_load(const char *fname, sif_doc_t **rdoc)
[b79903b]225{
[bff8619]226 sif_doc_t *doc;
[b79903b]227 sif_node_t *root = NULL;
228 errno_t rc;
[ac9b4f2]229 bool endtag;
[ca95ccd]230 FILE *f = NULL;
[b79903b]231
[bff8619]232 doc = calloc(1, sizeof(sif_doc_t));
233 if (doc == NULL)
[b79903b]234 return ENOMEM;
235
[bff8619]236 doc->fname = str_dup(fname);
237 if (doc->fname == NULL) {
[1dcba91]238 rc = ENOMEM;
239 goto error;
240 }
241
242 f = fopen(fname, "r");
[9c5e3a5]243 if (f == NULL) {
244 rc = EIO;
[b79903b]245 goto error;
246 }
247
[ac9b4f2]248 rc = sif_import_node(NULL, f, &root, &endtag);
249 if (rc != EOK || endtag == true)
[b79903b]250 goto error;
251
[9c5e3a5]252 if (str_cmp(root->ntype, "sif") != 0) {
[b79903b]253 rc = EIO;
254 goto error;
255 }
256
[ca95ccd]257 fclose(f);
[bff8619]258 doc->root = root;
259 *rdoc = doc;
[b79903b]260 return EOK;
261error:
262 sif_node_delete(root);
[bff8619]263 free(doc);
[ca95ccd]264 if (f != NULL)
265 fclose(f);
[b79903b]266 return rc;
267}
268
[bff8619]269/** Delete SIF document.
[b79903b]270 *
[bff8619]271 * @param doc SIF document
[b79903b]272 * @return EOK on success or error code
273 */
[bff8619]274void sif_delete(sif_doc_t *doc)
[b79903b]275{
[bff8619]276 sif_node_delete(doc->root);
277 free(doc);
[b79903b]278}
279
280/** Return root node.
281 *
[bff8619]282 * @param doc SIF document
[b79903b]283 */
[bff8619]284sif_node_t *sif_get_root(sif_doc_t *doc)
[b79903b]285{
[bff8619]286 return doc->root;
[b79903b]287}
288
289/** Get first child of a node.
290 *
291 * @param parent Parent node
292 * @return First child node or @c NULL if @a parent has no children
293 */
294sif_node_t *sif_node_first_child(sif_node_t *parent)
295{
296 link_t *link;
297
298 link = list_first(&parent->children);
299 if (link == NULL)
300 return NULL;
301
302 return list_get_instance(link, sif_node_t, lparent);
303}
304
305/** Get next child of a node.
306 *
307 * @param current Current node
308 * @return Next child (i.e. next sibling of @a current)
309 */
310sif_node_t *sif_node_next_child(sif_node_t *current)
311{
312 link_t *link;
313
314 link = list_next(&current->lparent, &current->parent->children);
315 if (link == NULL)
316 return NULL;
317
318 return list_get_instance(link, sif_node_t, lparent);
319}
320
321/** Get node type.
322 *
323 * @param node SIF node
324 * @return Pointer to string, valid until next modification.
325 */
326const char *sif_node_get_type(sif_node_t *node)
327{
328 return node->ntype;
329}
330
331/** Get node attribute.
332 *
333 * @param node SIF node
334 * @param aname Attribute name
335 *
336 * @return Attribute value or @c NULL if attribute is not set
337 */
338const char *sif_node_get_attr(sif_node_t *node, const char *aname)
339{
[7b87e1d]340 odlink_t *link;
341 sif_attr_t *attr;
342
343 link = odict_find_eq(&node->attrs, (void *)aname, NULL);
344 if (link == NULL)
345 return NULL;
346
347 attr = odict_get_instance(link, sif_attr_t, lattrs);
348 return attr->avalue;
[b79903b]349}
350
[bff8619]351/** Save SIF document to file.
352 * *
353 * @param doc SIF document
354 * @param fname File name
[b79903b]355 * @return EOK on success or error code
356 */
[bff8619]357errno_t sif_save(sif_doc_t *doc, const char *fname)
[b79903b]358{
[bff8619]359 FILE *f = NULL;
[b79903b]360 errno_t rc;
361
[bff8619]362 f = fopen(fname, "w");
363 if (f == NULL) {
364 rc = EIO;
365 goto error;
366 }
[b79903b]367
[bff8619]368 rc = sif_export_node(doc->root, f);
[b79903b]369 if (rc != EOK)
[bff8619]370 goto error;
[b79903b]371
[bff8619]372 if (fflush(f) == EOF) {
373 rc = EIO;
374 goto error;
375 }
[1dcba91]376
[ca95ccd]377 fclose(f);
[b79903b]378 return EOK;
[bff8619]379error:
380 if (f != NULL)
381 fclose(f);
382 return rc;
[b79903b]383}
384
385/** Prepend new child.
386 *
387 * Create a new child and prepend it at the beginning of children list of
388 * @a parent.
389 *
390 * @param parent Parent node
391 * @param ctype Child type
392 * @param rchild Place to store pointer to new child
393 *
394 * @return EOK on success or ENOMEM if out of memory
395 */
[bff8619]396errno_t sif_node_prepend_child(sif_node_t *parent, const char *ctype,
397 sif_node_t **rchild)
[b79903b]398{
399 sif_node_t *child;
400
401 child = sif_node_new(parent);
402 if (child == NULL)
403 return ENOMEM;
404
405 child->ntype = str_dup(ctype);
406 if (child->ntype == NULL) {
407 sif_node_delete(child);
408 return ENOMEM;
409 }
410
411 list_prepend(&child->lparent, &parent->children);
412
413 *rchild = child;
414 return EOK;
415}
416
417/** Append new child.
418 *
419 * Create a new child and append it at the end of children list of @a parent.
420 *
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 */
[bff8619]427errno_t sif_node_append_child(sif_node_t *parent, const char *ctype,
428 sif_node_t **rchild)
[b79903b]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_append(&child->lparent, &parent->children);
443
444 *rchild = child;
445 return EOK;
446}
447
448/** Insert new child before existing child.
449 *
450 * Create a new child and insert it before an existing child.
451 *
452 * @param sibling Sibling before which to insert
453 * @param ctype Child type
454 * @param rchild Place to store pointer to new child
455 *
456 * @return EOK on success or ENOMEM if out of memory
457 */
[bff8619]458errno_t sif_node_insert_before(sif_node_t *sibling, const char *ctype,
459 sif_node_t **rchild)
[b79903b]460{
461 sif_node_t *child;
462
463 child = sif_node_new(sibling->parent);
464 if (child == NULL)
465 return ENOMEM;
466
467 child->ntype = str_dup(ctype);
468 if (child->ntype == NULL) {
469 sif_node_delete(child);
470 return ENOMEM;
471 }
472
473 list_insert_before(&child->lparent, &sibling->lparent);
474
475 *rchild = child;
476 return EOK;
477}
478
479/** Insert new child after existing child.
480 *
481 * Create a new child and insert it after an existing child.
482 *
483 * @param sibling Sibling after which to insert
484 * @param ctype Child type
485 * @param rchild Place to store pointer to new child
486 *
487 * @return EOK on success or ENOMEM if out of memory
488 */
[bff8619]489errno_t sif_node_insert_after(sif_node_t *sibling, const char *ctype,
490 sif_node_t **rchild)
[b79903b]491{
492 sif_node_t *child;
493
494 child = sif_node_new(sibling->parent);
495 if (child == NULL)
496 return ENOMEM;
497
498 child->ntype = str_dup(ctype);
499 if (child->ntype == NULL) {
500 sif_node_delete(child);
501 return ENOMEM;
502 }
503
504 list_insert_after(&child->lparent, &sibling->lparent);
505
506 *rchild = child;
507 return EOK;
508}
509
510/** Destroy SIF node.
511 *
512 * @param node Node to destroy
513 */
[bff8619]514void sif_node_destroy(sif_node_t *node)
[b79903b]515{
516 list_remove(&node->lparent);
517 sif_node_delete(node);
518}
519
520/** Set node attribute.
521 *
522 * @param node SIF node
523 * @param aname Attribute name
524 * @param value Attribute value
525 *
526 * @return EOK on success, ENOMEM if out of memory
527 */
[bff8619]528errno_t sif_node_set_attr(sif_node_t *node, const char *aname,
529 const char *avalue)
[b79903b]530{
[7b87e1d]531 odlink_t *link;
532 sif_attr_t *attr;
533 char *cvalue;
534
535 link = odict_find_eq(&node->attrs, (void *)aname, NULL);
536
537 if (link != NULL) {
538 attr = odict_get_instance(link, sif_attr_t, lattrs);
539 cvalue = str_dup(avalue);
540 if (cvalue == NULL)
541 return ENOMEM;
542
543 free(attr->avalue);
544 attr->avalue = cvalue;
545 } else {
546 attr = sif_attr_new(node);
547 if (attr == NULL)
548 return ENOMEM;
549
550 attr->aname = str_dup(aname);
551 if (attr->aname == NULL) {
552 sif_attr_delete(attr);
553 return ENOMEM;
554 }
555
556 attr->avalue = str_dup(avalue);
557 if (attr->avalue == NULL) {
558 sif_attr_delete(attr);
559 return ENOMEM;
560 }
561
562 odict_insert(&attr->lattrs, &node->attrs, NULL);
563 }
564
[b79903b]565 return EOK;
566}
567
568/** Unset node attribute.
569 *
570 * @param node Node
571 * @param aname Attribute name
572 */
[bff8619]573void sif_node_unset_attr(sif_node_t *node, const char *aname)
[b79903b]574{
[7b87e1d]575 odlink_t *link;
576 sif_attr_t *attr;
577
578 link = odict_find_eq(&node->attrs, (void *)aname, NULL);
579 if (link == NULL)
580 return;
581
582 attr = odict_get_instance(link, sif_attr_t, lattrs);
583 odict_remove(link);
584 sif_attr_delete(attr);
[b79903b]585}
586
[ac9b4f2]587/** Export node name to file.
588 *
589 * Export node name to file.
590 *
591 * @param str String
592 * @param f File
593 * @return EOK on success, EIO on I/O error
594 */
595static errno_t sif_export_name(const char *str, FILE *f)
596{
597 if (fputs(str, f) == EOF)
598 return EIO;
599
600 return EOK;
601}
602
[b79903b]603/** Export string to file.
604 *
[ac9b4f2]605 * Export string to file (the string is double-quoted and escaped).
[b79903b]606 *
607 * @param str String
608 * @param f File
609 * @return EOK on success, EIO on I/O error
610 */
611static errno_t sif_export_string(const char *str, FILE *f)
612{
613 const char *cp;
614
[ac9b4f2]615 if (fputc('"', f) == EOF)
[b79903b]616 return EIO;
617
618 cp = str;
619 while (*cp != '\0') {
[ac9b4f2]620 if (*cp == '<') {
621 if (fputs("&lt;", f) == EOF)
622 return EIO;
623 } else if (*cp == '"') {
624 if (fputs("&quot;", f) == EOF)
625 return EIO;
626 } else {
627 if (fputc(*cp, f) == EOF)
[b79903b]628 return EIO;
629 }
[ac9b4f2]630
[b79903b]631 ++cp;
632 }
633
[ac9b4f2]634 if (fputc('"', f) == EOF)
[b79903b]635 return EIO;
636
637 return EOK;
638}
639
[ac9b4f2]640/** Read characters from file, make sure they match the specified sequence.
641 *
642 * @param f File
643 * @param chars Expected sequence of characters to be read
644 *
645 * @return EOK on success, EIO on I/O error or mismatch
646 */
647static errno_t sif_get_verify_chars(FILE *f, const char *chars)
648{
649 const char *cp;
650 char c;
651
652 cp = chars;
653 while (*cp != '\0') {
654 c = fgetc(f);
655 if (c != *cp)
656 return EIO;
657 ++cp;
658 }
659
660 return EOK;
661}
662
663/** Import name from file.
664 * *
665 * @param f File
666 * @param rstr Place to store pointer to newly allocated string
667 * @return EOK on success, EIO on I/O error
668 */
669static errno_t sif_import_name(FILE *f, char **rstr)
670{
671 char *str;
672 char *nstr;
673 size_t str_size;
674 size_t sidx;
675 int c;
676 errno_t rc;
677
678 str_size = 1;
679 sidx = 0;
680 str = malloc(str_size + 1);
681 if (str == NULL)
682 return ENOMEM;
683
684 c = fgetc(f);
685 if (!sif_is_name_start_char(c)) {
686 rc = EIO;
687 goto error;
688 }
689
690 while (true) {
691 if (sidx >= str_size) {
692 str_size *= 2;
693 nstr = realloc(str, str_size + 1);
694 if (nstr == NULL) {
695 rc = ENOMEM;
696 goto error;
697 }
698
699 str = nstr;
700 }
701
702 str[sidx++] = c;
703
704 c = fgetc(f);
705 if (!sif_is_name_char(c))
706 break;
707 }
708
709 ungetc(c, f);
710
711 str[sidx] = '\0';
712 *rstr = str;
713 return EOK;
714error:
715 free(str);
716 return rc;
717}
718
[9c5e3a5]719/** Import string from file.
720 *
721 * Import string from file (the string in the file must be
[ac9b4f2]722 * properly quoted and escaped).
[9c5e3a5]723 *
724 * @param f File
725 * @param rstr Place to store pointer to newly allocated string
726 * @return EOK on success, EIO on I/O error
727 */
728static errno_t sif_import_string(FILE *f, char **rstr)
729{
730 char *str;
[153dd3b]731 char *nstr;
[9c5e3a5]732 size_t str_size;
733 size_t sidx;
[1c398db2]734 int c;
[9c5e3a5]735 errno_t rc;
736
737 str_size = 1;
738 sidx = 0;
739 str = malloc(str_size + 1);
740 if (str == NULL)
741 return ENOMEM;
742
743 c = fgetc(f);
[ac9b4f2]744 if (c != '"') {
[9c5e3a5]745 rc = EIO;
746 goto error;
747 }
748
749 while (true) {
750 c = fgetc(f);
751 if (c == EOF) {
752 rc = EIO;
753 goto error;
754 }
755
[ac9b4f2]756 if (c == '"')
[9c5e3a5]757 break;
758
[ac9b4f2]759 if (c == '&') {
[9c5e3a5]760 c = fgetc(f);
761 if (c == EOF) {
762 rc = EIO;
763 goto error;
764 }
[ac9b4f2]765
766 if (c == 'q') {
767 rc = sif_get_verify_chars(f, "uot;");
768 if (rc != EOK)
769 goto error;
770 } else if (c == 'l') {
771 rc = sif_get_verify_chars(f, "t;");
772 if (rc != EOK)
773 goto error;
774 } else {
775 rc = EIO;
776 goto error;
777 }
[9c5e3a5]778 }
779
780 if (sidx >= str_size) {
781 str_size *= 2;
[153dd3b]782 nstr = realloc(str, str_size + 1);
783 if (nstr == NULL) {
[9c5e3a5]784 rc = ENOMEM;
785 goto error;
786 }
[153dd3b]787
788 str = nstr;
[9c5e3a5]789 }
790
791 str[sidx++] = c;
792 }
793
794 str[sidx] = '\0';
795 *rstr = str;
796 return EOK;
797error:
798 free(str);
799 return rc;
800}
801
[7b87e1d]802/** Import SIF attribute from file.
803 *
804 * @param node Node under which attribute shou
805 * @param f File
806 * @param rattr Place to store pointer to imported SIF attribute
807 * @return EOK on success, EIO on I/O error
808 */
809static errno_t sif_import_attr(sif_node_t *node, FILE *f, sif_attr_t **rattr)
810{
811 errno_t rc;
812 char *aname = NULL;
813 char *avalue = NULL;
814 sif_attr_t *attr;
815 int c;
816
[ac9b4f2]817 rc = sif_import_name(f, &aname);
[7b87e1d]818 if (rc != EOK)
819 goto error;
820
821 c = fgetc(f);
822 if (c != '=') {
823 rc = EIO;
824 goto error;
825 }
826
827 rc = sif_import_string(f, &avalue);
828 if (rc != EOK)
829 goto error;
830
831 attr = sif_attr_new(node);
832 if (attr == NULL) {
833 rc = ENOMEM;
834 goto error;
835 }
836
837 attr->aname = aname;
838 attr->avalue = avalue;
839
840 *rattr = attr;
841 return EOK;
842error:
843 if (aname != NULL)
844 free(aname);
845 if (avalue != NULL)
846 free(avalue);
847 return rc;
848}
849
850/** Export SIF attribute to file.
851 *
852 * @param attr SIF attribute
853 * @param f File
854 * @return EOK on success, EIO on I/O error
855 */
856static errno_t sif_export_attr(sif_attr_t *attr, FILE *f)
857{
858 errno_t rc;
859
[ac9b4f2]860 rc = sif_export_name(attr->aname, f);
[7b87e1d]861 if (rc != EOK)
862 return rc;
863
864 if (fputc('=', f) == EOF)
865 return EIO;
866
867 rc = sif_export_string(attr->avalue, f);
868 if (rc != EOK)
869 return rc;
870
871 return EOK;
872}
873
[b79903b]874/** Export SIF node to file.
875 *
876 * @param node SIF node
877 * @param f File
878 * @return EOK on success, EIO on I/O error
879 */
880static errno_t sif_export_node(sif_node_t *node, FILE *f)
881{
882 errno_t rc;
[7b87e1d]883 sif_attr_t *attr;
[b79903b]884 sif_node_t *child;
885
[ac9b4f2]886 if (fputc('<', f) == EOF)
887 return EIO;
888
889 rc = sif_export_name(node->ntype, f);
[b79903b]890 if (rc != EOK)
891 return rc;
892
[7b87e1d]893 /* Attributes */
894
895 attr = sif_node_first_attr(node);
896 while (attr != NULL) {
[ac9b4f2]897 if (fputc(' ', f) == EOF)
898 return EIO;
899
[7b87e1d]900 rc = sif_export_attr(attr, f);
901 if (rc != EOK)
902 return rc;
903
904 attr = sif_node_next_attr(attr);
905 }
906
[ac9b4f2]907 if (fputs(">\n", f) == EOF)
[7b87e1d]908 return EIO;
909
910 /* Child nodes */
911
[b79903b]912 child = sif_node_first_child(node);
913 while (child != NULL) {
914 rc = sif_export_node(child, f);
915 if (rc != EOK)
916 return rc;
917
918 child = sif_node_next_child(child);
919 }
920
[ac9b4f2]921 if (fputs("</", f) == EOF)
922 return EIO;
923
924 rc = sif_export_name(node->ntype, f);
925 if (rc != EOK)
926 return rc;
927
928 if (fputs(">\n", f) == EOF)
[b79903b]929 return EIO;
930
931 return EOK;
932}
933
[9c5e3a5]934/** Import SIF node from file.
935 *
936 * @param parent Parent node
937 * @param f File
938 * @param rnode Place to store pointer to imported node
[ac9b4f2]939 * @param rendtag Place to store @c true iff end tag is encountered
[9c5e3a5]940 * @return EOK on success, EIO on I/O error
941 */
[ac9b4f2]942static errno_t sif_import_node(sif_node_t *parent, FILE *f, sif_node_t **rnode,
943 bool *rendtag)
[9c5e3a5]944{
945 errno_t rc;
946 sif_node_t *node = NULL;
947 sif_node_t *child;
[ee8d4d6]948 sif_attr_t *attr = NULL;
[ac9b4f2]949 bool endtag;
950 bool cendtag;
[9c5e3a5]951 char *ntype;
[1c398db2]952 int c;
[9c5e3a5]953
954 node = sif_node_new(parent);
955 if (node == NULL)
956 return ENOMEM;
957
[ac9b4f2]958 c = fgetc(f);
959 while (isspace(c))
960 c = fgetc(f);
961
962 if (c != '<') {
963 rc = EIO;
964 goto error;
965 }
966
967 c = fgetc(f);
968 if (c == '/') {
969 endtag = true;
970 } else {
971 endtag = false;
972 ungetc(c, f);
973 }
974
975 rc = sif_import_name(f, &ntype);
[9c5e3a5]976 if (rc != EOK)
977 goto error;
978
979 node->ntype = ntype;
980
[7b87e1d]981 /* Attributes */
982
983 c = fgetc(f);
984 if (c == EOF) {
985 rc = EIO;
986 goto error;
987 }
988
[ac9b4f2]989 while (c != '>') {
990 /* End tags cannot have attributes */
991 if (endtag) {
992 rc = EIO;
993 goto error;
994 }
995
996 while (isspace(c))
997 c = fgetc(f);
[7b87e1d]998 ungetc(c, f);
999
1000 rc = sif_import_attr(node, f, &attr);
1001 if (rc != EOK)
1002 goto error;
1003
1004 odict_insert(&attr->lattrs, &node->attrs, NULL);
1005
1006 c = fgetc(f);
1007 if (c == EOF) {
1008 rc = EIO;
1009 goto error;
1010 }
1011 }
1012
1013 /* Child nodes */
1014
[ac9b4f2]1015 if (!endtag) {
1016 while (true) {
1017 rc = sif_import_node(node, f, &child, &cendtag);
1018 if (rc != EOK)
1019 goto error;
[9c5e3a5]1020
[ac9b4f2]1021 if (cendtag) {
1022 sif_node_delete(child);
1023 break;
1024 }
[9c5e3a5]1025
[ac9b4f2]1026 list_append(&child->lparent, &node->children);
[9c5e3a5]1027 }
1028 }
1029
1030 *rnode = node;
[ac9b4f2]1031 *rendtag = endtag;
[9c5e3a5]1032 return EOK;
1033error:
1034 sif_node_delete(node);
1035 return rc;
1036}
1037
[7b87e1d]1038/** Get first attribute or a node.
1039 *
1040 * @param node SIF node
1041 * @return First attribute or @c NULL if there is none
1042 */
1043static sif_attr_t *sif_node_first_attr(sif_node_t *node)
1044{
1045 odlink_t *link;
1046
1047 link = odict_first(&node->attrs);
1048 if (link == NULL)
1049 return NULL;
1050
1051 return odict_get_instance(link, sif_attr_t, lattrs);
1052}
1053
1054/** Get next attribute or a node.
1055 *
1056 * @param cur Current attribute
1057 * @return Next attribute or @c NULL if there is none
1058 */
1059static sif_attr_t *sif_node_next_attr(sif_attr_t *cur)
1060{
1061 odlink_t *link;
1062
1063 link = odict_next(&cur->lattrs, &cur->node->attrs);
1064 if (link == NULL)
1065 return NULL;
1066
1067 return odict_get_instance(link, sif_attr_t, lattrs);
1068}
1069
1070/** Get key callback for ordered dictionary of node attributes.
1071 *
1072 * @param link Ordered dictionary link of attribute
1073 * @return Pointer to attribute name
1074 */
1075static void *sif_attr_getkey(odlink_t *link)
1076{
1077 return (void *)odict_get_instance(link, sif_attr_t, lattrs)->aname;
1078}
1079
1080/** Comparison callback for ordered dictionary of node attributes.
1081 *
1082 * @param a Name of first attribute
1083 * @param b Name of second attribute
1084 * @return Less than zero, zero or greater than zero, if a < b, a == b, a > b,
1085 * respectively.
1086 */
1087static int sif_attr_cmp(void *a, void *b)
1088{
1089 char *ca, *cb;
1090
1091 ca = (char *)a;
1092 cb = (char *)b;
1093
1094 return str_cmp(ca, cb);
1095}
1096
[b79903b]1097/** @}
1098 */
Note: See TracBrowser for help on using the repository browser.