Index: .gitignore
===================================================================
--- .gitignore	(revision 6bb136b2fbaaf208b1d7f318f3ef4951346fdf35)
+++ .gitignore	(revision b79903b9e7df762a73fa73393777fd7e694a1690)
@@ -82,4 +82,5 @@
 uspace/app/blkdump/blkdump
 uspace/app/bnchmark/bnchmark
+uspace/app/contacts/contacts
 uspace/app/corecfg/corecfg
 uspace/app/cpptest/cpptest
@@ -380,4 +381,5 @@
 uspace/lib/posix/redefs-hide-libc-symbols.list
 uspace/lib/posix/test-libposix
+uspace/lib/sif/test-libsif
 uspace/lib/uri/test-liburi
 uspace/srv/audio/hound/hound
Index: boot/Makefile.common
===================================================================
--- boot/Makefile.common	(revision 6bb136b2fbaaf208b1d7f318f3ef4951346fdf35)
+++ boot/Makefile.common	(revision b79903b9e7df762a73fa73393777fd7e694a1690)
@@ -176,4 +176,5 @@
 	$(USPACE_PATH)/app/blkdump/blkdump \
 	$(USPACE_PATH)/app/bnchmark/bnchmark \
+	$(USPACE_PATH)/app/contacts/contacts \
 	$(USPACE_PATH)/app/corecfg/corecfg \
 	$(USPACE_PATH)/app/cpptest/cpptest \
Index: uspace/Makefile
===================================================================
--- uspace/Makefile	(revision 6bb136b2fbaaf208b1d7f318f3ef4951346fdf35)
+++ uspace/Makefile	(revision b79903b9e7df762a73fa73393777fd7e694a1690)
@@ -39,4 +39,5 @@
 	app/blkdump \
 	app/bnchmark \
+	app/contacts \
 	app/corecfg \
 	app/cpptest \
@@ -230,4 +231,5 @@
 	lib/fmtutil \
 	lib/scsi \
+	lib/sif \
 	lib/compress \
 	lib/drv \
