Index: uspace/lib/sif/src/sif.c
===================================================================
--- uspace/lib/sif/src/sif.c	(revision 1c398db2b6550c5d1519f899db5d82cfc422fbc1)
+++ uspace/lib/sif/src/sif.c	(revision 7cd7a8d678fba9c835468054b7578fabbbdf58d2)
@@ -60,4 +60,6 @@
  */
 
+#include <adt/list.h>
+#include <adt/odict.h>
 #include <errno.h>
 #include <stdio.h>
@@ -69,8 +71,13 @@
 static errno_t sif_export_node(sif_node_t *, FILE *);
 static errno_t sif_import_node(sif_node_t *, FILE *, sif_node_t **);
+static sif_attr_t *sif_node_first_attr(sif_node_t *);
+static sif_attr_t *sif_node_next_attr(sif_attr_t *);
+static void sif_attr_delete(sif_attr_t *);
+static void *sif_attr_getkey(odlink_t *);
+static int sif_attr_cmp(void *, void *);
 
 /** Create new SIF node.
  *
- * @param parent Parent onde
+ * @param parent Parent node
  * @return Pointer to new node on success or @c NULL if out of memory
  */
@@ -84,4 +91,5 @@
 
 	node->parent = parent;
+	odict_initialize(&node->attrs, sif_attr_getkey, sif_attr_cmp);
 	list_initialize(&node->children);
 
@@ -92,4 +100,5 @@
  *
  * Delete a SIF node that has been already unlinked from the tree.
+ * This will also delete any attributes or child nodes.
  *
  * @param node Node
@@ -97,11 +106,68 @@
 static void sif_node_delete(sif_node_t *node)
 {
+	sif_attr_t *attr;
+	sif_node_t *child;
+
 	if (node == NULL)
 		return;
 
+	assert(!link_used(&node->lparent));
+
 	if (node->ntype != NULL)
 		free(node->ntype);
 
+	attr = sif_node_first_attr(node);
+	while (attr != NULL) {
+		odict_remove(&attr->lattrs);
+		sif_attr_delete(attr);
+		attr = sif_node_first_attr(node);
+	}
+
+	child = sif_node_first_child(node);
+	while (child != NULL) {
+		list_remove(&child->lparent);
+		sif_node_delete(child);
+		child = sif_node_first_child(node);
+	}
+
 	free(node);
+}
+
+/** Create new SIF attribute.
+ *
+ * @param node Containing node
+ * @return Pointer to new node on success or @c NULL if out of memory
+ */
+static sif_attr_t *sif_attr_new(sif_node_t *node)
+{
+	sif_attr_t *attr;
+
+	attr = calloc(1, sizeof(sif_attr_t));
+	if (attr == NULL)
+		return NULL;
+
+	attr->node = node;
+	return attr;
+}
+
+/** Delete SIF attribute.
+ *
+ * Delete a SIF attribute that has been already unlinked from is node.
+ *
+ * @param attr Attribute
+ */
+static void sif_attr_delete(sif_attr_t *attr)
+{
+	if (attr == NULL)
+		return;
+
+	assert(!odlink_used(&attr->lattrs));
+
+	if (attr->aname != NULL)
+		free(attr->aname);
+	if (attr->avalue != NULL)
+		free(attr->avalue);
+
+	free(attr);
 }
 
@@ -114,4 +180,63 @@
  */
 errno_t sif_create(const char *fname, sif_sess_t **rsess)
