Index: uspace/lib/conf/Makefile
===================================================================
--- uspace/lib/conf/Makefile	(revision bb154c64b6e4b7d6a2a8e19760b6ae60156847b4)
+++ uspace/lib/conf/Makefile	(revision bb154c64b6e4b7d6a2a8e19760b6ae60156847b4)
@@ -0,0 +1,39 @@
+#
+# Copyright (c) 2005 Martin Decky
+# Copyright (c) 2007 Jakub Jermar
+# 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 = libconf
+EXTRA_CFLAGS += -I./include
+
+SOURCES = \
+	src/configuration.c \
+	src/ini.c \
+	src/text_parse.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/lib/conf/include/conf/configuration.h
===================================================================
--- uspace/lib/conf/include/conf/configuration.h	(revision bb154c64b6e4b7d6a2a8e19760b6ae60156847b4)
+++ uspace/lib/conf/include/conf/configuration.h	(revision bb154c64b6e4b7d6a2a8e19760b6ae60156847b4)
@@ -0,0 +1,49 @@
+#ifndef _CONF_CONFIGURATION_H
+#define _CONF_CONFIGURATION_H
+
+#include <conf/ini.h>
+#include <conf/text_parse.h>
+#include <stdbool.h>
+
+/** Structure represents a declaration of a configuration value */
+typedef struct {
+	/** Value name */
+	const char *name;
+	
+	/** Parse and store given string
+	 *
+	 * @param[in]  string  string to parse (it's whitespace-trimmed)
+	 * @param[out] dst     pointer where to store parsed result (must be
+	 *                     preallocated)
+	 * @param[out] parse   parse struct to store parsing errors
+	 * @param[in]  lineno  line number of the original string
+	 *
+	 * @return  true on success
+	 * @return  false on error (e.g. format error, low memory)
+	 */
+	bool (*parse)(const char *, void *, text_parse_t *, size_t);
+
+	/** Offset in structure where parsed value ought be stored */
+	size_t offset;
+
+	/** String representation of default value
+	 *
+	 * Format is same as in input. NULL value denotes required
+	 * configuration value.
+	 */
+	const char *default_value;
+} config_item_t;
+
+/** Code of configuration processing error */
+typedef enum {
+	CONFIGURATION_EMISSING_ITEM = -1
+} config_error_t;
+
+#define CONFIGURATION_ITEM_SENTINEL {NULL}
+
+extern int config_load_ini_section(config_item_t *, ini_section_t *, void *,
+    text_parse_t *);
+
+extern bool config_parse_string(const char *, void *, text_parse_t *, size_t);
+
+#endif
Index: uspace/lib/conf/include/conf/ini.h
===================================================================
--- uspace/lib/conf/include/conf/ini.h	(revision bb154c64b6e4b7d6a2a8e19760b6ae60156847b4)
+++ uspace/lib/conf/include/conf/ini.h	(revision bb154c64b6e4b7d6a2a8e19760b6ae60156847b4)
@@ -0,0 +1,97 @@
+#ifndef _CONF_INI_H
+#define _CONF_INI_H
+
+#include <adt/hash_table.h>
+#include <conf/text_parse.h>
+#include <stdbool.h>
+
+/** INI file parsing error code */
+typedef enum {
+	INI_EOK = 0,
+	INI_ETOO_LONG = -101,
+	INI_EDUP_SECTION = -102,
+	INI_EASSIGN_EXPECTED = -103,
+	INI_EBRACKET_EXPECTED = -104
+} ini_error_t;
+
+/** Configuration from INI file
+ *
+ * Configuration consists of (named) sections and each section contains
+ * key-value pairs (both strings). Current implementation doesn't specify
+ * ordering of pairs and there can be multiple pairs with the same key (i.e.
+ * usable for set unions, not overwriting values).
+ *
+ * Sections are uniquely named and there can be at most one unnamed section,
+ * referred to as the default section.
+ *
+ * Configuration structure is owner of all key/value strings. If you need any
+ * of these strings out of the structure's lifespan, you have to make your own
+ * copy.
+ */
+typedef struct {
+	hash_table_t sections;
+} ini_configuration_t;
+
+/** INI configuration section */
+typedef struct {
+	ht_link_t ht_link;
+
+	/** Line number where section header is */
+	size_t lineno;
+
+	/** Name of the section (or NULL for default section) */
+	char *name;
+	hash_table_t items;
+} ini_section_t;
+
+/** INI configuration item iterator
+ *
+ * Use ini_item_iterator_* functions to manipulate the iterator.
+ *
+ * Example:
+ * @code
+ * ini_item_iterator_t it = ini_section_get_iterator(section, key);
+ * for (; ini_item_iterator_valid(&it); ini_item_iterator_inc(&it) {
+ * 	const char *value = ini_item_iterator_value(&it);
+ * 	...
+ * }
+ * @endcode
+ *
+ */
+typedef struct {
+	/** Section's hash table */
+	hash_table_t *table;
+
+	ht_link_t *cur_item;
+	ht_link_t *first_item;
+
+	/** Incremented flag
+	 *
+	 * Iterator implementation wraps hash table's cyclic lists. The flag
+	 * and initial hash table item allows linearization for iterator
+	 * interface.
+	 */
+	bool incremented;
+} ini_item_iterator_t;
+
+
+extern void ini_configuration_init(ini_configuration_t *);
+extern void ini_configuration_deinit(ini_configuration_t *);
+
+extern int ini_parse_file(const char *, ini_configuration_t *, text_parse_t *);
+
+extern ini_section_t *ini_get_section(ini_configuration_t *, const char *);
+
+extern ini_item_iterator_t ini_section_get_iterator(ini_section_t *, const char *);
+
+extern bool ini_item_iterator_valid(ini_item_iterator_t *);
+
+extern void ini_item_iterator_inc(ini_item_iterator_t *);
+
+extern const char *ini_item_iterator_value(ini_item_iterator_t *);
+
+extern size_t ini_item_iterator_lineno(ini_item_iterator_t *);
+
+
+
+#endif
Index: uspace/lib/conf/include/conf/text_parse.h
===================================================================
--- uspace/lib/conf/include/conf/text_parse.h	(revision bb154c64b6e4b7d6a2a8e19760b6ae60156847b4)
+++ uspace/lib/conf/include/conf/text_parse.h	(revision bb154c64b6e4b7d6a2a8e19760b6ae60156847b4)
@@ -0,0 +1,33 @@
+#ifndef _CONF_TEXT_PARSE_H
+#define _CONF_TEXT_PARSE_H
+
+#include <adt/list.h>
+#include <stdbool.h>
+
+/** Container of (recoverable) errors that could occur while parsing */
+typedef struct {
+	/** List of text_parse_error_t */
+	list_t errors;
+
+	/** Boolean flag to indicate errors (in case list couldn't be filled) */
+	bool has_error;
+} text_parse_t;
+
+/** An error of parsing */
+typedef struct {
+	link_t link;
+
+	/** Line where the error originated */
+	size_t lineno;
+
+	/** Code of parse error (name is chosen not to clash with errno) */
+	int parse_errno;
+} text_parse_error_t;
+
+
+extern void text_parse_init(text_parse_t *);
+extern void text_parse_deinit(text_parse_t *);
+
+extern void text_parse_raise_error(text_parse_t *, size_t, int);
+
+#endif
Index: uspace/lib/conf/src/configuration.c
===================================================================
--- uspace/lib/conf/src/configuration.c	(revision bb154c64b6e4b7d6a2a8e19760b6ae60156847b4)
+++ uspace/lib/conf/src/configuration.c	(revision bb154c64b6e4b7d6a2a8e19760b6ae60156847b4)
@@ -0,0 +1,99 @@
+#include "conf/configuration.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <str.h>
+
+static int config_load_item(config_item_t *config_item,
+    ini_item_iterator_t *it, void *dst, text_parse_t *parse)
+{
+	size_t cnt = 0;
+	void *field_dst = (char *)dst + config_item->offset;
+	bool has_error = false;
+
+	for (; ini_item_iterator_valid(it); ini_item_iterator_inc(it), ++cnt) {
+		const char *string = ini_item_iterator_value(it);
+		const size_t lineno = ini_item_iterator_lineno(it);
+		has_error = has_error ||
+		    config_item->parse(string, field_dst, parse, lineno);
+	}
+
+	if (cnt == 0) {
+		if (config_item->default_value == NULL) {
+			return ENOENT;
+		}
+		bool result = config_item->parse(config_item->default_value,
+		    field_dst, parse, 0);
+		/* Default string should be always correct */
+		assert(result);
+	}
+
+	return has_error ? EINVAL : EOK;
+}
+
+/** Process INI section as values to a structure
+ *
+ * @param[in]  specification  Mark-terminated array of config_item_t specifying
+ *                            available configuration values. The mark is value
+ *                            whose name is NULL, you can use
+ *                            CONFIGURATION_ITEM_SENTINEL.
+ * @param[in]  section        INI section with raw string data
+ * @param[out] dst            pointer to structure that holds parsed values
+ * @param[out] parse          structure for recording any parsing errors
+ *
+ * @return EOK on success
+ * @return EINVAL on any parsing errors (details in parse structure)
+ */
+int config_load_ini_section(config_item_t *specification,
+    ini_section_t *section, void *dst, text_parse_t *parse)
+{
+	bool has_error = false;
+
+	config_item_t *config_item = specification;
+	while (config_item->name != NULL) {
+		ini_item_iterator_t iterator =
+		    ini_section_get_iterator(section, config_item->name);
+
+		int rc = config_load_item(config_item, &iterator, dst, parse);
+		switch (rc) {
+		case ENOENT:
+			has_error = true;
+			text_parse_raise_error(parse, section->lineno,
+			    CONFIGURATION_EMISSING_ITEM);
+			break;
+		case EINVAL:
+			has_error = true;
+			/* Parser should've raised proper errors */
+			break;
+		case EOK:
+			/* empty (nothing to do) */
+			break;
+		default:
+			assert(false);
+		}
+
+		++config_item;
+	}
+
+	return has_error ? EOK : EINVAL;
+}
+
+/** Parse string (copy) to destination
+ *
+ * @param[out]  dst      pointer to char * where dedicated copy will be stored
+ *
+ * @return  true   on success
+ * @return  false  on error (typically low memory)
+ */
+bool config_parse_string(const char *string, void *dst, text_parse_t *parse,
+    size_t lineno)
+{
+	char *my_string = str_dup(string);
+	if (my_string) {
+		return false;
+	}
+
+	char **char_dst = dst;
+	*char_dst = my_string;
+	return true;
+}
Index: uspace/lib/conf/src/ini.c
===================================================================
--- uspace/lib/conf/src/ini.c	(revision bb154c64b6e4b7d6a2a8e19760b6ae60156847b4)
+++ uspace/lib/conf/src/ini.c	(revision bb154c64b6e4b7d6a2a8e19760b6ae60156847b4)
@@ -0,0 +1,423 @@
+#include <adt/hash.h>
+#include <adt/hash_table.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <str.h>
+
+#include "conf/ini.h"
+
+#define LINE_BUFFER 256
+
+/** Representation of key-value pair from INI file
+ *
+ * @note Structure is owner of its string data.
+ */
+typedef struct {
+	ht_link_t ht_link;
+
+	/** First line from where the item was extracted. */
+	size_t lineno;
+
+	char *key;
+	char *value;
+} ini_item_t;
+
+
+/* Necessary forward declarations */
+static void ini_section_destroy(ini_section_t **);
+static void ini_item_destroy(ini_item_t **);
+
+/* Hash table functions */
+static size_t ini_section_ht_hash(const ht_link_t *item)
+{
+	ini_section_t *section =
+	    hash_table_get_inst(item, ini_section_t, ht_link);
+	/* Nameless default section */
+	if (section->name == NULL) {
+		return 0;
+	}
+
+	return hash_string(section->name);
+}
+
+static size_t ini_section_ht_key_hash(void *key)
+{
+	return hash_string((const char *)key);
+}
+
+static bool ini_section_ht_equal(const ht_link_t *item1, const ht_link_t *item2)
+{
+	return str_cmp(
+	    hash_table_get_inst(item1, ini_section_t, ht_link)->name,
+	    hash_table_get_inst(item2, ini_section_t, ht_link)->name) == 0;
+}
+
+static bool ini_section_ht_key_equal(void *key, const ht_link_t *item)
+{
+	return str_cmp((const char *)key,
+	    hash_table_get_inst(item, ini_section_t, ht_link)->name) == 0;
+}
+
+static void ini_section_ht_remove(ht_link_t *item)
+{
+	ini_section_t *section = hash_table_get_inst(item, ini_section_t, ht_link);
+	ini_section_destroy(&section);
+}
+
+
+static size_t ini_item_ht_hash(const ht_link_t *item)
+{
+	ini_item_t *ini_item =
+	    hash_table_get_inst(item, ini_item_t, ht_link);
+	assert(ini_item->key);
+	return hash_string(ini_item->key);
+}
+
+static size_t ini_item_ht_key_hash(void *key)
+{
+	return hash_string((const char *)key);
+}
+
+static bool ini_item_ht_equal(const ht_link_t *item1, const ht_link_t *item2)
+{
+	return str_cmp(
+	    hash_table_get_inst(item1, ini_item_t, ht_link)->key,
+	    hash_table_get_inst(item2, ini_item_t, ht_link)->key) == 0;
+}
+
+static bool ini_item_ht_key_equal(void *key, const ht_link_t *item)
+{
+	return str_cmp((const char *)key,
+	    hash_table_get_inst(item, ini_item_t, ht_link)->key) == 0;
+}
+
+static void ini_item_ht_remove(ht_link_t *item)
+{
+	ini_item_t *ini_item = hash_table_get_inst(item, ini_item_t, ht_link);
+	ini_item_destroy(&ini_item);
+}
+
+
+static hash_table_ops_t configuration_ht_ops = {
+	.hash            = &ini_section_ht_hash,
+	.key_hash        = &ini_section_ht_key_hash,
+	.equal           = &ini_section_ht_equal,
+	.key_equal       = &ini_section_ht_key_equal,
+	.remove_callback = &ini_section_ht_remove
+};
+
+static hash_table_ops_t section_ht_ops = {
+	.hash            = &ini_item_ht_hash,
+	.key_hash        = &ini_item_ht_key_hash,
+	.equal           = &ini_item_ht_equal,
+	.key_equal       = &ini_item_ht_key_equal,
+	.remove_callback = &ini_item_ht_remove
+};
+
+/* Actual INI functions */
+
+void ini_configuration_init(ini_configuration_t *conf)
+{
+	hash_table_create(&conf->sections, 0, 0, &configuration_ht_ops);
+}
+
+/** INI configuration destructor
+ *
+ * Release all resources of INI structure but the structure itself.
+ */
+void ini_configuration_deinit(ini_configuration_t *conf)
+{
+	hash_table_destroy(&conf->sections);
+}
+
+static void ini_section_init(ini_section_t *section)
+{
+	hash_table_create(&section->items, 0, 0, &section_ht_ops);
+	section->name = NULL;
+}
+
+static ini_section_t* ini_section_create(void)
+{
+	ini_section_t *section = malloc(sizeof(ini_section_t));
+	if (section != NULL) {
+		ini_section_init(section);
+	}
+	return section;
+}
+
+static void ini_section_destroy(ini_section_t **section_ptr)
+{
+	ini_section_t *section = *section_ptr;
+	if (section == NULL) {
+		return;
+	}
+	hash_table_destroy(&section->items);
+	free(section->name);
+	free(section);
+	*section_ptr = NULL;
+}
+
+static void ini_item_init(ini_item_t *item)
+{
+	item->key = NULL;
+	item->value = NULL;
+}
+
+static ini_item_t *ini_item_create(void)
+{
+	ini_item_t *item = malloc(sizeof(ini_item_t));
+	if (item != NULL) {
+		ini_item_init(item);
+	}
+	return item;
+}
+
+static void ini_item_destroy(ini_item_t **item_ptr)
+{
+	ini_item_t *item = *item_ptr;
+	if (item == NULL) {
+		return;
+	}
+	free(item->key);
+	free(item->value);
+	free(item);
+	*item_ptr = NULL;
+}
+
+/** Parse file contents to INI structure
+ *
+ * @param[in]    filename
+ * @param[out]   conf      initialized structure for configuration
+ * @param[out]   parse     initialized structure to keep parsing errors
+ *
+ * @return EOK on success
+ * @return EIO when file cannot be opened
+ * @return ENOMEM
+ * @return EINVAL on parse error (details in parse structure)
+ */
+int ini_parse_file(const char *filename, ini_configuration_t *conf,
+    text_parse_t *parse)
+{
+	int rc = EOK;
+	FILE *f = NULL;
+	char *line_buffer = NULL;
+
+	f = fopen(filename, "r");
+	if (f == NULL) {
+		rc = EIO;
+		goto finish;
+	}
+
+	line_buffer = malloc(LINE_BUFFER);
+	if (line_buffer == NULL) {
+		rc = ENOMEM;
+		goto finish;
+	}
+
+	char *line = NULL;
+	ini_section_t *cur_section = NULL;
+	size_t lineno = 0;
+
+	while ((line = fgets(line_buffer, LINE_BUFFER - 1, f))) {
+		++lineno;
+		size_t line_len = str_size(line);
+		if (line[line_len - 1] != '\n') {
+			text_parse_raise_error(parse, lineno, INI_ETOO_LONG);
+			rc = EINVAL;
+			/* Cannot recover terminate parsing */
+			goto finish;
+		}
+		/* Ingore leading/trailing whitespace */
+		str_rtrim(line, '\n');
+		str_ltrim(line, ' ');
+		str_rtrim(line, ' ');
+
+		/* Empty line */
+		if (str_size(line) == 0) {
+			continue;
+		}
+
+		/* Comment line */
+		if (line[0] == ';' || line[0] == '#') {
+			continue;
+		}
+		
+		/* Start new section */
+		if (line[0] == '[') {
+			cur_section = ini_section_create();
+			if (cur_section == NULL) {
+				rc = ENOMEM;
+				goto finish;
+			}
+
+			char *close_bracket = str_chr(line, ']');
+			if (close_bracket == NULL) {
+				ini_section_destroy(&cur_section);
+				text_parse_raise_error(parse, lineno,
+				    INI_EBRACKET_EXPECTED);
+				rc = EINVAL;
+				goto finish;
+			}
+
+			cur_section->lineno = lineno;
+			*close_bracket = '\0';
+			cur_section->name = str_dup(line + 1);
+			
+			if (!hash_table_insert_unique(&conf->sections,
+			    &cur_section->ht_link)) {
+				ini_section_destroy(&cur_section);
+				text_parse_raise_error(parse, lineno,
+				    INI_EDUP_SECTION);
+				rc = EINVAL;
+				goto finish;
+			}
+
+			continue;
+		}
+
+		/* Create a default section if none was specified */
+		if (cur_section == NULL) {
+			cur_section = ini_section_create();
+			if (cur_section == NULL) {
+				rc = ENOMEM;
+				goto finish;
+			}
+			cur_section->lineno = lineno;
+
+			bool inserted = hash_table_insert_unique(&conf->sections,
+			    &cur_section->ht_link);
+			assert(inserted);
+		}
+
+		/* Parse key-value pairs */
+		ini_item_t *item = ini_item_create();
+		if (item == NULL) {
+			rc = ENOMEM;
+			goto finish;
+		}
+		item->lineno = lineno;
+
+		char *assign_char = str_chr(line, '=');
+		if (assign_char == NULL) {
+			rc = EINVAL;
+			text_parse_raise_error(parse, lineno,
+			    INI_EASSIGN_EXPECTED);
+			goto finish;
+		}
+
+		*assign_char = '\0';
+		char *key = line;
+		str_ltrim(key, ' ');
+		str_rtrim(key, ' ');
+		item->key = str_dup(key);
+		if (item->key == NULL) {
+			ini_item_destroy(&item);
+			rc = ENOMEM;
+			goto finish;
+		}
+
+		char *value = assign_char + 1;
+		str_ltrim(value, ' ');
+		str_rtrim(value, ' ');
+		item->value = str_dup(value);
+		if (item->value == NULL) {
+			ini_item_destroy(&item);
+			rc = ENOMEM;
+			goto finish;
+		}
+
+		hash_table_insert(&cur_section->items, &item->ht_link);
+	}
+
+finish:
+	if (f) {
+		fclose(f);
+	}
+	free(line_buffer);
+
+	return rc;
+}
+
+
+/** Get a section from configuration
+ *
+ * @param[in]  ini_configuration
+ * @param[in]  section_name       name of section or NULL for default section
+ *
+ * @return   Section with given name
+ * @return   NULL when no such section exits
+ */
+ini_section_t *ini_get_section(ini_configuration_t *ini_conf,
+    const char *section_name)
+{
+	ht_link_t *item = hash_table_find(&ini_conf->sections,
+	    (void *)section_name);
+	if (item == NULL) {
+		return NULL;
+	}
+
+	return hash_table_get_inst(item, ini_section_t, ht_link);
+}
+
+/** Get item iterator to items with given key in the section
+ *
+ * @param[in]  section
+ * @param[in]  key
+ *
+ * @return Always return iterator (even when there's no item with given key)
+ */
+ini_item_iterator_t ini_section_get_iterator(ini_section_t *section,
+   const char *key)
+{
+	ini_item_iterator_t result;
+	result.first_item = hash_table_find(&section->items, (void *)key);
+	result.cur_item = result.first_item;
+	result.table = &section->items;
+	result.incremented = false;
+
+	return result;
+}
+
+bool ini_item_iterator_valid(ini_item_iterator_t *iterator)
+{
+	bool empty = (iterator->cur_item != NULL);
+	bool looped = (iterator->cur_item == iterator->first_item);
+	return empty || (looped && iterator->incremented);
+}
+
+/** Move iterator to next item (of the same key)
+ *
+ * @param[in]  iterator  valid iterator
+ */
+void ini_item_iterator_inc(ini_item_iterator_t *iterator)
+{
+	iterator->cur_item =
+	    hash_table_find_next(iterator->table, iterator->cur_item);
+	iterator->incremented = true;
+}
+
+/** Get item value for current iterator
+ *
+ * @param[in]  iterator  valid iterator
+ */
+const char *ini_item_iterator_value(ini_item_iterator_t *iterator)
+{
+	ini_item_t *ini_item =
+	    hash_table_get_inst(iterator->cur_item, ini_item_t, ht_link);
+	return ini_item->value;
+}
+
+/** Get item line number for current iterator
+ *
+ * @param[in]  iterator  valid iterator
+ *
+ * @return Line number of input where item was originally defined.
+ */
+size_t ini_item_iterator_lineno(ini_item_iterator_t *iterator)
+{
+	ini_item_t *ini_item =
+	    hash_table_get_inst(iterator->cur_item, ini_item_t, ht_link);
+	return ini_item->lineno;
+}
Index: uspace/lib/conf/src/text_parse.c
===================================================================
--- uspace/lib/conf/src/text_parse.c	(revision bb154c64b6e4b7d6a2a8e19760b6ae60156847b4)
+++ uspace/lib/conf/src/text_parse.c	(revision bb154c64b6e4b7d6a2a8e19760b6ae60156847b4)
@@ -0,0 +1,50 @@
+#include "conf/text_parse.h"
+
+#include <assert.h>
+#include <stdlib.h>
+
+/** Constructor of parse structure */
+void text_parse_init(text_parse_t *parse)
+{
+	assert(parse);
+	list_initialize(&parse->errors);
+	parse->has_error = false;
+}
+
+/** Destructor of parse structure
+ *
+ * It must be called before parse structure goes out of scope in order to avoid
+ * memory leaks.
+ */
+void text_parse_deinit(text_parse_t *parse)
+{
+	assert(parse);
+	list_foreach_safe(parse->errors, cur_link, next_link) {
+		list_remove(cur_link);
+		text_parse_error_t *error =
+		    list_get_instance(cur_link, text_parse_error_t, link);
+		free(error);
+	}
+}
+
+/** Add an error to parse structure
+ *
+ * @param[in]  parse        the parse structure
+ * @param[in]  lineno       line number where error originated (or zero)
+ * @param[in]  parse_errno  user error code
+ */
+void text_parse_raise_error(text_parse_t *parse, size_t lineno,
+    int parse_errno) {
+	assert(parse);
+
+	parse->has_error = true;
+
+	text_parse_error_t *error = malloc(sizeof(text_parse_error_t));
+	/* Silently ignore failed malloc, has_error flag is set anyway */
+	if (!error) {
+		return;
+	}
+	error->lineno = lineno;
+	error->parse_errno = parse_errno;
+	list_append(&error->link, &parse->errors);
+}