Index: uspace/app/contacts/Makefile
===================================================================
--- uspace/app/contacts/Makefile	(revision b79903b9e7df762a73fa73393777fd7e694a1690)
+++ uspace/app/contacts/Makefile	(revision b79903b9e7df762a73fa73393777fd7e694a1690)
@@ -0,0 +1,36 @@
+#
+# Copyright (c) 2018 Jiri Svoboda
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# - Redistributions of source code must retain the above copyright
+#   notice, this list of conditions and the following disclaimer.
+# - Redistributions in binary form must reproduce the above copyright
+#   notice, this list of conditions and the following disclaimer in the
+#   documentation and/or other materials provided with the distribution.
+# - The name of the author may not be used to endorse or promote products
+#   derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+USPACE_PREFIX = ../..
+BINARY = contacts
+LIBS = clui sif
+
+SOURCES = \
+	contacts.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/app/contacts/contacts.c
===================================================================
--- uspace/app/contacts/contacts.c	(revision b79903b9e7df762a73fa73393777fd7e694a1690)
+++ uspace/app/contacts/contacts.c	(revision b79903b9e7df762a73fa73393777fd7e694a1690)
@@ -0,0 +1,326 @@
+/*
+ * Copyright (c) 2018 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup contacts
+ * @{
+ */
+
+/**
+ * @file Contact list application.
+ *
+ * Maintain a contact list / address book. The main purpose of this
+ * trivial application is to serve as an example of using SIF.
+ */
+
+#include <adt/list.h>
+#include <errno.h>
+#include <nchoice.h>
+#include <sif.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <str.h>
+
+/** Contacts */
+typedef struct {
+	/** Open SIF repository */
+	sif_sess_t *repo;
+	/** Entries SIF node */
+	sif_node_t *nentries;
+	/** Entries list (of contacts_entry_t) */
+	list_t entries;
+} contacts_t;
+
+/** Contact entry */
+typedef struct {
+	/** Link to contacts_t.entries */
+	link_t lentries;
+} contacts_entry_t;
+
+/** Actions in contact menu */
+typedef enum {
+	/** Create new contact */
+	ac_create_contact,
+	/** Delete contact */
+	ac_delete_contact,
+	/** Exit */
+	ac_exit
+} contact_action_t;
+
+/** Open contacts repo or create it if it does not exist.
+ *
+ * @param fname File name
+ * @param rcontacts Place to store pointer to contacts object.
+ *
+ * @return EOK on success or error code
+ */
+static errno_t contacts_open(const char *fname, contacts_t **rcontacts)
+{
+	contacts_t *contacts = NULL;
+	sif_sess_t *repo = NULL;
+	sif_trans_t *trans = NULL;
+	sif_node_t *node;
+	const char *ntype;
+	errno_t rc;
+
+	contacts = calloc(1, sizeof(contacts_t));
+	if (contacts == NULL)
+		return ENOMEM;
+
+	list_initialize(&contacts->entries);
+
+	/* Try to open an existing repository. */
+	rc = sif_open(fname, &repo);
+	if (rc != EOK) {
+		/* Failed to open existing, create new repository */
+		rc = sif_create(fname, &repo);
+		if (rc != EOK)
+			goto error;
+
+		/* Start a transaction */
+		rc = sif_trans_begin(repo, &trans);
+		if (rc != EOK)
+			goto error;
+
+		/* Create 'entries' node container for all entries */
+		rc = sif_node_append_child(trans, sif_get_root(repo), "entries",
+		    &contacts->nentries);
+		if (rc != EOK)
+			goto error;
+
+		/* Finish the transaction */
+		rc = sif_trans_end(trans);
+		if (rc != EOK)
+			goto error;
+
+		trans = NULL;
+	} else {
+		/*
+		 * Opened an existing repository. Find the 'entries' node.
+		 * It should be the very first child of the root node.
+		 * This is okay to do in general, as long as we don't
+		 * require forward compatibility (which we don't).
+		 */
+		node = sif_node_first_child(sif_get_root(repo));
+		if (node == NULL) {
+			rc = EIO;
+			goto error;
+		}
+
+		/* Verify it's the correct node type(!) */
+		ntype = sif_node_get_type(node);
+		if (str_cmp(ntype, "entries") != 0) {
+			rc = EIO;
+			goto error;
+		}
+
+		contacts->nentries = node;
+	}
+
+	contacts->repo = repo;
+	*rcontacts = contacts;
+
+	return EOK;
+error:
+	if (trans != NULL)
+		sif_trans_abort(trans);
+	if (repo != NULL)
+		(void) sif_close(repo);
+	if (contacts != NULL)
+		free(contacts);
+	return rc;
+}
+
+/** Interaction to create new contact.
+ *
+ * @param contacts Contacts
+ */
+static errno_t contacts_create_contact(contacts_t *contacts)
+{
+	tinput_t *tinput;
+	sif_trans_t *trans = NULL;
+	sif_node_t *nentry;
+	errno_t rc;
+	char *cname = NULL;
+
+	tinput = tinput_new();
+	if (tinput == NULL)
+		return ENOMEM;
+
+	printf("Contact name:\n");
+
+	rc = tinput_set_prompt(tinput, "?> ");
+	if (rc != EOK)
+		goto error;
+
+	rc = tinput_read(tinput, &cname);
+	if (rc != EOK)
+		goto error;
+
+	rc = sif_trans_begin(contacts->repo, &trans);
+	if (rc != EOK)
+		goto error;
+
+	rc = sif_node_append_child(trans, contacts->nentries, "entry", &nentry);
+	if (rc != EOK)
+		goto error;
+
+	rc = sif_node_set_attr(trans, nentry, "name", cname);
+	if (rc != EOK)
+		goto error;
+
+	rc = sif_trans_end(trans);
+	if (rc != EOK)
+		goto error;
+
+	trans = NULL;
+
+	free(cname);
+	tinput_destroy(tinput);
+error:
+	if (trans != NULL)
+		sif_trans_abort(trans);
+	if (cname != NULL)
+		free(cname);
+	return rc;
+}
+
+/** Interaction to delete contact.
+ *
+ * @param contacts Contacts
+ */
+static errno_t contacts_delete_contact(contacts_t *contacts)
+{
+	return EOK;
+}
+
+/** Close contacts repo.
+ *
+ * @param contacts Contacts
+ */
+static void contacts_close(contacts_t *contacts)
+{
+	printf("Closing repo %p\n", contacts->repo);
+	sif_close(contacts->repo);
+	free(contacts);
+}
+
+/** Run contacts main menu.
+ *
+ * @param contacts Contacts
+ * @return EOK on success or error code
+ */
+static errno_t contacts_main(contacts_t *contacts)
+{
+	nchoice_t *choice = NULL;
+	errno_t rc;
+	bool quit = false;
+	void *sel;
+
+	rc = nchoice_create(&choice);
+	if (rc != EOK) {
+		assert(rc == ENOMEM);
+		printf("Out of memory.\n");
+		goto error;
+	}
+
+	rc = nchoice_set_prompt(choice, "Select action");
+	if (rc != EOK) {
+		assert(rc == ENOMEM);
+		printf("Out of memory.\n");
+		goto error;
+	}
+
+	rc = nchoice_add(choice, "Create contact",
+	    (void *)ac_create_contact, 0);
+	if (rc != EOK) {
+		assert(rc == ENOMEM);
+		printf("Out of memory.\n");
+		goto error;
+	}
+
+	rc = nchoice_add(choice, "Delete contact",
+	    (void *)ac_delete_contact, 0);
+	if (rc != EOK) {
+		assert(rc == ENOMEM);
+		printf("Out of memory.\n");
+		goto error;
+	}
+
+	rc = nchoice_add(choice, "Exit",
+	    (void *)ac_exit, 0);
+	if (rc != EOK) {
+		assert(rc == ENOMEM);
+		printf("Out of memory.\n");
+		goto error;
+	}
+
+	while (!quit) {
+		rc = nchoice_get(choice, &sel);
+		if (rc != EOK) {
+			printf("Error getting user selection.\n");
+			return rc;
+		}
+
+		switch ((contact_action_t)sel) {
+		case ac_create_contact:
+			(void) contacts_create_contact(contacts);
+			break;
+		case ac_delete_contact:
+			(void) contacts_delete_contact(contacts);
+			break;
+		case ac_exit:
+			quit = true;
+			break;
+		}
+	}
+
+	nchoice_destroy(choice);
+	return EOK;
+error:
+	if (choice != NULL)
+		nchoice_destroy(choice);
+	return rc;
+}
+
+int main(void)
+{
+	errno_t rc;
+	contacts_t *contacts;
+
+	rc = contacts_open("contacts.sif", &contacts);
+	if (rc != EOK)
+		return 1;
+
+	rc = contacts_main(contacts);
+	contacts_close(contacts);
+
+	if (rc != EOK)
+		return 1;
+
+	return 0;
+}
Index: uspace/lib/c/include/stdio.h
===================================================================
--- uspace/lib/c/include/stdio.h	(revision 6bb136b2fbaaf208b1d7f318f3ef4951346fdf35)
+++ uspace/lib/c/include/stdio.h	(revision b79903b9e7df762a73fa73393777fd7e694a1690)
@@ -178,5 +178,5 @@
 
 extern FILE *tmpfile(void);