+{
+	sif_sess_t *sess;
+	sif_node_t *root = NULL;
+	sif_trans_t *trans = NULL;
+	errno_t rc;
+	FILE *f;
+
+	sess = calloc(1, sizeof(sif_sess_t));
+	if (sess == NULL)
+		return ENOMEM;
+
+	root = sif_node_new(NULL);
+	if (root == NULL) {
+		rc = ENOMEM;
+		goto error;
+	}
+
+	root->ntype = str_dup("sif");
+	if (root->ntype == NULL) {
+		rc = ENOMEM;
+		goto error;
+	}
+
+	f = fopen(fname, "w");
+	if (f == NULL) {
+		rc = EIO;
+		goto error;
+	}
+
+	sess->f = f;
+	sess->root = root;
+
+	/* Run a dummy trasaction to marshall initial repo state to file */
+	rc = sif_trans_begin(sess, &trans);
+	if (rc != EOK)
+		goto error;
+
+	rc = sif_trans_end(trans);
+	if (rc != EOK)
+		goto error;
+
+	*rsess = sess;
+	return EOK;
+error:
+	if (trans != NULL)
+		sif_trans_abort(trans);
+	sif_node_delete(root);
+	free(sess);
+	return rc;
+}
+
+/** Open an existing SIF repository.
+ *
+ * @param fname File name
+ * @param rsess Place to store pointer to new session.
+ *
+ * @return EOK on success or error code
+ */
+errno_t sif_open(const char *fname, sif_sess_t **rsess)
 {
 	sif_sess_t *sess;
@@ -124,21 +249,20 @@
 		return ENOMEM;
 
-	root = sif_node_new(NULL);
-	if (root == NULL) {
-		rc = ENOMEM;
-		goto error;
-	}
-
-	root->ntype = str_dup("sif");
-	if (root->ntype == NULL) {
-		rc = ENOMEM;
-		goto error;
-	}
-
-	f = fopen(fname, "w");
+	f = fopen(fname, "r+");
 	if (f == NULL) {
 		rc = EIO;
 		goto error;
 	}
+
+	rc = sif_import_node(NULL, f, &root);
+	if (rc != EOK)
+		goto error;
+
+	if (str_cmp(root->ntype, "sif") != 0) {
+		rc = EIO;
+		goto error;
+	}
+
+	sess->root = root;
 
 	sess->f = f;
@@ -152,49 +276,4 @@
 }
 
-/** Open an existing SIF repository.
- *
- * @param fname File name
- * @param rsess Place to store pointer to new session.
- *
- * @return EOK on success or error code
- */
-errno_t sif_open(const char *fname, sif_sess_t **rsess)
-{
-	sif_sess_t *sess;
-	sif_node_t *root = NULL;
-	errno_t rc;
-	FILE *f;
-
-	sess = calloc(1, sizeof(sif_sess_t));
-	if (sess == NULL)
-		return ENOMEM;
-
-	f = fopen(fname, "r+");
-	if (f == NULL) {
-		rc = EIO;
-		goto error;
-	}
-
-	rc = sif_import_node(NULL, f, &root);
-	if (rc != EOK)
-		goto error;
-
-	if (str_cmp(root->ntype, "sif") != 0) {
-		rc = EIO;
-		goto error;
-	}
-
-	sess->root = root;
-
-	sess->f = f;
-	sess->root = root;
-	*rsess = sess;
-	return EOK;
-error:
-	sif_node_delete(root);
-	free(sess);
-	return rc;
-}
-
 /** Close SIF session.
  *
@@ -274,5 +353,13 @@
 const char *sif_node_get_attr(sif_node_t *node, const char *aname)
 {
-	return NULL;
+	odlink_t *link;
+	sif_attr_t *attr;
+
+	link = odict_find_eq(&node->attrs, (void *)aname, NULL);
+	if (link == NULL)
+		return NULL;
+
+	attr = odict_get_instance(link, sif_attr_t, lattrs);
+	return attr->avalue;
 }
 
@@ -296,4 +383,7 @@
 
 /** Commit SIF transaction.
+ *
+ * Commit and free the transaction. If an error is returned, that means
+ * the transaction has not been freed (and sif_trans_abort() must be used).
  *
  * @param trans Transaction
@@ -320,4 +410,5 @@
 void sif_trans_abort(sif_trans_t *trans)
 {
+	free(trans);
 }
 
@@ -461,12 +552,4 @@
 void sif_node_destroy(sif_trans_t *trans, sif_node_t *node)
 {
-	sif_node_t *child;
-
-	child = sif_node_first_child(node);
-	while (child != NULL) {
-		sif_node_destroy(trans, child);
-		child = sif_node_first_child(node);
-	}
-
 	list_remove(&node->lparent);
 	sif_node_delete(node);
@@ -483,6 +566,40 @@
  */
 errno_t sif_node_set_attr(sif_trans_t *trans, sif_node_t *node,
-    const char *aname, const char *value)
-{
+    const char *aname, const char *avalue)
+{
+	odlink_t *link;
+	sif_attr_t *attr;
+	char *cvalue;
+
+	link = odict_find_eq(&node->attrs, (void *)aname, NULL);
+
+	if (link != NULL) {
+		attr = odict_get_instance(link, sif_attr_t, lattrs);
+		cvalue = str_dup(avalue);
+		if (cvalue == NULL)
+			return ENOMEM;
+
+		free(attr->avalue);
+		attr->avalue = cvalue;
+	} else {
+		attr = sif_attr_new(node);
+		if (attr == NULL)
+			return ENOMEM;
+
+		attr->aname = str_dup(aname);
+		if (attr->aname == NULL) {
+			sif_attr_delete(attr);
+			return ENOMEM;
+		}
+
+		attr->avalue = str_dup(avalue);
+		if (attr->avalue == NULL) {
+			sif_attr_delete(attr);
+			return ENOMEM;
+		}
+
+		odict_insert(&attr->lattrs, &node->attrs, NULL);
+	}
+
 	return EOK;
 }