-extern char *tmpnam(char *s) __attribute__((deprecated));
+extern char *tmpnam(char *s);
 
 #ifdef _HELENOS_SOURCE
Index: uspace/lib/sif/Makefile
===================================================================
--- uspace/lib/sif/Makefile	(revision b79903b9e7df762a73fa73393777fd7e694a1690)
+++ uspace/lib/sif/Makefile	(revision b79903b9e7df762a73fa73393777fd7e694a1690)
@@ -0,0 +1,40 @@
+#
+# Copyright (c) 2018 Jiri Svoboda
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# - Redistributions of source code must retain the above copyright
+#   notice, this list of conditions and the following disclaimer.
+# - Redistributions in binary form must reproduce the above copyright
+#   notice, this list of conditions and the following disclaimer in the
+#   documentation and/or other materials provided with the distribution.
+# - The name of the author may not be used to endorse or promote products
+#   derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+USPACE_PREFIX = ../..
+
+LIBRARY = libsif
+
+SOURCES = \
+	src/sif.c
+
+TEST_SOURCES = \
+	test/main.c \
+	test/sif.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/lib/sif/include/sif.h
===================================================================
--- uspace/lib/sif/include/sif.h	(revision b79903b9e7df762a73fa73393777fd7e694a1690)
+++ uspace/lib/sif/include/sif.h	(revision b79903b9e7df762a73fa73393777fd7e694a1690)
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2018 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup libsif
+ * @{
+ */
+/**
+ * @file
+ * @brief
+ */
+
+#ifndef SIF_H_
+#define SIF_H_
+
+#include <errno.h>
+
+struct sif_sess;
+typedef struct sif_sess sif_sess_t;
+
+struct sif_trans;
+typedef struct sif_trans sif_trans_t;
+
+struct sif_node;
+typedef struct sif_node sif_node_t;
+
+errno_t sif_create(const char *, sif_sess_t **);
+errno_t sif_open(const char *, sif_sess_t **);
+errno_t sif_close(sif_sess_t *);
+sif_node_t *sif_get_root(sif_sess_t *);
+
+sif_node_t *sif_node_first_child(sif_node_t *);
+sif_node_t *sif_node_next_child(sif_node_t *);
+const char *sif_node_get_type(sif_node_t *);
+const char *sif_node_get_attr(sif_node_t *, const char *);
+
+errno_t sif_trans_begin(sif_sess_t *, sif_trans_t **);
+void sif_trans_abort(sif_trans_t *);
+errno_t sif_trans_end(sif_trans_t *);
+
+errno_t sif_node_prepend_child(sif_trans_t *, sif_node_t *, const char *,
+    sif_node_t **);
+errno_t sif_node_append_child(sif_trans_t *, sif_node_t *, const char *,
+    sif_node_t **);
+errno_t sif_node_insert_before(sif_trans_t *, sif_node_t *, const char *,
+    sif_node_t **);
+errno_t sif_node_insert_after(sif_trans_t *, sif_node_t *, const char *,
+    sif_node_t **);
+void sif_node_destroy(sif_trans_t *, sif_node_t *);
+errno_t sif_node_set_attr(sif_trans_t *, sif_node_t *, const char *,
+    const char *);
+void sif_node_unset_attr(sif_trans_t *, sif_node_t *, const char *);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/sif/private/sif.h
===================================================================
--- uspace/lib/sif/private/sif.h	(revision b79903b9e7df762a73fa73393777fd7e694a1690)
+++ uspace/lib/sif/private/sif.h	(revision b79903b9e7df762a73fa73393777fd7e694a1690)
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2018 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup libsif
+ * @{
+ */
+/**
+ * @file
+ * @brief
+ */
+
+#ifndef PRIVATE_SIF_H_
+#define PRIVATE_SIF_H_
+
+#include <adt/list.h>
+#include <stdio.h>
+
+/** SIF session */
+struct sif_sess {
+	/** Backing file */
+	FILE *f;
+	/** Root node */
+	struct sif_node *root;
+};
+
+/** SIF transaction */
+struct sif_trans {
+	struct sif_sess *sess;
+};
+
+/** SIF node */
+struct sif_node {
+	/** Parent node or @c NULL in case of root node */
+	struct sif_node *parent;
+	/** Link to parent->children */
+	link_t lparent;
+	/** Node type */
+	char *ntype;
+	/** Child nodes */
+	list_t children;
+};
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/sif/src/sif.c
===================================================================
--- uspace/lib/sif/src/sif.c	(revision b79903b9e7df762a73fa73393777fd7e694a1690)
+++ uspace/lib/sif/src/sif.c	(revision b79903b9e7df762a73fa73393777fd7e694a1690)
@@ -0,0 +1,570 @@
+/*
+ * Copyright (c) 2018 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/** @addtogroup libsif
+ * @{
+ */
+/**
+ * @file Structured Information Format
+ *
+ * Structured Information Format (SIF) is an API that allows an application
+ * to maintain data in a persistent repository in a format that
+ *
+ *  - is structured (and hence extensible)
+ *  - allows atomic (transactional) updates
+ *  - allows efficient updates
+ *
+ * SIF is meant to be used as the basis for the storage backend used to
+ * maintain application or configuration data. SIF is *not* a (relational)
+ * database (not even close). The structure of a SIF repository is quite
+ * similar to an XML document that contains just tags with attributes
+ * (but no text).
+ *
+ * SIF can thus naturally express ordered lists (unlike a relational database).
+ * When contrasted to a (relational) database, SIF is much more primitive.
+ *
+ * In particular, SIF
+ *
+ *  - does not run on a separate server
+ *  - des not handle concurrency
+ *  - does not have a notion of types or data validation
+ *  - does not understand relations
+ *  - does not implement any kind of search/queries
+ *  - does not deal with data sets large enough not to fit in primary memory
+ *
+ * any kind of structure data validation is left up to the application.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <str.h>
+#include "../include/sif.h"
+#include "../private/sif.h"
+
+static errno_t sif_export_node(sif_node_t *, FILE *);
+
+/** Create new SIF node.
+ *
+ * @param parent Parent onde
+ * @return Pointer to new node on success or @c NULL if out of memory
+ */
+static sif_node_t *sif_node_new(sif_node_t *parent)
+{
+	sif_node_t *node;
+
+	node = calloc(1, sizeof(sif_node_t));
+	if (node == NULL)
+		return NULL;
+
+	node->parent = parent;
+	list_initialize(&node->children);
+
+	return node;
+}
+
+/** Delete SIF node.
+ *
+ * Delete a SIF node that has been already unlinked from the tree.
+ *
+ * @param node Node
+ */
+static void sif_node_delete(sif_node_t *node)
+{
+	if (node == NULL)
+		return;
+
+	if (node->ntype != NULL)
+		free(node->ntype);
+
+	free(node);
+}
+
+/** Create and open a 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_create(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;
+
+	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;
+	*rsess = sess;
+	return EOK;
+error:
+	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;
+	sif_node_t *root = 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, "r+");
+	if (f == NULL) {
+		rc = EIO;
+		goto error;
+	}
+
+	sess->f = f;
+	sess->root = root;
+	*rsess = sess;
+	return EOK;
+error:
+	sif_node_delete(root);
+	free(sess);
+	return rc;
+}
+
+/** Close SIF session.
+ *
+ * @param sess SIF session
+ * @return EOK on success or error code
+ */
+errno_t sif_close(sif_sess_t *sess)
+{
+	sif_node_delete(sess->root);
+
+	if (fclose(sess->f) < 0) {
+		free(sess);
+		return EIO;
+	}
+
+	return EOK;
+}
+
+/** Return root node.
+ *
+ * @param sess SIF session
+ */
+sif_node_t *sif_get_root(sif_sess_t *sess)
+{
+	return sess->root;
+}
+
+/** Get first child of a node.
+ *
+ * @param parent Parent node
+ * @return First child node or @c NULL if @a parent has no children
+ */
+sif_node_t *sif_node_first_child(sif_node_t *parent)
+{
+	link_t *link;
+
+	link = list_first(&parent->children);
+	if (link == NULL)
+		return NULL;
+
+	return list_get_instance(link, sif_node_t, lparent);
+}
+
+/** Get next child of a node.
+ *
+ * @param current Current node
+ * @return Next child (i.e. next sibling of @a current)
+ */
+sif_node_t *sif_node_next_child(sif_node_t *current)
+{
+	link_t *link;
+
+	link = list_next(&current->lparent, &current->parent->children);
+	if (link == NULL)
+		return NULL;
+
+	return list_get_instance(link, sif_node_t, lparent);
+}
+
+/** Get node type.
+ *
+ * @param node SIF node
+ * @return Pointer to string, valid until next modification.
+ */
+const char *sif_node_get_type(sif_node_t *node)
+{
+	return node->ntype;
+}
+
+/** Get node attribute.
+ *
+ * @param node SIF node
+ * @param aname Attribute name
+ *
+ * @return Attribute value or @c NULL if attribute is not set
+ */
+const char *sif_node_get_attr(sif_node_t *node, const char *aname)
+{
+	return NULL;
+}
+
+/** Begin SIF transaction.
+ *
+ * @param trans Transaction
+ * @return EOK on success or error code
+ */
+errno_t sif_trans_begin(sif_sess_t *sess, sif_trans_t **rtrans)
+{
+	sif_trans_t *trans;
+
+	trans = calloc(1, sizeof(sif_trans_t));
+	if (trans == NULL)
+		return ENOMEM;
+
+	trans->sess = sess;
+	*rtrans = trans;
+	return EOK;
+}
+
+/** Commit SIF transaction.
+ *
+ * @param trans Transaction
+ * @return EOK on success or error code
+ */
+errno_t sif_trans_end(sif_trans_t *trans)
+{
+	errno_t rc;
+
+	rewind(trans->sess->f);
+
+	rc = sif_export_node(trans->sess->root, trans->sess->f);
+	if (rc != EOK)
+		return rc;
+
+	free(trans);
+	return EOK;
+}
+
+/** Abort SIF transaction.
+ *
+ * @param trans Transaction
+ */
+void sif_trans_abort(sif_trans_t *trans)
+{
+}
+
+/** Prepend new child.
+ *
+ * Create a new child and prepend it at the beginning of children list of
+ * @a parent.
+ *
+ * @param trans Transaction
+ * @param parent Parent node
+ * @param ctype Child type
+ * @param rchild Place to store pointer to new child
+ *
+ * @return EOK on success or ENOMEM if out of memory
+ */
+errno_t sif_node_prepend_child(sif_trans_t *trans, sif_node_t *parent,
+    const char *ctype, sif_node_t **rchild)
+{
+	sif_node_t *child;
+
+	child = sif_node_new(parent);
+	if (child == NULL)
+		return ENOMEM;
+
+	child->ntype = str_dup(ctype);
+	if (child->ntype == NULL) {
+		sif_node_delete(child);
+		return ENOMEM;
+	}
+
+	list_prepend(&child->lparent, &parent->children);
+
+	*rchild = child;
+	return EOK;
+}
+
+/** Append new child.
+ *
+ * Create a new child and append it at the end of children list of @a parent.
+ *
+ * @param trans Transaction
+ * @param parent Parent node
+ * @param ctype Child type
+ * @param rchild Place to store pointer to new child
+ *
+ * @return EOK on success or ENOMEM if out of memory
+ */
+errno_t sif_node_append_child(sif_trans_t *trans, sif_node_t *parent,
+    const char *ctype, sif_node_t **rchild)
+{
+	sif_node_t *child;
+
+	child = sif_node_new(parent);
+	if (child == NULL)
+		return ENOMEM;
+
+	child->ntype = str_dup(ctype);
+	if (child->ntype == NULL) {
+		sif_node_delete(child);
+		return ENOMEM;
+	}
+
+	list_append(&child->lparent, &parent->children);
+
+	*rchild = child;
+	return EOK;
+}
+
+/** Insert new child before existing child.
+ *
+ * Create a new child and insert it before an existing child.
+ *
+ * @param trans Transaction
+ * @param sibling Sibling before which to insert
+ * @param ctype Child type
+ * @param rchild Place to store pointer to new child
+ *
+ * @return EOK on success or ENOMEM if out of memory
+ */
+errno_t sif_node_insert_before(sif_trans_t *trans, sif_node_t *sibling,
+    const char *ctype, sif_node_t **rchild)
+{
+	sif_node_t *child;
+
+	child = sif_node_new(sibling->parent);
+	if (child == NULL)
+		return ENOMEM;
+
+	child->ntype = str_dup(ctype);
+	if (child->ntype == NULL) {
+		sif_node_delete(child);
+		return ENOMEM;
+	}
+
+	list_insert_before(&child->lparent, &sibling->lparent);
+
+	*rchild = child;
+	return EOK;
+}
+
+/** Insert new child after existing child.
+ *
+ * Create a new child and insert it after an existing child.
+ *
+ * @param trans Transaction
+ * @param sibling Sibling after which to insert
+ * @param ctype Child type
+ * @param rchild Place to store pointer to new child
+ *
+ * @return EOK on success or ENOMEM if out of memory
+ */
+errno_t sif_node_insert_after(sif_trans_t *trans, sif_node_t *sibling,
+    const char *ctype, sif_node_t **rchild)
+{
+	sif_node_t *child;
+
+	child = sif_node_new(sibling->parent);
+	if (child == NULL)
+		return ENOMEM;
+
+	child->ntype = str_dup(ctype);
+	if (child->ntype == NULL) {
+		sif_node_delete(child);
+		return ENOMEM;
+	}
+
+	list_insert_after(&child->lparent, &sibling->lparent);
+
+	*rchild = child;
+	return EOK;
+}
+
+/** Destroy SIF node.
+ *
+ * This function does not return an error, but the transaction may still
+ * fail to complete.
+ *
+ * @param trans Transaction
+ * @param node Node to destroy
+ */
+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);
+}
+
+/** Set node attribute.
+ *
+ * @param trans Transaction
+ * @param node SIF node
+ * @param aname Attribute name
+ * @param value Attribute value
+ *
+ * @return EOK on success, ENOMEM if out of memory
+ */
+errno_t sif_node_set_attr(sif_trans_t *trans, sif_node_t *node,
+    const char *aname, const char *value)
+{
+	return EOK;
+}
+
+/** Unset node attribute.
+ *
+ * This function does not return an error, but the transaction may still
+ * fail to complete.
+ *
+ * @param trans Transaction
+ * @param node Node
+ * @param aname Attribute name
+ */
+void sif_node_unset_attr(sif_trans_t *trans, sif_node_t *node,
+    const char *aname)
+{
+}
+
+/** Export string to file.
+ *
+ * Export string to file (the string is bracketed and escaped).
+ *
+ * @param str String
+ * @param f File
+ * @return EOK on success, EIO on I/O error
+ */
+static errno_t sif_export_string(const char *str, FILE *f)
+{
+	const char *cp;
+
+	if (fputc('[', f) == EOF)
+		return EIO;
+
+	cp = str;
+	while (*cp != '\0') {
+		if (*cp == ']' || *cp == '\\') {
+			if (fputc('\\', f) == EOF)
+				return EIO;
+		}
+		if (fputc(*cp, f) == EOF)
+			return EIO;
+		++cp;
+	}
+
+	if (fputc(']', f) == EOF)
+		return EIO;
+
+	return EOK;
+}
+
+/** Export SIF node to file.
+ *
+ * @param node SIF node
+ * @param f File
+ * @return EOK on success, EIO on I/O error
+ */
+static errno_t sif_export_node(sif_node_t *node, FILE *f)
+{
+	errno_t rc;
+	sif_node_t *child;
+
+	rc = sif_export_string(node->ntype, f);
+	if (rc != EOK)
+		return rc;
+
+	if (fputc('{', f) == EOF)
+		return EIO;
+
+	child = sif_node_first_child(node);
+	while (child != NULL) {
+		rc = sif_export_node(child, f);
+		if (rc != EOK)
+			return rc;
+
+		child = sif_node_next_child(child);
+	}
+
+	if (fputc('}', f) == EOF)
+		return EIO;
+
+	return EOK;
+}
+
+/** @}
+ */
Index: uspace/lib/sif/test/main.c
===================================================================
--- uspace/lib/sif/test/main.c	(revision b79903b9e7df762a73fa73393777fd7e694a1690)
+++ uspace/lib/sif/test/main.c	(revision b79903b9e7df762a73fa73393777fd7e694a1690)
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2018 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <pcut/pcut.h>
+
+PCUT_INIT;
+
+PCUT_IMPORT(sif);
+
+PCUT_MAIN();
Index: uspace/lib/sif/test/sif.c
===================================================================
--- uspace/lib/sif/test/sif.c	(revision b79903b9e7df762a73fa73393777fd7e694a1690)
+++ uspace/lib/sif/test/sif.c	(revision b79903b9e7df762a73fa73393777fd7e694a1690)
@@ -0,0 +1,299 @@
+/*
+ * Copyright (c) 2018 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <errno.h>
+#include <pcut/pcut.h>
+#include <stdio.h>
+#include "../include/sif.h"
+
+PCUT_INIT;
+
+PCUT_TEST_SUITE(sif);
+
+/** Test sif_create. */
+PCUT_TEST(sif_create)
+{
+	sif_sess_t *sess;
+	errno_t rc;
+	int rv;
+	char *fname;
+	char *p;
+
+	fname = calloc(L_tmpnam, 1);
+	PCUT_ASSERT_NOT_NULL(fname);
+
+	p = tmpnam(fname);
+	PCUT_ASSERT_TRUE(p == fname);
+
+	rc = sif_create(fname, &sess);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = sif_close(sess);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rv = remove(fname);
+	PCUT_ASSERT_INT_EQUALS(0, rv);
+}
+
+/** Test sif_node_prepend_child. */
+PCUT_TEST(sif_node_prepend_child)
+{
+	sif_sess_t *sess;
+	sif_node_t *root;
+	sif_node_t *ca;
+	sif_node_t *cb;
+	sif_node_t *c1;
+	sif_node_t *c2;
+	sif_node_t *c3;
+	sif_trans_t *trans;
+	errno_t rc;
+	int rv;
+	char *fname;
+	char *p;
+
+	fname = calloc(L_tmpnam, 1);
+	PCUT_ASSERT_NOT_NULL(fname);
+
+	p = tmpnam(fname);
+	PCUT_ASSERT_TRUE(p == fname);
+
+	rc = sif_create(fname, &sess);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	root = sif_get_root(sess);
+
+	rc = sif_trans_begin(sess, &trans);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = sif_node_prepend_child(trans, root, "a", &ca);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = sif_node_prepend_child(trans, root, "b", &cb);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = sif_trans_end(trans);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	c1 = sif_node_first_child(root);
+	PCUT_ASSERT_TRUE(c1 == cb);
+
+	c2 = sif_node_next_child(c1);
+	PCUT_ASSERT_TRUE(c2 == ca);
+
+	c3 = sif_node_next_child(c2);
+	PCUT_ASSERT_TRUE(c3 == NULL);
+
+	rc = sif_close(sess);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rv = remove(fname);
+	PCUT_ASSERT_INT_EQUALS(0, rv);
+}
+
+/** Test sif_node_append_child. */
+PCUT_TEST(sif_node_append_child)
+{
+	sif_sess_t *sess;
+	sif_node_t *root;
+	sif_node_t *ca;
+	sif_node_t *cb;
+	sif_node_t *c1;
+	sif_node_t *c2;
+	sif_node_t *c3;
+	sif_trans_t *trans;
+	errno_t rc;
+	int rv;
+	char *fname;
+	char *p;
+
+	fname = calloc(L_tmpnam, 1);
+	PCUT_ASSERT_NOT_NULL(fname);
+
+	p = tmpnam(fname);
+	PCUT_ASSERT_TRUE(p == fname);
+
+	rc = sif_create(fname, &sess);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	root = sif_get_root(sess);
+
+	rc = sif_trans_begin(sess, &trans);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = sif_node_append_child(trans, root, "a", &ca);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = sif_node_append_child(trans, root, "b", &cb);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = sif_trans_end(trans);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	c1 = sif_node_first_child(root);
+	PCUT_ASSERT_TRUE(c1 == ca);
+
+	c2 = sif_node_next_child(c1);
+	PCUT_ASSERT_TRUE(c2 == cb);
+
+	c3 = sif_node_next_child(c2);
+	PCUT_ASSERT_TRUE(c3 == NULL);
+
+	rc = sif_close(sess);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rv = remove(fname);
+	PCUT_ASSERT_INT_EQUALS(0, rv);
+}
+
+/** Test sif_node_insert_before. */
+PCUT_TEST(sif_node_insert_before)
+{
+	sif_sess_t *sess;
+	sif_node_t *root;
+	sif_node_t *ca;
+	sif_node_t *cb;
+	sif_node_t *cc;
+	sif_node_t *c1;
+	sif_node_t *c2;
+	sif_node_t *c3;
+	sif_node_t *c4;
+	sif_trans_t *trans;
+	errno_t rc;
+	int rv;
+	char *fname;
+	char *p;
+
+	fname = calloc(L_tmpnam, 1);
+	PCUT_ASSERT_NOT_NULL(fname);
+
+	p = tmpnam(fname);
+	PCUT_ASSERT_TRUE(p == fname);
+
+	rc = sif_create(fname, &sess);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	root = sif_get_root(sess);
+
+	rc = sif_trans_begin(sess, &trans);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = sif_node_append_child(trans, root, "a", &ca);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = sif_node_append_child(trans, root, "c", &cc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = sif_node_insert_before(trans, cc, "b", &cb);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = sif_trans_end(trans);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	c1 = sif_node_first_child(root);
+	PCUT_ASSERT_TRUE(c1 == ca);
+
+	c2 = sif_node_next_child(c1);
+	PCUT_ASSERT_TRUE(c2 == cb);
+
+	c3 = sif_node_next_child(c2);
+	PCUT_ASSERT_TRUE(c3 == cc);
+
+	c4 = sif_node_next_child(c3);
+	PCUT_ASSERT_TRUE(c4 == NULL);
+
+	rc = sif_close(sess);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rv = remove(fname);
+	PCUT_ASSERT_INT_EQUALS(0, rv);
+}
+
+/** Test sif_node_insert_after. */
+PCUT_TEST(sif_node_insert_after)
+{
+	sif_sess_t *sess;
+	sif_node_t *root;
+	sif_node_t *ca;
+	sif_node_t *cb;
+	sif_node_t *cc;
+	sif_node_t *c1;
+	sif_node_t *c2;
+	sif_node_t *c3;
+	sif_node_t *c4;
+	sif_trans_t *trans;
+	errno_t rc;
+	int rv;
+	char *fname;
+	char *p;
+
+	fname = calloc(L_tmpnam, 1);
+	PCUT_ASSERT_NOT_NULL(fname);
+
+	p = tmpnam(fname);
+	PCUT_ASSERT_TRUE(p == fname);
+
+	rc = sif_create(fname, &sess);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	root = sif_get_root(sess);
+
+	rc = sif_trans_begin(sess, &trans);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = sif_node_append_child(trans, root, "a", &ca);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = sif_node_append_child(trans, root, "c", &cc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = sif_node_insert_after(trans, ca, "b", &cb);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = sif_trans_end(trans);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	c1 = sif_node_first_child(root);
+	PCUT_ASSERT_TRUE(c1 == ca);
+
+	c2 = sif_node_next_child(c1);
+	PCUT_ASSERT_TRUE(c2 == cb);
+
+	c3 = sif_node_next_child(c2);
+	PCUT_ASSERT_TRUE(c3 == cc);
+
+	c4 = sif_node_next_child(c3);
+	PCUT_ASSERT_TRUE(c4 == NULL);
+
+	rc = sif_close(sess);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rv = remove(fname);
+	PCUT_ASSERT_INT_EQUALS(0, rv);
+}
+
+PCUT_EXPORT(sif);