@@ -500,4 +617,14 @@
     const char *aname)
 {
+	odlink_t *link;
+	sif_attr_t *attr;
+
+	link = odict_find_eq(&node->attrs, (void *)aname, NULL);
+	if (link == NULL)
+		return;
+
+	attr = odict_get_instance(link, sif_attr_t, lattrs);
+	odict_remove(link);
+	sif_attr_delete(attr);
 }
 
@@ -601,4 +728,77 @@
 }
 
+/** Import SIF attribute from file.
+ *
+ * @param node Node under which attribute shou
+ * @param f File
+ * @param rattr Place to store pointer to imported SIF attribute
+ * @return EOK on success, EIO on I/O error
+ */
+static errno_t sif_import_attr(sif_node_t *node, FILE *f, sif_attr_t **rattr)
+{
+	errno_t rc;
+	char *aname = NULL;
+	char *avalue = NULL;
+	sif_attr_t *attr;
+	int c;
+
+	rc = sif_import_string(f, &aname);
+	if (rc != EOK)
+		goto error;
+
+	c = fgetc(f);
+	if (c != '=') {
+		rc = EIO;
+		goto error;
+	}
+
+	rc = sif_import_string(f, &avalue);
+	if (rc != EOK)
+		goto error;
+
+	attr = sif_attr_new(node);
+	if (attr == NULL) {
+		rc = ENOMEM;
+		goto error;
+	}
+
+	attr->aname = aname;
+	attr->avalue = avalue;
+
+	*rattr = attr;
+	return EOK;
+error:
+	if (aname != NULL)
+		free(aname);
+	if (avalue != NULL)
+		free(avalue);
+	return rc;
+}
+
+
+/** Export SIF attribute to file.
+ *
+ * @param attr SIF attribute
+ * @param f File
+ * @return EOK on success, EIO on I/O error
+ */
+static errno_t sif_export_attr(sif_attr_t *attr, FILE *f)
+{
+	errno_t rc;
+
+	rc = sif_export_string(attr->aname, f);
+	if (rc != EOK)
+		return rc;
+
+	if (fputc('=', f) == EOF)
+		return EIO;
+
+	rc = sif_export_string(attr->avalue, f);
+	if (rc != EOK)
+		return rc;
+
+	return EOK;
+}
+
 /** Export SIF node to file.
  *
@@ -610,4 +810,5 @@
 {
 	errno_t rc;
+	sif_attr_t *attr;
 	sif_node_t *child;
 
@@ -615,4 +816,23 @@
 	if (rc != EOK)
 		return rc;
+
+	/* Attributes */
+
+	if (fputc('(', f) == EOF)
+		return EIO;
+
+	attr = sif_node_first_attr(node);
+	while (attr != NULL) {
+		rc = sif_export_attr(attr, f);
+		if (rc != EOK)
+			return rc;
+
+		attr = sif_node_next_attr(attr);
+	}
+
+	if (fputc(')', f) == EOF)
+		return EIO;
+
+	/* Child nodes */
 
 	if (fputc('{', f) == EOF)
@@ -646,4 +866,5 @@
 	sif_node_t *node = NULL;
 	sif_node_t *child;
+	sif_attr_t *attr;
 	char *ntype;
 	int c;
@@ -659,6 +880,8 @@
 	node->ntype = ntype;
 
+	/* Attributes */
+
 	c = fgetc(f);
-	if (c != '{') {
+	if (c != '(') {
 		rc = EIO;
 		goto error;
@@ -671,12 +894,12 @@
 	}
 
-	while (c != '}') {
+	while (c != ')') {
 		ungetc(c, f);
 
-		rc = sif_import_node(node, f, &child);
+		rc = sif_import_attr(node, f, &attr);
 		if (rc != EOK)
 			goto error;
 
-		list_append(&child->lparent, &node->children);
+		odict_insert(&attr->lattrs, &node->attrs, NULL);
 
 		c = fgetc(f);
@@ -687,4 +910,34 @@
 	}
 
+	/* Child nodes */
+
+	c = fgetc(f);
+	if (c != '{') {
+		rc = EIO;
+		goto error;
+	}
+
+	c = fgetc(f);
+	if (c == EOF) {
+		rc = EIO;
+		goto error;
+	}
+
+	while (c != '}') {
+		ungetc(c, f);
+
+		rc = sif_import_node(node, f, &child);
+		if (rc != EOK)
+			goto error;
+
+		list_append(&child->lparent, &node->children);
+
+		c = fgetc(f);
+		if (c == EOF) {
+			rc = EIO;
+			goto error;
+		}
+	}
+
 	*rnode = node;
 	return EOK;
@@ -694,4 +947,63 @@
 }
 
+/** Get first attribute or a node.
+ *
+ * @param node SIF node
+ * @return First attribute or @c NULL if there is none
+ */
+static sif_attr_t *sif_node_first_attr(sif_node_t *node)
+{
+	odlink_t *link;
+
+	link = odict_first(&node->attrs);
+	if (link == NULL)
+		return NULL;
+
+	return odict_get_instance(link, sif_attr_t, lattrs);
+}
+
+/** Get next attribute or a node.
+ *
+ * @param cur Current attribute
+ * @return Next attribute or @c NULL if there is none
+ */
+static sif_attr_t *sif_node_next_attr(sif_attr_t *cur)
+{
+	odlink_t *link;
+
+	link = odict_next(&cur->lattrs, &cur->node->attrs);
+	if (link == NULL)
+		return NULL;
+
+	return odict_get_instance(link, sif_attr_t, lattrs);
+}
+
+/** Get key callback for ordered dictionary of node attributes.
+ *
+ * @param link Ordered dictionary link of attribute
+ * @return Pointer to attribute name
+ */
+static void *sif_attr_getkey(odlink_t *link)
+{
+	return (void *)odict_get_instance(link, sif_attr_t, lattrs)->aname;
+}
+
+/** Comparison callback for  ordered dictionary of node attributes.
+ *
+ * @param a Name of first attribute
+ * @param b Name of second attribute
+ * @return Less than zero, zero or greater than zero, if a < b, a == b, a > b,
+ *         respectively.
+ */
+static int sif_attr_cmp(void *a, void *b)
+{
+	char *ca, *cb;
+
+	ca = (char *)a;
+	cb = (char *)b;
+
+	return str_cmp(ca, cb);
+}
+
 /** @}
  */
