Index: uspace/lib/c/generic/adt/hash_table.c
===================================================================
--- uspace/lib/c/generic/adt/hash_table.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
+++ uspace/lib/c/generic/adt/hash_table.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
@@ -0,0 +1,216 @@
+/*
+ * Copyright (c) 2008 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.
+ */
+
+/** @addtogroup libc
+ * @{
+ */
+/** @file
+ */
+
+/*
+ * This is an implementation of generic chained hash table.
+ */
+
+#include <adt/hash_table.h>
+#include <adt/list.h>
+#include <unistd.h>
+#include <malloc.h>
+#include <assert.h>
+#include <stdio.h>
+#include <str.h>
+
+/** Create chained hash table.
+ *
+ * @param h		Hash table structure. Will be initialized by this call.
+ * @param m		Number of hash table buckets.
+ * @param max_keys	Maximal number of keys needed to identify an item.
+ * @param op		Hash table operations structure.
+ * @return		True on success
+ */
+int hash_table_create(hash_table_t *h, hash_count_t m, hash_count_t max_keys,
+    hash_table_operations_t *op)
+{
+	hash_count_t i;
+
+	assert(h);
+	assert(op && op->hash && op->compare);
+	assert(max_keys > 0);
+	
+	h->entry = malloc(m * sizeof(link_t));
+	if (!h->entry) {
+		printf("cannot allocate memory for hash table\n");
+		return false;
+	}
+	memset((void *) h->entry, 0,  m * sizeof(link_t));
+	
+	for (i = 0; i < m; i++)
+		list_initialize(&h->entry[i]);
+	
+	h->entries = m;
+	h->max_keys = max_keys;
+	h->op = op;
+	return true;
+}
+
+/** Destroy a hash table instance.
+ *
+ * @param h		Hash table to be destroyed.
+ */
+void hash_table_destroy(hash_table_t *h)
+{
+	assert(h);
+	assert(h->entry);
+	free(h->entry);
+}
+
+/** Insert item into a hash table.
+ *
+ * @param h		Hash table.
+ * @param key		Array of all keys necessary to compute hash index.
+ * @param item		Item to be inserted into the hash table.
+ */
+void hash_table_insert(hash_table_t *h, unsigned long key[], link_t *item)
+{
+	hash_index_t chain;
+
+	assert(item);
+	assert(h && h->op && h->op->hash && h->op->compare);
+
+	chain = h->op->hash(key);
+	assert(chain < h->entries);
+	
+	list_append(item, &h->entry[chain]);
+}
+
+/** Search hash table for an item matching keys.
+ *
+ * @param h		Hash table.
+ * @param key		Array of all keys needed to compute hash index.
+ *
+ * @return		Matching item on success, NULL if there is no such item.
+ */
+link_t *hash_table_find(hash_table_t *h, unsigned long key[])
+{
+	link_t *cur;
+	hash_index_t chain;
+
+	assert(h && h->op && h->op->hash && h->op->compare);
+
+	chain = h->op->hash(key);
+	assert(chain < h->entries);
+	
+	for (cur = h->entry[chain].next; cur != &h->entry[chain];
+	    cur = cur->next) {
+		if (h->op->compare(key, h->max_keys, cur)) {
+			/*
+			 * The entry is there.
+			 */
+			return cur;
+		}
+	}
+	
+	return NULL;
+}
+
+/** Remove all matching items from hash table.
+ *
+ * For each removed item, h->remove_callback() is called.
+ *
+ * @param h		Hash table.
+ * @param key		Array of keys that will be compared against items of
+ * 			the hash table.
+ * @param keys		Number of keys in the 'key' array.
+ */
+void hash_table_remove(hash_table_t *h, unsigned long key[], hash_count_t keys)
+{
+	hash_index_t chain;
+	link_t *cur;
+
+	assert(h && h->op && h->op->hash && h->op->compare &&
+	    h->op->remove_callback);
+	assert(keys <= h->max_keys);
+	
+	if (keys == h->max_keys) {
+
+		/*
+		 * All keys are known, hash_table_find() can be used to find the
+		 * entry.
+		 */
+	
+		cur = hash_table_find(h, key);
+		if (cur) {
+			list_remove(cur);
+			h->op->remove_callback(cur);
+		}
+		return;
+	}
+	
+	/*
+	 * Fewer keys were passed.
+	 * Any partially matching entries are to be removed.
+	 */
+	for (chain = 0; chain < h->entries; chain++) {
+		for (cur = h->entry[chain].next; cur != &h->entry[chain];
+		    cur = cur->next) {
+			if (h->op->compare(key, keys, cur)) {
+				link_t *hlp;
+				
+				hlp = cur;
+				cur = cur->prev;
+				
+				list_remove(hlp);
+				h->op->remove_callback(hlp);
+				
+				continue;
+			}
+		}
+	}
+}
+
+/** Apply fucntion to all items in hash table.
+ *
+ * @param h		Hash table.
+ * @param f		Function to be applied.
+ * @param arg		Argument to be passed to the function.
+ */
+void
+hash_table_apply(hash_table_t *h, void (*f)(link_t *, void *), void *arg)
+{
+	hash_index_t bucket;
+	link_t *cur;
+
+	for (bucket = 0; bucket < h->entries; bucket++) {
+		for (cur = h->entry[bucket].next; cur != &h->entry[bucket];
+		    cur = cur->next) {
+			f(cur, arg);
+		}
+	}
+}
+
+/** @}
+ */
Index: uspace/lib/c/generic/adt/list.c
===================================================================
--- uspace/lib/c/generic/adt/list.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
+++ uspace/lib/c/generic/adt/list.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2004 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.
+ */
+
+/** @addtogroup libc
+ * @{
+ */
+/** @file
+ */
+
+#include <adt/list.h>
+
+
+/** Check for membership
+ *
+ * Check whether link is contained in the list head.
+ * The membership is defined as pointer equivalence.
+ *
+ * @param link Item to look for.
+ * @param head List to look in.
+ *
+ * @return true if link is contained in head, false otherwise.
+ *
+ */
+int list_member(const link_t *link, const link_t *head)
+{
+	int found = 0;
+	link_t *hlp = head->next;
+	
+	while (hlp != head) {
+		if (hlp == link) {
+			found = 1;
+			break;
+		}
+		hlp = hlp->next;
+	}
+	
+	return found;
+}
+
+
+/** Concatenate two lists
+ *
+ * Concatenate lists head1 and head2, producing a single
+ * list head1 containing items from both (in head1, head2
+ * order) and empty list head2.
+ *
+ * @param head1 First list and concatenated output
+ * @param head2 Second list and empty output.
+ *
+ */
+void list_concat(link_t *head1, link_t *head2)
+{
+	if (list_empty(head2))
+		return;
+	
+	head2->next->prev = head1->prev;
+	head2->prev->next = head1;
+	head1->prev->next = head2->next;
+	head1->prev = head2->prev;
+	list_initialize(head2);
+}
+
+
+/** Count list items
+ *
+ * Return the number of items in the list.
+ *
+ * @param link List to count.
+ *
+ * @return Number of items in the list.
+ *
+ */
+unsigned int list_count(const link_t *link)
+{
+	unsigned int count = 0;
+	link_t *hlp = link->next;
+	
+	while (hlp != link) {
+		count++;
+		hlp = hlp->next;
+	}
+	
+	return count;
+}
+
+/** @}
+ */
Index: uspace/lib/c/generic/as.c
===================================================================
--- uspace/lib/c/generic/as.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
+++ uspace/lib/c/generic/as.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2006 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.
+ */
+
+/** @addtogroup libc
+ * @{
+ */
+/** @file
+ */
+
+#include <as.h>
+#include <libc.h>
+#include <unistd.h>
+#include <align.h>
+#include <sys/types.h>
+#include <bitops.h>
+#include <malloc.h>
+
+/** Last position allocated by as_get_mappable_page */
+static uintptr_t last_allocated = 0;
+
+/** Create address space area.
+ *
+ * @param address Virtual address where to place new address space area.
+ * @param size    Size of the area.
+ * @param flags   Flags describing type of the area.
+ *
+ * @return address on success, (void *) -1 otherwise.
+ *
+ */
+void *as_area_create(void *address, size_t size, int flags)
+{
+	return (void *) __SYSCALL3(SYS_AS_AREA_CREATE, (sysarg_t) address,
+	    (sysarg_t) size, (sysarg_t) flags);
+}
+
+/** Resize address space area.
+ *
+ * @param address Virtual address pointing into already existing address space
+ *                area.
+ * @param size    New requested size of the area.
+ * @param flags   Currently unused.
+ *
+ * @return zero on success or a code from @ref errno.h on failure.
+ *
+ */
+int as_area_resize(void *address, size_t size, int flags)
+{
+	return __SYSCALL3(SYS_AS_AREA_RESIZE, (sysarg_t) address,
+	    (sysarg_t) size, (sysarg_t) flags);
+}
+
+/** Destroy address space area.
+ *
+ * @param address Virtual address pointing into the address space area being
+ *                destroyed.
+ *
+ * @return zero on success or a code from @ref errno.h on failure.
+ *
+ */
+int as_area_destroy(void *address)
+{
+	return __SYSCALL1(SYS_AS_AREA_DESTROY, (sysarg_t) address);
+}
+
+/** Change address-space area flags.
+ *
+ * @param address Virtual address pointing into the address space area being
+ *                modified.
+ * @param flags   New flags describing type of the area.
+ *
+ * @return zero on success or a code from @ref errno.h on failure.
+ *
+ */
+int as_area_change_flags(void *address, int flags)
+{
+	return __SYSCALL2(SYS_AS_AREA_CHANGE_FLAGS, (sysarg_t) address,
+	    (sysarg_t) flags);
+}
+
+/** Return pointer to some unmapped area, where fits new as_area
+ *
+ * @param size Requested size of the allocation.
+ *
+ * @return pointer to the beginning
+ *
+ */
+void *as_get_mappable_page(size_t size)
+{
+	if (size == 0)
+		return NULL;
+	
+	size_t sz = 1 << (fnzb(size - 1) + 1);
+	if (last_allocated == 0)
+		last_allocated = get_max_heap_addr();
+	
+	/*
+	 * Make sure we allocate from naturally aligned address.
+	 */
+	uintptr_t res = ALIGN_UP(last_allocated, sz);
+	last_allocated = res + ALIGN_UP(size, PAGE_SIZE);
+	
+	return ((void *) res);
+}
+
+/** @}
+ */
Index: uspace/lib/c/generic/async.c
===================================================================
--- uspace/lib/c/generic/async.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
+++ uspace/lib/c/generic/async.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
@@ -0,0 +1,1564 @@
+/*
+ * Copyright (c) 2006 Ondrej Palkovsky
+ * 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 libc
+ * @{
+ */
+/** @file
+ */
+
+/**
+ * Asynchronous library
+ *
+ * The aim of this library is to provide a facility for writing programs which
+ * utilize the asynchronous nature of HelenOS IPC, yet using a normal way of
+ * programming.
+ *
+ * You should be able to write very simple multithreaded programs, the async
+ * framework will automatically take care of most synchronization problems.
+ *
+ * Default semantics:
+ * - async_send_*(): Send asynchronously. If the kernel refuses to send
+ *                   more messages, [ try to get responses from kernel, if
+ *                   nothing found, might try synchronous ]
+ *
+ * Example of use (pseudo C):
+ *
+ * 1) Multithreaded client application
+ *
+ *   fibril_create(fibril1, ...);
+ *   fibril_create(fibril2, ...);
+ *   ...
+ *
+ *   int fibril1(void *arg)
+ *   {
+ *     conn = ipc_connect_me_to();
+ *     c1 = async_send(conn);
+ *     c2 = async_send(conn);
+ *     async_wait_for(c1);
+ *     async_wait_for(c2);
+ *     ...
+ *   }
+ *
+ *
+ * 2) Multithreaded server application
+ *
+ *   main()
+ *   {
+ *     async_manager();
+ *   }
+ *
+ *   my_client_connection(icallid, *icall)
+ *   {
+ *     if (want_refuse) {
+ *       ipc_answer_0(icallid, ELIMIT);
+ *       return;
+ *     }
+ *     ipc_answer_0(icallid, EOK);
+ *
+ *     callid = async_get_call(&call);
+ *     somehow_handle_the_call(callid, call);
+ *     ipc_answer_2(callid, 1, 2, 3);
+ *
+ *     callid = async_get_call(&call);
+ *     ...
+ *   }
+ *
+ */
+
+#include <futex.h>
+#include <async.h>
+#include <async_priv.h>
+#include <fibril.h>
+#include <stdio.h>
+#include <adt/hash_table.h>
+#include <adt/list.h>
+#include <ipc/ipc.h>
+#include <assert.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <arch/barrier.h>
+#include <bool.h>
+
+atomic_t async_futex = FUTEX_INITIALIZER;
+
+/** Number of threads waiting for IPC in the kernel. */
+atomic_t threads_in_ipc_wait = { 0 };
+
+typedef struct {
+	awaiter_t wdata;
+	
+	/** If reply was received. */
+	bool done;
+	
+	/** Pointer to where the answer data is stored. */
+	ipc_call_t *dataptr;
+	
+	ipcarg_t retval;
+} amsg_t;
+
+/**
+ * Structures of this type are used to group information about a call and a
+ * message queue link.
+ */
+typedef struct {
+	link_t link;
+	ipc_callid_t callid;
+	ipc_call_t call;
+} msg_t;
+
+typedef struct {
+	awaiter_t wdata;
+	
+	/** Hash table link. */
+	link_t link;
+	
+	/** Incoming phone hash. */
+	ipcarg_t in_phone_hash;
+	
+	/** Messages that should be delivered to this fibril. */
+	link_t msg_queue;
+	
+	/** Identification of the opening call. */
+	ipc_callid_t callid;
+	/** Call data of the opening call. */
+	ipc_call_t call;
+	
+	/** Identification of the closing call. */
+	ipc_callid_t close_callid;
+	
+	/** Fibril function that will be used to handle the connection. */
+	void (*cfibril)(ipc_callid_t, ipc_call_t *);
+} connection_t;
+
+/** Identifier of the incoming connection handled by the current fibril. */
+fibril_local connection_t *FIBRIL_connection;
+
+static void default_client_connection(ipc_callid_t callid, ipc_call_t *call);
+static void default_interrupt_received(ipc_callid_t callid, ipc_call_t *call);
+
+/**
+ * Pointer to a fibril function that will be used to handle connections.
+ */
+static async_client_conn_t client_connection = default_client_connection;
+
+/**
+ * Pointer to a fibril function that will be used to handle interrupt
+ * notifications.
+ */
+static async_client_conn_t interrupt_received = default_interrupt_received;
+
+static hash_table_t conn_hash_table;
+static LIST_INITIALIZE(timeout_list);
+
+#define CONN_HASH_TABLE_CHAINS  32
+
+/** Compute hash into the connection hash table based on the source phone hash.
+ *
+ * @param key Pointer to source phone hash.
+ *
+ * @return Index into the connection hash table.
+ *
+ */
+static hash_index_t conn_hash(unsigned long *key)
+{
+	assert(key);
+	return (((*key) >> 4) % CONN_HASH_TABLE_CHAINS);
+}
+
+/** Compare hash table item with a key.
+ *
+ * @param key  Array containing the source phone hash as the only item.
+ * @param keys Expected 1 but ignored.
+ * @param item Connection hash table item.
+ *
+ * @return True on match, false otherwise.
+ *
+ */
+static int conn_compare(unsigned long key[], hash_count_t keys, link_t *item)
+{
+	connection_t *hs = hash_table_get_instance(item, connection_t, link);
+	return (key[0] == hs->in_phone_hash);
+}
+
+/** Connection hash table removal callback function.
+ *
+ * This function is called whenever a connection is removed from the connection
+ * hash table.
+ *
+ * @param item Connection hash table item being removed.
+ *
+ */
+static void conn_remove(link_t *item)
+{
+	free(hash_table_get_instance(item, connection_t, link));
+}
+
+
+/** Operations for the connection hash table. */
+static hash_table_operations_t conn_hash_table_ops = {
+	.hash = conn_hash,
+	.compare = conn_compare,
+	.remove_callback = conn_remove
+};
+
+/** Sort in current fibril's timeout request.
+ *
+ * @param wd Wait data of the current fibril.
+ *
+ */
+void async_insert_timeout(awaiter_t *wd)
+{
+	wd->to_event.occurred = false;
+	wd->to_event.inlist = true;
+	
+	link_t *tmp = timeout_list.next;
+	while (tmp != &timeout_list) {
+		awaiter_t *cur;
+		
+		cur = list_get_instance(tmp, awaiter_t, to_event.link);
+		if (tv_gteq(&cur->to_event.expires, &wd->to_event.expires))
+			break;
+		tmp = tmp->next;
+	}
+	
+	list_append(&wd->to_event.link, tmp);
+}
+
+/** Try to route a call to an appropriate connection fibril.
+ *
+ * If the proper connection fibril is found, a message with the call is added to
+ * its message queue. If the fibril was not active, it is activated and all
+ * timeouts are unregistered.
+ *
+ * @param callid Hash of the incoming call.
+ * @param call   Data of the incoming call.
+ *
+ * @return False if the call doesn't match any connection.
+ *         True if the call was passed to the respective connection fibril.
+ *
+ */
+static bool route_call(ipc_callid_t callid, ipc_call_t *call)
+{
+	futex_down(&async_futex);
+	
+	unsigned long key = call->in_phone_hash;
+	link_t *hlp = hash_table_find(&conn_hash_table, &key);
+	
+	if (!hlp) {
+		futex_up(&async_futex);
+		return false;
+	}
+	
+	connection_t *conn = hash_table_get_instance(hlp, connection_t, link);
+	
+	msg_t *msg = malloc(sizeof(*msg));
+	if (!msg) {
+		futex_up(&async_futex);
+		return false;
+	}
+	
+	msg->callid = callid;
+	msg->call = *call;
+	list_append(&msg->link, &conn->msg_queue);
+	
+	if (IPC_GET_METHOD(*call) == IPC_M_PHONE_HUNGUP)
+		conn->close_callid = callid;
+	
+	/* If the connection fibril is waiting for an event, activate it */
+	if (!conn->wdata.active) {
+		
+		/* If in timeout list, remove it */
+		if (conn->wdata.to_event.inlist) {
+			conn->wdata.to_event.inlist = false;
+			list_remove(&conn->wdata.to_event.link);
+		}
+		
+		conn->wdata.active = true;
+		fibril_add_ready(conn->wdata.fid);
+	}
+	
+	futex_up(&async_futex);
+	return true;
+}
+
+/** Notification fibril.
+ *
+ * When a notification arrives, a fibril with this implementing function is
+ * created. It calls interrupt_received() and does the final cleanup.
+ *
+ * @param arg Message structure pointer.
+ *
+ * @return Always zero.
+ *
+ */
+static int notification_fibril(void *arg)
+{
+	msg_t *msg = (msg_t *) arg;
+	interrupt_received(msg->callid, &msg->call);
+	
+	free(msg);
+	return 0;
+}
+
+/** Process interrupt notification.
+ *
+ * A new fibril is created which would process the notification.
+ *
+ * @param callid Hash of the incoming call.
+ * @param call   Data of the incoming call.
+ *
+ * @return False if an error occured.
+ *         True if the call was passed to the notification fibril.
+ *
+ */
+static bool process_notification(ipc_callid_t callid, ipc_call_t *call)
+{
+	futex_down(&async_futex);
+	
+	msg_t *msg = malloc(sizeof(*msg));
+	if (!msg) {
+		futex_up(&async_futex);
+		return false;
+	}
+	
+	msg->callid = callid;
+	msg->call = *call;
+	
+	fid_t fid = fibril_create(notification_fibril, msg);
+	fibril_add_ready(fid);
+	
+	futex_up(&async_futex);
+	return true;
+}
+
+/** Return new incoming message for the current (fibril-local) connection.
+ *
+ * @param call  Storage where the incoming call data will be stored.
+ * @param usecs Timeout in microseconds. Zero denotes no timeout.
+ *
+ * @return If no timeout was specified, then a hash of the
+ *         incoming call is returned. If a timeout is specified,
+ *         then a hash of the incoming call is returned unless
+ *         the timeout expires prior to receiving a message. In
+ *         that case zero is returned.
+ *
+ */
+ipc_callid_t async_get_call_timeout(ipc_call_t *call, suseconds_t usecs)
+{
+	assert(FIBRIL_connection);
+	
+	/* Why doing this?
+	 * GCC 4.1.0 coughs on FIBRIL_connection-> dereference.
+	 * GCC 4.1.1 happilly puts the rdhwr instruction in delay slot.
+	 *           I would never expect to find so many errors in
+	 *           a compiler.
+	 */
+	connection_t *conn = FIBRIL_connection;
+	
+	futex_down(&async_futex);
+	
+	if (usecs) {
+		gettimeofday(&conn->wdata.to_event.expires, NULL);
+		tv_add(&conn->wdata.to_event.expires, usecs);
+	} else
+		conn->wdata.to_event.inlist = false;
+	
+	/* If nothing in queue, wait until something arrives */
+	while (list_empty(&conn->msg_queue)) {
+		if (conn->close_callid) {
+			/*
+			 * Handle the case when the connection was already
+			 * closed by the client but the server did not notice
+			 * the first IPC_M_PHONE_HUNGUP call and continues to
+			 * call async_get_call_timeout(). Repeat
+			 * IPC_M_PHONE_HUNGUP until the caller notices. 
+			 */
+			memset(call, 0, sizeof(ipc_call_t));
+			IPC_SET_METHOD(*call, IPC_M_PHONE_HUNGUP);
+			futex_up(&async_futex);
+			return conn->close_callid;
+		}
+
+		if (usecs)
+			async_insert_timeout(&conn->wdata);
+		
+		conn->wdata.active = false;
+		
+		/*
+		 * Note: the current fibril will be rescheduled either due to a
+		 * timeout or due to an arriving message destined to it. In the
+		 * former case, handle_expired_timeouts() and, in the latter
+		 * case, route_call() will perform the wakeup.
+		 */
+		fibril_switch(FIBRIL_TO_MANAGER);
+		
+		/*
+		 * Futex is up after getting back from async_manager.
+		 * Get it again.
+		 */
+		futex_down(&async_futex);
+		if ((usecs) && (conn->wdata.to_event.occurred)
+		    && (list_empty(&conn->msg_queue))) {
+			/* If we timed out -> exit */
+			futex_up(&async_futex);
+			return 0;
+		}
+	}
+	
+	msg_t *msg = list_get_instance(conn->msg_queue.next, msg_t, link);
+	list_remove(&msg->link);
+	
+	ipc_callid_t callid = msg->callid;
+	*call = msg->call;
+	free(msg);
+	
+	futex_up(&async_futex);
+	return callid;
+}
+
+/** Default fibril function that gets called to handle new connection.
+ *
+ * This function is defined as a weak symbol - to be redefined in user code.
+ *
+ * @param callid Hash of the incoming call.
+ * @param call   Data of the incoming call.
+ *
+ */
+static void default_client_connection(ipc_callid_t callid, ipc_call_t *call)
+{
+	ipc_answer_0(callid, ENOENT);
+}
+
+/** Default fibril function that gets called to handle interrupt notifications.
+ *
+ * This function is defined as a weak symbol - to be redefined in user code.
+ *
+ * @param callid Hash of the incoming call.
+ * @param call   Data of the incoming call.
+ *
+ */
+static void default_interrupt_received(ipc_callid_t callid, ipc_call_t *call)
+{
+}
+
+/** Wrapper for client connection fibril.
+ *
+ * When a new connection arrives, a fibril with this implementing function is
+ * created. It calls client_connection() and does the final cleanup.
+ *
+ * @param arg Connection structure pointer.
+ *
+ * @return Always zero.
+ *
+ */
+static int connection_fibril(void *arg)
+{
+	/*
+	 * Setup fibril-local connection pointer and call client_connection().
+	 *
+	 */
+	FIBRIL_connection = (connection_t *) arg;
+	FIBRIL_connection->cfibril(FIBRIL_connection->callid,
+	    &FIBRIL_connection->call);
+	
+	/* Remove myself from the connection hash table */
+	futex_down(&async_futex);
+	unsigned long key = FIBRIL_connection->in_phone_hash;
+	hash_table_remove(&conn_hash_table, &key, 1);
+	futex_up(&async_futex);
+	
+	/* Answer all remaining messages with EHANGUP */
+	while (!list_empty(&FIBRIL_connection->msg_queue)) {
+		msg_t *msg;
+		
+		msg = list_get_instance(FIBRIL_connection->msg_queue.next,
+		    msg_t, link);
+		list_remove(&msg->link);
+		ipc_answer_0(msg->callid, EHANGUP);
+		free(msg);
+	}
+	
+	if (FIBRIL_connection->close_callid)
+		ipc_answer_0(FIBRIL_connection->close_callid, EOK);
+	
+	return 0;
+}
+
+/** Create a new fibril for a new connection.
+ *
+ * Create new fibril for connection, fill in connection structures and inserts
+ * it into the hash table, so that later we can easily do routing of messages to
+ * particular fibrils.
+ *
+ * @param in_phone_hash Identification of the incoming connection.
+ * @param callid        Hash of the opening IPC_M_CONNECT_ME_TO call.
+ *                      If callid is zero, the connection was opened by
+ *                      accepting the IPC_M_CONNECT_TO_ME call and this function
+ *                      is called directly by the server.
+ * @param call          Call data of the opening call.
+ * @param cfibril       Fibril function that should be called upon opening the
+ *                      connection.
+ *
+ * @return New fibril id or NULL on failure.
+ *
+ */
+fid_t async_new_connection(ipcarg_t in_phone_hash, ipc_callid_t callid,
+    ipc_call_t *call, void (*cfibril)(ipc_callid_t, ipc_call_t *))
+{
+	connection_t *conn = malloc(sizeof(*conn));
+	if (!conn) {
+		if (callid)
+			ipc_answer_0(callid, ENOMEM);
+		return NULL;
+	}
+	
+	conn->in_phone_hash = in_phone_hash;
+	list_initialize(&conn->msg_queue);
+	conn->callid = callid;
+	conn->close_callid = 0;
+	
+	if (call)
+		conn->call = *call;
+	
+	/* We will activate the fibril ASAP */
+	conn->wdata.active = true;
+	conn->cfibril = cfibril;
+	conn->wdata.fid = fibril_create(connection_fibril, conn);
+	
+	if (!conn->wdata.fid) {
+		free(conn);
+		if (callid)
+			ipc_answer_0(callid, ENOMEM);
+		return NULL;
+	}
+	
+	/* Add connection to the connection hash table */
+	unsigned long key = conn->in_phone_hash;
+	
+	futex_down(&async_futex);
+	hash_table_insert(&conn_hash_table, &key, &conn->link);
+	futex_up(&async_futex);
+	
+	fibril_add_ready(conn->wdata.fid);
+	
+	return conn->wdata.fid;
+}
+
+/** Handle a call that was received.
+ *
+ * If the call has the IPC_M_CONNECT_ME_TO method, a new connection is created.
+ * Otherwise the call is routed to its connection fibril.
+ *
+ * @param callid Hash of the incoming call.
+ * @param call   Data of the incoming call.
+ *
+ */
+static void handle_call(ipc_callid_t callid, ipc_call_t *call)
+{
+	/* Unrouted call - do some default behaviour */
+	if ((callid & IPC_CALLID_NOTIFICATION)) {
+		process_notification(callid, call);
+		goto out;
+	}
+	
+	switch (IPC_GET_METHOD(*call)) {
+	case IPC_M_CONNECT_ME:
+	case IPC_M_CONNECT_ME_TO:
+		/* Open new connection with fibril etc. */
+		async_new_connection(IPC_GET_ARG5(*call), callid, call,
+		    client_connection);
+		goto out;
+	}
+	
+	/* Try to route the call through the connection hash table */
+	if (route_call(callid, call))
+		goto out;
+	
+	/* Unknown call from unknown phone - hang it up */
+	ipc_answer_0(callid, EHANGUP);
+	return;
+	
+out:
+	;
+}
+
+/** Fire all timeouts that expired. */
+static void handle_expired_timeouts(void)
+{
+	struct timeval tv;
+	gettimeofday(&tv, NULL);
+	
+	futex_down(&async_futex);
+	
+	link_t *cur = timeout_list.next;
+	while (cur != &timeout_list) {
+		awaiter_t *waiter;
+		
+		waiter = list_get_instance(cur, awaiter_t, to_event.link);
+		if (tv_gt(&waiter->to_event.expires, &tv))
+			break;
+
+		cur = cur->next;
+
+		list_remove(&waiter->to_event.link);
+		waiter->to_event.inlist = false;
+		waiter->to_event.occurred = true;
+		
+		/*
+		 * Redundant condition?
+		 * The fibril should not be active when it gets here.
+		 */
+		if (!waiter->active) {
+			waiter->active = true;
+			fibril_add_ready(waiter->fid);
+		}
+	}
+	
+	futex_up(&async_futex);
+}
+
+/** Endless loop dispatching incoming calls and answers.
+ *
+ * @return Never returns.
+ *
+ */
+static int async_manager_worker(void)
+{
+	while (true) {
+		if (fibril_switch(FIBRIL_FROM_MANAGER)) {
+			futex_up(&async_futex); 
+			/*
+			 * async_futex is always held when entering a manager
+			 * fibril.
+			 */
+			continue;
+		}
+		
+		futex_down(&async_futex);
+		
+		suseconds_t timeout;
+		if (!list_empty(&timeout_list)) {
+			awaiter_t *waiter = list_get_instance(timeout_list.next,
+			    awaiter_t, to_event.link);
+			
+			struct timeval tv;
+			gettimeofday(&tv, NULL);
+			
+			if (tv_gteq(&tv, &waiter->to_event.expires)) {
+				futex_up(&async_futex);
+				handle_expired_timeouts();
+				continue;
+			} else
+				timeout = tv_sub(&waiter->to_event.expires,
+				    &tv);
+		} else
+			timeout = SYNCH_NO_TIMEOUT;
+		
+		futex_up(&async_futex);
+
+		atomic_inc(&threads_in_ipc_wait);
+		
+		ipc_call_t call;
+		ipc_callid_t callid = ipc_wait_cycle(&call, timeout,
+		    SYNCH_FLAGS_NONE);
+		
+		atomic_dec(&threads_in_ipc_wait);
+
+		if (!callid) {
+			handle_expired_timeouts();
+			continue;
+		}
+		
+		if (callid & IPC_CALLID_ANSWERED)
+			continue;
+		
+		handle_call(callid, &call);
+	}
+	
+	return 0;
+}
+
+/** Function to start async_manager as a standalone fibril.
+ *
+ * When more kernel threads are used, one async manager should exist per thread.
+ *
+ * @param arg Unused.
+ * @return Never returns.
+ *
+ */
+static int async_manager_fibril(void *arg)
+{
+	futex_up(&async_futex);
+	
+	/*
+	 * async_futex is always locked when entering manager
+	 */
+	async_manager_worker();
+	
+	return 0;
+}
+
+/** Add one manager to manager list. */
+void async_create_manager(void)
+{
+	fid_t fid = fibril_create(async_manager_fibril, NULL);
+	fibril_add_manager(fid);
+}
+
+/** Remove one manager from manager list */
+void async_destroy_manager(void)
+{
+	fibril_remove_manager();
+}
+
+/** Initialize the async framework.
+ *
+ * @return Zero on success or an error code.
+ */
+int __async_init(void)
+{
+	if (!hash_table_create(&conn_hash_table, CONN_HASH_TABLE_CHAINS, 1,
+	    &conn_hash_table_ops)) {
+		printf("%s: cannot create hash table\n", "async");
+		return ENOMEM;
+	}
+	
+	return 0;
+}
+
+/** Reply received callback.
+ *
+ * This function is called whenever a reply for an asynchronous message sent out
+ * by the asynchronous framework is received.
+ *
+ * Notify the fibril which is waiting for this message that it has arrived.
+ *
+ * @param arg    Pointer to the asynchronous message record.
+ * @param retval Value returned in the answer.
+ * @param data   Call data of the answer.
+ */
+static void reply_received(void *arg, int retval, ipc_call_t *data)
+{
+	futex_down(&async_futex);
+	
+	amsg_t *msg = (amsg_t *) arg;
+	msg->retval = retval;
+	
+	/* Copy data after futex_down, just in case the call was detached */
+	if ((msg->dataptr) && (data))
+		*msg->dataptr = *data;
+	
+	write_barrier();
+	
+	/* Remove message from timeout list */
+	if (msg->wdata.to_event.inlist)
+		list_remove(&msg->wdata.to_event.link);
+	
+	msg->done = true;
+	if (!msg->wdata.active) {
+		msg->wdata.active = true;
+		fibril_add_ready(msg->wdata.fid);
+	}
+	
+	futex_up(&async_futex);
+}
+
+/** Send message and return id of the sent message.
+ *
+ * The return value can be used as input for async_wait() to wait for
+ * completion.
+ *
+ * @param phoneid Handle of the phone that will be used for the send.
+ * @param method  Service-defined method.
+ * @param arg1    Service-defined payload argument.
+ * @param arg2    Service-defined payload argument.
+ * @param arg3    Service-defined payload argument.
+ * @param arg4    Service-defined payload argument.
+ * @param dataptr If non-NULL, storage where the reply data will be
+ *                stored.
+ *
+ * @return Hash of the sent message or 0 on error.
+ *
+ */
+aid_t async_send_fast(int phoneid, ipcarg_t method, ipcarg_t arg1,
+    ipcarg_t arg2, ipcarg_t arg3, ipcarg_t arg4, ipc_call_t *dataptr)
+{
+	amsg_t *msg = malloc(sizeof(*msg));
+	
+	if (!msg)
+		return 0;
+	
+	msg->done = false;
+	msg->dataptr = dataptr;
+	
+	msg->wdata.to_event.inlist = false;
+	/* We may sleep in the next method, but it will use its own mechanism */
+	msg->wdata.active = true;
+	
+	ipc_call_async_4(phoneid, method, arg1, arg2, arg3, arg4, msg,
+	    reply_received, true);
+	
+	return (aid_t) msg;
+}
+
+/** Send message and return id of the sent message
+ *
+ * The return value can be used as input for async_wait() to wait for
+ * completion.
+ *
+ * @param phoneid Handle of the phone that will be used for the send.
+ * @param method  Service-defined method.
+ * @param arg1    Service-defined payload argument.
+ * @param arg2    Service-defined payload argument.
+ * @param arg3    Service-defined payload argument.
+ * @param arg4    Service-defined payload argument.
+ * @param arg5    Service-defined payload argument.
+ * @param dataptr If non-NULL, storage where the reply data will be
+ *                stored.
+ *
+ * @return Hash of the sent message or 0 on error.
+ *
+ */
+aid_t async_send_slow(int phoneid, ipcarg_t method, ipcarg_t arg1,
+    ipcarg_t arg2, ipcarg_t arg3, ipcarg_t arg4, ipcarg_t arg5,
+    ipc_call_t *dataptr)
+{
+	amsg_t *msg = malloc(sizeof(*msg));
+	
+	if (!msg)
+		return 0;
+	
+	msg->done = false;
+	msg->dataptr = dataptr;
+	
+	msg->wdata.to_event.inlist = false;
+	/* We may sleep in next method, but it will use its own mechanism */
+	msg->wdata.active = true;
+	
+	ipc_call_async_5(phoneid, method, arg1, arg2, arg3, arg4, arg5, msg,
+	    reply_received, true);
+	
+	return (aid_t) msg;
+}
+
+/** Wait for a message sent by the async framework.
+ *
+ * @param amsgid Hash of the message to wait for.
+ * @param retval Pointer to storage where the retval of the answer will
+ *               be stored.
+ *
+ */
+void async_wait_for(aid_t amsgid, ipcarg_t *retval)
+{
+	amsg_t *msg = (amsg_t *) amsgid;
+	
+	futex_down(&async_futex);
+	if (msg->done) {
+		futex_up(&async_futex);
+		goto done;
+	}
+	
+	msg->wdata.fid = fibril_get_id();
+	msg->wdata.active = false;
+	msg->wdata.to_event.inlist = false;
+	
+	/* Leave the async_futex locked when entering this function */
+	fibril_switch(FIBRIL_TO_MANAGER);
+	
+	/* Futex is up automatically after fibril_switch */
+	
+done:
+	if (retval)
+		*retval = msg->retval;
+	
+	free(msg);
+}
+
+/** Wait for a message sent by the async framework, timeout variant.
+ *
+ * @param amsgid  Hash of the message to wait for.
+ * @param retval  Pointer to storage where the retval of the answer will
+ *                be stored.
+ * @param timeout Timeout in microseconds.
+ *
+ * @return Zero on success, ETIMEOUT if the timeout has expired.
+ *
+ */
+int async_wait_timeout(aid_t amsgid, ipcarg_t *retval, suseconds_t timeout)
+{
+	amsg_t *msg = (amsg_t *) amsgid;
+	
+	/* TODO: Let it go through the event read at least once */
+	if (timeout < 0)
+		return ETIMEOUT;
+	
+	futex_down(&async_futex);
+	if (msg->done) {
+		futex_up(&async_futex);
+		goto done;
+	}
+	
+	gettimeofday(&msg->wdata.to_event.expires, NULL);
+	tv_add(&msg->wdata.to_event.expires, timeout);
+	
+	msg->wdata.fid = fibril_get_id();
+	msg->wdata.active = false;
+	async_insert_timeout(&msg->wdata);
+	
+	/* Leave the async_futex locked when entering this function */
+	fibril_switch(FIBRIL_TO_MANAGER);
+	
+	/* Futex is up automatically after fibril_switch */
+	
+	if (!msg->done)
+		return ETIMEOUT;
+	
+done:
+	if (retval)
+		*retval = msg->retval;
+	
+	free(msg);
+	
+	return 0;
+}
+
+/** Wait for specified time.
+ *
+ * The current fibril is suspended but the thread continues to execute.
+ *
+ * @param timeout Duration of the wait in microseconds.
+ *
+ */
+void async_usleep(suseconds_t timeout)
+{
+	amsg_t *msg = malloc(sizeof(*msg));
+	
+	if (!msg)
+		return;
+	
+	msg->wdata.fid = fibril_get_id();
+	msg->wdata.active = false;
+	
+	gettimeofday(&msg->wdata.to_event.expires, NULL);
+	tv_add(&msg->wdata.to_event.expires, timeout);
+	
+	futex_down(&async_futex);
+	
+	async_insert_timeout(&msg->wdata);
+	
+	/* Leave the async_futex locked when entering this function */
+	fibril_switch(FIBRIL_TO_MANAGER);
+	
+	/* Futex is up automatically after fibril_switch() */
+	
+	free(msg);
+}
+
+/** Setter for client_connection function pointer.
+ *
+ * @param conn Function that will implement a new connection fibril.
+ *
+ */
+void async_set_client_connection(async_client_conn_t conn)
+{
+	client_connection = conn;
+}
+
+/** Setter for interrupt_received function pointer.
+ *
+ * @param intr Function that will implement a new interrupt
+ *             notification fibril.
+ */
+void async_set_interrupt_received(async_client_conn_t intr)
+{
+	interrupt_received = intr;
+}
+
+/** Pseudo-synchronous message sending - fast version.
+ *
+ * Send message asynchronously and return only after the reply arrives.
+ *
+ * This function can only transfer 4 register payload arguments. For
+ * transferring more arguments, see the slower async_req_slow().
+ *
+ * @param phoneid Hash of the phone through which to make the call.
+ * @param method  Method of the call.
+ * @param arg1    Service-defined payload argument.
+ * @param arg2    Service-defined payload argument.
+ * @param arg3    Service-defined payload argument.
+ * @param arg4    Service-defined payload argument.
+ * @param r1      If non-NULL, storage for the 1st reply argument.
+ * @param r2      If non-NULL, storage for the 2nd reply argument.
+ * @param r3      If non-NULL, storage for the 3rd reply argument.
+ * @param r4      If non-NULL, storage for the 4th reply argument.
+ * @param r5      If non-NULL, storage for the 5th reply argument.
+ *
+ * @return Return code of the reply or a negative error code.
+ *
+ */
+ipcarg_t async_req_fast(int phoneid, ipcarg_t method, ipcarg_t arg1,
+    ipcarg_t arg2, ipcarg_t arg3, ipcarg_t arg4, ipcarg_t *r1, ipcarg_t *r2,
+    ipcarg_t *r3, ipcarg_t *r4, ipcarg_t *r5)
+{
+	ipc_call_t result;
+	aid_t eid = async_send_4(phoneid, method, arg1, arg2, arg3, arg4,
+	    &result);
+	
+	ipcarg_t rc;
+	async_wait_for(eid, &rc);
+	
+	if (r1)
+		*r1 = IPC_GET_ARG1(result);
+	
+	if (r2)
+		*r2 = IPC_GET_ARG2(result);
+	
+	if (r3)
+		*r3 = IPC_GET_ARG3(result);
+	
+	if (r4)
+		*r4 = IPC_GET_ARG4(result);
+	
+	if (r5)
+		*r5 = IPC_GET_ARG5(result);
+	
+	return rc;
+}
+
+/** Pseudo-synchronous message sending - slow version.
+ *
+ * Send message asynchronously and return only after the reply arrives.
+ *
+ * @param phoneid Hash of the phone through which to make the call.
+ * @param method  Method of the call.
+ * @param arg1    Service-defined payload argument.
+ * @param arg2    Service-defined payload argument.
+ * @param arg3    Service-defined payload argument.
+ * @param arg4    Service-defined payload argument.
+ * @param arg5    Service-defined payload argument.
+ * @param r1      If non-NULL, storage for the 1st reply argument.
+ * @param r2      If non-NULL, storage for the 2nd reply argument.
+ * @param r3      If non-NULL, storage for the 3rd reply argument.
+ * @param r4      If non-NULL, storage for the 4th reply argument.
+ * @param r5      If non-NULL, storage for the 5th reply argument.
+ *
+ * @return Return code of the reply or a negative error code.
+ *
+ */
+ipcarg_t async_req_slow(int phoneid, ipcarg_t method, ipcarg_t arg1,
+    ipcarg_t arg2, ipcarg_t arg3, ipcarg_t arg4, ipcarg_t arg5, ipcarg_t *r1,
+    ipcarg_t *r2, ipcarg_t *r3, ipcarg_t *r4, ipcarg_t *r5)
+{
+	ipc_call_t result;
+	aid_t eid = async_send_5(phoneid, method, arg1, arg2, arg3, arg4, arg5,
+	    &result);
+	
+	ipcarg_t rc;
+	async_wait_for(eid, &rc);
+	
+	if (r1)
+		*r1 = IPC_GET_ARG1(result);
+	
+	if (r2)
+		*r2 = IPC_GET_ARG2(result);
+	
+	if (r3)
+		*r3 = IPC_GET_ARG3(result);
+	
+	if (r4)
+		*r4 = IPC_GET_ARG4(result);
+	
+	if (r5)
+		*r5 = IPC_GET_ARG5(result);
+	
+	return rc;
+}
+
+/** Wrapper for making IPC_M_CONNECT_ME_TO calls using the async framework.
+ * 
+ * Ask through phone for a new connection to some service.
+ *
+ * @param phoneid	Phone handle used for contacting the other side.
+ * @param arg1		User defined argument.
+ * @param arg2		User defined argument.
+ * @param arg3		User defined argument.
+ *
+ * @return		New phone handle on success or a negative error code.
+ */
+int
+async_connect_me_to(int phoneid, ipcarg_t arg1, ipcarg_t arg2, ipcarg_t arg3)
+{
+	int rc;
+	ipcarg_t newphid;
+
+	rc = async_req_3_5(phoneid, IPC_M_CONNECT_ME_TO, arg1, arg2, arg3, NULL,
+	    NULL, NULL, NULL, &newphid);
+	
+	if (rc != EOK)	
+		return rc;
+
+	return newphid;
+}
+
+/** Wrapper for making IPC_M_CONNECT_ME_TO calls using the async framework.
+ * 
+ * Ask through phone for a new connection to some service and block until
+ * success.
+ *
+ * @param phoneid	Phone handle used for contacting the other side.
+ * @param arg1		User defined argument.
+ * @param arg2		User defined argument.
+ * @param arg3		User defined argument.
+ *
+ * @return		New phone handle on success or a negative error code.
+ */
+int
+async_connect_me_to_blocking(int phoneid, ipcarg_t arg1, ipcarg_t arg2,
+    ipcarg_t arg3)
+{
+	int rc;
+	ipcarg_t newphid;
+
+	rc = async_req_4_5(phoneid, IPC_M_CONNECT_ME_TO, arg1, arg2, arg3,
+	    IPC_FLAG_BLOCKING, NULL, NULL, NULL, NULL, &newphid);
+	
+	if (rc != EOK)	
+		return rc;
+
+	return newphid;
+}
+
+/** Wrapper for making IPC_M_SHARE_IN calls using the async framework.
+ *
+ * @param phoneid	Phone that will be used to contact the receiving side.
+ * @param dst		Destination address space area base.
+ * @param size		Size of the destination address space area.
+ * @param arg		User defined argument.
+ * @param flags		Storage where the received flags will be stored. Can be
+ *			NULL.
+ *
+ * @return		Zero on success or a negative error code from errno.h.
+ */
+int async_share_in_start(int phoneid, void *dst, size_t size, ipcarg_t arg,
+    int *flags)
+{
+	int res;
+	sysarg_t tmp_flags;
+	res = async_req_3_2(phoneid, IPC_M_SHARE_IN, (ipcarg_t) dst,
+	    (ipcarg_t) size, arg, NULL, &tmp_flags);
+	if (flags)
+		*flags = tmp_flags;
+	return res;
+}
+
+/** Wrapper for receiving the IPC_M_SHARE_IN calls using the async framework.
+ *
+ * This wrapper only makes it more comfortable to receive IPC_M_SHARE_IN calls
+ * so that the user doesn't have to remember the meaning of each IPC argument.
+ *
+ * So far, this wrapper is to be used from within a connection fibril.
+ *
+ * @param callid	Storage where the hash of the IPC_M_SHARE_IN call will
+ * 			be stored.
+ * @param size		Destination address space area size.	
+ *
+ * @return		Non-zero on success, zero on failure.
+ */
+int async_share_in_receive(ipc_callid_t *callid, size_t *size)
+{
+	ipc_call_t data;
+	
+	assert(callid);
+	assert(size);
+
+	*callid = async_get_call(&data);
+	if (IPC_GET_METHOD(data) != IPC_M_SHARE_IN)
+		return 0;
+	*size = (size_t) IPC_GET_ARG2(data);
+	return 1;
+}
+
+/** Wrapper for answering the IPC_M_SHARE_IN calls using the async framework.
+ *
+ * This wrapper only makes it more comfortable to answer IPC_M_DATA_READ calls
+ * so that the user doesn't have to remember the meaning of each IPC argument.
+ *
+ * @param callid	Hash of the IPC_M_DATA_READ call to answer.
+ * @param src		Source address space base.
+ * @param flags		Flags to be used for sharing. Bits can be only cleared.
+ *
+ * @return		Zero on success or a value from @ref errno.h on failure.
+ */
+int async_share_in_finalize(ipc_callid_t callid, void *src, int flags)
+{
+	return ipc_share_in_finalize(callid, src, flags);
+}
+
+/** Wrapper for making IPC_M_SHARE_OUT calls using the async framework.
+ *
+ * @param phoneid	Phone that will be used to contact the receiving side.
+ * @param src		Source address space area base address.
+ * @param flags		Flags to be used for sharing. Bits can be only cleared.
+ *
+ * @return		Zero on success or a negative error code from errno.h.
+ */
+int async_share_out_start(int phoneid, void *src, int flags)
+{
+	return async_req_3_0(phoneid, IPC_M_SHARE_OUT, (ipcarg_t) src, 0,
+	    (ipcarg_t) flags);
+}
+
+/** Wrapper for receiving the IPC_M_SHARE_OUT calls using the async framework.
+ *
+ * This wrapper only makes it more comfortable to receive IPC_M_SHARE_OUT calls
+ * so that the user doesn't have to remember the meaning of each IPC argument.
+ *
+ * So far, this wrapper is to be used from within a connection fibril.
+ *
+ * @param callid	Storage where the hash of the IPC_M_SHARE_OUT call will
+ * 			be stored.
+ * @param size		Storage where the source address space area size will be
+ *			stored.
+ * @param flags		Storage where the sharing flags will be stored.
+ *
+ * @return		Non-zero on success, zero on failure.
+ */
+int async_share_out_receive(ipc_callid_t *callid, size_t *size, int *flags)
+{
+	ipc_call_t data;
+	
+	assert(callid);
+	assert(size);
+	assert(flags);
+
+	*callid = async_get_call(&data);
+	if (IPC_GET_METHOD(data) != IPC_M_SHARE_OUT)
+		return 0;
+	*size = (size_t) IPC_GET_ARG2(data);
+	*flags = (int) IPC_GET_ARG3(data);
+	return 1;
+}
+
+/** Wrapper for answering the IPC_M_SHARE_OUT calls using the async framework.
+ *
+ * This wrapper only makes it more comfortable to answer IPC_M_SHARE_OUT calls
+ * so that the user doesn't have to remember the meaning of each IPC argument.
+ *
+ * @param callid	Hash of the IPC_M_DATA_WRITE call to answer.
+ * @param dst		Destination address space area base address.	
+ *
+ * @return		Zero on success or a value from @ref errno.h on failure.
+ */
+int async_share_out_finalize(ipc_callid_t callid, void *dst)
+{
+	return ipc_share_out_finalize(callid, dst);
+}
+
+
+/** Wrapper for making IPC_M_DATA_READ calls using the async framework.
+ *
+ * @param phoneid	Phone that will be used to contact the receiving side.
+ * @param dst		Address of the beginning of the destination buffer.
+ * @param size		Size of the destination buffer.
+ *
+ * @return		Zero on success or a negative error code from errno.h.
+ */
+int async_data_read_start(int phoneid, void *dst, size_t size)
+{
+	return async_req_2_0(phoneid, IPC_M_DATA_READ, (ipcarg_t) dst,
+	    (ipcarg_t) size);
+}
+
+/** Wrapper for receiving the IPC_M_DATA_READ calls using the async framework.
+ *
+ * This wrapper only makes it more comfortable to receive IPC_M_DATA_READ calls
+ * so that the user doesn't have to remember the meaning of each IPC argument.
+ *
+ * So far, this wrapper is to be used from within a connection fibril.
+ *
+ * @param callid	Storage where the hash of the IPC_M_DATA_READ call will
+ * 			be stored.
+ * @param size		Storage where the maximum size will be stored. Can be
+ *			NULL.
+ *
+ * @return		Non-zero on success, zero on failure.
+ */
+int async_data_read_receive(ipc_callid_t *callid, size_t *size)
+{
+	ipc_call_t data;
+	
+	assert(callid);
+
+	*callid = async_get_call(&data);
+	if (IPC_GET_METHOD(data) != IPC_M_DATA_READ)
+		return 0;
+	if (size)
+		*size = (size_t) IPC_GET_ARG2(data);
+	return 1;
+}
+
+/** Wrapper for answering the IPC_M_DATA_READ calls using the async framework.
+ *
+ * This wrapper only makes it more comfortable to answer IPC_M_DATA_READ calls
+ * so that the user doesn't have to remember the meaning of each IPC argument.
+ *
+ * @param callid	Hash of the IPC_M_DATA_READ call to answer.
+ * @param src		Source address for the IPC_M_DATA_READ call.
+ * @param size		Size for the IPC_M_DATA_READ call. Can be smaller than
+ *			the maximum size announced by the sender.
+ *
+ * @return		Zero on success or a value from @ref errno.h on failure.
+ */
+int async_data_read_finalize(ipc_callid_t callid, const void *src, size_t size)
+{
+	return ipc_data_read_finalize(callid, src, size);
+}
+
+/** Wrapper for forwarding any read request
+ *
+ *
+ */
+int async_data_read_forward_fast(int phoneid, ipcarg_t method, ipcarg_t arg1,
+    ipcarg_t arg2, ipcarg_t arg3, ipcarg_t arg4, ipc_call_t *dataptr)
+{
+	ipc_callid_t callid;
+	if (!async_data_read_receive(&callid, NULL)) {
+		ipc_answer_0(callid, EINVAL);
+		return EINVAL;
+	}
+	
+	aid_t msg = async_send_fast(phoneid, method, arg1, arg2, arg3, arg4,
+	    dataptr);
+	if (msg == 0) {
+		ipc_answer_0(callid, EINVAL);
+		return EINVAL;
+	}
+	
+	int retval = ipc_forward_fast(callid, phoneid, 0, 0, 0,
+	    IPC_FF_ROUTE_FROM_ME);
+	if (retval != EOK) {
+		async_wait_for(msg, NULL);
+		ipc_answer_0(callid, retval);
+		return retval;
+	}
+	
+	ipcarg_t rc;
+	async_wait_for(msg, &rc);
+	
+	return (int) rc;
+}
+
+/** Wrapper for making IPC_M_DATA_WRITE calls using the async framework.
+ *
+ * @param phoneid Phone that will be used to contact the receiving side.
+ * @param src     Address of the beginning of the source buffer.
+ * @param size    Size of the source buffer.
+ *
+ * @return Zero on success or a negative error code from errno.h.
+ *
+ */
+int async_data_write_start(int phoneid, const void *src, size_t size)
+{
+	return async_req_2_0(phoneid, IPC_M_DATA_WRITE, (ipcarg_t) src,
+	    (ipcarg_t) size);
+}
+
+/** Wrapper for receiving the IPC_M_DATA_WRITE calls using the async framework.
+ *
+ * This wrapper only makes it more comfortable to receive IPC_M_DATA_WRITE calls
+ * so that the user doesn't have to remember the meaning of each IPC argument.
+ *
+ * So far, this wrapper is to be used from within a connection fibril.
+ *
+ * @param callid Storage where the hash of the IPC_M_DATA_WRITE call will
+ *               be stored.
+ * @param size   Storage where the suggested size will be stored. May be
+ *               NULL
+ *
+ * @return Non-zero on success, zero on failure.
+ *
+ */
+int async_data_write_receive(ipc_callid_t *callid, size_t *size)
+{
+	ipc_call_t data;
+	
+	assert(callid);
+	
+	*callid = async_get_call(&data);
+	if (IPC_GET_METHOD(data) != IPC_M_DATA_WRITE)
+		return 0;
+	
+	if (size)
+		*size = (size_t) IPC_GET_ARG2(data);
+	
+	return 1;
+}
+
+/** Wrapper for answering the IPC_M_DATA_WRITE calls using the async framework.
+ *
+ * This wrapper only makes it more comfortable to answer IPC_M_DATA_WRITE calls
+ * so that the user doesn't have to remember the meaning of each IPC argument.
+ *
+ * @param callid Hash of the IPC_M_DATA_WRITE call to answer.
+ * @param dst    Final destination address for the IPC_M_DATA_WRITE call.
+ * @param size   Final size for the IPC_M_DATA_WRITE call.
+ *
+ * @return Zero on success or a value from @ref errno.h on failure.
+ *
+ */
+int async_data_write_finalize(ipc_callid_t callid, void *dst, size_t size)
+{
+	return ipc_data_write_finalize(callid, dst, size);
+}
+
+/** Wrapper for receiving binary data or strings
+ *
+ * This wrapper only makes it more comfortable to use async_data_write_*
+ * functions to receive binary data or strings.
+ *
+ * @param data       Pointer to data pointer (which should be later disposed
+ *                   by free()). If the operation fails, the pointer is not
+ *                   touched.
+ * @param nullterm   If true then the received data is always zero terminated.
+ *                   This also causes to allocate one extra byte beyond the
+ *                   raw transmitted data.
+ * @param min_size   Minimum size (in bytes) of the data to receive.
+ * @param max_size   Maximum size (in bytes) of the data to receive. 0 means
+ *                   no limit.
+ * @param granulariy If non-zero then the size of the received data has to
+ *                   be divisible by this value.
+ * @param received   If not NULL, the size of the received data is stored here.
+ *
+ * @return Zero on success or a value from @ref errno.h on failure.
+ *
+ */
+int async_data_write_accept(void **data, const bool nullterm,
+    const size_t min_size, const size_t max_size, const size_t granularity,
+    size_t *received)
+{
+	ipc_callid_t callid;
+	size_t size;
+	if (!async_data_write_receive(&callid, &size)) {
+		ipc_answer_0(callid, EINVAL);
+		return EINVAL;
+	}
+	
+	if (size < min_size) {
+		ipc_answer_0(callid, EINVAL);
+		return EINVAL;
+	}
+	
+	if ((max_size > 0) && (size > max_size)) {
+		ipc_answer_0(callid, EINVAL);
+		return EINVAL;
+	}
+	
+	if ((granularity > 0) && ((size % granularity) != 0)) {
+		ipc_answer_0(callid, EINVAL);
+		return EINVAL;
+	}
+	
+	void *_data;
+	
+	if (nullterm)
+		_data = malloc(size + 1);
+	else
+		_data = malloc(size);
+	
+	if (_data == NULL) {
+		ipc_answer_0(callid, ENOMEM);
+		return ENOMEM;
+	}
+	
+	int rc = async_data_write_finalize(callid, _data, size);
+	if (rc != EOK) {
+		free(_data);
+		return rc;
+	}
+	
+	if (nullterm)
+		((char *) _data)[size] = 0;
+	
+	*data = _data;
+	if (received != NULL)
+		*received = size;
+	
+	return EOK;
+}
+
+/** Wrapper for voiding any data that is about to be received
+ *
+ * This wrapper can be used to void any pending data
+ *
+ * @param retval Error value from @ref errno.h to be returned to the caller.
+ *
+ */
+void async_data_write_void(const int retval)
+{
+	ipc_callid_t callid;
+	async_data_write_receive(&callid, NULL);
+	ipc_answer_0(callid, retval);
+}
+
+/** Wrapper for forwarding any data that is about to be received
+ *
+ *
+ */
+int async_data_write_forward_fast(int phoneid, ipcarg_t method, ipcarg_t arg1,
+    ipcarg_t arg2, ipcarg_t arg3, ipcarg_t arg4, ipc_call_t *dataptr)
+{
+	ipc_callid_t callid;
+	if (!async_data_write_receive(&callid, NULL)) {
+		ipc_answer_0(callid, EINVAL);
+		return EINVAL;
+	}
+	
+	aid_t msg = async_send_fast(phoneid, method, arg1, arg2, arg3, arg4,
+	    dataptr);
+	if (msg == 0) {
+		ipc_answer_0(callid, EINVAL);
+		return EINVAL;
+	}
+	
+	int retval = ipc_forward_fast(callid, phoneid, 0, 0, 0,
+	    IPC_FF_ROUTE_FROM_ME);
+	if (retval != EOK) {
+		async_wait_for(msg, NULL);
+		ipc_answer_0(callid, retval);
+		return retval;
+	}
+	
+	ipcarg_t rc;
+	async_wait_for(msg, &rc);
+	
+	return (int) rc;
+}
+
+/** @}
+ */
Index: uspace/lib/c/generic/cap.c
===================================================================
--- uspace/lib/c/generic/cap.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
+++ uspace/lib/c/generic/cap.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2006 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.
+ */
+
+/** @addtogroup libc
+ * @{
+ */
+/**
+ * @file	cap.c
+ * @brief	Functions to grant/revoke capabilities to/from a task.
+ */
+
+#include <cap.h>
+#include <task.h>
+#include <libc.h>
+#include <kernel/syscall/sysarg64.h>
+
+/** Grant capabilities to a task.
+ *
+ * @param id Destination task ID.
+ * @param caps Capabilities to grant.
+ *
+ * @return Zero on success or a value from @ref errno.h on failure.
+ */
+int cap_grant(task_id_t id, unsigned int caps)
+{
+	sysarg64_t arg;
+	
+	arg.value = (unsigned long long) id;
+
+	return __SYSCALL2(SYS_CAP_GRANT, (sysarg_t) &arg, (sysarg_t) caps);
+}
+
+/** Revoke capabilities from a task.
+ *
+ * @param id Destination task ID.
+ * @param caps Capabilities to revoke.
+ *
+ * @return Zero on success or a value from @ref errno.h on failure.
+ */
+int cap_revoke(task_id_t id, unsigned int caps)
+{
+	sysarg64_t arg;
+	
+	arg.value = (unsigned long long) id;
+
+	return __SYSCALL2(SYS_CAP_REVOKE, (sysarg_t) &arg, (sysarg_t) caps);
+}
+
+/** @}
+ */
Index: uspace/lib/c/generic/clipboard.c
===================================================================
--- uspace/lib/c/generic/clipboard.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
+++ uspace/lib/c/generic/clipboard.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 2009 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 libc
+ * @{
+ */
+/** @file
+ * @brief System clipboard API.
+ *
+ * The clipboard data is managed by the clipboard service and it is shared by
+ * the entire system.
+ *
+ */
+
+#include <clipboard.h>
+#include <ipc/services.h>
+#include <ipc/clipboard.h>
+#include <async.h>
+#include <str.h>
+#include <errno.h>
+#include <malloc.h>
+
+static int clip_phone = -1;
+
+/** Connect to clipboard server
+ *
+ */
+static void clip_connect(void)
+{
+	while (clip_phone < 0)
+		clip_phone = ipc_connect_me_to_blocking(PHONE_NS, SERVICE_CLIPBOARD, 0, 0);
+}
+
+/** Copy string to clipboard.
+ *
+ * Sets the clipboard contents to @a str. Passing an empty string or NULL
+ * makes the clipboard empty.
+ *
+ * @param str String to put to clipboard or NULL.
+ *
+ * @return Zero on success or negative error code.
+ *
+ */
+int clipboard_put_str(const char *str)
+{
+	size_t size = str_size(str);
+	
+	if (size == 0) {
+		async_serialize_start();
+		clip_connect();
+		
+		ipcarg_t rc = async_req_1_0(clip_phone, CLIPBOARD_PUT_DATA, CLIPBOARD_TAG_NONE);
+		
+		async_serialize_end();
+		
+		return (int) rc;
+	} else {
+		async_serialize_start();
+		clip_connect();
+		
+		aid_t req = async_send_1(clip_phone, CLIPBOARD_PUT_DATA, CLIPBOARD_TAG_DATA, NULL);
+		ipcarg_t rc = async_data_write_start(clip_phone, (void *) str, size);
+		if (rc != EOK) {
+			ipcarg_t rc_orig;
+			async_wait_for(req, &rc_orig);
+			async_serialize_end();
+			if (rc_orig == EOK)
+				return (int) rc;
+			else
+				return (int) rc_orig;
+		}
+		
+		async_wait_for(req, &rc);
+		async_serialize_end();
+		
+		return (int) rc;
+	}
+}
+
+/** Get a copy of clipboard contents.
+ *
+ * Returns a new string that can be deallocated with free().
+ *
+ * @param str Here pointer to the newly allocated string is stored.
+ *
+ * @return Zero on success or negative error code.
+ *
+ */
+int clipboard_get_str(char **str)
+{
+	/* Loop until clipboard read succesful */
+	while (true) {
+		async_serialize_start();
+		clip_connect();
+		
+		ipcarg_t size;
+		ipcarg_t tag;
+		ipcarg_t rc = async_req_0_2(clip_phone, CLIPBOARD_CONTENT, &size, &tag);
+		
+		async_serialize_end();
+		
+		if (rc != EOK)
+			return (int) rc;
+		
+		char *sbuf;
+		
+		switch (tag) {
+		case CLIPBOARD_TAG_NONE:
+			sbuf = malloc(1);
+			if (sbuf == NULL)
+				return ENOMEM;
+			
+			sbuf[0] = 0;
+			*str = sbuf;
+			return EOK;
+		case CLIPBOARD_TAG_DATA:
+			sbuf = malloc(size + 1);
+			if (sbuf == NULL)
+				return ENOMEM;
+			
+			async_serialize_start();
+			
+			aid_t req = async_send_1(clip_phone, CLIPBOARD_GET_DATA, tag, NULL);
+			rc = async_data_read_start(clip_phone, (void *) sbuf, size);
+			if ((int) rc == EOVERFLOW) {
+				/*
+				 * The data in the clipboard has changed since
+				 * the last call of CLIPBOARD_CONTENT
+				 */
+				async_serialize_end();
+				break;
+			}
+			
+			if (rc != EOK) {
+				ipcarg_t rc_orig;
+				async_wait_for(req, &rc_orig);
+				async_serialize_end();
+				if (rc_orig == EOK)
+					return (int) rc;
+				else
+					return (int) rc_orig;
+			}
+			
+			async_wait_for(req, &rc);
+			async_serialize_end();
+			
+			if (rc == EOK) {
+				sbuf[size] = 0;
+				*str = sbuf;
+			}
+			
+			return rc;
+		default:
+			return EINVAL;
+		}
+	}
+}
+
+/** @}
+ */
Index: uspace/lib/c/generic/ddi.c
===================================================================
--- uspace/lib/c/generic/ddi.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
+++ uspace/lib/c/generic/ddi.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2006 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.
+ */
+
+/** @addtogroup libc
+ * @{
+ */
+/** @file
+ */ 
+
+#include <ddi.h>
+#include <libarch/ddi.h>
+#include <libc.h>
+#include <task.h>
+#include <as.h>
+#include <align.h>
+#include <libarch/config.h>
+#include <kernel/ddi/ddi_arg.h>
+
+/** Return unique device number.
+ *
+ * @return New unique device number.
+ *
+ */
+int device_assign_devno(void)
+{
+	return __SYSCALL0(SYS_DEVICE_ASSIGN_DEVNO);
+}
+
+/** Map piece of physical memory to task.
+ *
+ * Caller of this function must have the CAP_MEM_MANAGER capability.
+ *
+ * @param pf		Physical address of the starting frame.
+ * @param vp		Virtual address of the starting page.
+ * @param pages		Number of pages to map.
+ * @param flags		Flags for the new address space area.
+ *
+ * @return 		0 on success, EPERM if the caller lacks the
+ *			CAP_MEM_MANAGER capability, ENOENT if there is no task
+ *			with specified ID and ENOMEM if there was some problem
+ *			in creating address space area.
+ */
+int physmem_map(void *pf, void *vp, unsigned long pages, int flags)
+{
+	return __SYSCALL4(SYS_PHYSMEM_MAP, (sysarg_t) pf, (sysarg_t) vp, pages,
+	    flags);
+}
+
+/** Enable I/O space range to task.
+ *
+ * Caller of this function must have the IO_MEM_MANAGER capability.
+ *
+ * @param id		Task ID.
+ * @param ioaddr	Starting address of the I/O range.
+ * @param size		Size of the range.
+ *
+ * @return		0 on success, EPERM if the caller lacks the
+ *			CAP_IO_MANAGER capability, ENOENT if there is no task
+ *			with specified ID and ENOMEM if there was some problem
+ *			in allocating memory.
+ */
+int iospace_enable(task_id_t id, void *ioaddr, unsigned long size)
+{
+	ddi_ioarg_t arg;
+
+	arg.task_id = id;
+	arg.ioaddr = ioaddr;
+	arg.size = size;
+
+	return __SYSCALL1(SYS_IOSPACE_ENABLE, (sysarg_t) &arg);
+}
+
+/** Interrupt control
+ *
+ * @param enable	1 - enable interrupts, 0 - disable interrupts
+ */
+int preemption_control(int enable)
+{
+	return __SYSCALL1(SYS_PREEMPT_CONTROL, (sysarg_t) enable);
+}
+
+/** Enable PIO for specified I/O range.
+ *
+ * @param pio_addr	I/O start address.
+ * @param size		Size of the I/O region.
+ * @param use_addr	Address where the final address for application's PIO
+ * 			will be stored.
+ *
+ * @return		Zero on success or negative error code.
+ */
+int pio_enable(void *pio_addr, size_t size, void **use_addr)
+{
+	void *phys;
+	void *virt;
+	size_t offset;
+	unsigned int pages;
+
+#ifdef IO_SPACE_BOUNDARY
+	if (pio_addr < IO_SPACE_BOUNDARY) {
+		*use_addr = pio_addr;
+		return iospace_enable(task_get_id(), pio_addr, size);
+	}
+#endif
+
+	phys = (void *) ALIGN_DOWN((uintptr_t) pio_addr, PAGE_SIZE);
+	offset = pio_addr - phys;
+	pages = ALIGN_UP(offset + size, PAGE_SIZE) >> PAGE_WIDTH;
+	virt = as_get_mappable_page(pages << PAGE_WIDTH);
+	*use_addr = virt + offset;
+	return physmem_map(phys, virt, pages, AS_AREA_READ | AS_AREA_WRITE);
+}
+
+/** @}
+ */
Index: uspace/lib/c/generic/devmap.c
===================================================================
--- uspace/lib/c/generic/devmap.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
+++ uspace/lib/c/generic/devmap.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
@@ -0,0 +1,439 @@
+/*
+ * Copyright (c) 2007 Josef Cejka
+ * Copyright (c) 2009 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 <str.h>
+#include <ipc/ipc.h>
+#include <ipc/services.h>
+#include <ipc/devmap.h>
+#include <devmap.h>
+#include <async.h>
+#include <errno.h>
+#include <malloc.h>
+#include <bool.h>
+
+static int devmap_phone_driver = -1;
+static int devmap_phone_client = -1;
+
+/** Get phone to device mapper task. */
+int devmap_get_phone(devmap_interface_t iface, unsigned int flags)
+{
+	switch (iface) {
+	case DEVMAP_DRIVER:
+		if (devmap_phone_driver >= 0)
+			return devmap_phone_driver;
+		
+		if (flags & IPC_FLAG_BLOCKING)
+			devmap_phone_driver = ipc_connect_me_to_blocking(PHONE_NS,
+			    SERVICE_DEVMAP, DEVMAP_DRIVER, 0);
+		else
+			devmap_phone_driver = ipc_connect_me_to(PHONE_NS,
+			    SERVICE_DEVMAP, DEVMAP_DRIVER, 0);
+		
+		return devmap_phone_driver;
+	case DEVMAP_CLIENT:
+		if (devmap_phone_client >= 0)
+			return devmap_phone_client;
+		
+		if (flags & IPC_FLAG_BLOCKING)
+			devmap_phone_client = ipc_connect_me_to_blocking(PHONE_NS,
+			    SERVICE_DEVMAP, DEVMAP_CLIENT, 0);
+		else
+			devmap_phone_client = ipc_connect_me_to(PHONE_NS,
+			    SERVICE_DEVMAP, DEVMAP_CLIENT, 0);
+		
+		return devmap_phone_client;
+	default:
+		return -1;
+	}
+}
+
+void devmap_hangup_phone(devmap_interface_t iface)
+{
+	switch (iface) {
+	case DEVMAP_DRIVER:
+		if (devmap_phone_driver >= 0) {
+			ipc_hangup(devmap_phone_driver);
+			devmap_phone_driver = -1;
+		}
+		break;
+	case DEVMAP_CLIENT:
+		if (devmap_phone_client >= 0) {
+			ipc_hangup(devmap_phone_client);
+			devmap_phone_client = -1;
+		}
+		break;
+	default:
+		break;
+	}
+}
+
+/** Register new driver with devmap. */
+int devmap_driver_register(const char *name, async_client_conn_t conn)
+{
+	int phone = devmap_get_phone(DEVMAP_DRIVER, IPC_FLAG_BLOCKING);
+	
+	if (phone < 0)
+		return phone;
+	
+	async_serialize_start();
+	
+	ipc_call_t answer;
+	aid_t req = async_send_2(phone, DEVMAP_DRIVER_REGISTER, 0, 0, &answer);
+	
+	ipcarg_t retval = async_data_write_start(phone, name, str_size(name));
+	if (retval != EOK) {
+		async_wait_for(req, NULL);
+		async_serialize_end();
+		return -1;
+	}
+	
+	async_set_client_connection(conn);
+	
+	ipcarg_t callback_phonehash;
+	ipc_connect_to_me(phone, 0, 0, 0, &callback_phonehash);
+	async_wait_for(req, &retval);
+	
+	async_serialize_end();
+	
+	return retval;
+}
+
+/** Register new device.
+ *
+ * @param namespace Namespace name.
+ * @param fqdn      Fully qualified device name.
+ * @param handle    Output: Handle to the created instance of device.
+ *
+ */
+int devmap_device_register(const char *fqdn, dev_handle_t *handle)
+{
+	int phone = devmap_get_phone(DEVMAP_DRIVER, IPC_FLAG_BLOCKING);
+	
+	if (phone < 0)
+		return phone;
+	
+	async_serialize_start();
+	
+	ipc_call_t answer;
+	aid_t req = async_send_2(phone, DEVMAP_DEVICE_REGISTER, 0, 0,
+	    &answer);
+	
+	ipcarg_t retval = async_data_write_start(phone, fqdn, str_size(fqdn));
+	if (retval != EOK) {
+		async_wait_for(req, NULL);
+		async_serialize_end();
+		return retval;
+	}
+	
+	async_wait_for(req, &retval);
+	
+	async_serialize_end();
+	
+	if (retval != EOK) {
+		if (handle != NULL)
+			*handle = -1;
+		return retval;
+	}
+	
+	if (handle != NULL)
+		*handle = (dev_handle_t) IPC_GET_ARG1(answer);
+	
+	return retval;
+}
+
+int devmap_device_get_handle(const char *fqdn, dev_handle_t *handle, unsigned int flags)
+{
+	int phone = devmap_get_phone(DEVMAP_CLIENT, flags);
+	
+	if (phone < 0)
+		return phone;
+	
+	async_serialize_start();
+	
+	ipc_call_t answer;
+	aid_t req = async_send_2(phone, DEVMAP_DEVICE_GET_HANDLE, flags, 0,
+	    &answer);
+	
+	ipcarg_t retval = async_data_write_start(phone, fqdn, str_size(fqdn));
+	if (retval != EOK) {
+		async_wait_for(req, NULL);
+		async_serialize_end();
+		return retval;
+	}
+	
+	async_wait_for(req, &retval);
+	
+	async_serialize_end();
+	
+	if (retval != EOK) {
+		if (handle != NULL)
+			*handle = (dev_handle_t) -1;
+		return retval;
+	}
+	
+	if (handle != NULL)
+		*handle = (dev_handle_t) IPC_GET_ARG1(answer);
+	
+	return retval;
+}
+
+int devmap_namespace_get_handle(const char *name, dev_handle_t *handle, unsigned int flags)
+{
+	int phone = devmap_get_phone(DEVMAP_CLIENT, flags);
+	
+	if (phone < 0)
+		return phone;
+	
+	async_serialize_start();
+	
+	ipc_call_t answer;
+	aid_t req = async_send_2(phone, DEVMAP_NAMESPACE_GET_HANDLE, flags, 0,
+	    &answer);
+	
+	ipcarg_t retval = async_data_write_start(phone, name, str_size(name));
+	if (retval != EOK) {
+		async_wait_for(req, NULL);
+		async_serialize_end();
+		return retval;
+	}
+	
+	async_wait_for(req, &retval);
+	
+	async_serialize_end();
+	
+	if (retval != EOK) {
+		if (handle != NULL)
+			*handle = (dev_handle_t) -1;
+		return retval;
+	}
+	
+	if (handle != NULL)
+		*handle = (dev_handle_t) IPC_GET_ARG1(answer);
+	
+	return retval;
+}
+
+devmap_handle_type_t devmap_handle_probe(dev_handle_t handle)
+{
+	int phone = devmap_get_phone(DEVMAP_CLIENT, IPC_FLAG_BLOCKING);
+	
+	if (phone < 0)
+		return phone;
+	
+	ipcarg_t type;
+	int retval = async_req_1_1(phone, DEVMAP_HANDLE_PROBE, handle, &type);
+	if (retval != EOK)
+		return DEV_HANDLE_NONE;
+	
+	return (devmap_handle_type_t) type;
+}
+
+int devmap_device_connect(dev_handle_t handle, unsigned int flags)
+{
+	int phone;
+	
+	if (flags & IPC_FLAG_BLOCKING) {
+		phone = ipc_connect_me_to_blocking(PHONE_NS, SERVICE_DEVMAP,
+		    DEVMAP_CONNECT_TO_DEVICE, handle);
+	} else {
+		phone = ipc_connect_me_to(PHONE_NS, SERVICE_DEVMAP,
+		    DEVMAP_CONNECT_TO_DEVICE, handle);
+	}
+	
+	return phone;
+}
+
+int devmap_null_create(void)
+{
+	int phone = devmap_get_phone(DEVMAP_CLIENT, IPC_FLAG_BLOCKING);
+	
+	if (phone < 0)
+		return -1;
+	
+	ipcarg_t null_id;
+	int retval = async_req_0_1(phone, DEVMAP_NULL_CREATE, &null_id);
+	if (retval != EOK)
+		return -1;
+	
+	return (int) null_id;
+}
+
+void devmap_null_destroy(int null_id)
+{
+	int phone = devmap_get_phone(DEVMAP_CLIENT, IPC_FLAG_BLOCKING);
+	
+	if (phone < 0)
+		return;
+	
+	async_req_1_0(phone, DEVMAP_NULL_DESTROY, (ipcarg_t) null_id);
+}
+
+static size_t devmap_count_namespaces_internal(int phone)
+{
+	ipcarg_t count;
+	int retval = async_req_0_1(phone, DEVMAP_GET_NAMESPACE_COUNT, &count);
+	if (retval != EOK)
+		return 0;
+	
+	return count;
+}
+
+static size_t devmap_count_devices_internal(int phone, dev_handle_t ns_handle)
+{
+	ipcarg_t count;
+	int retval = async_req_1_1(phone, DEVMAP_GET_DEVICE_COUNT, ns_handle, &count);
+	if (retval != EOK)
+		return 0;
+	
+	return count;
+}
+
+size_t devmap_count_namespaces(void)
+{
+	int phone = devmap_get_phone(DEVMAP_CLIENT, IPC_FLAG_BLOCKING);
+	
+	if (phone < 0)
+		return 0;
+	
+	return devmap_count_namespaces_internal(phone);
+}
+
+size_t devmap_count_devices(dev_handle_t ns_handle)
+{
+	int phone = devmap_get_phone(DEVMAP_CLIENT, IPC_FLAG_BLOCKING);
+	
+	if (phone < 0)
+		return 0;
+	
+	return devmap_count_devices_internal(phone, ns_handle);
+}
+
+size_t devmap_get_namespaces(dev_desc_t **data)
+{
+	int phone = devmap_get_phone(DEVMAP_CLIENT, IPC_FLAG_BLOCKING);
+	
+	if (phone < 0)
+		return 0;
+	
+	/* Loop until namespaces read succesful */
+	while (true) {
+		size_t count = devmap_count_namespaces_internal(phone);
+		if (count == 0)
+			return 0;
+		
+		dev_desc_t *devs = (dev_desc_t *) calloc(count, sizeof(dev_desc_t));
+		if (devs == NULL)
+			return 0;
+		
+		async_serialize_start();
+		
+		ipc_call_t answer;
+		aid_t req = async_send_0(phone, DEVMAP_GET_NAMESPACES, &answer);
+		
+		int rc = async_data_read_start(phone, devs, count * sizeof(dev_desc_t));
+		if (rc == EOVERFLOW) {
+			/*
+			 * Number of namespaces has changed since
+			 * the last call of DEVMAP_DEVICE_GET_NAMESPACE_COUNT
+			 */
+			async_serialize_end();
+			free(devs);
+			continue;
+		}
+		
+		if (rc != EOK) {
+			async_wait_for(req, NULL);
+			async_serialize_end();
+			free(devs);
+			return 0;
+		}
+		
+		ipcarg_t retval;
+		async_wait_for(req, &retval);
+		async_serialize_end();
+		
+		if (retval != EOK)
+			return 0;
+		
+		*data = devs;
+		return count;
+	}
+}
+
+size_t devmap_get_devices(dev_handle_t ns_handle, dev_desc_t **data)
+{
+	int phone = devmap_get_phone(DEVMAP_CLIENT, IPC_FLAG_BLOCKING);
+	
+	if (phone < 0)
+		return 0;
+	
+	/* Loop until namespaces read succesful */
+	while (true) {
+		size_t count = devmap_count_devices_internal(phone, ns_handle);
+		if (count == 0)
+			return 0;
+		
+		dev_desc_t *devs = (dev_desc_t *) calloc(count, sizeof(dev_desc_t));
+		if (devs == NULL)
+			return 0;
+		
+		async_serialize_start();
+		
+		ipc_call_t answer;
+		aid_t req = async_send_1(phone, DEVMAP_GET_DEVICES, ns_handle, &answer);
+		
+		int rc = async_data_read_start(phone, devs, count * sizeof(dev_desc_t));
+		if (rc == EOVERFLOW) {
+			/*
+			 * Number of devices has changed since
+			 * the last call of DEVMAP_DEVICE_GET_DEVICE_COUNT
+			 */
+			async_serialize_end();
+			free(devs);
+			continue;
+		}
+		
+		if (rc != EOK) {
+			async_wait_for(req, NULL);
+			async_serialize_end();
+			free(devs);
+			return 0;
+		}
+		
+		ipcarg_t retval;
+		async_wait_for(req, &retval);
+		async_serialize_end();
+		
+		if (retval != EOK)
+			return 0;
+		
+		*data = devs;
+		return count;
+	}
+}
Index: uspace/lib/c/generic/err.c
===================================================================
--- uspace/lib/c/generic/err.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
+++ uspace/lib/c/generic/err.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2006 Ondrej Palkovsky
+ * 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 libc
+ * @{
+ */
+/** @file
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+/* TODO
+void errx(int __status, __const char *__format, ...)
+{
+	_exit(0);
+}
+*/
+
+/** @}
+ */
Index: uspace/lib/c/generic/errno.c
===================================================================
--- uspace/lib/c/generic/errno.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
+++ uspace/lib/c/generic/errno.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2009 Martin Decky
+ * 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 libc
+ * @{
+ */
+/** @file
+ */
+
+#include <errno.h>
+#include <fibril.h>
+
+int _errno;
+
+/** @}
+ */
Index: uspace/lib/c/generic/event.c
===================================================================
--- uspace/lib/c/generic/event.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
+++ uspace/lib/c/generic/event.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2009 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.
+ */
+
+/** @addtogroup libc
+ * @{
+ * @}
+ */
+
+/** @addtogroup libc
+ */
+/** @file
+ */ 
+
+#include <libc.h>
+#include <event.h>
+#include <kernel/ipc/event_types.h>
+#include <ipc/ipc.h>
+
+/** Subscribe for event notifications.
+ *
+ * @param evno   Event number.
+ * @param method Use this method for notifying me.
+ *
+ * @return Value returned by the kernel.
+ */
+int event_subscribe(event_type_t e, ipcarg_t method)
+{
+	return __SYSCALL2(SYS_EVENT_SUBSCRIBE, (sysarg_t) e, (sysarg_t) method);
+}
+
+/** @}
+ */
Index: uspace/lib/c/generic/fibril.c
===================================================================
--- uspace/lib/c/generic/fibril.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
+++ uspace/lib/c/generic/fibril.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
@@ -0,0 +1,354 @@
+/*
+ * Copyright (c) 2006 Ondrej Palkovsky
+ * 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.
+ */
+
+/** @addtogroup libc
+ * @{
+ */
+/** @file
+ */
+
+#include <adt/list.h>
+#include <fibril.h>
+#include <thread.h>
+#include <tls.h>
+#include <malloc.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <arch/barrier.h>
+#include <libarch/faddr.h>
+#include <futex.h>
+#include <assert.h>
+#include <async.h>
+
+#ifndef FIBRIL_INITIAL_STACK_PAGES_NO
+#define FIBRIL_INITIAL_STACK_PAGES_NO	1
+#endif
+
+/**
+ * This futex serializes access to ready_list, serialized_list and manager_list.
+ */ 
+static atomic_t fibril_futex = FUTEX_INITIALIZER;
+
+static LIST_INITIALIZE(ready_list);
+static LIST_INITIALIZE(serialized_list);
+static LIST_INITIALIZE(manager_list);
+
+static void fibril_main(void);
+
+/** Number of threads that are executing a manager fibril. */
+static int threads_in_manager;
+/** Number of threads that are executing a manager fibril and are serialized. */
+static int serialized_threads;	/* Protected by async_futex */
+/** Fibril-local count of serialization. If > 0, we must not preempt */
+static fibril_local int serialization_count;
+
+/** Setup fibril information into TCB structure */
+fibril_t *fibril_setup(void)
+{
+	fibril_t *f;
+	tcb_t *tcb;
+
+	tcb = __make_tls();
+	if (!tcb)
+		return NULL;
+
+	f = malloc(sizeof(fibril_t));
+	if (!f) {
+		__free_tls(tcb);
+		return NULL;
+	}
+
+	tcb->fibril_data = f;
+	f->tcb = tcb;
+
+	f->func = NULL;
+	f->arg = NULL;
+	f->stack = NULL;
+	f->clean_after_me = NULL;
+	f->retval = 0;
+	f->flags = 0;
+
+	return f;
+}
+
+void fibril_teardown(fibril_t *f)
+{
+	__free_tls(f->tcb);
+	free(f);
+}
+
+/** Function that spans the whole life-cycle of a fibril.
+ *
+ * Each fibril begins execution in this function. Then the function implementing
+ * the fibril logic is called.  After its return, the return value is saved.
+ * The fibril then switches to another fibril, which cleans up after it.
+ */
+void fibril_main(void)
+{
+	fibril_t *f = __tcb_get()->fibril_data;
+
+	/* Call the implementing function. */
+	f->retval = f->func(f->arg);
+
+	fibril_switch(FIBRIL_FROM_DEAD);
+	/* not reached */
+}
+
+/** Switch from the current fibril.
+ *
+ * If calling with FIBRIL_TO_MANAGER parameter, the async_futex should be
+ * held.
+ *
+ * @param stype		Switch type. One of FIBRIL_PREEMPT, FIBRIL_TO_MANAGER,
+ * 			FIBRIL_FROM_MANAGER, FIBRIL_FROM_DEAD. The parameter
+ * 			describes the circumstances of the switch.
+ * @return		Return 0 if there is no ready fibril,
+ * 			return 1 otherwise.
+ */
+int fibril_switch(fibril_switch_type_t stype)
+{
+	int retval = 0;
+	
+	futex_down(&fibril_futex);
+	
+	if (stype == FIBRIL_PREEMPT && list_empty(&ready_list))
+		goto ret_0;
+	
+	if (stype == FIBRIL_FROM_MANAGER) {
+		if ((list_empty(&ready_list)) && (list_empty(&serialized_list)))
+			goto ret_0;
+		
+		/*
+		 * Do not preempt if there is not enough threads to run the
+		 * ready fibrils which are not serialized.
+		 */
+		if ((list_empty(&serialized_list)) &&
+		    (threads_in_manager <= serialized_threads)) {
+			goto ret_0;
+		}
+	}
+	
+	/* If we are going to manager and none exists, create it */
+	if ((stype == FIBRIL_TO_MANAGER) || (stype == FIBRIL_FROM_DEAD)) {
+		while (list_empty(&manager_list)) {
+			futex_up(&fibril_futex);
+			async_create_manager();
+			futex_down(&fibril_futex);
+		}
+	}
+	
+	fibril_t *srcf = __tcb_get()->fibril_data;
+	if (stype != FIBRIL_FROM_DEAD) {
+		
+		/* Save current state */
+		if (!context_save(&srcf->ctx)) {
+			if (serialization_count)
+				srcf->flags &= ~FIBRIL_SERIALIZED;
+			
+			if (srcf->clean_after_me) {
+				/*
+				 * Cleanup after the dead fibril from which we
+				 * restored context here.
+				 */
+				void *stack = srcf->clean_after_me->stack;
+				if (stack) {
+					/*
+					 * This check is necessary because a
+					 * thread could have exited like a
+					 * normal fibril using the
+					 * FIBRIL_FROM_DEAD switch type. In that
+					 * case, its fibril will not have the
+					 * stack member filled.
+					 */
+					free(stack);
+				}
+				fibril_teardown(srcf->clean_after_me);
+				srcf->clean_after_me = NULL;
+			}
+			
+			return 1;	/* futex_up already done here */
+		}
+		
+		/* Save myself to the correct run list */
+		if (stype == FIBRIL_PREEMPT)
+			list_append(&srcf->link, &ready_list);
+		else if (stype == FIBRIL_FROM_MANAGER) {
+			list_append(&srcf->link, &manager_list);
+			threads_in_manager--;
+		} else {
+			/*
+			 * If stype == FIBRIL_TO_MANAGER, don't put ourselves to
+			 * any list, we should already be somewhere, or we will
+			 * be lost.
+			 */
+		}
+	}
+	
+	/* Choose a new fibril to run */
+	fibril_t *dstf;
+	if ((stype == FIBRIL_TO_MANAGER) || (stype == FIBRIL_FROM_DEAD)) {
+		dstf = list_get_instance(manager_list.next, fibril_t, link);
+		if (serialization_count && stype == FIBRIL_TO_MANAGER) {
+			serialized_threads++;
+			srcf->flags |= FIBRIL_SERIALIZED;
+		}
+		threads_in_manager++;
+		
+		if (stype == FIBRIL_FROM_DEAD) 
+			dstf->clean_after_me = srcf;
+	} else {
+		if (!list_empty(&serialized_list)) {
+			dstf = list_get_instance(serialized_list.next, fibril_t,
+			    link);
+			serialized_threads--;
+		} else {
+			dstf = list_get_instance(ready_list.next, fibril_t,
+			    link);
+		}
+	}
+	list_remove(&dstf->link);
+	
+	futex_up(&fibril_futex);
+	context_restore(&dstf->ctx);
+	/* not reached */
+	
+ret_0:
+	futex_up(&fibril_futex);
+	return retval;
+}
+
+/** Create a new fibril.
+ *
+ * @param func		Implementing function of the new fibril.
+ * @param arg		Argument to pass to func.
+ *
+ * @return		Return 0 on failure or TLS of the new fibril.
+ */
+fid_t fibril_create(int (*func)(void *), void *arg)
+{
+	fibril_t *f;
+
+	f = fibril_setup();
+	if (!f) 
+		return 0;
+	f->stack = (char *) malloc(FIBRIL_INITIAL_STACK_PAGES_NO *
+	    getpagesize());
+	if (!f->stack) {
+		fibril_teardown(f);
+		return 0;
+	}
+	
+	f->func = func;
+	f->arg = arg;
+
+	context_save(&f->ctx);
+	context_set(&f->ctx, FADDR(fibril_main), f->stack,
+	    FIBRIL_INITIAL_STACK_PAGES_NO * getpagesize(), f->tcb);
+
+	return (fid_t) f;
+}
+
+/** Add a fibril to the ready list.
+ *
+ * @param fid		Pointer to the fibril structure of the fibril to be
+ *			added.
+ */
+void fibril_add_ready(fid_t fid)
+{
+	fibril_t *f;
+
+	f = (fibril_t *) fid;
+	futex_down(&fibril_futex);
+	if ((f->flags & FIBRIL_SERIALIZED))
+		list_append(&f->link, &serialized_list);
+	else
+		list_append(&f->link, &ready_list);
+	futex_up(&fibril_futex);
+}
+
+/** Add a fibril to the manager list.
+ *
+ * @param fid		Pointer to the fibril structure of the fibril to be
+ *			added.
+ */
+void fibril_add_manager(fid_t fid)
+{
+	fibril_t *f;
+
+	f = (fibril_t *) fid;
+
+	futex_down(&fibril_futex);
+	list_append(&f->link, &manager_list);
+	futex_up(&fibril_futex);
+}
+
+/** Remove one manager from the manager list. */
+void fibril_remove_manager(void)
+{
+	futex_down(&fibril_futex);
+	if (list_empty(&manager_list)) {
+		futex_up(&fibril_futex);
+		return;
+	}
+	list_remove(manager_list.next);
+	futex_up(&fibril_futex);
+}
+
+/** Return fibril id of the currently running fibril.
+ *
+ * @return fibril ID of the currently running fibril.
+ *
+ */
+fid_t fibril_get_id(void)
+{
+	return (fid_t) __tcb_get()->fibril_data;
+}
+
+/** Disable preemption
+ *
+ * If the fibril wants to send several message in a row and does not want to be
+ * preempted, it should start async_serialize_start() in the beginning of
+ * communication and async_serialize_end() in the end. If it is a true
+ * multithreaded application, it should protect the communication channel by a
+ * futex as well.
+ *
+ */
+void fibril_inc_sercount(void)
+{
+	serialization_count++;
+}
+
+/** Restore the preemption counter to the previous state. */
+void fibril_dec_sercount(void)
+{
+	serialization_count--;
+}
+
+/** @}
+ */
Index: uspace/lib/c/generic/fibril_synch.c
===================================================================
--- uspace/lib/c/generic/fibril_synch.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
+++ uspace/lib/c/generic/fibril_synch.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
@@ -0,0 +1,308 @@
+/*
+ * Copyright (c) 2009 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.
+ */
+
+/** @addtogroup libc
+ * @{
+ */
+/** @file
+ */
+
+#include <fibril_synch.h>
+#include <fibril.h>
+#include <async.h>
+#include <async_priv.h>
+#include <adt/list.h>
+#include <futex.h>
+#include <sys/time.h>
+#include <errno.h>
+#include <assert.h>
+
+static void optimize_execution_power(void)
+{
+	/*
+	 * When waking up a worker fibril previously blocked in fibril
+	 * synchronization, chances are that there is an idle manager fibril
+	 * waiting for IPC, that could start executing the awakened worker
+	 * fibril right away. We try to detect this and bring the manager
+	 * fibril back to fruitful work.
+	 */
+	if (atomic_get(&threads_in_ipc_wait) > 0)
+		ipc_poke();
+}
+
+void fibril_mutex_initialize(fibril_mutex_t *fm)
+{
+	fm->counter = 1;
+	list_initialize(&fm->waiters);
+}
+
+void fibril_mutex_lock(fibril_mutex_t *fm)
+{
+	futex_down(&async_futex);
+	if (fm->counter-- <= 0) {
+		awaiter_t wdata;
+
+		wdata.fid = fibril_get_id();
+		wdata.active = false;
+		wdata.wu_event.inlist = true;
+		link_initialize(&wdata.wu_event.link);
+		list_append(&wdata.wu_event.link, &fm->waiters);
+		fibril_switch(FIBRIL_TO_MANAGER);
+	} else {
+		futex_up(&async_futex);
+	}
+}
+
+bool fibril_mutex_trylock(fibril_mutex_t *fm)
+{
+	bool locked = false;
+	
+	futex_down(&async_futex);
+	if (fm->counter > 0) {
+		fm->counter--;
+		locked = true;
+	}
+	futex_up(&async_futex);
+	
+	return locked;
+}
+
+static void _fibril_mutex_unlock_unsafe(fibril_mutex_t *fm)
+{
+	assert(fm->counter <= 0);
+	if (fm->counter++ < 0) {
+		link_t *tmp;
+		awaiter_t *wdp;
+	
+		assert(!list_empty(&fm->waiters));
+		tmp = fm->waiters.next;
+		wdp = list_get_instance(tmp, awaiter_t, wu_event.link);
+		wdp->active = true;
+		wdp->wu_event.inlist = false;
+		list_remove(&wdp->wu_event.link);
+		fibril_add_ready(wdp->fid);
+		optimize_execution_power();
+	}
+}
+
+void fibril_mutex_unlock(fibril_mutex_t *fm)
+{
+	futex_down(&async_futex);
+	_fibril_mutex_unlock_unsafe(fm);
+	futex_up(&async_futex);
+}
+
+void fibril_rwlock_initialize(fibril_rwlock_t *frw)
+{
+	frw->writers = 0;
+	frw->readers = 0;
+	list_initialize(&frw->waiters);
+}
+
+void fibril_rwlock_read_lock(fibril_rwlock_t *frw)
+{
+	futex_down(&async_futex);
+	if (frw->writers) {
+		fibril_t *f = (fibril_t *) fibril_get_id();
+		awaiter_t wdata;
+
+		wdata.fid = (fid_t) f;
+		wdata.active = false;
+		wdata.wu_event.inlist = true;
+		link_initialize(&wdata.wu_event.link);
+		f->flags &= ~FIBRIL_WRITER;
+		list_append(&wdata.wu_event.link, &frw->waiters);
+		fibril_switch(FIBRIL_TO_MANAGER);
+	} else {
+		frw->readers++;
+		futex_up(&async_futex);
+	}
+}
+
+void fibril_rwlock_write_lock(fibril_rwlock_t *frw)
+{
+	futex_down(&async_futex);
+	if (frw->writers || frw->readers) {
+		fibril_t *f = (fibril_t *) fibril_get_id();
+		awaiter_t wdata;
+
+		wdata.fid = (fid_t) f;
+		wdata.active = false;
+		wdata.wu_event.inlist = true;
+		link_initialize(&wdata.wu_event.link);
+		f->flags |= FIBRIL_WRITER;
+		list_append(&wdata.wu_event.link, &frw->waiters);
+		fibril_switch(FIBRIL_TO_MANAGER);
+	} else {
+		frw->writers++;
+		futex_up(&async_futex);
+	}
+}
+
+static void _fibril_rwlock_common_unlock(fibril_rwlock_t *frw)
+{
+	futex_down(&async_futex);
+	assert(frw->readers || (frw->writers == 1));
+	if (frw->readers) {
+		if (--frw->readers)
+			goto out;
+	} else {
+		frw->writers--;
+	}
+	
+	assert(!frw->readers && !frw->writers);
+	
+	while (!list_empty(&frw->waiters)) {
+		link_t *tmp = frw->waiters.next;
+		awaiter_t *wdp;
+		fibril_t *f;
+		
+		wdp = list_get_instance(tmp, awaiter_t, wu_event.link);
+		f = (fibril_t *) wdp->fid;
+		
+		if (f->flags & FIBRIL_WRITER) {
+			if (frw->readers)
+				break;
+			wdp->active = true;
+			wdp->wu_event.inlist = false;
+			list_remove(&wdp->wu_event.link);
+			fibril_add_ready(wdp->fid);
+			frw->writers++;
+			optimize_execution_power();
+			break;
+		} else {
+			wdp->active = true;
+			wdp->wu_event.inlist = false;
+			list_remove(&wdp->wu_event.link);
+			fibril_add_ready(wdp->fid);
+			frw->readers++;
+			optimize_execution_power();
+		}
+	}
+out:
+	futex_up(&async_futex);
+}
+
+void fibril_rwlock_read_unlock(fibril_rwlock_t *frw)
+{
+	_fibril_rwlock_common_unlock(frw);
+}
+
+void fibril_rwlock_write_unlock(fibril_rwlock_t *frw)
+{
+	_fibril_rwlock_common_unlock(frw);
+}
+
+void fibril_condvar_initialize(fibril_condvar_t *fcv)
+{
+	list_initialize(&fcv->waiters);
+}
+
+int
+fibril_condvar_wait_timeout(fibril_condvar_t *fcv, fibril_mutex_t *fm,
+    suseconds_t timeout)
+{
+	awaiter_t wdata;
+
+	if (timeout < 0)
+		return ETIMEOUT;
+
+	wdata.fid = fibril_get_id();
+	wdata.active = false;
+	
+	wdata.to_event.inlist = timeout > 0;
+	wdata.to_event.occurred = false;
+	link_initialize(&wdata.to_event.link);
+
+	wdata.wu_event.inlist = true;
+	link_initialize(&wdata.wu_event.link);
+
+	futex_down(&async_futex);
+	if (timeout) {
+		gettimeofday(&wdata.to_event.expires, NULL);
+		tv_add(&wdata.to_event.expires, timeout);
+		async_insert_timeout(&wdata);
+	}
+	list_append(&wdata.wu_event.link, &fcv->waiters);
+	_fibril_mutex_unlock_unsafe(fm);
+	fibril_switch(FIBRIL_TO_MANAGER);
+	fibril_mutex_lock(fm);
+
+	/* async_futex not held after fibril_switch() */
+	futex_down(&async_futex);
+	if (wdata.to_event.inlist)
+		list_remove(&wdata.to_event.link);
+	if (wdata.wu_event.inlist)
+		list_remove(&wdata.wu_event.link);
+	futex_up(&async_futex);
+	
+	return wdata.to_event.occurred ? ETIMEOUT : EOK;
+}
+
+void fibril_condvar_wait(fibril_condvar_t *fcv, fibril_mutex_t *fm)
+{
+	int rc;
+
+	rc = fibril_condvar_wait_timeout(fcv, fm, 0);
+	assert(rc == EOK);
+}
+
+static void _fibril_condvar_wakeup_common(fibril_condvar_t *fcv, bool once)
+{
+	link_t *tmp;
+	awaiter_t *wdp;
+
+	futex_down(&async_futex);
+	while (!list_empty(&fcv->waiters)) {
+		tmp = fcv->waiters.next;
+		wdp = list_get_instance(tmp, awaiter_t, wu_event.link);
+		list_remove(&wdp->wu_event.link);
+		wdp->wu_event.inlist = false;
+		if (!wdp->active) {
+			wdp->active = true;
+			fibril_add_ready(wdp->fid);
+			optimize_execution_power();
+			if (once)
+				break;
+		}
+	}
+	futex_up(&async_futex);
+}
+
+void fibril_condvar_signal(fibril_condvar_t *fcv)
+{
+	_fibril_condvar_wakeup_common(fcv, true);
+}
+
+void fibril_condvar_broadcast(fibril_condvar_t *fcv)
+{
+	_fibril_condvar_wakeup_common(fcv, false);
+}
+
+/** @}
+ */
Index: uspace/lib/c/generic/futex.c
===================================================================
--- uspace/lib/c/generic/futex.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
+++ uspace/lib/c/generic/futex.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2008 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.
+ */
+
+/** @addtogroup libc
+ * @{
+ */
+/** @file
+ */ 
+
+#include <futex.h>
+#include <atomic.h>
+#include <libc.h>
+#include <sys/types.h>
+
+/** Initialize futex counter.
+ *
+ * @param futex		Futex.
+ * @param val		Initialization value.
+ */
+void futex_initialize(futex_t *futex, int val)
+{
+	atomic_set(futex, val);
+}
+
+/** Try to down the futex.
+ *
+ * @param futex		Futex.
+ * @return		Non-zero if the futex was acquired.
+ * @return		Zero if the futex was not acquired.
+ */
+int futex_trydown(futex_t *futex)
+{
+	return cas(futex, 1, 0);
+}
+
+/** Down the futex.
+ *
+ * @param futex		Futex.
+ * @return		ENOENT if there is no such virtual address.
+ * @return		Zero in the uncontended case. 
+ * @return		Otherwise one of ESYNCH_OK_ATOMIC or ESYNCH_OK_BLOCKED.
+ */
+int futex_down(futex_t *futex)
+{
+	if ((atomic_signed_t) atomic_predec(futex) < 0)
+		return __SYSCALL1(SYS_FUTEX_SLEEP, (sysarg_t) &futex->count);
+
+	return 0;
+}
+
+/** Up the futex.
+ *
+ * @param futex		Futex.
+ * @return		ENOENT if there is no such virtual address.
+ * @return		Zero in the uncontended case.
+ */
+int futex_up(futex_t *futex)
+{
+	if ((atomic_signed_t) atomic_postinc(futex) < 0)
+		return __SYSCALL1(SYS_FUTEX_WAKEUP, (sysarg_t) &futex->count);
+		
+	return 0;
+}
+
+/** @}
+ */
Index: uspace/lib/c/generic/getopt.c
===================================================================
--- uspace/lib/c/generic/getopt.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
+++ uspace/lib/c/generic/getopt.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
@@ -0,0 +1,479 @@
+/*	$NetBSD: getopt_long.c,v 1.21.4.1 2008/01/09 01:34:14 matt Exp $	*/
+
+/*-
+ * Copyright (c) 2000 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Dieter Baron and Thomas Klausner.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``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 FOUNDATION OR CONTRIBUTORS
+ * 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.
+ */
+
+/* Ported to HelenOS August 2008 by Tim Post <echo@echoreply.us> */
+
+#include <assert.h>
+#include <stdarg.h>
+#include <err.h>
+#include <errno.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <str.h>
+
+/* HelenOS Port : We're incorporating only the modern getopt_long with wrappers
+ * to keep legacy getopt() usage from breaking. All references to REPLACE_GETOPT
+ * are dropped, we just include the code */
+
+int	opterr = 1;		/* if error message should be printed */
+int	optind = 1;		/* index into parent argv vector */
+int	optopt = '?';		/* character checked for validity */
+int	optreset;		/* reset getopt */
+const char *optarg;		/* argument associated with option */
+
+
+#define IGNORE_FIRST	(*options == '-' || *options == '+')
+#define PRINT_ERROR	((opterr) && ((*options != ':') \
+				      || (IGNORE_FIRST && options[1] != ':')))
+/*HelenOS Port - POSIXLY_CORRECT is always false */
+#define IS_POSIXLY_CORRECT 0
+#define PERMUTE         (!IS_POSIXLY_CORRECT && !IGNORE_FIRST)
+/* XXX: GNU ignores PC if *options == '-' */
+#define IN_ORDER        (!IS_POSIXLY_CORRECT && *options == '-')
+
+/* return values */
+#define	BADCH	(int)'?'
+#define	BADARG		((IGNORE_FIRST && options[1] == ':') \
+			 || (*options == ':') ? (int)':' : (int)'?')
+#define INORDER (int)1
+
+#define	EMSG	""
+
+static int getopt_internal(int, char **, const char *);
+static int gcd(int, int);
+static void permute_args(int, int, int, char **);
+
+static const char *place = EMSG; /* option letter processing */
+
+/* XXX: set optreset to 1 rather than these two */
+static int nonopt_start = -1; /* first non option argument (for permute) */
+static int nonopt_end = -1;   /* first option after non options (for permute) */
+
+/* Error messages */
+
+/* HelenOS Port: Calls to warnx() were eliminated (as we have no stderr that
+ * may be redirected) and replaced with printf. As such, error messages now
+ * end in a newline */
+
+static const char recargchar[] = "option requires an argument -- %c\n";
+static const char recargstring[] = "option requires an argument -- %s\n";
+static const char ambig[] = "ambiguous option -- %.*s\n";
+static const char noarg[] = "option doesn't take an argument -- %.*s\n";
+static const char illoptchar[] = "unknown option -- %c\n";
+static const char illoptstring[] = "unknown option -- %s\n";
+
+
+/*
+ * Compute the greatest common divisor of a and b.
+ */
+static int
+gcd(a, b)
+	int a;
+	int b;
+{
+	int c;
+
+	c = a % b;
+	while (c != 0) {
+		a = b;
+		b = c;
+		c = a % b;
+	}
+	   
+	return b;
+}
+
+/*
+ * Exchange the block from nonopt_start to nonopt_end with the block
+ * from nonopt_end to opt_end (keeping the same order of arguments
+ * in each block).
+ */
+static void
+permute_args(panonopt_start, panonopt_end, opt_end, nargv)
+	int panonopt_start;
+	int panonopt_end;
+	int opt_end;
+	char **nargv;
+{
+	int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
+	char *swap;
+
+	assert(nargv != NULL);
+
+	/*
+	 * compute lengths of blocks and number and size of cycles
+	 */
+	nnonopts = panonopt_end - panonopt_start;
+	nopts = opt_end - panonopt_end;
+	ncycle = gcd(nnonopts, nopts);
+	cyclelen = (opt_end - panonopt_start) / ncycle;
+
+	for (i = 0; i < ncycle; i++) {
+		cstart = panonopt_end+i;
+		pos = cstart;
+		for (j = 0; j < cyclelen; j++) {
+			if (pos >= panonopt_end)
+				pos -= nnonopts;
+			else
+				pos += nopts;
+			swap = nargv[pos];
+			nargv[pos] = nargv[cstart];
+			nargv[cstart] = swap;
+		}
+	}
+}
+
+/*
+ * getopt_internal --
+ *	Parse argc/argv argument vector.  Called by user level routines.
+ *  Returns -2 if -- is found (can be long option or end of options marker).
+ */
+static int
+getopt_internal(nargc, nargv, options)
+	int nargc;
+	char **nargv;
+	const char *options;
+{
+	const char *oli;				/* option letter list index */
+	int optchar;
+
+	assert(nargv != NULL);
+	assert(options != NULL);
+
+	optarg = NULL;
+
+	/*
+	 * XXX Some programs (like rsyncd) expect to be able to
+	 * XXX re-initialize optind to 0 and have getopt_long(3)
+	 * XXX properly function again.  Work around this braindamage.
+	 */
+	if (optind == 0)
+		optind = 1;
+
+	if (optreset)
+		nonopt_start = nonopt_end = -1;
+start:
+	if (optreset || !*place) {		/* update scanning pointer */
+		optreset = 0;
+		if (optind >= nargc) {          /* end of argument vector */
+			place = EMSG;
+			if (nonopt_end != -1) {
+				/* do permutation, if we have to */
+				permute_args(nonopt_start, nonopt_end,
+				    optind, nargv);
+				optind -= nonopt_end - nonopt_start;
+			}
+			else if (nonopt_start != -1) {
+				/*
+				 * If we skipped non-options, set optind
+				 * to the first of them.
+				 */
+				optind = nonopt_start;
+			}
+			nonopt_start = nonopt_end = -1;
+			return -1;
+		}
+		if ((*(place = nargv[optind]) != '-')
+		    || (place[1] == '\0')) {    /* found non-option */
+			place = EMSG;
+			if (IN_ORDER) {
+				/*
+				 * GNU extension: 
+				 * return non-option as argument to option 1
+				 */
+				optarg = nargv[optind++];
+				return INORDER;
+			}
+			if (!PERMUTE) {
+				/*
+				 * if no permutation wanted, stop parsing
+				 * at first non-option
+				 */
+				return -1;
+			}
+			/* do permutation */
+			if (nonopt_start == -1)
+				nonopt_start = optind;
+			else if (nonopt_end != -1) {
+				permute_args(nonopt_start, nonopt_end,
+				    optind, nargv);
+				nonopt_start = optind -
+				    (nonopt_end - nonopt_start);
+				nonopt_end = -1;
+			}
+			optind++;
+			/* process next argument */
+			goto start;
+		}
+		if (nonopt_start != -1 && nonopt_end == -1)
+			nonopt_end = optind;
+		if (place[1] && *++place == '-') {	/* found "--" */
+			place++;
+			return -2;
+		}
+	}
+	if ((optchar = (int)*place++) == (int)':' ||
+	    (oli = str_chr(options + (IGNORE_FIRST ? 1 : 0), optchar)) == NULL) {
+		/* option letter unknown or ':' */
+		if (!*place)
+			++optind;
+		if (PRINT_ERROR)
+			printf(illoptchar, optchar);
+		optopt = optchar;
+		return BADCH;
+	}
+	if (optchar == 'W' && oli[1] == ';') {		/* -W long-option */
+		/* XXX: what if no long options provided (called by getopt)? */
+		if (*place) 
+			return -2;
+
+		if (++optind >= nargc) {	/* no arg */
+			place = EMSG;
+			if (PRINT_ERROR)
+				printf(recargchar, optchar);
+			optopt = optchar;
+			return BADARG;
+		} else				/* white space */
+			place = nargv[optind];
+		/*
+		 * Handle -W arg the same as --arg (which causes getopt to
+		 * stop parsing).
+		 */
+		return -2;
+	}
+	if (*++oli != ':') {			/* doesn't take argument */
+		if (!*place)
+			++optind;
+	} else {				/* takes (optional) argument */
+		optarg = NULL;
+		if (*place)			/* no white space */
+			optarg = place;
+		/* XXX: disable test for :: if PC? (GNU doesn't) */
+		else if (oli[1] != ':') {	/* arg not optional */
+			if (++optind >= nargc) {	/* no arg */
+				place = EMSG;
+				if (PRINT_ERROR)
+					printf(recargchar, optchar);
+				optopt = optchar;
+				return BADARG;
+			} else
+				optarg = nargv[optind];
+		}
+		place = EMSG;
+		++optind;
+	}
+	/* dump back option letter */
+	return optchar;
+}
+
+/*
+ * getopt --
+ *	Parse argc/argv argument vector.
+ */
+int
+getopt(nargc, nargv, options)
+	int nargc;
+	char * const *nargv;
+	const char *options;
+{
+	int retval;
+
+	assert(nargv != NULL);
+	assert(options != NULL);
+
+	retval = getopt_internal(nargc, (char **)nargv, options);
+	if (retval == -2) {
+		++optind;
+		/*
+		 * We found an option (--), so if we skipped non-options,
+		 * we have to permute.
+		 */
+		if (nonopt_end != -1) {
+			permute_args(nonopt_start, nonopt_end, optind,
+				       (char **)nargv);
+			optind -= nonopt_end - nonopt_start;
+		}
+		nonopt_start = nonopt_end = -1;
+		retval = -1;
+	}
+	return retval;
+}
+
+/*
+ * getopt_long --
+ *	Parse argc/argv argument vector.
+ */
+int
+getopt_long(nargc, nargv, options, long_options, idx)
+	int nargc;
+	char * const *nargv;
+	const char *options;
+	const struct option *long_options;
+	int *idx;
+{
+	int retval;
+
+#define IDENTICAL_INTERPRETATION(_x, _y)				\
+	(long_options[(_x)].has_arg == long_options[(_y)].has_arg &&	\
+	 long_options[(_x)].flag == long_options[(_y)].flag &&		\
+	 long_options[(_x)].val == long_options[(_y)].val)
+
+	assert(nargv != NULL);
+	assert(options != NULL);
+	assert(long_options != NULL);
+	/* idx may be NULL */
+
+	retval = getopt_internal(nargc, (char **)nargv, options);
+	if (retval == -2) {
+		char *current_argv;
+		const char *has_equal;
+		size_t current_argv_len;
+		int i, ambiguous, match;
+
+		current_argv = (char *)place;
+		match = -1;
+		ambiguous = 0;
+
+		optind++;
+		place = EMSG;
+
+		if (*current_argv == '\0') {		/* found "--" */
+			/*
+			 * We found an option (--), so if we skipped
+			 * non-options, we have to permute.
+			 */
+			if (nonopt_end != -1) {
+				permute_args(nonopt_start, nonopt_end,
+				    optind, (char **)nargv);
+				optind -= nonopt_end - nonopt_start;
+			}
+			nonopt_start = nonopt_end = -1;
+			return -1;
+		}
+		if ((has_equal = str_chr(current_argv, '=')) != NULL) {
+			/* argument found (--option=arg) */
+			current_argv_len = has_equal - current_argv;
+			has_equal++;
+		} else
+			current_argv_len = str_size(current_argv);
+	    
+		for (i = 0; long_options[i].name; i++) {
+			/* find matching long option */
+			if (str_lcmp(current_argv, long_options[i].name,
+			    str_nlength(current_argv, current_argv_len)))
+				continue;
+
+			if (str_size(long_options[i].name) ==
+			    (unsigned)current_argv_len) {
+				/* exact match */
+				match = i;
+				ambiguous = 0;
+				break;
+			}
+			if (match == -1)		/* partial match */
+				match = i;
+			else if (!IDENTICAL_INTERPRETATION(i, match))
+				ambiguous = 1;
+		}
+		if (ambiguous) {
+			/* ambiguous abbreviation */
+			if (PRINT_ERROR)
+				printf(ambig, (int)current_argv_len,
+				     current_argv);
+			optopt = 0;
+			return BADCH;
+		}
+		if (match != -1) {			/* option found */
+		        if (long_options[match].has_arg == no_argument
+			    && has_equal) {
+				if (PRINT_ERROR)
+					printf(noarg, (int)current_argv_len,
+					     current_argv);
+				/*
+				 * XXX: GNU sets optopt to val regardless of
+				 * flag
+				 */
+				if (long_options[match].flag == NULL)
+					optopt = long_options[match].val;
+				else
+					optopt = 0;
+				return BADARG;
+			}
+			if (long_options[match].has_arg == required_argument ||
+			    long_options[match].has_arg == optional_argument) {
+				if (has_equal)
+					optarg = has_equal;
+				else if (long_options[match].has_arg ==
+				    required_argument) {
+					/*
+					 * optional argument doesn't use
+					 * next nargv
+					 */
+					optarg = nargv[optind++];
+				}
+			}
+			if ((long_options[match].has_arg == required_argument)
+			    && (optarg == NULL)) {
+				/*
+				 * Missing argument; leading ':'
+				 * indicates no error should be generated
+				 */
+				if (PRINT_ERROR)
+					printf(recargstring, current_argv);
+				/*
+				 * XXX: GNU sets optopt to val regardless
+				 * of flag
+				 */
+				if (long_options[match].flag == NULL)
+					optopt = long_options[match].val;
+				else
+					optopt = 0;
+				--optind;
+				return BADARG;
+			}
+		} else {			/* unknown option */
+			if (PRINT_ERROR)
+				printf(illoptstring, current_argv);
+			optopt = 0;
+			return BADCH;
+		}
+		if (long_options[match].flag) {
+			*long_options[match].flag = long_options[match].val;
+			retval = 0;
+		} else 
+			retval = long_options[match].val;
+		if (idx)
+			*idx = match;
+	}
+	return retval;
+#undef IDENTICAL_INTERPRETATION
+}
+
Index: uspace/lib/c/generic/io/asprintf.c
===================================================================
--- uspace/lib/c/generic/io/asprintf.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
+++ uspace/lib/c/generic/io/asprintf.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2006 Josef Cejka
+ * Copyright (c) 2008 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.
+ */
+
+/** @addtogroup libc
+ * @{
+ */
+/** @file
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <str.h>
+#include <io/printf_core.h>
+
+static int asprintf_str_write(const char *str, size_t count, void *unused)
+{
+	return str_nlength(str, count);
+}
+
+static int asprintf_wstr_write(const wchar_t *str, size_t count, void *unused)
+{
+	return wstr_nlength(str, count);
+}
+
+/** Allocate and print to string.
+ *
+ * @param strp Address of the pointer where to store the address of
+ *             the newly allocated string.
+ * @fmt        Format string.
+ *
+ * @return Number of characters printed or a negative error code.
+ *
+ */
+int asprintf(char **strp, const char *fmt, ...)
+{
+	struct printf_spec ps = {
+		asprintf_str_write,
+		asprintf_wstr_write,
+		NULL
+	};
+	
+	va_list args;
+	va_start(args, fmt);
+	
+	int ret = printf_core(fmt, &ps, args);
+	va_end(args);
+	
+	if (ret > 0) {
+		*strp = malloc(STR_BOUNDS(ret) + 1);
+		if (*strp == NULL)
+			return -1;
+		
+		va_start(args, fmt);
+		vsnprintf(*strp, STR_BOUNDS(ret) + 1, fmt, args);
+		va_end(args);
+	}
+	
+	return ret;
+}
+
+/** @}
+ */
Index: uspace/lib/c/generic/io/console.c
===================================================================
--- uspace/lib/c/generic/io/console.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
+++ uspace/lib/c/generic/io/console.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 2006 Josef Cejka
+ * Copyright (c) 2006 Jakub Vana
+ * Copyright (c) 2008 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 libc
+ * @{
+ */
+/** @file
+ */
+
+#include <libc.h>
+#include <async.h>
+#include <io/console.h>
+#include <ipc/console.h>
+
+void console_clear(int phone)
+{
+	async_msg_0(phone, CONSOLE_CLEAR);
+}
+
+int console_get_size(int phone, int *cols, int *rows)
+{
+	ipcarg_t cols_v;
+	ipcarg_t rows_v;
+	int rc;
+
+	rc = async_req_0_2(phone, CONSOLE_GET_SIZE, &cols_v, &rows_v);
+
+	*cols = (int) cols_v;
+	*rows = (int) rows_v;
+	return rc;
+}
+
+void console_set_style(int phone, int style)
+{
+	async_msg_1(phone, CONSOLE_SET_STYLE, style);
+}
+
+void console_set_color(int phone, int fg_color, int bg_color, int flags)
+{
+	async_msg_3(phone, CONSOLE_SET_COLOR, fg_color, bg_color, flags);
+}
+
+void console_set_rgb_color(int phone, int fg_color, int bg_color)
+{
+	async_msg_2(phone, CONSOLE_SET_RGB_COLOR, fg_color, bg_color);
+}
+
+void console_cursor_visibility(int phone, bool show)
+{
+	async_msg_1(phone, CONSOLE_CURSOR_VISIBILITY, show != false);
+}
+
+int console_get_color_cap(int phone, int *ccap)
+{
+	ipcarg_t ccap_tmp;
+	int rc;
+
+	rc = async_req_0_1(phone, CONSOLE_GET_COLOR_CAP, &ccap_tmp);
+	*ccap = ccap_tmp;
+
+	return rc;
+}
+
+void console_kcon_enable(int phone)
+{
+	async_msg_0(phone, CONSOLE_KCON_ENABLE);
+}
+
+int console_get_pos(int phone, int *col, int *row)
+{
+	ipcarg_t col_v;
+	ipcarg_t row_v;
+	int rc;
+
+	rc = async_req_0_2(phone, CONSOLE_GET_POS, &col_v, &row_v);
+
+	*col = (int) col_v;
+	*row = (int) row_v;
+	return rc;
+}
+
+void console_goto(int phone, int col, int row)
+{
+	async_msg_2(phone, CONSOLE_GOTO, col, row);
+}
+
+bool console_get_event(int phone, console_event_t *event)
+{
+	ipcarg_t type;
+	ipcarg_t key;
+	ipcarg_t mods;
+	ipcarg_t c;
+	
+	int rc = async_req_0_4(phone, CONSOLE_GET_EVENT, &type, &key, &mods, &c);
+	if (rc < 0)
+		return false;
+	
+	event->type = type;
+	event->key = key;
+	event->mods = mods;
+	event->c = c;
+	
+	return true;
+}
+
+/** @}
+ */
Index: uspace/lib/c/generic/io/io.c
===================================================================
--- uspace/lib/c/generic/io/io.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
+++ uspace/lib/c/generic/io/io.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
@@ -0,0 +1,780 @@
+/*
+ * Copyright (c) 2005 Martin Decky
+ * 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 libc
+ * @{
+ */
+/** @file
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <str.h>
+#include <errno.h>
+#include <bool.h>
+#include <malloc.h>
+#include <io/klog.h>
+#include <vfs/vfs.h>
+#include <ipc/devmap.h>
+#include <adt/list.h>
+
+static void _ffillbuf(FILE *stream);
+static void _fflushbuf(FILE *stream);
+
+static FILE stdin_null = {
+	.fd = -1,
+	.error = true,
+	.eof = true,
+	.klog = false,
+	.phone = -1,
+	.btype = _IONBF,
+	.buf = NULL,
+	.buf_size = 0,
+	.buf_head = NULL,
+	.buf_tail = NULL,
+	.buf_state = _bs_empty
+};
+
+static FILE stdout_klog = {
+	.fd = -1,
+	.error = false,
+	.eof = false,
+	.klog = true,
+	.phone = -1,
+	.btype = _IOLBF,
+	.buf = NULL,
+	.buf_size = BUFSIZ,
+	.buf_head = NULL,
+	.buf_tail = NULL,
+	.buf_state = _bs_empty
+};
+
+static FILE stderr_klog = {
+	.fd = -1,
+	.error = false,
+	.eof = false,
+	.klog = true,
+	.phone = -1,
+	.btype = _IONBF,
+	.buf = NULL,
+	.buf_size = 0,
+	.buf_head = NULL,
+	.buf_tail = NULL,
+	.buf_state = _bs_empty
+};
+
+FILE *stdin = NULL;
+FILE *stdout = NULL;
+FILE *stderr = NULL;
+
+static LIST_INITIALIZE(files);
+
+void __stdio_init(int filc, fdi_node_t *filv[])
+{
+	if (filc > 0) {
+		stdin = fopen_node(filv[0], "r");
+	} else {
+		stdin = &stdin_null;
+		list_append(&stdin->link, &files);
+	}
+	
+	if (filc > 1) {
+		stdout = fopen_node(filv[1], "w");
+	} else {
+		stdout = &stdout_klog;
+		list_append(&stdout->link, &files);
+	}
+	
+	if (filc > 2) {
+		stderr = fopen_node(filv[2], "w");
+	} else {
+		stderr = &stderr_klog;
+		list_append(&stderr->link, &files);
+	}
+}
+
+void __stdio_done(void)
+{
+	link_t *link = files.next;
+	
+	while (link != &files) {
+		FILE *file = list_get_instance(link, FILE, link);
+		fclose(file);
+		link = files.next;
+	}
+}
+
+static bool parse_mode(const char *mode, int *flags)
+{
+	/* Parse mode except first character. */
+	const char *mp = mode;
+	if (*mp++ == 0) {
+		errno = EINVAL;
+		return false;
+	}
+	
+	if ((*mp == 'b') || (*mp == 't'))
+		mp++;
+	
+	bool plus;
+	if (*mp == '+') {
+		mp++;
+		plus = true;
+	} else
+		plus = false;
+	
+	if (*mp != 0) {
+		errno = EINVAL;
+		return false;
+	}
+	
+	/* Parse first character of mode and determine flags for open(). */
+	switch (mode[0]) {
+	case 'r':
+		*flags = plus ? O_RDWR : O_RDONLY;
+		break;
+	case 'w':
+		*flags = (O_TRUNC | O_CREAT) | (plus ? O_RDWR : O_WRONLY);
+		break;
+	case 'a':
+		/* TODO: a+ must read from beginning, append to the end. */
+		if (plus) {
+			errno = ENOTSUP;
+			return false;
+		}
+		*flags = (O_APPEND | O_CREAT) | (plus ? O_RDWR : O_WRONLY);
+	default:
+		errno = EINVAL;
+		return false;
+	}
+	
+	return true;
+}
+
+/** Set stream buffer. */
+void setvbuf(FILE *stream, void *buf, int mode, size_t size)
+{
+	stream->btype = mode;
+	stream->buf = buf;
+	stream->buf_size = size;
+	stream->buf_head = stream->buf;
+	stream->buf_tail = stream->buf;
+	stream->buf_state = _bs_empty;
+}
+
+static void _setvbuf(FILE *stream)
+{
+	/* FIXME: Use more complex rules for setting buffering options. */
+	
+	switch (stream->fd) {
+	case 1:
+		setvbuf(stream, NULL, _IOLBF, BUFSIZ);
+		break;
+	case 0:
+	case 2:
+		setvbuf(stream, NULL, _IONBF, 0);
+		break;
+	default:
+		setvbuf(stream, NULL, _IOFBF, BUFSIZ);
+	}
+}
+
+/** Allocate stream buffer. */
+static int _fallocbuf(FILE *stream)
+{
+	assert(stream->buf == NULL);
+	
+	stream->buf = malloc(stream->buf_size);
+	if (stream->buf == NULL) {
+		errno = ENOMEM;
+		return -1;
+	}
+	
+	stream->buf_head = stream->buf;
+	stream->buf_tail = stream->buf;
+	return 0;
+}
+
+/** Open a stream.
+ *
+ * @param path Path of the file to open.
+ * @param mode Mode string, (r|w|a)[b|t][+].
+ *
+ */
+FILE *fopen(const char *path, const char *mode)
+{
+	int flags;
+	if (!parse_mode(mode, &flags))
+		return NULL;
+	
+	/* Open file. */
+	FILE *stream = malloc(sizeof(FILE));
+	if (stream == NULL) {
+		errno = ENOMEM;
+		return NULL;
+	}
+	
+	stream->fd = open(path, flags, 0666);
+	if (stream->fd < 0) {
+		/* errno was set by open() */
+		free(stream);
+		return NULL;
+	}
+	
+	stream->error = false;
+	stream->eof = false;
+	stream->klog = false;
+	stream->phone = -1;
+	stream->need_sync = false;
+	_setvbuf(stream);
+	
+	list_append(&stream->link, &files);
+	
+	return stream;
+}
+
+FILE *fdopen(int fd, const char *mode)
+{
+	/* Open file. */
+	FILE *stream = malloc(sizeof(FILE));
+	if (stream == NULL) {
+		errno = ENOMEM;
+		return NULL;
+	}
+	
+	stream->fd = fd;
+	stream->error = false;
+	stream->eof = false;
+	stream->klog = false;
+	stream->phone = -1;
+	stream->need_sync = false;
+	_setvbuf(stream);
+	
+	list_append(&stream->link, &files);
+	
+	return stream;
+}
+
+FILE *fopen_node(fdi_node_t *node, const char *mode)
+{
+	int flags;
+	if (!parse_mode(mode, &flags))
+		return NULL;
+	
+	/* Open file. */
+	FILE *stream = malloc(sizeof(FILE));
+	if (stream == NULL) {
+		errno = ENOMEM;
+		return NULL;
+	}
+	
+	stream->fd = open_node(node, flags);
+	if (stream->fd < 0) {
+		/* errno was set by open_node() */
+		free(stream);
+		return NULL;
+	}
+	
+	stream->error = false;
+	stream->eof = false;
+	stream->klog = false;
+	stream->phone = -1;
+	stream->need_sync = false;
+	_setvbuf(stream);
+	
+	list_append(&stream->link, &files);
+	
+	return stream;
+}
+
+int fclose(FILE *stream)
+{
+	int rc = 0;
+	
+	fflush(stream);
+	
+	if (stream->phone >= 0)
+		ipc_hangup(stream->phone);
+	
+	if (stream->fd >= 0)
+		rc = close(stream->fd);
+	
+	list_remove(&stream->link);
+	
+	if ((stream != &stdin_null)
+	    && (stream != &stdout_klog)
+	    && (stream != &stderr_klog))
+		free(stream);
+	
+	stream = NULL;
+	
+	if (rc != 0) {
+		/* errno was set by close() */
+		return EOF;
+	}
+	
+	return 0;
+}
+
+/** Read from a stream (unbuffered).
+ *
+ * @param buf    Destination buffer.
+ * @param size   Size of each record.
+ * @param nmemb  Number of records to read.
+ * @param stream Pointer to the stream.
+ */
+static size_t _fread(void *buf, size_t size, size_t nmemb, FILE *stream)
+{
+	size_t left, done;
+
+	if (size == 0 || nmemb == 0)
+		return 0;
+
+	left = size * nmemb;
+	done = 0;
+	
+	while ((left > 0) && (!stream->error) && (!stream->eof)) {
+		ssize_t rd = read(stream->fd, buf + done, left);
+		
+		if (rd < 0)
+			stream->error = true;
+		else if (rd == 0)
+			stream->eof = true;
+		else {
+			left -= rd;
+			done += rd;
+		}
+	}
+	
+	return (done / size);
+}
+
+/** Write to a stream (unbuffered).
+ *
+ * @param buf    Source buffer.
+ * @param size   Size of each record.
+ * @param nmemb  Number of records to write.
+ * @param stream Pointer to the stream.
+ */
+static size_t _fwrite(const void *buf, size_t size, size_t nmemb, FILE *stream)
+{
+	size_t left;
+	size_t done;
+
+	if (size == 0 || nmemb == 0)
+		return 0;
+
+	left = size * nmemb;
+	done = 0;
+
+	while ((left > 0) && (!stream->error)) {
+		ssize_t wr;
+		
+		if (stream->klog)
+			wr = klog_write(buf + done, left);
+		else
+			wr = write(stream->fd, buf + done, left);
+		
+		if (wr <= 0)
+			stream->error = true;
+		else {
+			left -= wr;
+			done += wr;
+		}
+	}
+
+	if (done > 0)
+		stream->need_sync = true;
+	
+	return (done / size);
+}
+
+/** Read some data in stream buffer. */
+static void _ffillbuf(FILE *stream)
+{
+	ssize_t rc;
+
+	stream->buf_head = stream->buf_tail = stream->buf;
+
+	rc = read(stream->fd, stream->buf, stream->buf_size);
+	if (rc < 0) {
+		stream->error = true;
+		return;
+	}
+
+	if (rc == 0) {
+		stream->eof = true;
+		return;
+	}
+
+	stream->buf_head += rc;
+	stream->buf_state = _bs_read;
+}
+
+/** Write out stream buffer, do not sync stream. */
+static void _fflushbuf(FILE *stream)
+{
+	size_t bytes_used;
+
+	if ((!stream->buf) || (stream->btype == _IONBF) || (stream->error))
+		return;
+
+	bytes_used = stream->buf_head - stream->buf_tail;
+	if (bytes_used == 0)
+		return;
+
+	/* If buffer has prefetched read data, we need to seek back. */
+	if (stream->buf_state == _bs_read)
+		lseek(stream->fd, - (ssize_t) bytes_used, SEEK_CUR);
+
+	/* If buffer has unwritten data, we need to write them out. */
+	if (stream->buf_state == _bs_write)
+		(void) _fwrite(stream->buf_tail, 1, bytes_used, stream);
+
+	stream->buf_head = stream->buf;
+	stream->buf_tail = stream->buf;
+	stream->buf_state = _bs_empty;
+}
+
+/** Read from a stream.
+ *
+ * @param dest   Destination buffer.
+ * @param size   Size of each record.
+ * @param nmemb  Number of records to read.
+ * @param stream Pointer to the stream.
+ *
+ */
+size_t fread(void *dest, size_t size, size_t nmemb, FILE *stream)
+{
+	uint8_t *dp;
+	size_t bytes_left;
+	size_t now;
+	size_t data_avail;
+	size_t total_read;
+	size_t i;
+
+	if (size == 0 || nmemb == 0)
+		return 0;
+
+	/* If not buffered stream, read in directly. */
+	if (stream->btype == _IONBF) {
+		now = _fread(dest, size, nmemb, stream);
+		return now;
+	}
+
+	/* Make sure no data is pending write. */
+	if (stream->buf_state == _bs_write)
+		_fflushbuf(stream);
+
+	/* Perform lazy allocation of stream buffer. */
+	if (stream->buf == NULL) {
+		if (_fallocbuf(stream) != 0)
+			return 0; /* Errno set by _fallocbuf(). */
+	}
+
+	bytes_left = size * nmemb;
+	total_read = 0;
+	dp = (uint8_t *) dest;
+
+	while ((!stream->error) && (!stream->eof) && (bytes_left > 0)) {
+		if (stream->buf_head == stream->buf_tail)
+			_ffillbuf(stream);
+
+		if (stream->error || stream->eof)
+			break;
+
+		data_avail = stream->buf_head - stream->buf_tail;
+
+		if (bytes_left > data_avail)
+			now = data_avail;
+		else
+			now = bytes_left;
+
+		for (i = 0; i < now; i++) {
+			dp[i] = stream->buf_tail[i];
+		}
+
+		dp += now;
+		stream->buf_tail += now;
+		bytes_left -= now;
+		total_read += now;
+	}
+
+	return (total_read / size);
+}
+
+
+/** Write to a stream.
+ *
+ * @param buf    Source buffer.
+ * @param size   Size of each record.
+ * @param nmemb  Number of records to write.
+ * @param stream Pointer to the stream.
+ *
+ */
+size_t fwrite(const void *buf, size_t size, size_t nmemb, FILE *stream)
+{
+	uint8_t *data;
+	size_t bytes_left;
+	size_t now;
+	size_t buf_free;
+	size_t total_written;
+	size_t i;
+	uint8_t b;
+	bool need_flush;
+
+	if (size == 0 || nmemb == 0)
+		return 0;
+
+	/* If not buffered stream, write out directly. */
+	if (stream->btype == _IONBF) {
+		now = _fwrite(buf, size, nmemb, stream);
+		fflush(stream);
+		return now;
+	}
+
+	/* Make sure buffer contains no prefetched data. */
+	if (stream->buf_state == _bs_read)
+		_fflushbuf(stream);
+
+
+	/* Perform lazy allocation of stream buffer. */
+	if (stream->buf == NULL) {
+		if (_fallocbuf(stream) != 0)
+			return 0; /* Errno set by _fallocbuf(). */
+	}
+	
+	data = (uint8_t *) buf;
+	bytes_left = size * nmemb;
+	total_written = 0;
+	need_flush = false;
+	
+	while ((!stream->error) && (bytes_left > 0)) {
+		buf_free = stream->buf_size - (stream->buf_head - stream->buf);
+		if (bytes_left > buf_free)
+			now = buf_free;
+		else
+			now = bytes_left;
+		
+		for (i = 0; i < now; i++) {
+			b = data[i];
+			stream->buf_head[i] = b;
+			
+			if ((b == '\n') && (stream->btype == _IOLBF))
+				need_flush = true;
+		}
+		
+		buf += now;
+		stream->buf_head += now;
+		buf_free -= now;
+		bytes_left -= now;
+		total_written += now;
+		
+		if (buf_free == 0) {
+			/* Only need to drain buffer. */
+			_fflushbuf(stream);
+			need_flush = false;
+		}
+	}
+	
+	if (total_written > 0)
+		stream->buf_state = _bs_write;
+
+	if (need_flush)
+		fflush(stream);
+	
+	return (total_written / size);
+}
+
+int fputc(wchar_t c, FILE *stream)
+{
+	char buf[STR_BOUNDS(1)];
+	size_t sz = 0;
+	
+	if (chr_encode(c, buf, &sz, STR_BOUNDS(1)) == EOK) {
+		size_t wr = fwrite(buf, 1, sz, stream);
+		
+		if (wr < sz)
+			return EOF;
+		
+		return (int) c;
+	}
+	
+	return EOF;
+}
+
+int putchar(wchar_t c)
+{
+	return fputc(c, stdout);
+}
+
+int fputs(const char *str, FILE *stream)
+{
+	return fwrite(str, str_size(str), 1, stream);
+}
+
+int puts(const char *str)
+{
+	return fputs(str, stdout);
+}
+
+int fgetc(FILE *stream)
+{
+	char c;
+	
+	/* This could be made faster by only flushing when needed. */
+	if (stdout)
+		fflush(stdout);
+	if (stderr)
+		fflush(stderr);
+	
+	if (fread(&c, sizeof(char), 1, stream) < sizeof(char))
+		return EOF;
+	
+	return (int) c;
+}
+
+char *fgets(char *str, int size, FILE *stream)
+{
+	int c;
+	int idx;
+
+	idx = 0;
+	while (idx < size - 1) {
+		c = fgetc(stream);
+		if (c == EOF)
+			break;
+
+		str[idx++] = c;
+
+		if (c == '\n')
+			break;
+	}
+
+	if (ferror(stream))
+		return NULL;
+
+	if (idx == 0)
+		return NULL;
+
+	str[idx] = '\0';
+	return str;
+}
+
+int getchar(void)
+{
+	return fgetc(stdin);
+}
+
+int fseek(FILE *stream, off64_t offset, int whence)
+{
+	off64_t rc;
+
+	_fflushbuf(stream);
+
+	rc = lseek(stream->fd, offset, whence);
+	if (rc == (off64_t) (-1)) {
+		/* errno has been set by lseek64. */
+		return -1;
+	}
+
+	stream->eof = false;
+	return 0;
+}
+
+off64_t ftell(FILE *stream)
+{
+	return lseek(stream->fd, 0, SEEK_CUR);
+}
+
+void rewind(FILE *stream)
+{
+	(void) fseek(stream, 0, SEEK_SET);
+}
+
+int fflush(FILE *stream)
+{
+	_fflushbuf(stream);
+	
+	if (stream->klog) {
+		klog_update();
+		return EOK;
+	}
+	
+	if (stream->fd >= 0 && stream->need_sync) {
+		/**
+		 * Better than syncing always, but probably still not the
+		 * right thing to do.
+		 */
+		stream->need_sync = false;
+		return fsync(stream->fd);
+	}
+	
+	return ENOENT;
+}
+
+int feof(FILE *stream)
+{
+	return stream->eof;
+}
+
+int ferror(FILE *stream)
+{
+	return stream->error;
+}
+
+void clearerr(FILE *stream)
+{
+	stream->eof = false;
+	stream->error = false;
+}
+
+int fphone(FILE *stream)
+{
+	if (stream->fd >= 0) {
+		if (stream->phone < 0)
+			stream->phone = fd_phone(stream->fd);
+		
+		return stream->phone;
+	}
+	
+	return -1;
+}
+
+int fnode(FILE *stream, fdi_node_t *node)
+{
+	if (stream->fd >= 0)
+		return fd_node(stream->fd, node);
+	
+	return ENOENT;
+}
+
+/** @}
+ */
Index: uspace/lib/c/generic/io/klog.c
===================================================================
--- uspace/lib/c/generic/io/klog.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
+++ uspace/lib/c/generic/io/klog.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2006 Josef Cejka
+ * Copyright (c) 2006 Jakub Vana
+ * 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 libc
+ * @{
+ */
+/** @file
+ */
+
+#include <libc.h>
+#include <str.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <io/klog.h>
+
+size_t klog_write(const void *buf, size_t size)
+{
+	ssize_t ret = (ssize_t) __SYSCALL3(SYS_KLOG, 1, (sysarg_t) buf, size);
+	
+	if (ret >= 0)
+		return (size_t) ret;
+	
+	return 0;
+}
+
+void klog_update(void)
+{
+	(void) __SYSCALL3(SYS_KLOG, 1, NULL, 0);
+}
+
+/** @}
+ */
Index: uspace/lib/c/generic/io/printf.c
===================================================================
--- uspace/lib/c/generic/io/printf.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
+++ uspace/lib/c/generic/io/printf.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2006 Josef Cejka
+ * 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 libc
+ * @{
+ */
+/** @file
+ */
+
+#include <io/printf_core.h>
+#include <stdio.h>
+
+/** Print formatted text.
+ *
+ * @param stream Output stream
+ * @param fmt    Format string
+ *
+ * \see For more details about format string see printf_core.
+ *
+ */
+int fprintf(FILE *stream, const char *fmt, ...)
+{
+	va_list args;
+	va_start(args, fmt);
+	
+	int ret = vfprintf(stream, fmt, args);
+	
+	va_end(args);
+	
+	return ret;
+}
+
+/** Print formatted text to stdout.
+ *
+ * @param fmt Format string
+ *
+ * \see For more details about format string see printf_core.
+ *
+ */
+int printf(const char *fmt, ...)
+{
+	va_list args;
+	va_start(args, fmt);
+	
+	int ret = vprintf(fmt, args);
+	
+	va_end(args);
+	
+	return ret;
+}
+
+/** @}
+ */
Index: uspace/lib/c/generic/io/printf_core.c
===================================================================
--- uspace/lib/c/generic/io/printf_core.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
+++ uspace/lib/c/generic/io/printf_core.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
@@ -0,0 +1,891 @@
+/*
+ * Copyright (c) 2001-2004 Jakub Jermar
+ * Copyright (c) 2006 Josef Cejka
+ * Copyright (c) 2009 Martin Decky
+ * 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 generic
+ * @{
+ */
+/**
+ * @file
+ * @brief Printing functions.
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <io/printf_core.h>
+#include <ctype.h>
+#include <str.h>
+
+/** show prefixes 0x or 0 */
+#define __PRINTF_FLAG_PREFIX       0x00000001
+/** signed / unsigned number */
+#define __PRINTF_FLAG_SIGNED       0x00000002
+/** print leading zeroes */
+#define __PRINTF_FLAG_ZEROPADDED   0x00000004
+/** align to left */
+#define __PRINTF_FLAG_LEFTALIGNED  0x00000010
+/** always show + sign */
+#define __PRINTF_FLAG_SHOWPLUS     0x00000020
+/** print space instead of plus */
+#define __PRINTF_FLAG_SPACESIGN    0x00000040
+/** show big characters */
+#define __PRINTF_FLAG_BIGCHARS     0x00000080
+/** number has - sign */
+#define __PRINTF_FLAG_NEGATIVE     0x00000100
+
+/**
+ * Buffer big enough for 64-bit number printed in base 2, sign, prefix and 0
+ * to terminate string... (last one is only for better testing end of buffer by
+ * zero-filling subroutine)
+ */
+#define PRINT_NUMBER_BUFFER_SIZE  (64 + 5)
+
+/** Enumeration of possible arguments types.
+ */
+typedef enum {
+	PrintfQualifierByte = 0,
+	PrintfQualifierShort,
+	PrintfQualifierInt,
+	PrintfQualifierLong,
+	PrintfQualifierLongLong,
+	PrintfQualifierPointer
+} qualifier_t;
+
+static char nullstr[] = "(NULL)";
+static char digits_small[] = "0123456789abcdef";
+static char digits_big[] = "0123456789ABCDEF";
+static char invalch = U_SPECIAL;
+
+/** Print one or more characters without adding newline.
+ *
+ * @param buf  Buffer holding characters with size of
+ *             at least size bytes. NULL is not allowed!
+ * @param size Size of the buffer in bytes.
+ * @param ps   Output method and its data.
+ *
+ * @return Number of characters printed.
+ *
+ */
+static int printf_putnchars(const char *buf, size_t size,
+    printf_spec_t *ps)
+{
+	return ps->str_write((void *) buf, size, ps->data);
+}
+
+/** Print one or more wide characters without adding newline.
+ *
+ * @param buf  Buffer holding wide characters with size of
+ *             at least size bytes. NULL is not allowed!
+ * @param size Size of the buffer in bytes.
+ * @param ps   Output method and its data.
+ *
+ * @return Number of wide characters printed.
+ *
+ */
+static int printf_wputnchars(const wchar_t *buf, size_t size,
+    printf_spec_t *ps)
+{
+	return ps->wstr_write((void *) buf, size, ps->data);
+}
+
+/** Print string without adding a newline.
+ *
+ * @param str String to print.
+ * @param ps  Write function specification and support data.
+ *
+ * @return Number of characters printed.
+ *
+ */
+static int printf_putstr(const char *str, printf_spec_t *ps)
+{
+	if (str == NULL)
+		return printf_putnchars(nullstr, str_size(nullstr), ps);
+	
+	return ps->str_write((void *) str, str_size(str), ps->data);
+}
+
+/** Print one ASCII character.
+ *
+ * @param c  ASCII character to be printed.
+ * @param ps Output method.
+ *
+ * @return Number of characters printed.
+ *
+ */
+static int printf_putchar(const char ch, printf_spec_t *ps)
+{
+	if (!ascii_check(ch))
+		return ps->str_write((void *) &invalch, 1, ps->data);
+	
+	return ps->str_write(&ch, 1, ps->data);
+}
+
+/** Print one wide character.
+ *
+ * @param c  Wide character to be printed.
+ * @param ps Output method.
+ *
+ * @return Number of characters printed.
+ *
+ */
+static int printf_putwchar(const wchar_t ch, printf_spec_t *ps)
+{
+	if (!chr_check(ch))
+		return ps->str_write((void *) &invalch, 1, ps->data);
+	
+	return ps->wstr_write(&ch, sizeof(wchar_t), ps->data);
+}
+
+/** Print one formatted ASCII character.
+ *
+ * @param ch    Character to print.
+ * @param width Width modifier.
+ * @param flags Flags that change the way the character is printed.
+ *
+ * @return Number of characters printed, negative value on failure.
+ *
+ */
+static int print_char(const char ch, int width, uint32_t flags, printf_spec_t *ps)
+{
+	size_t counter = 0;
+	if (!(flags & __PRINTF_FLAG_LEFTALIGNED)) {
+		while (--width > 0) {
+			/*
+			 * One space is consumed by the character itself, hence
+			 * the predecrement.
+			 */
+			if (printf_putchar(' ', ps) > 0)
+				counter++;
+		}
+	}
+	
+	if (printf_putchar(ch, ps) > 0)
+		counter++;
+	
+	while (--width > 0) {
+		/*
+		 * One space is consumed by the character itself, hence
+		 * the predecrement.
+		 */
+		if (printf_putchar(' ', ps) > 0)
+			counter++;
+	}
+	
+	return (int) (counter + 1);
+}
+
+/** Print one formatted wide character.
+ *
+ * @param ch    Character to print.
+ * @param width Width modifier.
+ * @param flags Flags that change the way the character is printed.
+ *
+ * @return Number of characters printed, negative value on failure.
+ *
+ */
+static int print_wchar(const wchar_t ch, int width, uint32_t flags, printf_spec_t *ps)
+{
+	size_t counter = 0;
+	if (!(flags & __PRINTF_FLAG_LEFTALIGNED)) {
+		while (--width > 0) {
+			/*
+			 * One space is consumed by the character itself, hence
+			 * the predecrement.
+			 */
+			if (printf_putchar(' ', ps) > 0)
+				counter++;
+		}
+	}
+	
+	if (printf_putwchar(ch, ps) > 0)
+		counter++;
+	
+	while (--width > 0) {
+		/*
+		 * One space is consumed by the character itself, hence
+		 * the predecrement.
+		 */
+		if (printf_putchar(' ', ps) > 0)
+			counter++;
+	}
+	
+	return (int) (counter + 1);
+}
+
+/** Print string.
+ *
+ * @param str       String to be printed.
+ * @param width     Width modifier.
+ * @param precision Precision modifier.
+ * @param flags     Flags that modify the way the string is printed.
+ *
+ * @return Number of characters printed, negative value on failure.
+ */
+static int print_str(char *str, int width, unsigned int precision,
+    uint32_t flags, printf_spec_t *ps)
+{
+	if (str == NULL)
+		return printf_putstr(nullstr, ps);
+
+	/* Print leading spaces. */
+	size_t strw = str_length(str);
+	if (precision == 0)
+		precision = strw;
+
+	/* Left padding */
+	size_t counter = 0;
+	width -= precision;
+	if (!(flags & __PRINTF_FLAG_LEFTALIGNED)) {
+		while (width-- > 0) {
+			if (printf_putchar(' ', ps) == 1)
+				counter++;
+		}
+	}
+
+	/* Part of @a str fitting into the alloted space. */
+	int retval;
+	size_t size = str_lsize(str, precision);
+	if ((retval = printf_putnchars(str, size, ps)) < 0)
+		return -counter;
+
+	counter += retval;
+
+	/* Right padding */
+	while (width-- > 0) {
+		if (printf_putchar(' ', ps) == 1)
+			counter++;
+	}
+
+	return ((int) counter);
+
+}
+
+/** Print wide string.
+ *
+ * @param str       Wide string to be printed.
+ * @param width     Width modifier.
+ * @param precision Precision modifier.
+ * @param flags     Flags that modify the way the string is printed.
+ *
+ * @return Number of wide characters printed, negative value on failure.
+ */
+static int print_wstr(wchar_t *str, int width, unsigned int precision,
+    uint32_t flags, printf_spec_t *ps)
+{
+	if (str == NULL)
+		return printf_putstr(nullstr, ps);
+	
+	/* Print leading spaces. */
+	size_t strw = wstr_length(str);
+	if (precision == 0)
+		precision = strw;
+	
+	/* Left padding */
+	size_t counter = 0;
+	width -= precision;
+	if (!(flags & __PRINTF_FLAG_LEFTALIGNED)) {
+		while (width-- > 0) {
+			if (printf_putchar(' ', ps) == 1)
+				counter++;
+		}
+	}
+	
+	/* Part of @a wstr fitting into the alloted space. */
+	int retval;
+	size_t size = wstr_lsize(str, precision);
+	if ((retval = printf_wputnchars(str, size, ps)) < 0)
+		return -counter;
+	
+	counter += retval;
+	
+	/* Right padding */
+	while (width-- > 0) {
+		if (printf_putchar(' ', ps) == 1)
+			counter++;
+	}
+
+	return ((int) counter);
+}
+
+/** Print a number in a given base.
+ *
+ * Print significant digits of a number in given base.
+ *
+ * @param num       Number to print.
+ * @param width     Width modifier.
+ * @param precision Precision modifier.
+ * @param base      Base to print the number in (must be between 2 and 16).
+ * @param flags     Flags that modify the way the number is printed.
+ *
+ * @return Number of characters printed.
+ *
+ */
+static int print_number(uint64_t num, int width, int precision, int base,
+    uint32_t flags, printf_spec_t *ps)
+{
+	char *digits;
+	if (flags & __PRINTF_FLAG_BIGCHARS)
+		digits = digits_big;
+	else
+		digits = digits_small;
+	
+	char data[PRINT_NUMBER_BUFFER_SIZE];
+	char *ptr = &data[PRINT_NUMBER_BUFFER_SIZE - 1];
+	
+	/* Size of number with all prefixes and signs */
+	int size = 0;
+	
+	/* Put zero at end of string */
+	*ptr-- = 0;
+	
+	if (num == 0) {
+		*ptr-- = '0';
+		size++;
+	} else {
+		do {
+			*ptr-- = digits[num % base];
+			size++;
+		} while (num /= base);
+	}
+	
+	/* Size of plain number */
+	int number_size = size;
+	
+	/*
+	 * Collect the sum of all prefixes/signs/etc. to calculate padding and
+	 * leading zeroes.
+	 */
+	if (flags & __PRINTF_FLAG_PREFIX) {
+		switch(base) {
+		case 2:
+			/* Binary formating is not standard, but usefull */
+			size += 2;
+			break;
+		case 8:
+			size++;
+			break;
+		case 16:
+			size += 2;
+			break;
+		}
+	}
+	
+	char sgn = 0;
+	if (flags & __PRINTF_FLAG_SIGNED) {
+		if (flags & __PRINTF_FLAG_NEGATIVE) {
+			sgn = '-';
+			size++;
+		} else if (flags & __PRINTF_FLAG_SHOWPLUS) {
+			sgn = '+';
+			size++;
+		} else if (flags & __PRINTF_FLAG_SPACESIGN) {
+			sgn = ' ';
+			size++;
+		}
+	}
+	
+	if (flags & __PRINTF_FLAG_LEFTALIGNED)
+		flags &= ~__PRINTF_FLAG_ZEROPADDED;
+	
+	/*
+	 * If the number is left-aligned or precision is specified then
+	 * padding with zeros is ignored.
+	 */
+	if (flags & __PRINTF_FLAG_ZEROPADDED) {
+		if ((precision == 0) && (width > size))
+			precision = width - size + number_size;
+	}
+	
+	/* Print leading spaces */
+	if (number_size > precision) {
+		/* Print the whole number, not only a part */
+		precision = number_size;
+	}
+	
+	width -= precision + size - number_size;
+	size_t counter = 0;
+	
+	if (!(flags & __PRINTF_FLAG_LEFTALIGNED)) {
+		while (width-- > 0) {
+			if (printf_putchar(' ', ps) == 1)
+				counter++;
+		}
+	}
+	
+	/* Print sign */
+	if (sgn) {
+		if (printf_putchar(sgn, ps) == 1)
+			counter++;
+	}
+	
+	/* Print prefix */
+	if (flags & __PRINTF_FLAG_PREFIX) {
+		switch(base) {
+		case 2:
+			/* Binary formating is not standard, but usefull */
+			if (printf_putchar('0', ps) == 1)
+				counter++;
+			if (flags & __PRINTF_FLAG_BIGCHARS) {
+				if (printf_putchar('B', ps) == 1)
+					counter++;
+			} else {
+				if (printf_putchar('b', ps) == 1)
+					counter++;
+			}
+			break;
+		case 8:
+			if (printf_putchar('o', ps) == 1)
+				counter++;
+			break;
+		case 16:
+			if (printf_putchar('0', ps) == 1)
+				counter++;
+			if (flags & __PRINTF_FLAG_BIGCHARS) {
+				if (printf_putchar('X', ps) == 1)
+					counter++;
+			} else {
+				if (printf_putchar('x', ps) == 1)
+					counter++;
+			}
+			break;
+		}
+	}
+	
+	/* Print leading zeroes */
+	precision -= number_size;
+	while (precision-- > 0) {
+		if (printf_putchar('0', ps) == 1)
+			counter++;
+	}
+	
+	/* Print the number itself */
+	int retval;
+	if ((retval = printf_putstr(++ptr, ps)) > 0)
+		counter += retval;
+	
+	/* Print trailing spaces */
+	
+	while (width-- > 0) {
+		if (printf_putchar(' ', ps) == 1)
+			counter++;
+	}
+	
+	return ((int) counter);
+}
+
+/** Print formatted string.
+ *
+ * Print string formatted according to the fmt parameter and variadic arguments.
+ * Each formatting directive must have the following form:
+ *
+ *  \% [ FLAGS ] [ WIDTH ] [ .PRECISION ] [ TYPE ] CONVERSION
+ *
+ * FLAGS:@n
+ *  - "#" Force to print prefix. For \%o conversion, the prefix is 0, for
+ *        \%x and \%X prefixes are 0x and 0X and for conversion \%b the
+ *        prefix is 0b.
+ *
+ *  - "-" Align to left.
+ *
+ *  - "+" Print positive sign just as negative.
+ *
+ *  - " " If the printed number is positive and "+" flag is not set,
+ *        print space in place of sign.
+ *
+ *  - "0" Print 0 as padding instead of spaces. Zeroes are placed between
+ *        sign and the rest of the number. This flag is ignored if "-"
+ *        flag is specified.
+ *
+ * WIDTH:@n
+ *  - Specify the minimal width of a printed argument. If it is bigger,
+ *    width is ignored. If width is specified with a "*" character instead of
+ *    number, width is taken from parameter list. And integer parameter is
+ *    expected before parameter for processed conversion specification. If
+ *    this value is negative its absolute value is taken and the "-" flag is
+ *    set.
+ *
+ * PRECISION:@n
+ *  - Value precision. For numbers it specifies minimum valid numbers.
+ *    Smaller numbers are printed with leading zeroes. Bigger numbers are not
+ *    affected. Strings with more than precision characters are cut off. Just
+ *    as with width, an "*" can be used used instead of a number. An integer
+ *    value is then expected in parameters. When both width and precision are
+ *    specified using "*", the first parameter is used for width and the
+ *    second one for precision.
+ *
+ * TYPE:@n
+ *  - "hh" Signed or unsigned char.@n
+ *  - "h"  Signed or unsigned short.@n
+ *  - ""   Signed or unsigned int (default value).@n
+ *  - "l"  Signed or unsigned long int.@n
+ *         If conversion is "c", the character is wchar_t (wide character).@n
+ *         If conversion is "s", the string is wchar_t * (wide string).@n
+ *  - "ll" Signed or unsigned long long int.@n
+ *
+ * CONVERSION:@n
+ *  - % Print percentile character itself.
+ *
+ *  - c Print single character. The character is expected to be plain
+ *      ASCII (e.g. only values 0 .. 127 are valid).@n
+ *      If type is "l", then the character is expected to be wide character
+ *      (e.g. values 0 .. 0x10ffff are valid).
+ *
+ *  - s Print zero terminated string. If a NULL value is passed as
+ *      value, "(NULL)" is printed instead.@n
+ *      If type is "l", then the string is expected to be wide string.
+ *
+ *  - P, p Print value of a pointer. Void * value is expected and it is
+ *         printed in hexadecimal notation with prefix (as with \%#X / \%#x
+ *         for 32-bit or \%#X / \%#x for 64-bit long pointers).
+ *
+ *  - b Print value as unsigned binary number. Prefix is not printed by
+ *      default. (Nonstandard extension.)
+ *
+ *  - o Print value as unsigned octal number. Prefix is not printed by
+ *      default.
+ *
+ *  - d, i Print signed decimal number. There is no difference between d
+ *         and i conversion.
+ *
+ *  - u Print unsigned decimal number.
+ *
+ *  - X, x Print hexadecimal number with upper- or lower-case. Prefix is
+ *         not printed by default.
+ *
+ * All other characters from fmt except the formatting directives are printed
+ * verbatim.
+ *
+ * @param fmt Format NULL-terminated string.
+ *
+ * @return Number of characters printed, negative value on failure.
+ *
+ */
+int printf_core(const char *fmt, printf_spec_t *ps, va_list ap)
+{
+	size_t i;        /* Index of the currently processed character from fmt */
+	size_t nxt = 0;  /* Index of the next character from fmt */
+	size_t j = 0;    /* Index to the first not printed nonformating character */
+	
+	size_t counter = 0;   /* Number of characters printed */
+	int retval;           /* Return values from nested functions */
+	
+	while (true) {
+		i = nxt;
+		wchar_t uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
+		
+		if (uc == 0)
+			break;
+		
+		/* Control character */
+		if (uc == '%') {
+			/* Print common characters if any processed */
+			if (i > j) {
+				if ((retval = printf_putnchars(&fmt[j], i - j, ps)) < 0) {
+					/* Error */
+					counter = -counter;
+					goto out;
+				}
+				counter += retval;
+			}
+			
+			j = i;
+			
+			/* Parse modifiers */
+			uint32_t flags = 0;
+			bool end = false;
+			
+			do {
+				i = nxt;
+				uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
+				switch (uc) {
+				case '#':
+					flags |= __PRINTF_FLAG_PREFIX;
+					break;
+				case '-':
+					flags |= __PRINTF_FLAG_LEFTALIGNED;
+					break;
+				case '+':
+					flags |= __PRINTF_FLAG_SHOWPLUS;
+					break;
+				case ' ':
+					flags |= __PRINTF_FLAG_SPACESIGN;
+					break;
+				case '0':
+					flags |= __PRINTF_FLAG_ZEROPADDED;
+					break;
+				default:
+					end = true;
+				};
+			} while (!end);
+			
+			/* Width & '*' operator */
+			int width = 0;
+			if (isdigit(uc)) {
+				while (true) {
+					width *= 10;
+					width += uc - '0';
+					
+					i = nxt;
+					uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
+					if (uc == 0)
+						break;
+					if (!isdigit(uc))
+						break;
+				}
+			} else if (uc == '*') {
+				/* Get width value from argument list */
+				i = nxt;
+				uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
+				width = (int) va_arg(ap, int);
+				if (width < 0) {
+					/* Negative width sets '-' flag */
+					width *= -1;
+					flags |= __PRINTF_FLAG_LEFTALIGNED;
+				}
+			}
+			
+			/* Precision and '*' operator */
+			int precision = 0;
+			if (uc == '.') {
+				i = nxt;
+				uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
+				if (isdigit(uc)) {
+					while (true) {
+						precision *= 10;
+						precision += uc - '0';
+						
+						i = nxt;
+						uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
+						if (uc == 0)
+							break;
+						if (!isdigit(uc))
+							break;
+					}
+				} else if (uc == '*') {
+					/* Get precision value from the argument list */
+					i = nxt;
+					uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
+					precision = (int) va_arg(ap, int);
+					if (precision < 0) {
+						/* Ignore negative precision */
+						precision = 0;
+					}
+				}
+			}
+			
+			qualifier_t qualifier;
+			
+			switch (uc) {
+			/** @todo Unimplemented qualifiers:
+			 *        t ptrdiff_t - ISO C 99
+			 */
+			case 'h':
+				/* Char or short */
+				qualifier = PrintfQualifierShort;
+				i = nxt;
+				uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
+				if (uc == 'h') {
+					i = nxt;
+					uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
+					qualifier = PrintfQualifierByte;
+				}
+				break;
+			case 'l':
+				/* Long or long long */
+				qualifier = PrintfQualifierLong;
+				i = nxt;
+				uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
+				if (uc == 'l') {
+					i = nxt;
+					uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
+					qualifier = PrintfQualifierLongLong;
+				}
+				break;
+			default:
+				/* Default type */
+				qualifier = PrintfQualifierInt;
+			}
+			
+			unsigned int base = 10;
+			
+			switch (uc) {
+			/*
+			 * String and character conversions.
+			 */
+			case 's':
+				if (qualifier == PrintfQualifierLong)
+					retval = print_wstr(va_arg(ap, wchar_t *), width, precision, flags, ps);
+				else
+					retval = print_str(va_arg(ap, char *), width, precision, flags, ps);
+				
+				if (retval < 0) {
+					counter = -counter;
+					goto out;
+				}
+				
+				counter += retval;
+				j = nxt;
+				goto next_char;
+			case 'c':
+				if (qualifier == PrintfQualifierLong)
+					retval = print_wchar(va_arg(ap, wchar_t), width, flags, ps);
+				else
+					retval = print_char(va_arg(ap, unsigned int), width, flags, ps);
+				
+				if (retval < 0) {
+					counter = -counter;
+					goto out;
+				};
+				
+				counter += retval;
+				j = nxt;
+				goto next_char;
+			
+			/*
+			 * Integer values
+			 */
+			case 'P':
+				/* Pointer */
+				flags |= __PRINTF_FLAG_BIGCHARS;
+			case 'p':
+				flags |= __PRINTF_FLAG_PREFIX;
+				base = 16;
+				qualifier = PrintfQualifierPointer;
+				break;
+			case 'b':
+				base = 2;
+				break;
+			case 'o':
+				base = 8;
+				break;
+			case 'd':
+			case 'i':
+				flags |= __PRINTF_FLAG_SIGNED;
+			case 'u':
+				break;
+			case 'X':
+				flags |= __PRINTF_FLAG_BIGCHARS;
+			case 'x':
+				base = 16;
+				break;
+			
+			/* Percentile itself */
+			case '%':
+				j = i;
+				goto next_char;
+			
+			/*
+			 * Bad formatting.
+			 */
+			default:
+				/*
+				 * Unknown format. Now, j is the index of '%'
+				 * so we will print whole bad format sequence.
+				 */
+				goto next_char;
+			}
+			
+			/* Print integers */
+			size_t size;
+			uint64_t number;
+			switch (qualifier) {
+			case PrintfQualifierByte:
+				size = sizeof(unsigned char);
+				number = (uint64_t) va_arg(ap, unsigned int);
+				break;
+			case PrintfQualifierShort:
+				size = sizeof(unsigned short);
+				number = (uint64_t) va_arg(ap, unsigned int);
+				break;
+			case PrintfQualifierInt:
+				size = sizeof(unsigned int);
+				number = (uint64_t) va_arg(ap, unsigned int);
+				break;
+			case PrintfQualifierLong:
+				size = sizeof(unsigned long);
+				number = (uint64_t) va_arg(ap, unsigned long);
+				break;
+			case PrintfQualifierLongLong:
+				size = sizeof(unsigned long long);
+				number = (uint64_t) va_arg(ap, unsigned long long);
+				break;
+			case PrintfQualifierPointer:
+				size = sizeof(void *);
+				number = (uint64_t) (unsigned long) va_arg(ap, void *);
+				break;
+			default:
+				/* Unknown qualifier */
+				counter = -counter;
+				goto out;
+			}
+			
+			if (flags & __PRINTF_FLAG_SIGNED) {
+				if (number & (0x1 << (size * 8 - 1))) {
+					flags |= __PRINTF_FLAG_NEGATIVE;
+					
+					if (size == sizeof(uint64_t)) {
+						number = -((int64_t) number);
+					} else {
+						number = ~number;
+						number &=
+						    ~(0xFFFFFFFFFFFFFFFFll <<
+						    (size * 8));
+						number++;
+					}
+				}
+			}
+			
+			if ((retval = print_number(number, width, precision,
+			    base, flags, ps)) < 0) {
+				counter = -counter;
+				goto out;
+			}
+			
+			counter += retval;
+			j = nxt;
+		}
+next_char:
+		;
+	}
+	
+	if (i > j) {
+		if ((retval = printf_putnchars(&fmt[j], i - j, ps)) < 0) {
+			/* Error */
+			counter = -counter;
+			goto out;
+		}
+		counter += retval;
+	}
+	
+out:
+	return ((int) counter);
+}
+
+/** @}
+ */
Index: uspace/lib/c/generic/io/snprintf.c
===================================================================
--- uspace/lib/c/generic/io/snprintf.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
+++ uspace/lib/c/generic/io/snprintf.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2006 Josef Cejka
+ * 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 libc
+ * @{
+ */
+/** @file
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <io/printf_core.h>
+
+/** Print formatted to the given buffer with limited size.
+ *
+ * @param str  Buffer
+ * @param size Buffer size
+ * @param fmt  Format string
+ *
+ * \see For more details about format string see printf_core.
+ *
+ */
+int snprintf(char *str, size_t size, const char *fmt, ...)
+{
+	va_list args;
+	va_start(args, fmt);
+	
+	int ret = vsnprintf(str, size, fmt, args);
+	
+	va_end(args);
+	
+	return ret;
+}
+
+/** @}
+ */
Index: uspace/lib/c/generic/io/vprintf.c
===================================================================
--- uspace/lib/c/generic/io/vprintf.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
+++ uspace/lib/c/generic/io/vprintf.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2006 Josef Cejka
+ * 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 libc
+ * @{
+ */
+/** @file
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <io/printf_core.h>
+#include <futex.h>
+#include <async.h>
+#include <str.h>
+
+static atomic_t printf_futex = FUTEX_INITIALIZER;
+
+static int vprintf_str_write(const char *str, size_t size, void *stream)
+{
+	size_t wr = fwrite(str, 1, size, (FILE *) stream);
+	return str_nlength(str, wr);
+}
+
+static int vprintf_wstr_write(const wchar_t *str, size_t size, void *stream)
+{
+	size_t offset = 0;
+	size_t chars = 0;
+	
+	while (offset < size) {
+		if (fputc(str[chars], (FILE *) stream) <= 0)
+			break;
+		
+		chars++;
+		offset += sizeof(wchar_t);
+	}
+	
+	return chars;
+}
+
+/** Print formatted text.
+ *
+ * @param stream Output stream
+ * @param fmt    Format string
+ * @param ap     Format parameters
+ *
+ * \see For more details about format string see printf_core.
+ *
+ */
+int vfprintf(FILE *stream, const char *fmt, va_list ap)
+{
+	struct printf_spec ps = {
+		vprintf_str_write,
+		vprintf_wstr_write,
+		stream
+	};
+	
+	/*
+	 * Prevent other threads to execute printf_core()
+	 */
+	futex_down(&printf_futex);
+	
+	/*
+	 * Prevent other fibrils of the same thread
+	 * to execute printf_core()
+	 */
+	async_serialize_start();
+	
+	int ret = printf_core(fmt, &ps, ap);
+	
+	async_serialize_end();
+	futex_up(&printf_futex);
+	
+	return ret;
+}
+
+/** Print formatted text to stdout.
+ *
+ * @param file Output stream
+ * @param fmt  Format string
+ * @param ap   Format parameters
+ *
+ * \see For more details about format string see printf_core.
+ *
+ */
+int vprintf(const char *fmt, va_list ap)
+{
+	return vfprintf(stdout, fmt, ap);
+}
+
+/** @}
+ */
Index: uspace/lib/c/generic/io/vsnprintf.c
===================================================================
--- uspace/lib/c/generic/io/vsnprintf.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
+++ uspace/lib/c/generic/io/vsnprintf.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
@@ -0,0 +1,186 @@
+/*
+ * Copyright (c) 2006 Josef Cejka
+ * 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 libc
+ * @{
+ */
+/** @file
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <str.h>
+#include <io/printf_core.h>
+#include <errno.h>
+
+typedef struct {
+	size_t size;    /* Total size of the buffer (in bytes) */
+	size_t len;     /* Number of already used bytes */
+	char *dst;      /* Destination */
+} vsnprintf_data_t;
+
+/** Write string to given buffer.
+ *
+ * Write at most data->size plain characters including trailing zero.
+ * According to C99, snprintf() has to return number of characters that
+ * would have been written if enough space had been available. Hence
+ * the return value is not the number of actually printed characters
+ * but size of the input string.
+ *
+ * @param str  Source string to print.
+ * @param size Number of plain characters in str.
+ * @param data Structure describing destination string, counter
+ *             of used space and total string size.
+ *
+ * @return Number of characters to print (not characters actually
+ *         printed).
+ *
+ */
+static int vsnprintf_str_write(const char *str, size_t size, vsnprintf_data_t *data)
+{
+	size_t left = data->size - data->len;
+	
+	if (left == 0)
+		return ((int) size);
+	
+	if (left == 1) {
+		/* We have only one free byte left in buffer
+		 * -> store trailing zero
+		 */
+		data->dst[data->size - 1] = 0;
+		data->len = data->size;
+		return ((int) size);
+	}
+	
+	if (left <= size) {
+		/* We do not have enough space for the whole string
+		 * with the trailing zero => print only a part
+		 * of string
+		 */
+		size_t index = 0;
+		
+		while (index < size) {
+			wchar_t uc = str_decode(str, &index, size);
+			
+			if (chr_encode(uc, data->dst, &data->len, data->size - 1) != EOK)
+				break;
+		}
+		
+		/* Put trailing zero at end, but not count it
+		 * into data->len so it could be rewritten next time
+		 */
+		data->dst[data->len] = 0;
+		
+		return ((int) size);
+	}
+	
+	/* Buffer is big enough to print the whole string */
+	memcpy((void *)(data->dst + data->len), (void *) str, size);
+	data->len += size;
+	
+	/* Put trailing zero at end, but not count it
+	 * into data->len so it could be rewritten next time
+	 */
+	data->dst[data->len] = 0;
+	
+	return ((int) size);
+}
+
+/** Write wide string to given buffer.
+ *
+ * Write at most data->size plain characters including trailing zero.
+ * According to C99, snprintf() has to return number of characters that
+ * would have been written if enough space had been available. Hence
+ * the return value is not the number of actually printed characters
+ * but size of the input string.
+ *
+ * @param str  Source wide string to print.
+ * @param size Number of bytes in str.
+ * @param data Structure describing destination string, counter
+ *             of used space and total string size.
+ *
+ * @return Number of wide characters to print (not characters actually
+ *         printed).
+ *
+ */
+static int vsnprintf_wstr_write(const wchar_t *str, size_t size, vsnprintf_data_t *data)
+{
+	size_t index = 0;
+	
+	while (index < (size / sizeof(wchar_t))) {
+		size_t left = data->size - data->len;
+		
+		if (left == 0)
+			return ((int) size);
+		
+		if (left == 1) {
+			/* We have only one free byte left in buffer
+			 * -> store trailing zero
+			 */
+			data->dst[data->size - 1] = 0;
+			data->len = data->size;
+			return ((int) size);
+		}
+		
+		if (chr_encode(str[index], data->dst, &data->len, data->size - 1) != EOK)
+			break;
+		
+		index++;
+	}
+	
+	/* Put trailing zero at end, but not count it
+	 * into data->len so it could be rewritten next time
+	 */
+	data->dst[data->len] = 0;
+	
+	return ((int) size);
+}
+
+int vsnprintf(char *str, size_t size, const char *fmt, va_list ap)
+{
+	vsnprintf_data_t data = {
+		size,
+		0,
+		str
+	};
+	printf_spec_t ps = {
+		(int(*) (const char *, size_t, void *)) vsnprintf_str_write,
+		(int(*) (const wchar_t *, size_t, void *)) vsnprintf_wstr_write,
+		&data
+	};
+	
+	/* Print 0 at end of string - fix the case that nothing will be printed */
+	if (size > 0)
+		str[0] = 0;
+	
+	/* vsnprintf_write ensures that str will be terminated by zero. */
+	return printf_core(fmt, &ps, ap);
+}
+
+/** @}
+ */
Index: uspace/lib/c/generic/ipc.c
===================================================================
--- uspace/lib/c/generic/ipc.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
+++ uspace/lib/c/generic/ipc.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
@@ -0,0 +1,860 @@
+/*
+ * Copyright (c) 2006 Ondrej Palkovsky
+ * 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 libc
+ * @{
+ * @}
+ */
+
+/** @addtogroup libcipc IPC
+ * @brief HelenOS uspace IPC
+ * @{
+ * @ingroup libc
+ */
+/** @file
+ */ 
+
+#include <ipc/ipc.h>
+#include <libc.h>
+#include <malloc.h>
+#include <errno.h>
+#include <adt/list.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <futex.h>
+#include <kernel/synch/synch.h>
+#include <async.h>
+#include <fibril.h>
+#include <assert.h>
+
+/**
+ * Structures of this type are used for keeping track of sent asynchronous calls
+ * and queing unsent calls.
+ */
+typedef struct {
+	link_t list;
+
+	ipc_async_callback_t callback;
+	void *private;
+	union {
+		ipc_callid_t callid;
+		struct {
+			ipc_call_t data;
+			int phoneid;
+		} msg;
+	} u;
+	fid_t fid;	/**< Fibril waiting for sending this call. */
+} async_call_t;
+
+LIST_INITIALIZE(dispatched_calls);
+
+/** List of asynchronous calls that were not accepted by kernel.
+ *
+ * It is protected by async_futex, because if the call cannot be sent into the
+ * kernel, the async framework is used automatically.
+ */
+LIST_INITIALIZE(queued_calls);
+
+static atomic_t ipc_futex = FUTEX_INITIALIZER;
+
+/** Make a fast synchronous call.
+ *
+ * Only three payload arguments can be passed using this function. However, this
+ * function is faster than the generic ipc_call_sync_slow() because the payload
+ * is passed directly in registers.
+ *
+ * @param phoneid	Phone handle for the call.
+ * @param method	Requested method.
+ * @param arg1		Service-defined payload argument.
+ * @param arg2		Service-defined payload argument.
+ * @param arg3		Service-defined payload argument.
+ * @param result1	If non-NULL, the return ARG1 will be stored there.
+ * @param result2	If non-NULL, the return ARG2 will be stored there.
+ * @param result3	If non-NULL, the return ARG3 will be stored there.
+ * @param result4	If non-NULL, the return ARG4 will be stored there.
+ * @param result5	If non-NULL, the return ARG5 will be stored there.
+ *
+ * @return		Negative values represent errors returned by IPC.
+ *			Otherwise the RETVAL of the answer is returned.
+ */
+int
+ipc_call_sync_fast(int phoneid, ipcarg_t method, ipcarg_t arg1, ipcarg_t arg2,
+    ipcarg_t arg3, ipcarg_t *result1, ipcarg_t *result2, ipcarg_t *result3,
+    ipcarg_t *result4, ipcarg_t *result5)
+{
+	ipc_call_t resdata;
+	int callres;
+	
+	callres = __SYSCALL6(SYS_IPC_CALL_SYNC_FAST, phoneid, method, arg1,
+	    arg2, arg3, (sysarg_t) &resdata);
+	if (callres)
+		return callres;
+	if (result1)
+		*result1 = IPC_GET_ARG1(resdata);
+	if (result2)
+		*result2 = IPC_GET_ARG2(resdata);
+	if (result3)
+		*result3 = IPC_GET_ARG3(resdata);
+	if (result4)
+		*result4 = IPC_GET_ARG4(resdata);
+	if (result5)
+		*result5 = IPC_GET_ARG5(resdata);
+
+	return IPC_GET_RETVAL(resdata);
+}
+
+/** Make a synchronous call transmitting 5 arguments of payload.
+ *
+ * @param phoneid	Phone handle for the call.
+ * @param method	Requested method.
+ * @param arg1		Service-defined payload argument.
+ * @param arg2		Service-defined payload argument.
+ * @param arg3		Service-defined payload argument.
+ * @param arg4		Service-defined payload argument.
+ * @param arg5		Service-defined payload argument.
+ * @param result1	If non-NULL, storage for the first return argument.
+ * @param result2	If non-NULL, storage for the second return argument.
+ * @param result3	If non-NULL, storage for the third return argument.
+ * @param result4	If non-NULL, storage for the fourth return argument.
+ * @param result5	If non-NULL, storage for the fifth return argument.
+ *
+ * @return		Negative value means IPC error.
+ *			Otherwise the RETVAL of the answer.
+ */
+int
+ipc_call_sync_slow(int phoneid, ipcarg_t method, ipcarg_t arg1, ipcarg_t arg2,
+    ipcarg_t arg3, ipcarg_t arg4, ipcarg_t arg5, ipcarg_t *result1,
+    ipcarg_t *result2, ipcarg_t *result3, ipcarg_t *result4, ipcarg_t *result5)
+{
+	ipc_call_t data;
+	int callres;
+
+	IPC_SET_METHOD(data, method);
+	IPC_SET_ARG1(data, arg1);
+	IPC_SET_ARG2(data, arg2);
+	IPC_SET_ARG3(data, arg3);
+	IPC_SET_ARG4(data, arg4);
+	IPC_SET_ARG5(data, arg5);
+
+	callres = __SYSCALL3(SYS_IPC_CALL_SYNC_SLOW, phoneid, (sysarg_t) &data,
+	    (sysarg_t) &data);
+	if (callres)
+		return callres;
+
+	if (result1)
+		*result1 = IPC_GET_ARG1(data);
+	if (result2)
+		*result2 = IPC_GET_ARG2(data);
+	if (result3)
+		*result3 = IPC_GET_ARG3(data);
+	if (result4)
+		*result4 = IPC_GET_ARG4(data);
+	if (result5)
+		*result5 = IPC_GET_ARG5(data);
+
+	return IPC_GET_RETVAL(data);
+}
+
+/** Syscall to send asynchronous message.
+ *
+ * @param phoneid	Phone handle for the call.
+ * @param data		Call data with the request.
+ *
+ * @return		Hash of the call or an error code.
+ */
+static ipc_callid_t _ipc_call_async(int phoneid, ipc_call_t *data)
+{
+	return __SYSCALL2(SYS_IPC_CALL_ASYNC_SLOW, phoneid, (sysarg_t) data);
+}
+
+/** Prolog to ipc_call_async_*() functions.
+ *
+ * @param private	Argument for the answer/error callback.
+ * @param callback	Answer/error callback.
+ *
+ * @return		New, partially initialized async_call structure or NULL.
+ */
+static inline async_call_t *ipc_prepare_async(void *private,
+    ipc_async_callback_t callback)
+{
+	async_call_t *call;
+
+	call = malloc(sizeof(*call));
+	if (!call) {
+		if (callback)
+			callback(private, ENOMEM, NULL);
+		return NULL;
+	}
+	call->callback = callback;
+	call->private = private;
+
+	return call;
+}
+
+/** Epilogue of ipc_call_async_*() functions.
+ *
+ * @param callid	Value returned by the SYS_IPC_CALL_ASYNC_* syscall.
+ * @param phoneid	Phone handle through which the call was made.
+ * @param call		async_call structure returned by ipc_prepare_async().
+ * @param can_preempt	If non-zero, the current fibril can be preempted in this
+ *			call.
+ */
+static inline void ipc_finish_async(ipc_callid_t callid, int phoneid,
+    async_call_t *call, int can_preempt)
+{
+	if (!call) { /* Nothing to do regardless if failed or not */
+		futex_up(&ipc_futex);
+		return;
+	}
+
+	if (callid == (ipc_callid_t) IPC_CALLRET_FATAL) {
+		futex_up(&ipc_futex);
+		/* Call asynchronous handler with error code */
+		if (call->callback)
+			call->callback(call->private, ENOENT, NULL);
+		free(call);
+		return;
+	}
+
+	if (callid == (ipc_callid_t) IPC_CALLRET_TEMPORARY) {
+		futex_up(&ipc_futex);
+
+		call->u.msg.phoneid = phoneid;
+		
+		futex_down(&async_futex);
+		list_append(&call->list, &queued_calls);
+
+		if (can_preempt) {
+			call->fid = fibril_get_id();
+			fibril_switch(FIBRIL_TO_MANAGER);
+			/* Async futex unlocked by previous call */
+		} else {
+			call->fid = 0;
+			futex_up(&async_futex);
+		}
+		return;
+	}
+	call->u.callid = callid;
+	/* Add call to the list of dispatched calls */
+	list_append(&call->list, &dispatched_calls);
+	futex_up(&ipc_futex);
+	
+}
+
+/** Make a fast asynchronous call.
+ *
+ * This function can only handle four arguments of payload. It is, however,
+ * faster than the more generic ipc_call_async_slow().
+ *
+ * Note that this function is a void function.
+ * During normal opertation, answering this call will trigger the callback.
+ * In case of fatal error, call the callback handler with the proper error code.
+ * If the call cannot be temporarily made, queue it.
+ *
+ * @param phoneid	Phone handle for the call.
+ * @param method	Requested method.
+ * @param arg1		Service-defined payload argument.
+ * @param arg2		Service-defined payload argument.
+ * @param arg3		Service-defined payload argument.
+ * @param arg4		Service-defined payload argument.
+ * @param private	Argument to be passed to the answer/error callback.
+ * @param callback	Answer or error callback.
+ * @param can_preempt	If non-zero, the current fibril will be preempted in
+ *			case the kernel temporarily refuses to accept more
+ *			asynchronous calls.
+ */
+void ipc_call_async_fast(int phoneid, ipcarg_t method, ipcarg_t arg1,
+    ipcarg_t arg2, ipcarg_t arg3, ipcarg_t arg4, void *private,
+    ipc_async_callback_t callback, int can_preempt)
+{
+	async_call_t *call = NULL;
+	ipc_callid_t callid;
+
+	if (callback) {
+		call = ipc_prepare_async(private, callback);
+		if (!call)
+			return;
+	}
+
+	/*
+	 * We need to make sure that we get callid before another thread
+	 * accesses the queue again.
+	 */
+	futex_down(&ipc_futex);
+	callid = __SYSCALL6(SYS_IPC_CALL_ASYNC_FAST, phoneid, method, arg1,
+	    arg2, arg3, arg4);
+
+	if (callid == (ipc_callid_t) IPC_CALLRET_TEMPORARY) {
+		if (!call) {
+			call = ipc_prepare_async(private, callback);
+			if (!call)
+				return;
+		}
+		IPC_SET_METHOD(call->u.msg.data, method);
+		IPC_SET_ARG1(call->u.msg.data, arg1);
+		IPC_SET_ARG2(call->u.msg.data, arg2);
+		IPC_SET_ARG3(call->u.msg.data, arg3);
+		IPC_SET_ARG4(call->u.msg.data, arg4);
+		/*
+		 * To achieve deterministic behavior, we always zero out the
+		 * arguments that are beyond the limits of the fast version.
+		 */
+		IPC_SET_ARG5(call->u.msg.data, 0);
+	}
+	ipc_finish_async(callid, phoneid, call, can_preempt);
+}
+
+/** Make an asynchronous call transmitting the entire payload.
+ *
+ * Note that this function is a void function.
+ * During normal opertation, answering this call will trigger the callback.
+ * In case of fatal error, call the callback handler with the proper error code.
+ * If the call cannot be temporarily made, queue it.
+ *
+ * @param phoneid	Phone handle for the call.
+ * @param method	Requested method.
+ * @param arg1		Service-defined payload argument.
+ * @param arg2		Service-defined payload argument.
+ * @param arg3		Service-defined payload argument.
+ * @param arg4		Service-defined payload argument.
+ * @param arg5		Service-defined payload argument.
+ * @param private	Argument to be passed to the answer/error callback.
+ * @param callback	Answer or error callback.
+ * @param can_preempt	If non-zero, the current fibril will be preempted in
+ *			case the kernel temporarily refuses to accept more
+ *			asynchronous calls.
+ *
+ */
+void ipc_call_async_slow(int phoneid, ipcarg_t method, ipcarg_t arg1,
+    ipcarg_t arg2, ipcarg_t arg3, ipcarg_t arg4, ipcarg_t arg5, void *private,
+    ipc_async_callback_t callback, int can_preempt)
+{
+	async_call_t *call;
+	ipc_callid_t callid;
+
+	call = ipc_prepare_async(private, callback);
+	if (!call)
+		return;
+
+	IPC_SET_METHOD(call->u.msg.data, method);
+	IPC_SET_ARG1(call->u.msg.data, arg1);
+	IPC_SET_ARG2(call->u.msg.data, arg2);
+	IPC_SET_ARG3(call->u.msg.data, arg3);
+	IPC_SET_ARG4(call->u.msg.data, arg4);
+	IPC_SET_ARG5(call->u.msg.data, arg5);
+	/*
+	 * We need to make sure that we get callid before another thread
+	 * accesses the queue again.
+	 */
+	futex_down(&ipc_futex);
+	callid = _ipc_call_async(phoneid, &call->u.msg.data);
+
+	ipc_finish_async(callid, phoneid, call, can_preempt);
+}
+
+
+/** Answer a received call - fast version.
+ *
+ * The fast answer makes use of passing retval and first four arguments in
+ * registers. If you need to return more, use the ipc_answer_slow() instead.
+ *
+ * @param callid	Hash of the call being answered.
+ * @param retval	Return value.
+ * @param arg1		First return argument.
+ * @param arg2		Second return argument.
+ * @param arg3		Third return argument.
+ * @param arg4		Fourth return argument.
+ *
+ * @return		Zero on success or a value from @ref errno.h on failure.
+ */
+ipcarg_t ipc_answer_fast(ipc_callid_t callid, ipcarg_t retval, ipcarg_t arg1,
+    ipcarg_t arg2, ipcarg_t arg3, ipcarg_t arg4)
+{
+	return __SYSCALL6(SYS_IPC_ANSWER_FAST, callid, retval, arg1, arg2, arg3,
+	    arg4);
+}
+
+/** Answer a received call - slow full version.
+ *
+ * @param callid	Hash of the call being answered.
+ * @param retval	Return value.
+ * @param arg1		First return argument.
+ * @param arg2		Second return argument.
+ * @param arg3		Third return argument.
+ * @param arg4		Fourth return argument.
+ * @param arg5		Fifth return argument.
+ *
+ * @return		Zero on success or a value from @ref errno.h on failure.
+ */
+ipcarg_t ipc_answer_slow(ipc_callid_t callid, ipcarg_t retval, ipcarg_t arg1,
+    ipcarg_t arg2, ipcarg_t arg3, ipcarg_t arg4, ipcarg_t arg5)
+{
+	ipc_call_t data;
+
+	IPC_SET_RETVAL(data, retval);
+	IPC_SET_ARG1(data, arg1);
+	IPC_SET_ARG2(data, arg2);
+	IPC_SET_ARG3(data, arg3);
+	IPC_SET_ARG4(data, arg4);
+	IPC_SET_ARG5(data, arg5);
+
+	return __SYSCALL2(SYS_IPC_ANSWER_SLOW, callid, (sysarg_t) &data);
+}
+
+
+/** Try to dispatch queued calls from the async queue. */
+static void try_dispatch_queued_calls(void)
+{
+	async_call_t *call;
+	ipc_callid_t callid;
+
+	/** @todo
+	 * Integrate intelligently ipc_futex, so that it is locked during
+	 * ipc_call_async_*(), until it is added to dispatched_calls.
+	 */
+	futex_down(&async_futex);
+	while (!list_empty(&queued_calls)) {
+		call = list_get_instance(queued_calls.next, async_call_t, list);
+		callid = _ipc_call_async(call->u.msg.phoneid,
+		    &call->u.msg.data);
+		if (callid == (ipc_callid_t) IPC_CALLRET_TEMPORARY) {
+			break;
+		}
+		list_remove(&call->list);
+
+		futex_up(&async_futex);
+		if (call->fid)
+			fibril_add_ready(call->fid);
+		
+		if (callid == (ipc_callid_t) IPC_CALLRET_FATAL) {
+			if (call->callback)
+				call->callback(call->private, ENOENT, NULL);
+			free(call);
+		} else {
+			call->u.callid = callid;
+			futex_down(&ipc_futex);
+			list_append(&call->list, &dispatched_calls);
+			futex_up(&ipc_futex);
+		}
+		futex_down(&async_futex);
+	}
+	futex_up(&async_futex);
+}
+
+/** Handle a received answer.
+ *
+ * Find the hash of the answer and call the answer callback.
+ *
+ * @todo Make it use hash table.
+ *
+ * @param callid	Hash of the received answer.
+ *			The answer has the same hash as the request OR'ed with
+ *			the IPC_CALLID_ANSWERED bit.
+ * @param data		Call data of the answer.
+ */
+static void handle_answer(ipc_callid_t callid, ipc_call_t *data)
+{
+	link_t *item;
+	async_call_t *call;
+
+	callid &= ~IPC_CALLID_ANSWERED;
+	
+	futex_down(&ipc_futex);
+	for (item = dispatched_calls.next; item != &dispatched_calls;
+	    item = item->next) {
+		call = list_get_instance(item, async_call_t, list);
+		if (call->u.callid == callid) {
+			list_remove(&call->list);
+			futex_up(&ipc_futex);
+			if (call->callback)
+				call->callback(call->private, 
+				    IPC_GET_RETVAL(*data), data);
+			free(call);
+			return;
+		}
+	}
+	futex_up(&ipc_futex);
+}
+
+
+/** Wait for a first call to come.
+ *
+ * @param call		Storage where the incoming call data will be stored.
+ * @param usec		Timeout in microseconds
+ * @param flags		Flags passed to SYS_IPC_WAIT (blocking, nonblocking).
+ *
+ * @return		Hash of the call. Note that certain bits have special
+ *			meaning. IPC_CALLID_ANSWERED will be set in an answer
+ *			and IPC_CALLID_NOTIFICATION is used for notifications.
+ *			
+ */
+ipc_callid_t ipc_wait_cycle(ipc_call_t *call, uint32_t usec, int flags)
+{
+	ipc_callid_t callid;
+
+	callid = __SYSCALL3(SYS_IPC_WAIT, (sysarg_t) call, usec, flags);
+	/* Handle received answers */
+	if (callid & IPC_CALLID_ANSWERED) {
+		handle_answer(callid, call);
+		try_dispatch_queued_calls();
+	}
+
+	return callid;
+}
+
+/** Wait some time for an IPC call.
+ *
+ * The call will return after an answer is received.
+ *
+ * @param call		Storage where the incoming call data will be stored.
+ * @param usec		Timeout in microseconds.
+ *
+ * @return		Hash of the answer.
+ */
+ipc_callid_t ipc_wait_for_call_timeout(ipc_call_t *call, uint32_t usec)
+{
+	ipc_callid_t callid;
+
+	do {
+		callid = ipc_wait_cycle(call, usec, SYNCH_FLAGS_NONE);
+	} while (callid & IPC_CALLID_ANSWERED);
+
+	return callid;
+}
+
+/** Check if there is an IPC call waiting to be picked up.
+ *
+ * @param call		Storage where the incoming call will be stored.
+ * @return		Hash of the answer.
+ */
+ipc_callid_t ipc_trywait_for_call(ipc_call_t *call)
+{
+	ipc_callid_t callid;
+
+	do {
+		callid = ipc_wait_cycle(call, SYNCH_NO_TIMEOUT,
+		    SYNCH_FLAGS_NON_BLOCKING);
+	} while (callid & IPC_CALLID_ANSWERED);
+
+	return callid;
+}
+
+/** Interrupt one thread of this task from waiting for IPC. */
+void ipc_poke(void)
+{
+	__SYSCALL0(SYS_IPC_POKE);
+}
+
+/** Ask destination to do a callback connection.
+ *
+ * @param phoneid	Phone handle used for contacting the other side.
+ * @param arg1		Service-defined argument.
+ * @param arg2		Service-defined argument.
+ * @param arg3		Service-defined argument.
+ * @param phonehash	Storage where the library will store an opaque
+ *			identifier of the phone that will be used for incoming
+ *			calls. This identifier can be used for connection
+ *			tracking.
+ *
+ * @return		Zero on success or a negative error code.
+ */
+int ipc_connect_to_me(int phoneid, int arg1, int arg2, int arg3, 
+    ipcarg_t *phonehash)
+{
+	return ipc_call_sync_3_5(phoneid, IPC_M_CONNECT_TO_ME, arg1, arg2,
+	    arg3, NULL, NULL, NULL, NULL, phonehash);
+}
+
+/** Ask through phone for a new connection to some service.
+ *
+ * @param phoneid	Phone handle used for contacting the other side.
+ * @param arg1		User defined argument.
+ * @param arg2		User defined argument.
+ * @param arg3		User defined argument.
+ *
+ * @return		New phone handle on success or a negative error code.
+ */
+int ipc_connect_me_to(int phoneid, int arg1, int arg2, int arg3)
+{
+	ipcarg_t newphid;
+	int res;
+
+	res = ipc_call_sync_3_5(phoneid, IPC_M_CONNECT_ME_TO, arg1, arg2, arg3,
+	    NULL, NULL, NULL, NULL, &newphid);
+	if (res)
+		return res;
+	return newphid;
+}
+
+/** Ask through phone for a new connection to some service.
+ *
+ * If the connection is not available at the moment, the
+ * call will block.
+ *
+ * @param phoneid	Phone handle used for contacting the other side.
+ * @param arg1		User defined argument.
+ * @param arg2		User defined argument.
+ * @param arg3		User defined argument.
+ *
+ * @return		New phone handle on success or a negative error code.
+ */
+int ipc_connect_me_to_blocking(int phoneid, int arg1, int arg2, int arg3)
+{
+	ipcarg_t newphid;
+	int res;
+
+	res = ipc_call_sync_4_5(phoneid, IPC_M_CONNECT_ME_TO, arg1, arg2, arg3,
+	    IPC_FLAG_BLOCKING, NULL, NULL, NULL, NULL, &newphid);
+	if (res)
+		return res;
+	return newphid;
+}
+
+/** Hang up a phone.
+ *
+ * @param phoneid	Handle of the phone to be hung up.
+ *
+ * @return		Zero on success or a negative error code.
+ */
+int ipc_hangup(int phoneid)
+{
+	return __SYSCALL1(SYS_IPC_HANGUP, phoneid);
+}
+
+/** Register IRQ notification.
+ *
+ * @param inr		IRQ number.
+ * @param devno		Device number of the device generating inr.
+ * @param method	Use this method for notifying me.
+ * @param ucode		Top-half pseudocode handler.
+ *
+ * @return		Value returned by the kernel.
+ */
+int ipc_register_irq(int inr, int devno, int method, irq_code_t *ucode)
+{
+	return __SYSCALL4(SYS_IPC_REGISTER_IRQ, inr, devno, method,
+	    (sysarg_t) ucode);
+}
+
+/** Unregister IRQ notification.
+ *
+ * @param inr		IRQ number.
+ * @param devno		Device number of the device generating inr.
+ *
+ * @return		Value returned by the kernel.
+ */
+int ipc_unregister_irq(int inr, int devno)
+{
+	return __SYSCALL2(SYS_IPC_UNREGISTER_IRQ, inr, devno);
+}
+
+/** Forward a received call to another destination.
+ *
+ * @param callid	Hash of the call to forward.
+ * @param phoneid	Phone handle to use for forwarding.
+ * @param method	New method for the forwarded call.
+ * @param arg1		New value of the first argument for the forwarded call.
+ * @param arg2		New value of the second argument for the forwarded call.
+ * @param mode		Flags specifying mode of the forward operation.
+ *
+ * @return		Zero on success or an error code.
+ *
+ * For non-system methods, the old method, arg1 and arg2 are rewritten by the
+ * new values. For system methods, the new method, arg1 and arg2 are written 
+ * to the old arg1, arg2 and arg3, respectivelly. Calls with immutable 
+ * methods are forwarded verbatim.
+ */
+int ipc_forward_fast(ipc_callid_t callid, int phoneid, int method,
+    ipcarg_t arg1, ipcarg_t arg2, int mode)
+{
+	return __SYSCALL6(SYS_IPC_FORWARD_FAST, callid, phoneid, method, arg1, 
+	    arg2, mode);
+}
+
+
+int ipc_forward_slow(ipc_callid_t callid, int phoneid, int method,
+    ipcarg_t arg1, ipcarg_t arg2, ipcarg_t arg3, ipcarg_t arg4, ipcarg_t arg5,
+    int mode)
+{
+	ipc_call_t data;
+
+	IPC_SET_METHOD(data, method);
+	IPC_SET_ARG1(data, arg1);
+	IPC_SET_ARG2(data, arg2);
+	IPC_SET_ARG3(data, arg3);
+	IPC_SET_ARG4(data, arg4);
+	IPC_SET_ARG5(data, arg5);
+
+	return __SYSCALL4(SYS_IPC_FORWARD_SLOW, callid, phoneid, (sysarg_t) &data, mode);
+}
+
+/** Wrapper for making IPC_M_SHARE_IN calls.
+ *
+ * @param phoneid	Phone that will be used to contact the receiving side.
+ * @param dst		Destination address space area base.
+ * @param size		Size of the destination address space area.
+ * @param arg		User defined argument.
+ * @param flags		Storage where the received flags will be stored. Can be
+ *			NULL.
+ *
+ * @return		Zero on success or a negative error code from errno.h.
+ */
+int ipc_share_in_start(int phoneid, void *dst, size_t size, ipcarg_t arg,
+    int *flags)
+{
+	sysarg_t tmp_flags = 0;
+	int res = ipc_call_sync_3_2(phoneid, IPC_M_SHARE_IN, (ipcarg_t) dst,
+	    (ipcarg_t) size, arg, NULL, &tmp_flags);
+	
+	if (flags)
+		*flags = tmp_flags;
+	
+	return res;
+}
+
+/** Wrapper for answering the IPC_M_SHARE_IN calls.
+ *
+ * This wrapper only makes it more comfortable to answer IPC_M_DATA_READ calls
+ * so that the user doesn't have to remember the meaning of each IPC argument.
+ *
+ * @param callid	Hash of the IPC_M_DATA_READ call to answer.
+ * @param src		Source address space base.
+ * @param flags		Flags to be used for sharing. Bits can be only cleared.
+ *
+ * @return		Zero on success or a value from @ref errno.h on failure.
+ */
+int ipc_share_in_finalize(ipc_callid_t callid, void *src, int flags)
+{
+	return ipc_answer_2(callid, EOK, (ipcarg_t) src, (ipcarg_t) flags);
+}
+
+/** Wrapper for making IPC_M_SHARE_OUT calls.
+ *
+ * @param phoneid	Phone that will be used to contact the receiving side.
+ * @param src		Source address space area base address.
+ * @param flags		Flags to be used for sharing. Bits can be only cleared.
+ *
+ * @return		Zero on success or a negative error code from errno.h.
+ */
+int ipc_share_out_start(int phoneid, void *src, int flags)
+{
+	return ipc_call_sync_3_0(phoneid, IPC_M_SHARE_OUT, (ipcarg_t) src, 0,
+	    (ipcarg_t) flags);
+}
+
+/** Wrapper for answering the IPC_M_SHARE_OUT calls.
+ *
+ * This wrapper only makes it more comfortable to answer IPC_M_SHARE_OUT calls
+ * so that the user doesn't have to remember the meaning of each IPC argument.
+ *
+ * @param callid	Hash of the IPC_M_DATA_WRITE call to answer.
+ * @param dst		Destination address space area base address.	
+ *
+ * @return		Zero on success or a value from @ref errno.h on failure.
+ */
+int ipc_share_out_finalize(ipc_callid_t callid, void *dst)
+{
+	return ipc_answer_1(callid, EOK, (ipcarg_t) dst);
+}
+
+
+/** Wrapper for making IPC_M_DATA_READ calls.
+ *
+ * @param phoneid	Phone that will be used to contact the receiving side.
+ * @param dst		Address of the beginning of the destination buffer.
+ * @param size		Size of the destination buffer.
+ *
+ * @return		Zero on success or a negative error code from errno.h.
+ */
+int ipc_data_read_start(int phoneid, void *dst, size_t size)
+{
+	return ipc_call_sync_2_0(phoneid, IPC_M_DATA_READ, (ipcarg_t) dst,
+	    (ipcarg_t) size);
+}
+
+/** Wrapper for answering the IPC_M_DATA_READ calls.
+ *
+ * This wrapper only makes it more comfortable to answer IPC_M_DATA_READ calls
+ * so that the user doesn't have to remember the meaning of each IPC argument.
+ *
+ * @param callid	Hash of the IPC_M_DATA_READ call to answer.
+ * @param src		Source address for the IPC_M_DATA_READ call.
+ * @param size		Size for the IPC_M_DATA_READ call. Can be smaller than
+ *			the maximum size announced by the sender.
+ *
+ * @return		Zero on success or a value from @ref errno.h on failure.
+ */
+int ipc_data_read_finalize(ipc_callid_t callid, const void *src, size_t size)
+{
+	return ipc_answer_2(callid, EOK, (ipcarg_t) src, (ipcarg_t) size);
+}
+
+/** Wrapper for making IPC_M_DATA_WRITE calls.
+ *
+ * @param phoneid	Phone that will be used to contact the receiving side.
+ * @param src		Address of the beginning of the source buffer.
+ * @param size		Size of the source buffer.
+ *
+ * @return		Zero on success or a negative error code from errno.h.
+ */
+int ipc_data_write_start(int phoneid, const void *src, size_t size)
+{
+	return ipc_call_sync_2_0(phoneid, IPC_M_DATA_WRITE, (ipcarg_t) src,
+	    (ipcarg_t) size);
+}
+
+/** Wrapper for answering the IPC_M_DATA_WRITE calls.
+ *
+ * This wrapper only makes it more comfortable to answer IPC_M_DATA_WRITE calls
+ * so that the user doesn't have to remember the meaning of each IPC argument.
+ *
+ * @param callid	Hash of the IPC_M_DATA_WRITE call to answer.
+ * @param dst		Final destination address for the IPC_M_DATA_WRITE call.
+ * @param size		Final size for the IPC_M_DATA_WRITE call.
+ *
+ * @return		Zero on success or a value from @ref errno.h on failure.
+ */
+int ipc_data_write_finalize(ipc_callid_t callid, void *dst, size_t size)
+{
+	return ipc_answer_2(callid, EOK, (ipcarg_t) dst, (ipcarg_t) size);
+}
+
+#include <kernel/syscall/sysarg64.h>
+/** Connect to a task specified by id.
+ */
+int ipc_connect_kbox(task_id_t id)
+{
+	sysarg64_t arg;
+
+	arg.value = (unsigned long long) id;
+
+	return __SYSCALL1(SYS_IPC_CONNECT_KBOX, (sysarg_t) &arg);
+}
+ 
+/** @}
+ */
Index: uspace/lib/c/generic/libc.c
===================================================================
--- uspace/lib/c/generic/libc.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
+++ uspace/lib/c/generic/libc.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2005 Martin Decky
+ * 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 lc Libc
+ * @brief HelenOS C library
+ * @{
+ * @}
+ */
+
+/** @addtogroup libc generic
+ * @ingroup lc
+ * @{
+ */
+
+/** @file
+ */
+
+#include <libc.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <malloc.h>
+#include <tls.h>
+#include <thread.h>
+#include <fibril.h>
+#include <ipc/ipc.h>
+#include <async.h>
+#include <as.h>
+#include <loader/pcb.h>
+
+extern int main(int argc, char *argv[]);
+
+void _exit(int status)
+{
+	thread_exit(status);
+}
+
+void __main(void *pcb_ptr)
+{
+	int retval;
+
+	__heap_init();
+	__async_init();
+	fibril_t *fibril = fibril_setup();
+	__tcb_set(fibril->tcb);
+	
+	/* Save the PCB pointer */
+	__pcb = (pcb_t *) pcb_ptr;
+	
+	int argc;
+	char **argv;
+	
+	if (__pcb == NULL) {
+		argc = 0;
+		argv = NULL;
+		__stdio_init(0, NULL);
+	} else {
+		argc = __pcb->argc;
+		argv = __pcb->argv;
+		__stdio_init(__pcb->filc, __pcb->filv);
+		(void) chdir(__pcb->cwd);
+	}
+	
+	retval = main(argc, argv);
+
+	__stdio_done();
+	(void) task_retval(retval);
+}
+
+void __exit(void)
+{
+	fibril_teardown(__tcb_get()->fibril_data);
+	_exit(0);
+}
+
+/** @}
+ */
Index: uspace/lib/c/generic/loader.c
===================================================================
--- uspace/lib/c/generic/loader.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
+++ uspace/lib/c/generic/loader.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
@@ -0,0 +1,344 @@
+/*
+ * Copyright (c) 2008 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 libc
+ * @{
+ */
+/** @file
+ */
+
+#include <ipc/ipc.h>
+#include <ipc/loader.h>
+#include <ipc/services.h>
+#include <libc.h>
+#include <task.h>
+#include <str.h>
+#include <stdlib.h>
+#include <async.h>
+#include <errno.h>
+#include <vfs/vfs.h>
+#include <loader/loader.h>
+
+/** Connect to a new program loader.
+ *
+ * Spawns a new program loader task and returns the connection structure.
+ *
+ * @param name Symbolic name to set on the newly created task.
+ *
+ * @return Pointer to the loader connection structure (should be
+ *         deallocated using free() after use).
+ *
+ */
+int loader_spawn(const char *name)
+{
+	return __SYSCALL2(SYS_PROGRAM_SPAWN_LOADER,
+	    (sysarg_t) name, str_size(name));
+}
+
+loader_t *loader_connect(void)
+{
+	int phone_id = ipc_connect_me_to_blocking(PHONE_NS, SERVICE_LOAD, 0, 0);
+	if (phone_id < 0)
+		return NULL;
+	
+	loader_t *ldr = malloc(sizeof(loader_t));
+	if (ldr == NULL)
+		return NULL;
+	
+	ldr->phone_id = phone_id;
+	return ldr;
+}
+
+/** Get ID of the new task.
+ *
+ * Retrieves the ID of the new task from the loader.
+ *
+ * @param ldr     Loader connection structure.
+ * @param task_id Points to a variable where the ID should be stored.
+ *
+ * @return Zero on success or negative error code.
+ *
+ */
+int loader_get_task_id(loader_t *ldr, task_id_t *task_id)
+{
+	/* Get task ID. */
+	ipc_call_t answer;
+	aid_t req = async_send_0(ldr->phone_id, LOADER_GET_TASKID, &answer);
+	int rc = async_data_read_start(ldr->phone_id, task_id, sizeof(task_id_t));
+	if (rc != EOK) {
+		async_wait_for(req, NULL);
+		return rc;
+	}
+	
+	ipcarg_t retval;
+	async_wait_for(req, &retval);
+	return (int) retval;
+}
+
+/** Set current working directory for the loaded task.
+ *
+ * Sets the current working directory for the loaded task.
+ *
+ * @param ldr  Loader connection structure.
+ *
+ * @return Zero on success or negative error code.
+ *
+ */
+int loader_set_cwd(loader_t *ldr)
+{
+	char *cwd;
+	size_t len;
+
+	cwd = (char *) malloc(MAX_PATH_LEN + 1);
+	if (!cwd)
+		return ENOMEM;
+	if (!getcwd(cwd, MAX_PATH_LEN + 1))
+		str_cpy(cwd, MAX_PATH_LEN + 1, "/"); 
+	len = str_length(cwd);
+	
+	ipc_call_t answer;
+	aid_t req = async_send_0(ldr->phone_id, LOADER_SET_CWD, &answer);
+	int rc = async_data_write_start(ldr->phone_id, cwd, len);
+	free(cwd);
+	if (rc != EOK) {
+		async_wait_for(req, NULL);
+		return rc;
+	}
+	
+	ipcarg_t retval;
+	async_wait_for(req, &retval);
+	return (int) retval;
+}
+
+/** Set pathname of the program to load.
+ *
+ * Sets the name of the program file to load. The name can be relative
+ * to the current working directory (it will be absolutized before
+ * sending to the loader).
+ *
+ * @param ldr  Loader connection structure.
+ * @param path Pathname of the program file.
+ *
+ * @return Zero on success or negative error code.
+ *
+ */
+int loader_set_pathname(loader_t *ldr, const char *path)
+{
+	size_t pa_len;
+	char *pa = absolutize(path, &pa_len);
+	if (!pa)
+		return 0;
+	
+	/* Send program pathname */
+	ipc_call_t answer;
+	aid_t req = async_send_0(ldr->phone_id, LOADER_SET_PATHNAME, &answer);
+	int rc = async_data_write_start(ldr->phone_id, (void *) pa, pa_len);
+	if (rc != EOK) {
+		async_wait_for(req, NULL);
+		return rc;
+	}
+	
+	free(pa);
+	
+	ipcarg_t retval;
+	async_wait_for(req, &retval);
+	return (int) retval;
+}
+
+/** Set command-line arguments for the program.
+ *
+ * Sets the vector of command-line arguments to be passed to the loaded
+ * program. By convention, the very first argument is typically the same as
+ * the command used to execute the program.
+ *
+ * @param ldr  Loader connection structure.
+ * @param argv NULL-terminated array of pointers to arguments.
+ *
+ * @return Zero on success or negative error code.
+ *
+ */
+int loader_set_args(loader_t *ldr, const char *const argv[])
+{
+	/*
+	 * Serialize the arguments into a single array. First
+	 * compute size of the buffer needed.
+	 */
+	const char *const *ap = argv;
+	size_t buffer_size = 0;
+	while (*ap != NULL) {
+		buffer_size += str_size(*ap) + 1;
+		ap++;
+	}
+	
+	char *arg_buf = malloc(buffer_size);
+	if (arg_buf == NULL)
+		return ENOMEM;
+	
+	/* Now fill the buffer with null-terminated argument strings */
+	ap = argv;
+	char *dp = arg_buf;
+	
+	while (*ap != NULL) {
+		str_cpy(dp, buffer_size - (dp - arg_buf), *ap);
+		dp += str_size(*ap) + 1;
+		ap++;
+	}
+	
+	/* Send serialized arguments to the loader */
+	ipc_call_t answer;
+	aid_t req = async_send_0(ldr->phone_id, LOADER_SET_ARGS, &answer);
+	ipcarg_t rc = async_data_write_start(ldr->phone_id, (void *) arg_buf, buffer_size);
+	if (rc != EOK) {
+		async_wait_for(req, NULL);
+		return rc;
+	}
+	
+	async_wait_for(req, &rc);
+	if (rc != EOK)
+		return rc;
+	
+	/* Free temporary buffer */
+	free(arg_buf);
+	
+	return EOK;
+}
+
+/** Set preset files for the program.
+ *
+ * Sets the vector of preset files to be passed to the loaded
+ * program. By convention, the first three files represent stdin,
+ * stdout and stderr respectively.
+ *
+ * @param ldr   Loader connection structure.
+ * @param files NULL-terminated array of pointers to files.
+ *
+ * @return Zero on success or negative error code.
+ *
+ */
+int loader_set_files(loader_t *ldr, fdi_node_t *const files[])
+{
+	/*
+	 * Serialize the arguments into a single array. First
+	 * compute size of the buffer needed.
+	 */
+	fdi_node_t *const *ap = files;
+	size_t count = 0;
+	while (*ap != NULL) {
+		count++;
+		ap++;
+	}
+	
+	fdi_node_t *files_buf;
+	files_buf = (fdi_node_t *) malloc(count * sizeof(fdi_node_t));
+	if (files_buf == NULL)
+		return ENOMEM;
+	
+	/* Fill the buffer */
+	size_t i;
+	for (i = 0; i < count; i++)
+		files_buf[i] = *files[i];
+	
+	/* Send serialized files to the loader */
+	ipc_call_t answer;
+	aid_t req = async_send_0(ldr->phone_id, LOADER_SET_FILES, &answer);
+	ipcarg_t rc = async_data_write_start(ldr->phone_id, (void *) files_buf,
+	    count * sizeof(fdi_node_t));
+	if (rc != EOK) {
+		async_wait_for(req, NULL);
+		return rc;
+	}
+	
+	async_wait_for(req, &rc);
+	if (rc != EOK)
+		return rc;
+	
+	/* Free temporary buffer */
+	free(files_buf);
+	
+	return EOK;
+}
+
+/** Instruct loader to load the program.
+ *
+ * If this function succeeds, the program has been successfully loaded
+ * and is ready to be executed.
+ *
+ * @param ldr Loader connection structure.
+ *
+ * @return Zero on success or negative error code.
+ *
+ */
+int loader_load_program(loader_t *ldr)
+{
+	return (int) async_req_0_0(ldr->phone_id, LOADER_LOAD);
+}
+
+/** Instruct loader to execute the program.
+ *
+ * Note that this function blocks until the loader actually replies
+ * so you cannot expect this function to return if you are debugging
+ * the task and its thread is stopped.
+ *
+ * After using this function, no further operations must be performed
+ * on the loader structure. It should be de-allocated using free().
+ *
+ * @param ldr Loader connection structure.
+ *
+ * @return Zero on success or negative error code.
+ *
+ */
+int loader_run(loader_t *ldr)
+{
+	int rc = async_req_0_0(ldr->phone_id, LOADER_RUN);
+	if (rc != EOK)
+		return rc;
+	
+	ipc_hangup(ldr->phone_id);
+	ldr->phone_id = 0;
+	return EOK;
+}
+
+/** Cancel the loader session.
+ *
+ * Tells the loader not to load any program and terminate.
+ * After using this function, no further operations must be performed
+ * on the loader structure. It should be de-allocated using free().
+ *
+ * @param ldr Loader connection structure.
+ *
+ * @return Zero on success or negative error code.
+ *
+ */
+void loader_abort(loader_t *ldr)
+{
+	ipc_hangup(ldr->phone_id);
+	ldr->phone_id = 0;
+}
+
+/** @}
+ */
Index: uspace/lib/c/generic/malloc.c
===================================================================
--- uspace/lib/c/generic/malloc.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
+++ uspace/lib/c/generic/malloc.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
@@ -0,0 +1,488 @@
+/*
+ * Copyright (c) 2009 Martin Decky
+ * Copyright (c) 2009 Petr Tuma
+ * 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 libc
+ * @{
+ */
+/** @file
+ */
+
+#include <malloc.h>
+#include <bool.h>
+#include <as.h>
+#include <align.h>
+#include <macros.h>
+#include <assert.h>
+#include <errno.h>
+#include <bitops.h>
+#include <mem.h>
+#include <adt/gcdlcm.h>
+
+/* Magic used in heap headers. */
+#define HEAP_BLOCK_HEAD_MAGIC  0xBEEF0101
+
+/* Magic used in heap footers. */
+#define HEAP_BLOCK_FOOT_MAGIC  0xBEEF0202
+
+/** Allocation alignment (this also covers the alignment of fields
+    in the heap header and footer) */
+#define BASE_ALIGN  16
+
+/**
+ * Either 4 * 256M on 32-bit architecures or 16 * 256M on 64-bit architectures
+ */
+#define MAX_HEAP_SIZE  (sizeof(uintptr_t) << 28)
+
+/**
+ *
+ */
+#define STRUCT_OVERHEAD  (sizeof(heap_block_head_t) + sizeof(heap_block_foot_t))
+
+/**
+ * Calculate real size of a heap block (with header and footer)
+ */
+#define GROSS_SIZE(size)  ((size) + STRUCT_OVERHEAD)
+
+/**
+ * Calculate net size of a heap block (without header and footer)
+ */
+#define NET_SIZE(size)  ((size) - STRUCT_OVERHEAD)
+
+
+/** Header of a heap block
+ *
+ */
+typedef struct {
+	/* Size of the block (including header and footer) */
+	size_t size;
+	
+	/* Indication of a free block */
+	bool free;
+	
+	/* A magic value to detect overwrite of heap header */
+	uint32_t magic;
+} heap_block_head_t;
+
+/** Footer of a heap block
+ *
+ */
+typedef struct {
+	/* Size of the block (including header and footer) */
+	size_t size;
+	
+	/* A magic value to detect overwrite of heap footer */
+	uint32_t magic;
+} heap_block_foot_t;
+
+/** Linker heap symbol */
+extern char _heap;
+
+/** Address of heap start */
+static void *heap_start = 0;
+
+/** Address of heap end */
+static void *heap_end = 0;
+
+/** Maximum heap size */
+static size_t max_heap_size = (size_t) -1;
+
+/** Current number of pages of heap area */
+static size_t heap_pages = 0;
+
+/** Initialize a heap block
+ *
+ * Fills in the structures related to a heap block.
+ *
+ * @param addr Address of the block.
+ * @param size Size of the block including the header and the footer.
+ * @param free Indication of a free block.
+ *
+ */
+static void block_init(void *addr, size_t size, bool free)
+{
+	/* Calculate the position of the header and the footer */
+	heap_block_head_t *head = (heap_block_head_t *) addr;
+	heap_block_foot_t *foot =
+	    (heap_block_foot_t *) (addr + size - sizeof(heap_block_foot_t));
+	
+	head->size = size;
+	head->free = free;
+	head->magic = HEAP_BLOCK_HEAD_MAGIC;
+	
+	foot->size = size;
+	foot->magic = HEAP_BLOCK_FOOT_MAGIC;
+}
+
+/** Check a heap block
+ *
+ * Verifies that the structures related to a heap block still contain
+ * the magic constants. This helps detect heap corruption early on.
+ *
+ * @param addr Address of the block.
+ *
+ */
+static void block_check(void *addr)
+{
+	heap_block_head_t *head = (heap_block_head_t *) addr;
+	
+	assert(head->magic == HEAP_BLOCK_HEAD_MAGIC);
+	
+	heap_block_foot_t *foot =
+	    (heap_block_foot_t *) (addr + head->size - sizeof(heap_block_foot_t));
+	
+	assert(foot->magic == HEAP_BLOCK_FOOT_MAGIC);
+	assert(head->size == foot->size);
+}
+
+static bool grow_heap(size_t size)
+{
+	if (size == 0)
+		return false;
+
+	if ((heap_start + size < heap_start) || (heap_end + size < heap_end))
+		return false;
+	
+	size_t heap_size = (size_t) (heap_end - heap_start);
+	
+	if ((max_heap_size != (size_t) -1) && (heap_size + size > max_heap_size))
+		return false;
+	
+	size_t pages = (size - 1) / PAGE_SIZE + 1;
+	
+	if (as_area_resize((void *) &_heap, (heap_pages + pages) * PAGE_SIZE, 0)
+	    == EOK) {
+		void *end = (void *) ALIGN_DOWN(((uintptr_t) &_heap) +
+		    (heap_pages + pages) * PAGE_SIZE, BASE_ALIGN);
+		block_init(heap_end, end - heap_end, true);
+		heap_pages += pages;
+		heap_end = end;
+		return true;
+	}
+	
+	return false;
+}
+
+static void shrink_heap(void)
+{
+	// TODO
+}
+
+/** Initialize the heap allocator
+ *
+ * Finds how much physical memory we have and creates
+ * the heap management structures that mark the whole
+ * physical memory as a single free block.
+ *
+ */
+void __heap_init(void)
+{
+	if (as_area_create((void *) &_heap, PAGE_SIZE,
+	    AS_AREA_WRITE | AS_AREA_READ)) {
+		heap_pages = 1;
+		heap_start = (void *) ALIGN_UP((uintptr_t) &_heap, BASE_ALIGN);
+		heap_end =
+		    (void *) ALIGN_DOWN(((uintptr_t) &_heap) + PAGE_SIZE, BASE_ALIGN);
+		
+		/* Make the entire area one large block. */
+		block_init(heap_start, heap_end - heap_start, true);
+	}
+}
+
+uintptr_t get_max_heap_addr(void)
+{
+	if (max_heap_size == (size_t) -1)
+		max_heap_size =
+		    max((size_t) (heap_end - heap_start), MAX_HEAP_SIZE);
+	
+	return ((uintptr_t) heap_start + max_heap_size);
+}
+
+static void split_mark(heap_block_head_t *cur, const size_t size)
+{
+	assert(cur->size >= size);
+	
+	/* See if we should split the block. */
+	size_t split_limit = GROSS_SIZE(size);
+	
+	if (cur->size > split_limit) {
+		/* Block big enough -> split. */
+		void *next = ((void *) cur) + size;
+		block_init(next, cur->size - size, true);
+		block_init(cur, size, false);
+	} else {
+		/* Block too small -> use as is. */
+		cur->free = false;
+	}
+}
+
+/** Allocate a memory block
+ *
+ * @param size  The size of the block to allocate.
+ * @param align Memory address alignment.
+ *
+ * @return the address of the block or NULL when not enough memory.
+ *
+ */
+static void *malloc_internal(const size_t size, const size_t align)
+{
+	if (align == 0)
+		return NULL;
+	
+	size_t falign = lcm(align, BASE_ALIGN);
+	size_t real_size = GROSS_SIZE(ALIGN_UP(size, falign));
+	
+	bool grown = false;
+	void *result;
+	
+loop:
+	result = NULL;
+	heap_block_head_t *cur = (heap_block_head_t *) heap_start;
+	
+	while ((result == NULL) && ((void *) cur < heap_end)) {
+		block_check(cur);
+		
+		/* Try to find a block that is free and large enough. */
+		if ((cur->free) && (cur->size >= real_size)) {
+			/* We have found a suitable block.
+			   Check for alignment properties. */
+			void *addr = ((void *) cur) + sizeof(heap_block_head_t);
+			void *aligned = (void *) ALIGN_UP(addr, falign);
+			
+			if (addr == aligned) {
+				/* Exact block start including alignment. */
+				split_mark(cur, real_size);
+				result = addr;
+			} else {
+				/* Block start has to be aligned */
+				size_t excess = (size_t) (aligned - addr);
+				
+				if (cur->size >= real_size + excess) {
+					/* The current block is large enough to fit
+					   data in including alignment */
+					if ((void *) cur > heap_start) {
+						/* There is a block before the current block.
+						   This previous block can be enlarged to compensate
+						   for the alignment excess */
+						heap_block_foot_t *prev_foot =
+						    ((void *) cur) - sizeof(heap_block_foot_t);
+						
+						heap_block_head_t *prev_head =
+						    (heap_block_head_t *) (((void *) cur) - prev_foot->size);
+						
+						block_check(prev_head);
+						
+						size_t reduced_size = cur->size - excess;
+						heap_block_head_t *next_head = ((void *) cur) + excess;
+						
+						if ((!prev_head->free) && (excess >= STRUCT_OVERHEAD)) {
+							/* The previous block is not free and there is enough
+							   space to fill in a new free block between the previous
+							   and current block */
+							block_init(cur, excess, true);
+						} else {
+							/* The previous block is free (thus there is no need to
+							   induce additional fragmentation to the heap) or the
+							   excess is small, thus just enlarge the previous block */
+							block_init(prev_head, prev_head->size + excess, prev_head->free);
+						}
+						
+						block_init(next_head, reduced_size, true);
+						split_mark(next_head, real_size);
+						result = aligned;
+						cur = next_head;
+					} else {
+						/* The current block is the first block on the heap.
+						   We have to make sure that the alignment excess
+						   is large enough to fit a new free block just
+						   before the current block */
+						while (excess < STRUCT_OVERHEAD) {
+							aligned += falign;
+							excess += falign;
+						}
+						
+						/* Check for current block size again */
+						if (cur->size >= real_size + excess) {
+							size_t reduced_size = cur->size - excess;
+							cur = (heap_block_head_t *) (heap_start + excess);
+							
+							block_init(heap_start, excess, true);
+							block_init(cur, reduced_size, true);
+							split_mark(cur, real_size);
+							result = aligned;
+						}
+					}
+				}
+			}
+		}
+		
+		/* Advance to the next block. */
+		cur = (heap_block_head_t *) (((void *) cur) + cur->size);
+	}
+	
+	if ((result == NULL) && (!grown)) {
+		if (grow_heap(real_size)) {
+			grown = true;
+			goto loop;
+		}
+	}
+	
+	return result;
+}
+
+void *calloc(const size_t nmemb, const size_t size)
+{
+	void *block = malloc(nmemb * size);
+	if (block == NULL)
+		return NULL;
+
+	memset(block, 0, nmemb * size);
+	return block;
+}
+
+void *malloc(const size_t size)
+{
+	return malloc_internal(size, BASE_ALIGN);
+}
+
+void *memalign(const size_t align, const size_t size)
+{
+	if (align == 0)
+		return NULL;
+	
+	size_t palign =
+	    1 << (fnzb(max(sizeof(void *), align) - 1) + 1);
+	
+	return malloc_internal(size, palign);
+}
+
+void *realloc(const void *addr, const size_t size)
+{
+	if (addr == NULL)
+		return malloc(size);
+	
+	/* Calculate the position of the header. */
+	heap_block_head_t *head =
+	    (heap_block_head_t *) (addr - sizeof(heap_block_head_t));
+	
+	assert((void *) head >= heap_start);
+	assert((void *) head < heap_end);
+	
+	block_check(head);
+	assert(!head->free);
+	
+	void *ptr = NULL;
+	size_t real_size = GROSS_SIZE(ALIGN_UP(size, BASE_ALIGN));
+	size_t orig_size = head->size;
+	
+	if (orig_size > real_size) {
+		/* Shrink */
+		if (orig_size - real_size >= STRUCT_OVERHEAD) {
+			/* Split the original block to a full block
+			   and a trailing free block */
+			block_init((void *) head, real_size, false);
+			block_init((void *) head + real_size,
+			    orig_size - real_size, true);
+			shrink_heap();
+		}
+		
+		ptr = ((void *) head) + sizeof(heap_block_head_t);
+	} else {
+		/* Look at the next block. If it is free and the size is
+		   sufficient then merge the two. */
+		heap_block_head_t *next_head =
+		    (heap_block_head_t *) (((void *) head) + head->size);
+		
+		if (((void *) next_head < heap_end) &&
+		    (head->size + next_head->size >= real_size) &&
+		    (next_head->free)) {
+			block_check(next_head);
+			block_init(head, head->size + next_head->size, false);
+			split_mark(head, real_size);
+			
+			ptr = ((void *) head) + sizeof(heap_block_head_t);
+		} else {
+			ptr = malloc(size);
+			if (ptr != NULL) {
+				memcpy(ptr, addr, NET_SIZE(orig_size));
+				free(addr);
+			}
+		}
+	}
+	
+	return ptr;
+}
+
+/** Free a memory block
+ *
+ * @param addr The address of the block.
+ */
+void free(const void *addr)
+{
+	/* Calculate the position of the header. */
+	heap_block_head_t *head
+	    = (heap_block_head_t *) (addr - sizeof(heap_block_head_t));
+	
+	assert((void *) head >= heap_start);
+	assert((void *) head < heap_end);
+	
+	block_check(head);
+	assert(!head->free);
+	
+	/* Mark the block itself as free. */
+	head->free = true;
+	
+	/* Look at the next block. If it is free, merge the two. */
+	heap_block_head_t *next_head
+	    = (heap_block_head_t *) (((void *) head) + head->size);
+	
+	if ((void *) next_head < heap_end) {
+		block_check(next_head);
+		if (next_head->free)
+			block_init(head, head->size + next_head->size, true);
+	}
+	
+	/* Look at the previous block. If it is free, merge the two. */
+	if ((void *) head > heap_start) {
+		heap_block_foot_t *prev_foot =
+		    (heap_block_foot_t *) (((void *) head) - sizeof(heap_block_foot_t));
+		
+		heap_block_head_t *prev_head =
+		    (heap_block_head_t *) (((void *) head) - prev_foot->size);
+		
+		block_check(prev_head);
+		
+		if (prev_head->free)
+			block_init(prev_head, prev_head->size + head->size, true);
+	}
+	
+	shrink_heap();
+}
+
+/** @}
+ */
Index: uspace/lib/c/generic/mem.c
===================================================================
--- uspace/lib/c/generic/mem.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
+++ uspace/lib/c/generic/mem.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
@@ -0,0 +1,239 @@
+/*
+ * Copyright (c) 2005 Martin Decky
+ * Copyright (c) 2008 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 libc
+ * @{
+ */
+/** @file
+ */
+
+#include <mem.h>
+#include <stdlib.h>
+#include <sys/types.h>
+
+/** Fill memory block with a constant value. */
+void *memset(void *dest, int b, size_t n)
+{
+	char *pb;
+	unsigned long *pw;
+	size_t word_size;
+	size_t n_words;
+
+	unsigned long pattern;
+	size_t i;
+	size_t fill;
+
+	/* Fill initial segment. */
+	word_size = sizeof(unsigned long);
+	fill = word_size - ((uintptr_t) dest & (word_size - 1));
+	if (fill > n) fill = n;
+
+	pb = dest;
+
+	i = fill;
+	while (i-- != 0)
+		*pb++ = b;
+
+	/* Compute remaining size. */
+	n -= fill;
+	if (n == 0) return dest;
+
+	n_words = n / word_size;
+	n = n % word_size;
+	pw = (unsigned long *) pb;
+
+	/* Create word-sized pattern for aligned segment. */
+	pattern = 0;
+	i = word_size;
+	while (i-- != 0)
+		pattern = (pattern << 8) | (uint8_t) b;
+
+	/* Fill aligned segment. */
+	i = n_words;
+	while (i-- != 0)
+		*pw++ = pattern;
+
+	pb = (char *) pw;
+
+	/* Fill final segment. */
+	i = n;
+	while (i-- != 0)
+		*pb++ = b;
+
+	return dest;
+}
+
+struct along {
+	unsigned long n;
+} __attribute__ ((packed));
+
+static void *unaligned_memcpy(void *dst, const void *src, size_t n)
+{
+	size_t i, j;
+	struct along *adst = dst;
+	const struct along *asrc = src;
+
+	for (i = 0; i < n / sizeof(unsigned long); i++)
+		adst[i].n = asrc[i].n;
+		
+	for (j = 0; j < n % sizeof(unsigned long); j++)
+		((unsigned char *) (((unsigned long *) dst) + i))[j] =
+		    ((unsigned char *) (((unsigned long *) src) + i))[j];
+		
+	return (char *) dst;
+}
+
+/** Copy memory block. */
+void *memcpy(void *dst, const void *src, size_t n)
+{
+	size_t i;
+	size_t mod, fill;
+	size_t word_size;
+	size_t n_words;
+
+	const unsigned long *srcw;
+	unsigned long *dstw;
+	const uint8_t *srcb;
+	uint8_t *dstb;
+
+	word_size = sizeof(unsigned long);
+
+	/*
+	 * Are source and destination addresses congruent modulo word_size?
+	 * If not, use unaligned_memcpy().
+	 */
+
+	if (((uintptr_t) dst & (word_size - 1)) !=
+	    ((uintptr_t) src & (word_size - 1)))
+ 		return unaligned_memcpy(dst, src, n);
+
+	/*
+	 * mod is the address modulo word size. fill is the length of the
+	 * initial buffer segment before the first word boundary.
+	 * If the buffer is very short, use unaligned_memcpy(), too.
+	 */
+
+	mod = (uintptr_t) dst & (word_size - 1);
+	fill = word_size - mod;
+	if (fill > n) fill = n;
+
+	/* Copy the initial segment. */
+
+	srcb = src;
+	dstb = dst;
+
+	i = fill;
+	while (i-- != 0)
+		*dstb++ = *srcb++;
+
+	/* Compute remaining length. */
+
+	n -= fill;
+	if (n == 0) return dst;
+
+	/* Pointers to aligned segment. */
+
+	dstw = (unsigned long *) dstb;
+	srcw = (const unsigned long *) srcb;
+
+	n_words = n / word_size;	/* Number of whole words to copy. */
+	n -= n_words * word_size;	/* Remaining bytes at the end. */
+
+	/* "Fast" copy. */
+	i = n_words;
+	while (i-- != 0)
+		*dstw++ = *srcw++;
+
+	/*
+	 * Copy the rest.
+	 */
+
+	srcb = (const uint8_t *) srcw;
+	dstb = (uint8_t *) dstw;
+
+	i = n;
+	while (i-- != 0)
+		*dstb++ = *srcb++;
+
+	return dst;
+}
+
+/** Move memory block with possible overlapping. */
+void *memmove(void *dst, const void *src, size_t n)
+{
+	const uint8_t *sp;
+	uint8_t *dp;
+
+	/* Nothing to do? */
+	if (src == dst)
+		return dst;
+
+	/* Non-overlapping? */
+	if (dst >= src + n || src >= dst + n) {	
+		return memcpy(dst, src, n);
+	}
+
+	/* Which direction? */
+	if (src > dst) {
+		/* Forwards. */
+		sp = src;
+		dp = dst;
+
+		while (n-- != 0)
+			*dp++ = *sp++;
+	} else {
+		/* Backwards. */
+		sp = src + (n - 1);
+		dp = dst + (n - 1);
+
+		while (n-- != 0)
+			*dp-- = *sp--;
+	}
+
+	return dst;
+}
+
+/** Compare two memory areas.
+ *
+ * @param s1		Pointer to the first area to compare.
+ * @param s2		Pointer to the second area to compare.
+ * @param len		Size of the first area in bytes. Both areas must have
+ * 			the same length.
+ * @return		If len is 0, return zero. If the areas match, return
+ * 			zero. Otherwise return non-zero.
+ */
+int bcmp(const char *s1, const char *s2, size_t len)
+{
+	for (; len && *s1++ == *s2++; len--)
+		;
+	return len;
+}
+
+/** @}
+ */
Index: uspace/lib/c/generic/mman.c
===================================================================
--- uspace/lib/c/generic/mman.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
+++ uspace/lib/c/generic/mman.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2006 Ondrej Palkovsky
+ * 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 libc
+ * @{
+ */
+/** @file
+ */
+
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <as.h>
+#include <unistd.h>
+
+void *mmap(void *start, size_t length, int prot, int flags, int fd,
+    aoff64_t offset)
+{
+	if (!start)
+		start = as_get_mappable_page(length);
+	
+//	if (!((flags & MAP_SHARED) ^ (flags & MAP_PRIVATE)))
+//		return MAP_FAILED;
+	
+	if (!(flags & MAP_ANONYMOUS))
+		return MAP_FAILED;
+	
+	return as_area_create(start, length, prot);
+}
+
+int munmap(void *start, size_t length)
+{
+	return as_area_destroy(start);
+}
+
+/** @}
+ */
Index: uspace/lib/c/generic/pcb.c
===================================================================
--- uspace/lib/c/generic/pcb.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
+++ uspace/lib/c/generic/pcb.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2008 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 libc
+ * @{
+ */
+/** @file
+ */
+
+#include <loader/pcb.h>
+
+pcb_t *__pcb;
+
+/** @}
+ */
Index: uspace/lib/c/generic/smc.c
===================================================================
--- uspace/lib/c/generic/smc.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
+++ uspace/lib/c/generic/smc.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2008 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 libc
+ * @{
+ */
+/** @file
+ */
+
+#include <libc.h>
+#include <sys/types.h>
+#include <smc.h>
+
+int smc_coherence(void *address, size_t size)
+{
+	return __SYSCALL2(SYS_SMC_COHERENCE, (sysarg_t) address,
+	    (sysarg_t) size);
+}
+
+/** @}
+ */
Index: uspace/lib/c/generic/stacktrace.c
===================================================================
--- uspace/lib/c/generic/stacktrace.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
+++ uspace/lib/c/generic/stacktrace.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2009 Jakub Jermar
+ * Copyright (c) 2010 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 libc
+ * @{
+ */
+/** @file
+ */
+
+#include <stacktrace.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <errno.h>
+
+static int stacktrace_read_uintptr(void *arg, uintptr_t addr, uintptr_t *data);
+
+void stacktrace_print_fp_pc(uintptr_t fp, uintptr_t pc)
+{
+	stacktrace_t st;
+	uintptr_t nfp;
+
+	st.op_arg = NULL;
+	st.read_uintptr = stacktrace_read_uintptr;
+
+	while (stacktrace_fp_valid(&st, fp)) {
+		printf("%p: %p()\n", fp, pc);
+		(void) stacktrace_ra_get(&st, fp, &pc);
+		(void) stacktrace_fp_prev(&st, fp, &nfp);
+		fp = nfp;
+	}
+}
+
+void stacktrace_print(void)
+{
+	stacktrace_prepare();
+	stacktrace_print_fp_pc(stacktrace_fp_get(), stacktrace_pc_get());
+	/*
+	 * Prevent the tail call optimization of the previous call by
+	 * making it a non-tail call.
+	 */
+	(void) stacktrace_fp_get();
+}
+
+static int stacktrace_read_uintptr(void *arg, uintptr_t addr, uintptr_t *data)
+{
+	(void) arg;
+	*data = *((uintptr_t *) addr);
+	return EOK;
+}
+
+/** @}
+ */
Index: uspace/lib/c/generic/stdlib.c
===================================================================
--- uspace/lib/c/generic/stdlib.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
+++ uspace/lib/c/generic/stdlib.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2006 Ondrej Palkovsky
+ * 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 libc
+ * @{
+ */
+/** @file
+ */
+
+#include <stdlib.h>
+
+static long glbl_seed = 1;
+
+long int random(void)
+{
+	return glbl_seed = ((1366*glbl_seed + 150889) % RAND_MAX);
+}
+
+void srandom(unsigned int seed)
+{
+	glbl_seed = seed;
+}
+
+/** @}
+ */
Index: uspace/lib/c/generic/str.c
===================================================================
--- uspace/lib/c/generic/str.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
+++ uspace/lib/c/generic/str.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
@@ -0,0 +1,982 @@
+/*
+ * Copyright (c) 2005 Martin Decky
+ * Copyright (c) 2008 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 libc
+ * @{
+ */
+/** @file
+ */
+
+#include <str.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <limits.h>
+#include <ctype.h>
+#include <malloc.h>
+#include <errno.h>
+#include <align.h>
+#include <mem.h>
+#include <str.h>
+
+/** Byte mask consisting of lowest @n bits (out of 8) */
+#define LO_MASK_8(n)  ((uint8_t) ((1 << (n)) - 1))
+
+/** Byte mask consisting of lowest @n bits (out of 32) */
+#define LO_MASK_32(n)  ((uint32_t) ((1 << (n)) - 1))
+
+/** Byte mask consisting of highest @n bits (out of 8) */
+#define HI_MASK_8(n)  (~LO_MASK_8(8 - (n)))
+
+/** Number of data bits in a UTF-8 continuation byte */
+#define CONT_BITS  6
+
+/** Decode a single character from a string.
+ *
+ * Decode a single character from a string of size @a size. Decoding starts
+ * at @a offset and this offset is moved to the beginning of the next
+ * character. In case of decoding error, offset generally advances at least
+ * by one. However, offset is never moved beyond size.
+ *
+ * @param str    String (not necessarily NULL-terminated).
+ * @param offset Byte offset in string where to start decoding.
+ * @param size   Size of the string (in bytes).
+ *
+ * @return Value of decoded character, U_SPECIAL on decoding error or
+ *         NULL if attempt to decode beyond @a size.
+ *
+ */
+wchar_t str_decode(const char *str, size_t *offset, size_t size)
+{
+	if (*offset + 1 > size)
+		return 0;
+	
+	/* First byte read from string */
+	uint8_t b0 = (uint8_t) str[(*offset)++];
+	
+	/* Determine code length */
+	
+	unsigned int b0_bits;  /* Data bits in first byte */
+	unsigned int cbytes;   /* Number of continuation bytes */
+	
+	if ((b0 & 0x80) == 0) {
+		/* 0xxxxxxx (Plain ASCII) */
+		b0_bits = 7;
+		cbytes = 0;
+	} else if ((b0 & 0xe0) == 0xc0) {
+		/* 110xxxxx 10xxxxxx */
+		b0_bits = 5;
+		cbytes = 1;
+	} else if ((b0 & 0xf0) == 0xe0) {
+		/* 1110xxxx 10xxxxxx 10xxxxxx */
+		b0_bits = 4;
+		cbytes = 2;
+	} else if ((b0 & 0xf8) == 0xf0) {
+		/* 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */
+		b0_bits = 3;
+		cbytes = 3;
+	} else {
+		/* 10xxxxxx -- unexpected continuation byte */
+		return U_SPECIAL;
+	}
+	
+	if (*offset + cbytes > size)
+		return U_SPECIAL;
+	
+	wchar_t ch = b0 & LO_MASK_8(b0_bits);
+	
+	/* Decode continuation bytes */
+	while (cbytes > 0) {
+		uint8_t b = (uint8_t) str[(*offset)++];
+		
+		/* Must be 10xxxxxx */
+		if ((b & 0xc0) != 0x80)
+			return U_SPECIAL;
+		
+		/* Shift data bits to ch */
+		ch = (ch << CONT_BITS) | (wchar_t) (b & LO_MASK_8(CONT_BITS));
+		cbytes--;
+	}
+	
+	return ch;
+}
+
+/** Encode a single character to string representation.
+ *
+ * Encode a single character to string representation (i.e. UTF-8) and store
+ * it into a buffer at @a offset. Encoding starts at @a offset and this offset
+ * is moved to the position where the next character can be written to.
+ *
+ * @param ch     Input character.
+ * @param str    Output buffer.
+ * @param offset Byte offset where to start writing.
+ * @param size   Size of the output buffer (in bytes).
+ *
+ * @return EOK if the character was encoded successfully, EOVERFLOW if there
+ *         was not enough space in the output buffer or EINVAL if the character
+ *         code was invalid.
+ */
+int chr_encode(const wchar_t ch, char *str, size_t *offset, size_t size)
+{
+	if (*offset >= size)
+		return EOVERFLOW;
+	
+	if (!chr_check(ch))
+		return EINVAL;
+	
+	/* Unsigned version of ch (bit operations should only be done
+	   on unsigned types). */
+	uint32_t cc = (uint32_t) ch;
+	
+	/* Determine how many continuation bytes are needed */
+	
+	unsigned int b0_bits;  /* Data bits in first byte */
+	unsigned int cbytes;   /* Number of continuation bytes */
+	
+	if ((cc & ~LO_MASK_32(7)) == 0) {
+		b0_bits = 7;
+		cbytes = 0;
+	} else if ((cc & ~LO_MASK_32(11)) == 0) {
+		b0_bits = 5;
+		cbytes = 1;
+	} else if ((cc & ~LO_MASK_32(16)) == 0) {
+		b0_bits = 4;
+		cbytes = 2;
+	} else if ((cc & ~LO_MASK_32(21)) == 0) {
+		b0_bits = 3;
+		cbytes = 3;
+	} else {
+		/* Codes longer than 21 bits are not supported */
+		return EINVAL;
+	}
+	
+	/* Check for available space in buffer */
+	if (*offset + cbytes >= size)
+		return EOVERFLOW;
+	
+	/* Encode continuation bytes */
+	unsigned int i;
+	for (i = cbytes; i > 0; i--) {
+		str[*offset + i] = 0x80 | (cc & LO_MASK_32(CONT_BITS));
+		cc = cc >> CONT_BITS;
+	}
+	
+	/* Encode first byte */
+	str[*offset] = (cc & LO_MASK_32(b0_bits)) | HI_MASK_8(8 - b0_bits - 1);
+	
+	/* Advance offset */
+	*offset += cbytes + 1;
+	
+	return EOK;
+}
+
+/** Get size of string.
+ *
+ * Get the number of bytes which are used by the string @a str (excluding the
+ * NULL-terminator).
+ *
+ * @param str String to consider.
+ *
+ * @return Number of bytes used by the string
+ *
+ */
+size_t str_size(const char *str)
+{
+	size_t size = 0;
+	
+	while (*str++ != 0)
+		size++;
+	
+	return size;
+}
+
+/** Get size of wide string.
+ *
+ * Get the number of bytes which are used by the wide string @a str (excluding the
+ * NULL-terminator).
+ *
+ * @param str Wide string to consider.
+ *
+ * @return Number of bytes used by the wide string
+ *
+ */
+size_t wstr_size(const wchar_t *str)
+{
+	return (wstr_length(str) * sizeof(wchar_t));
+}
+
+/** Get size of string with length limit.
+ *
+ * Get the number of bytes which are used by up to @a max_len first
+ * characters in the string @a str. If @a max_len is greater than
+ * the length of @a str, the entire string is measured (excluding the
+ * NULL-terminator).
+ *
+ * @param str     String to consider.
+ * @param max_len Maximum number of characters to measure.
+ *
+ * @return Number of bytes used by the characters.
+ *
+ */
+size_t str_lsize(const char *str, size_t max_len)
+{
+	size_t len = 0;
+	size_t offset = 0;
+	
+	while (len < max_len) {
+		if (str_decode(str, &offset, STR_NO_LIMIT) == 0)
+			break;
+		
+		len++;
+	}
+	
+	return offset;
+}
+
+/** Get size of wide string with length limit.
+ *
+ * Get the number of bytes which are used by up to @a max_len first
+ * wide characters in the wide string @a str. If @a max_len is greater than
+ * the length of @a str, the entire wide string is measured (excluding the
+ * NULL-terminator).
+ *
+ * @param str     Wide string to consider.
+ * @param max_len Maximum number of wide characters to measure.
+ *
+ * @return Number of bytes used by the wide characters.
+ *
+ */
+size_t wstr_lsize(const wchar_t *str, size_t max_len)
+{
+	return (wstr_nlength(str, max_len * sizeof(wchar_t)) * sizeof(wchar_t));
+}
+
+/** Get number of characters in a string.
+ *
+ * @param str NULL-terminated string.
+ *
+ * @return Number of characters in string.
+ *
+ */
+size_t str_length(const char *str)
+{
+	size_t len = 0;
+	size_t offset = 0;
+	
+	while (str_decode(str, &offset, STR_NO_LIMIT) != 0)
+		len++;
+	
+	return len;
+}
+
+/** Get number of characters in a wide string.
+ *
+ * @param str NULL-terminated wide string.
+ *
+ * @return Number of characters in @a str.
+ *
+ */
+size_t wstr_length(const wchar_t *wstr)
+{
+	size_t len = 0;
+	
+	while (*wstr++ != 0)
+		len++;
+	
+	return len;
+}
+
+/** Get number of characters in a string with size limit.
+ *
+ * @param str  NULL-terminated string.
+ * @param size Maximum number of bytes to consider.
+ *
+ * @return Number of characters in string.
+ *
+ */
+size_t str_nlength(const char *str, size_t size)
+{
+	size_t len = 0;
+	size_t offset = 0;
+	
+	while (str_decode(str, &offset, size) != 0)
+		len++;
+	
+	return len;
+}
+
+/** Get number of characters in a string with size limit.
+ *
+ * @param str  NULL-terminated string.
+ * @param size Maximum number of bytes to consider.
+ *
+ * @return Number of characters in string.
+ *
+ */
+size_t wstr_nlength(const wchar_t *str, size_t size)
+{
+	size_t len = 0;
+	size_t limit = ALIGN_DOWN(size, sizeof(wchar_t));
+	size_t offset = 0;
+	
+	while ((offset < limit) && (*str++ != 0)) {
+		len++;
+		offset += sizeof(wchar_t);
+	}
+	
+	return len;
+}
+
+/** Check whether character is plain ASCII.
+ *
+ * @return True if character is plain ASCII.
+ *
+ */
+bool ascii_check(wchar_t ch)
+{
+	if ((ch >= 0) && (ch <= 127))
+		return true;
+	
+	return false;
+}
+
+/** Check whether character is valid
+ *
+ * @return True if character is a valid Unicode code point.
+ *
+ */
+bool chr_check(wchar_t ch)
+{
+	if ((ch >= 0) && (ch <= 1114111))
+		return true;
+	
+	return false;
+}
+
+/** Compare two NULL terminated strings.
+ *
+ * Do a char-by-char comparison of two NULL-terminated strings.
+ * The strings are considered equal iff they consist of the same
+ * characters on the minimum of their lengths.
+ *
+ * @param s1 First string to compare.
+ * @param s2 Second string to compare.
+ *
+ * @return 0 if the strings are equal, -1 if first is smaller,
+ *         1 if second smaller.
+ *
+ */
+int str_cmp(const char *s1, const char *s2)
+{
+	wchar_t c1 = 0;
+	wchar_t c2 = 0;
+	
+	size_t off1 = 0;
+	size_t off2 = 0;
+
+	while (true) {
+		c1 = str_decode(s1, &off1, STR_NO_LIMIT);
+		c2 = str_decode(s2, &off2, STR_NO_LIMIT);
+
+		if (c1 < c2)
+			return -1;
+		
+		if (c1 > c2)
+			return 1;
+
+		if (c1 == 0 || c2 == 0)
+			break;		
+	}
+
+	return 0;
+}
+
+/** Compare two NULL terminated strings with length limit.
+ *
+ * Do a char-by-char comparison of two NULL-terminated strings.
+ * The strings are considered equal iff they consist of the same
+ * characters on the minimum of their lengths and the length limit.
+ *
+ * @param s1      First string to compare.
+ * @param s2      Second string to compare.
+ * @param max_len Maximum number of characters to consider.
+ *
+ * @return 0 if the strings are equal, -1 if first is smaller,
+ *         1 if second smaller.
+ *
+ */
+int str_lcmp(const char *s1, const char *s2, size_t max_len)
+{
+	wchar_t c1 = 0;
+	wchar_t c2 = 0;
+	
+	size_t off1 = 0;
+	size_t off2 = 0;
+	
+	size_t len = 0;
+
+	while (true) {
+		if (len >= max_len)
+			break;
+
+		c1 = str_decode(s1, &off1, STR_NO_LIMIT);
+		c2 = str_decode(s2, &off2, STR_NO_LIMIT);
+
+		if (c1 < c2)
+			return -1;
+
+		if (c1 > c2)
+			return 1;
+
+		if (c1 == 0 || c2 == 0)
+			break;
+
+		++len;	
+	}
+
+	return 0;
+
+}
+
+/** Copy string.
+ *
+ * Copy source string @a src to destination buffer @a dest.
+ * No more than @a size bytes are written. If the size of the output buffer
+ * is at least one byte, the output string will always be well-formed, i.e.
+ * null-terminated and containing only complete characters.
+ *
+ * @param dest   Destination buffer.
+ * @param count Size of the destination buffer (must be > 0).
+ * @param src   Source string.
+ */
+void str_cpy(char *dest, size_t size, const char *src)
+{
+	wchar_t ch;
+	size_t src_off;
+	size_t dest_off;
+
+	/* There must be space for a null terminator in the buffer. */
+	assert(size > 0);
+	
+	src_off = 0;
+	dest_off = 0;
+
+	while ((ch = str_decode(src, &src_off, STR_NO_LIMIT)) != 0) {
+		if (chr_encode(ch, dest, &dest_off, size - 1) != EOK)
+			break;
+	}
+
+	dest[dest_off] = '\0';
+}
+
+/** Copy size-limited substring.
+ *
+ * Copy prefix of string @a src of max. size @a size to destination buffer
+ * @a dest. No more than @a size bytes are written. The output string will
+ * always be well-formed, i.e. null-terminated and containing only complete
+ * characters.
+ *
+ * No more than @a n bytes are read from the input string, so it does not
+ * have to be null-terminated.
+ *
+ * @param dest   Destination buffer.
+ * @param count Size of the destination buffer (must be > 0).
+ * @param src   Source string.
+ * @param n	Maximum number of bytes to read from @a src.
+ */
+void str_ncpy(char *dest, size_t size, const char *src, size_t n)
+{
+	wchar_t ch;
+	size_t src_off;
+	size_t dest_off;
+
+	/* There must be space for a null terminator in the buffer. */
+	assert(size > 0);
+	
+	src_off = 0;
+	dest_off = 0;
+
+	while ((ch = str_decode(src, &src_off, n)) != 0) {
+		if (chr_encode(ch, dest, &dest_off, size - 1) != EOK)
+			break;
+	}
+
+	dest[dest_off] = '\0';
+}
+
+/** Append one string to another.
+ *
+ * Append source string @a src to string in destination buffer @a dest.
+ * Size of the destination buffer is @a dest. If the size of the output buffer
+ * is at least one byte, the output string will always be well-formed, i.e.
+ * null-terminated and containing only complete characters.
+ *
+ * @param dest   Destination buffer.
+ * @param count Size of the destination buffer.
+ * @param src   Source string.
+ */
+void str_append(char *dest, size_t size, const char *src)
+{
+	size_t dstr_size;
+
+	dstr_size = str_size(dest);
+	str_cpy(dest + dstr_size, size - dstr_size, src);
+}
+
+/** Convert wide string to string.
+ *
+ * Convert wide string @a src to string. The output is written to the buffer
+ * specified by @a dest and @a size. @a size must be non-zero and the string
+ * written will always be well-formed.
+ *
+ * @param dest	Destination buffer.
+ * @param size	Size of the destination buffer.
+ * @param src	Source wide string.
+ */
+void wstr_to_str(char *dest, size_t size, const wchar_t *src)
+{
+	wchar_t ch;
+	size_t src_idx;
+	size_t dest_off;
+
+	/* There must be space for a null terminator in the buffer. */
+	assert(size > 0);
+	
+	src_idx = 0;
+	dest_off = 0;
+
+	while ((ch = src[src_idx++]) != 0) {
+		if (chr_encode(ch, dest, &dest_off, size - 1) != EOK)
+			break;
+	}
+
+	dest[dest_off] = '\0';
+}
+
+/** Convert wide string to new string.
+ *
+ * Convert wide string @a src to string. Space for the new string is allocated
+ * on the heap.
+ *
+ * @param src	Source wide string.
+ * @return	New string.
+ */
+char *wstr_to_astr(const wchar_t *src)
+{
+	char dbuf[STR_BOUNDS(1)];
+	char *str;
+	wchar_t ch;
+
+	size_t src_idx;
+	size_t dest_off;
+	size_t dest_size;
+
+	/* Compute size of encoded string. */
+
+	src_idx = 0;
+	dest_size = 0;
+
+	while ((ch = src[src_idx++]) != 0) {
+		dest_off = 0;
+		if (chr_encode(ch, dbuf, &dest_off, STR_BOUNDS(1)) != EOK)
+			break;
+		dest_size += dest_off;
+	}
+
+	str = malloc(dest_size + 1);
+	if (str == NULL)
+		return NULL;
+
+	/* Encode string. */
+
+	src_idx = 0;
+	dest_off = 0;
+
+	while ((ch = src[src_idx++]) != 0) {
+		if (chr_encode(ch, str, &dest_off, dest_size) != EOK)
+			break;
+	}
+
+	str[dest_size] = '\0';
+	return str;
+}
+
+
+/** Convert string to wide string.
+ *
+ * Convert string @a src to wide string. The output is written to the
+ * buffer specified by @a dest and @a dlen. @a dlen must be non-zero
+ * and the wide string written will always be null-terminated.
+ *
+ * @param dest	Destination buffer.
+ * @param dlen	Length of destination buffer (number of wchars).
+ * @param src	Source string.
+ */
+void str_to_wstr(wchar_t *dest, size_t dlen, const char *src)
+{
+	size_t offset;
+	size_t di;
+	wchar_t c;
+
+	assert(dlen > 0);
+
+	offset = 0;
+	di = 0;
+
+	do {
+		if (di >= dlen - 1)
+			break;
+
+		c = str_decode(src, &offset, STR_NO_LIMIT);
+		dest[di++] = c;
+	} while (c != '\0');
+
+	dest[dlen - 1] = '\0';
+}
+
+/** Find first occurence of character in string.
+ *
+ * @param str String to search.
+ * @param ch  Character to look for.
+ *
+ * @return Pointer to character in @a str or NULL if not found.
+ */
+char *str_chr(const char *str, wchar_t ch)
+{
+	wchar_t acc;
+	size_t off = 0;
+	size_t last = 0;
+	
+	while ((acc = str_decode(str, &off, STR_NO_LIMIT)) != 0) {
+		if (acc == ch)
+			return (char *) (str + last);
+		last = off;
+	}
+	
+	return NULL;
+}
+
+/** Find last occurence of character in string.
+ *
+ * @param str String to search.
+ * @param ch  Character to look for.
+ *
+ * @return Pointer to character in @a str or NULL if not found.
+ */
+char *str_rchr(const char *str, wchar_t ch)
+{
+	wchar_t acc;
+	size_t off = 0;
+	size_t last = 0;
+	const char *res = NULL;
+	
+	while ((acc = str_decode(str, &off, STR_NO_LIMIT)) != 0) {
+		if (acc == ch)
+			res = (str + last);
+		last = off;
+	}
+	
+	return (char *) res;
+}
+
+/** Insert a wide character into a wide string.
+ *
+ * Insert a wide character into a wide string at position
+ * @a pos. The characters after the position are shifted.
+ *
+ * @param str     String to insert to.
+ * @param ch      Character to insert to.
+ * @param pos     Character index where to insert.
+ @ @param max_pos Characters in the buffer.
+ *
+ * @return True if the insertion was sucessful, false if the position
+ *         is out of bounds.
+ *
+ */
+bool wstr_linsert(wchar_t *str, wchar_t ch, size_t pos, size_t max_pos)
+{
+	size_t len = wstr_length(str);
+	
+	if ((pos > len) || (pos + 1 > max_pos))
+		return false;
+	
+	size_t i;
+	for (i = len; i + 1 > pos; i--)
+		str[i + 1] = str[i];
+	
+	str[pos] = ch;
+	
+	return true;
+}
+
+/** Remove a wide character from a wide string.
+ *
+ * Remove a wide character from a wide string at position
+ * @a pos. The characters after the position are shifted.
+ *
+ * @param str String to remove from.
+ * @param pos Character index to remove.
+ *
+ * @return True if the removal was sucessful, false if the position
+ *         is out of bounds.
+ *
+ */
+bool wstr_remove(wchar_t *str, size_t pos)
+{
+	size_t len = wstr_length(str);
+	
+	if (pos >= len)
+		return false;
+	
+	size_t i;
+	for (i = pos + 1; i <= len; i++)
+		str[i - 1] = str[i];
+	
+	return true;
+}
+
+int stricmp(const char *a, const char *b)
+{
+	int c = 0;
+	
+	while (a[c] && b[c] && (!(tolower(a[c]) - tolower(b[c]))))
+		c++;
+	
+	return (tolower(a[c]) - tolower(b[c]));
+}
+
+/** Convert string to a number. 
+ * Core of strtol and strtoul functions.
+ *
+ * @param nptr		Pointer to string.
+ * @param endptr	If not NULL, function stores here pointer to the first
+ * 			invalid character.
+ * @param base		Zero or number between 2 and 36 inclusive.
+ * @param sgn		It's set to 1 if minus found.
+ * @return		Result of conversion.
+ */
+static unsigned long
+_strtoul(const char *nptr, char **endptr, int base, char *sgn)
+{
+	unsigned char c;
+	unsigned long result = 0;
+	unsigned long a, b;
+	const char *str = nptr;
+	const char *tmpptr;
+	
+	while (isspace(*str))
+		str++;
+	
+	if (*str == '-') {
+		*sgn = 1;
+		++str;
+	} else if (*str == '+')
+		++str;
+	
+	if (base) {
+		if ((base == 1) || (base > 36)) {
+			/* FIXME: set errno to EINVAL */
+			return 0;
+		}
+		if ((base == 16) && (*str == '0') && ((str[1] == 'x') ||
+		    (str[1] == 'X'))) {
+			str += 2;
+		}
+	} else {
+		base = 10;
+		
+		if (*str == '0') {
+			base = 8;
+			if ((str[1] == 'X') || (str[1] == 'x'))  {
+				base = 16;
+				str += 2;
+			}
+		} 
+	}
+	
+	tmpptr = str;
+
+	while (*str) {
+		c = *str;
+		c = (c >= 'a' ? c - 'a' + 10 : (c >= 'A' ? c - 'A' + 10 :
+		    (c <= '9' ? c - '0' : 0xff)));
+		if (c > base) {
+			break;
+		}
+		
+		a = (result & 0xff) * base + c;
+		b = (result >> 8) * base + (a >> 8);
+		
+		if (b > (ULONG_MAX >> 8)) {
+			/* overflow */
+			/* FIXME: errno = ERANGE*/
+			return ULONG_MAX;
+		}
+	
+		result = (b << 8) + (a & 0xff);
+		++str;
+	}
+	
+	if (str == tmpptr) {
+		/*
+		 * No number was found => first invalid character is the first
+		 * character of the string.
+		 */
+		/* FIXME: set errno to EINVAL */
+		str = nptr;
+		result = 0;
+	}
+	
+	if (endptr)
+		*endptr = (char *) str;
+
+	if (nptr == str) { 
+		/*FIXME: errno = EINVAL*/
+		return 0;
+	}
+
+	return result;
+}
+
+/** Convert initial part of string to long int according to given base.
+ * The number may begin with an arbitrary number of whitespaces followed by
+ * optional sign (`+' or `-'). If the base is 0 or 16, the prefix `0x' may be
+ * inserted and the number will be taken as hexadecimal one. If the base is 0
+ * and the number begin with a zero, number will be taken as octal one (as with
+ * base 8). Otherwise the base 0 is taken as decimal.
+ *
+ * @param nptr		Pointer to string.
+ * @param endptr	If not NULL, function stores here pointer to the first
+ * 			invalid character.
+ * @param base		Zero or number between 2 and 36 inclusive.
+ * @return		Result of conversion.
+ */
+long int strtol(const char *nptr, char **endptr, int base)
+{
+	char sgn = 0;
+	unsigned long number = 0;
+	
+	number = _strtoul(nptr, endptr, base, &sgn);
+
+	if (number > LONG_MAX) {
+		if ((sgn) && (number == (unsigned long) (LONG_MAX) + 1)) {
+			/* FIXME: set 0 to errno */
+			return number;		
+		}
+		/* FIXME: set ERANGE to errno */
+		return (sgn ? LONG_MIN : LONG_MAX);	
+	}
+	
+	return (sgn ? -number : number);
+}
+
+char *str_dup(const char *src)
+{
+	size_t size = str_size(src);
+	void *dest = malloc(size + 1);
+	
+	if (dest == NULL)
+		return (char *) NULL;
+	
+	return (char *) memcpy(dest, src, size + 1);
+}
+
+char *str_ndup(const char *src, size_t max_size)
+{
+	size_t size = str_size(src);
+	if (size > max_size)
+		size = max_size;
+	
+	char *dest = (char *) malloc(size + 1);
+	
+	if (dest == NULL)
+		return (char *) NULL;
+	
+	memcpy(dest, src, size);
+	dest[size] = 0;
+	return dest;
+}
+
+
+/** Convert initial part of string to unsigned long according to given base.
+ * The number may begin with an arbitrary number of whitespaces followed by
+ * optional sign (`+' or `-'). If the base is 0 or 16, the prefix `0x' may be
+ * inserted and the number will be taken as hexadecimal one. If the base is 0
+ * and the number begin with a zero, number will be taken as octal one (as with
+ * base 8). Otherwise the base 0 is taken as decimal.
+ *
+ * @param nptr		Pointer to string.
+ * @param endptr	If not NULL, function stores here pointer to the first
+ * 			invalid character
+ * @param base		Zero or number between 2 and 36 inclusive.
+ * @return		Result of conversion.
+ */
+unsigned long strtoul(const char *nptr, char **endptr, int base)
+{
+	char sgn = 0;
+	unsigned long number = 0;
+	
+	number = _strtoul(nptr, endptr, base, &sgn);
+
+	return (sgn ? -number : number);
+}
+
+char *strtok(char *s, const char *delim)
+{
+	static char *next;
+
+	return strtok_r(s, delim, &next);
+}
+
+char *strtok_r(char *s, const char *delim, char **next)
+{
+	char *start, *end;
+
+	if (s == NULL)
+		s = *next;
+
+	/* Skip over leading delimiters. */
+	while (*s && (str_chr(delim, *s) != NULL)) ++s;
+	start = s;
+
+	/* Skip over token characters. */
+	while (*s && (str_chr(delim, *s) == NULL)) ++s;
+	end = s;
+	*next = (*s ? s + 1 : s);
+
+	if (start == end) {
+		return NULL;	/* No more tokens. */
+	}
+
+	/* Overwrite delimiter with NULL terminator. */
+	*end = '\0';
+	return start;
+}
+
+/** @}
+ */
Index: uspace/lib/c/generic/sysinfo.c
===================================================================
--- uspace/lib/c/generic/sysinfo.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
+++ uspace/lib/c/generic/sysinfo.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2006 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.
+ */
+
+/** @addtogroup libc
+ * @{
+ */
+/** @file
+ */ 
+
+#include <libc.h>
+#include <sysinfo.h>
+#include <str.h>
+
+sysarg_t sysinfo_value(const char *name)
+{
+	return __SYSCALL2(SYS_SYSINFO_VALUE, (sysarg_t) name,
+	    (sysarg_t) str_size(name));
+}
+
+/** @}
+ */
Index: uspace/lib/c/generic/task.c
===================================================================
--- uspace/lib/c/generic/task.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
+++ uspace/lib/c/generic/task.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 2006 Jakub Jermar
+ * Copyright (c) 2008 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 libc
+ * @{
+ */
+/** @file
+ */
+
+#include <task.h>
+#include <libc.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <loader/loader.h>
+#include <str.h>
+#include <ipc/ns.h>
+#include <macros.h>
+#include <async.h>
+
+task_id_t task_get_id(void)
+{
+	task_id_t task_id;
+	(void) __SYSCALL1(SYS_TASK_GET_ID, (sysarg_t) &task_id);
+	
+	return task_id;
+}
+
+/** Set the task name.
+ *
+ * @param name The new name, typically the command used to execute the
+ *             program.
+ *
+ * @return Zero on success or negative error code.
+ *
+ */
+int task_set_name(const char *name)
+{
+	return __SYSCALL2(SYS_TASK_SET_NAME, (sysarg_t) name, str_size(name));
+}
+
+/** Create a new task by running an executable from the filesystem.
+ *
+ * This is really just a convenience wrapper over the more complicated
+ * loader API.
+ *
+ * @param path pathname of the binary to execute
+ * @param argv command-line arguments
+ *
+ * @return ID of the newly created task or zero on error.
+ *
+ */
+task_id_t task_spawn(const char *path, const char *const args[])
+{
+	/* Connect to a program loader. */
+	loader_t *ldr = loader_connect();
+	if (ldr == NULL)
+		return 0;
+	
+	/* Get task ID. */
+	task_id_t task_id;
+	int rc = loader_get_task_id(ldr, &task_id);
+	if (rc != EOK)
+		goto error;
+	
+	/* Send spawner's current working directory. */
+	rc = loader_set_cwd(ldr);
+	if (rc != EOK)
+		goto error;
+	
+	/* Send program pathname. */
+	rc = loader_set_pathname(ldr, path);
+	if (rc != EOK)
+		goto error;
+	
+	/* Send arguments. */
+	rc = loader_set_args(ldr, args);
+	if (rc != EOK)
+		goto error;
+	
+	/* Send default files */
+	fdi_node_t *files[4];
+	fdi_node_t stdin_node;
+	fdi_node_t stdout_node;
+	fdi_node_t stderr_node;
+	
+	if ((stdin != NULL) && (fnode(stdin, &stdin_node) == EOK))
+		files[0] = &stdin_node;
+	else
+		files[0] = NULL;
+	
+	if ((stdout != NULL) && (fnode(stdout, &stdout_node) == EOK))
+		files[1] = &stdout_node;
+	else
+		files[1] = NULL;
+	
+	if ((stderr != NULL) && (fnode(stderr, &stderr_node) == EOK))
+		files[2] = &stderr_node;
+	else
+		files[2] = NULL;
+	
+	files[3] = NULL;
+	
+	rc = loader_set_files(ldr, files);
+	if (rc != EOK)
+		goto error;
+	
+	/* Load the program. */
+	rc = loader_load_program(ldr);
+	if (rc != EOK)
+		goto error;
+	
+	/* Run it. */
+	rc = loader_run(ldr);
+	if (rc != EOK)
+		goto error;
+	
+	/* Success */
+	free(ldr);
+	return task_id;
+	
+error:
+	/* Error exit */
+	loader_abort(ldr);
+	free(ldr);
+	
+	return 0;
+}
+
+int task_wait(task_id_t id, task_exit_t *texit, int *retval)
+{
+	ipcarg_t te, rv;
+	int rc;
+
+	rc = (int) async_req_2_2(PHONE_NS, NS_TASK_WAIT, LOWER32(id),
+	    UPPER32(id), &te, &rv);
+	*texit = te;
+	*retval = rv;
+
+	return rc;
+}
+
+int task_retval(int val)
+{
+	return (int) async_req_1_0(PHONE_NS, NS_RETVAL, val);
+}
+
+/** @}
+ */
Index: uspace/lib/c/generic/thread.c
===================================================================
--- uspace/lib/c/generic/thread.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
+++ uspace/lib/c/generic/thread.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 2006 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.
+ */
+
+/** @addtogroup libc
+ * @{
+ */
+/** @file
+ */ 
+
+#include <thread.h>
+#include <libc.h>
+#include <stdlib.h>
+#include <libarch/faddr.h>
+#include <kernel/proc/uarg.h>
+#include <fibril.h>
+#include <str.h>
+#include <async.h>
+
+#ifndef THREAD_INITIAL_STACK_PAGES_NO
+#define THREAD_INITIAL_STACK_PAGES_NO 1
+#endif
+
+/** Main thread function.
+ *
+ * This function is called from __thread_entry() and is used
+ * to call the thread's implementing function and perform cleanup
+ * and exit when thread returns back. Do not call this function
+ * directly.
+ *
+ * @param uarg Pointer to userspace argument structure.
+ */
+void __thread_main(uspace_arg_t *uarg)
+{
+	fibril_t *f;
+
+	f = fibril_setup();
+	__tcb_set(f->tcb);
+
+	uarg->uspace_thread_function(uarg->uspace_thread_arg);
+	/* XXX: we cannot free the userspace stack while running on it */
+//	free(uarg->uspace_stack);
+//	free(uarg);
+
+	/* If there is a manager, destroy it */
+	async_destroy_manager();
+	fibril_teardown(f);
+
+	thread_exit(0);
+}
+
+/** Create userspace thread.
+ *
+ * This function creates new userspace thread and allocates userspace
+ * stack and userspace argument structure for it.
+ *
+ * @param function Function implementing the thread.
+ * @param arg Argument to be passed to thread.
+ * @param name Symbolic name of the thread.
+ * @param tid Thread ID of the newly created thread.
+ *
+ * @return Zero on success or a code from @ref errno.h on failure.
+ */
+int thread_create(void (* function)(void *), void *arg, const char *name,
+    thread_id_t *tid)
+{
+	char *stack;
+	uspace_arg_t *uarg;
+	int rc;
+
+	stack = (char *) malloc(getpagesize() * THREAD_INITIAL_STACK_PAGES_NO);
+	if (!stack)
+		return -1;
+		
+	uarg = (uspace_arg_t *) malloc(sizeof(uspace_arg_t));
+	if (!uarg) {
+		free(stack);
+		return -1;
+	}
+	
+	uarg->uspace_entry = (void *) FADDR(__thread_entry);
+	uarg->uspace_stack = (void *) stack;
+	uarg->uspace_thread_function = function;
+	uarg->uspace_thread_arg = arg;
+	uarg->uspace_uarg = uarg;
+	
+	rc = __SYSCALL4(SYS_THREAD_CREATE, (sysarg_t) uarg, (sysarg_t) name,
+	    (sysarg_t) str_size(name), (sysarg_t) tid);
+	
+	if (rc) {
+		/*
+		 * Failed to create a new thread.
+		 * Free up the allocated structures.
+		 */
+		free(uarg);
+		free(stack);
+	}
+
+	return rc;
+}
+
+/** Terminate current thread.
+ *
+ * @param status Exit status. Currently not used.
+ */
+void thread_exit(int status)
+{
+	__SYSCALL1(SYS_THREAD_EXIT, (sysarg_t) status);
+	for (;;)
+		;
+}
+
+/** Detach thread.
+ *
+ * Currently not implemented.
+ *
+ * @param thread TID.
+ */
+void thread_detach(thread_id_t thread)
+{
+}
+
+/** Join thread.
+ *
+ * Currently not implemented.
+ *
+ * @param thread TID.
+ *
+ * @return Thread exit status.
+ */
+int thread_join(thread_id_t thread)
+{
+	return 0;
+}
+
+/** Get current thread ID.
+ *
+ * @return Current thread ID.
+ */
+thread_id_t thread_get_id(void)
+{
+	thread_id_t thread_id;
+
+	(void) __SYSCALL1(SYS_THREAD_GET_ID, (sysarg_t) &thread_id);
+
+	return thread_id;
+}
+
+/** @}
+ */
Index: uspace/lib/c/generic/time.c
===================================================================
--- uspace/lib/c/generic/time.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
+++ uspace/lib/c/generic/time.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
@@ -0,0 +1,212 @@
+/*
+ * Copyright (c) 2006 Ondrej Palkovsky
+ * 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 libc
+ * @{
+ */
+/** @file
+ */
+
+#include <sys/time.h>
+#include <unistd.h>
+#include <ipc/ipc.h>
+#include <stdio.h>
+#include <arch/barrier.h>
+#include <unistd.h>
+#include <atomic.h>
+#include <sysinfo.h>
+#include <ipc/services.h>
+#include <libc.h>
+
+#include <sysinfo.h>
+#include <as.h>
+#include <ddi.h>
+
+#include <time.h>
+
+/* Pointers to public variables with time */
+struct {
+	volatile sysarg_t seconds1;
+	volatile sysarg_t useconds;
+	volatile sysarg_t seconds2;
+} *ktime = NULL;
+
+/** Add microseconds to given timeval.
+ *
+ * @param tv		Destination timeval.
+ * @param usecs		Number of microseconds to add.
+ */
+void tv_add(struct timeval *tv, suseconds_t usecs)
+{
+	tv->tv_sec += usecs / 1000000;
+	tv->tv_usec += usecs % 1000000;
+	if (tv->tv_usec > 1000000) {
+		tv->tv_sec++;
+		tv->tv_usec -= 1000000;
+	}
+}
+
+/** Subtract two timevals.
+ *
+ * @param tv1		First timeval.
+ * @param tv2		Second timeval.
+ *
+ * @return		Return difference between tv1 and tv2 (tv1 - tv2) in
+ * 			microseconds.
+ */
+suseconds_t tv_sub(struct timeval *tv1, struct timeval *tv2)
+{
+	suseconds_t result;
+
+	result = tv1->tv_usec - tv2->tv_usec;
+	result += (tv1->tv_sec - tv2->tv_sec) * 1000000;
+
+	return result;
+}
+
+/** Decide if one timeval is greater than the other.
+ *
+ * @param t1		First timeval.
+ * @param t2		Second timeval.
+ *
+ * @return		Return true tv1 is greater than tv2. Otherwise return
+ * 			false.
+ */
+int tv_gt(struct timeval *tv1, struct timeval *tv2)
+{
+	if (tv1->tv_sec > tv2->tv_sec)
+		return 1;
+	if (tv1->tv_sec == tv2->tv_sec && tv1->tv_usec > tv2->tv_usec)
+		return 1;
+	return 0;
+}
+
+/** Decide if one timeval is greater than or equal to the other.
+ *
+ * @param tv1		First timeval.
+ * @param tv2		Second timeval.
+ *
+ * @return		Return true if tv1 is greater than or equal to tv2.
+ * 			Otherwise return false.
+ */
+int tv_gteq(struct timeval *tv1, struct timeval *tv2)
+{
+	if (tv1->tv_sec > tv2->tv_sec)
+		return 1;
+	if (tv1->tv_sec == tv2->tv_sec && tv1->tv_usec >= tv2->tv_usec)
+		return 1;
+	return 0;
+}
+
+
+/** POSIX gettimeofday
+ *
+ * The time variables are memory mapped(RO) from kernel, which updates
+ * them periodically. As it is impossible to read 2 values atomically, we
+ * use a trick: First read a seconds, then read microseconds, then
+ * read seconds again. If a second elapsed in the meantime, set it to zero. 
+ * This provides assurance, that at least the
+ * sequence of subsequent gettimeofday calls is ordered.
+ */
+int gettimeofday(struct timeval *tv, struct timezone *tz)
+{
+	void *mapping;
+	sysarg_t s1, s2;
+	int rights;
+	int res;
+
+	if (!ktime) {
+		mapping = as_get_mappable_page(PAGE_SIZE);
+		/* Get the mapping of kernel clock */
+		res = ipc_share_in_start_1_1(PHONE_NS, mapping, PAGE_SIZE,
+		    SERVICE_MEM_REALTIME, &rights);
+		if (res) {
+			printf("Failed to initialize timeofday memarea\n");
+			_exit(1);
+		}
+		if (!(rights & AS_AREA_READ)) {
+			printf("Received bad rights on time area: %X\n",
+			    rights);
+			as_area_destroy(mapping);
+			_exit(1);
+		}
+		ktime = mapping;
+	}
+	if (tz) {
+		tz->tz_minuteswest = 0;
+		tz->tz_dsttime = DST_NONE;
+	}
+
+	s2 = ktime->seconds2;
+	read_barrier();
+	tv->tv_usec = ktime->useconds;
+	read_barrier();
+	s1 = ktime->seconds1;
+	if (s1 != s2) {
+		tv->tv_usec = 0;
+		tv->tv_sec = s1 > s2 ? s1 : s2;
+	} else
+		tv->tv_sec = s1;
+
+	return 0;
+}
+
+time_t time(time_t *tloc)
+{
+	struct timeval tv;
+
+	if (gettimeofday(&tv, NULL))
+		return (time_t) -1;
+	if (tloc)
+		*tloc = tv.tv_sec;
+	return tv.tv_sec;
+}
+
+/** Wait unconditionally for specified number of microseconds */
+int usleep(useconds_t usec)
+{
+	(void) __SYSCALL1(SYS_THREAD_USLEEP, usec);
+	return 0;
+}
+
+/** Wait unconditionally for specified number of seconds */
+unsigned int sleep(unsigned int sec)
+{
+	/* Sleep in 1000 second steps to support
+	   full argument range */
+	while (sec > 0) {
+		unsigned int period = (sec > 1000) ? 1000 : sec;
+	
+		usleep(period * 1000000);
+		sec -= period;
+	}
+	return 0;
+}
+
+/** @}
+ */
Index: uspace/lib/c/generic/tls.c
===================================================================
--- uspace/lib/c/generic/tls.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
+++ uspace/lib/c/generic/tls.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2006 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.
+ */
+
+/** @addtogroup libc
+ * @{
+ */
+/** @file
+ *
+ * Support for thread-local storage, as described in:
+ * 	Drepper U.: ELF Handling For Thread-Local Storage, 2005
+ *
+ * Only static model is supported.
+ */ 
+
+#include <tls.h>
+#include <malloc.h>
+#include <str.h>
+#include <align.h>
+
+/** Create TLS (Thread Local Storage) data structures.
+ *
+ * The code requires, that sections .tdata and .tbss are adjacent. It may be
+ * changed in the future.
+ *
+ * @return Pointer to TCB.
+ */
+tcb_t *__make_tls(void)
+{
+	void *data;
+	tcb_t *tcb;
+	size_t tls_size = &_tbss_end - &_tdata_start;
+	
+	tcb = __alloc_tls(&data, tls_size);
+	
+	/*
+	 * Copy thread local data from the initialization image.
+	 */
+	memcpy(data, &_tdata_start, &_tdata_end - &_tdata_start);
+	/*
+	 * Zero out the thread local uninitialized data.
+	 */
+	memset(data + (&_tbss_start - &_tdata_start), 0,
+	    &_tbss_end - &_tbss_start);
+
+	return tcb;
+}
+
+void __free_tls(tcb_t *tcb)
+{
+	size_t tls_size = &_tbss_end - &_tdata_start;
+	__free_tls_arch(tcb, tls_size);
+}
+
+#ifdef CONFIG_TLS_VARIANT_1
+/** Allocate TLS variant 1 data structures.
+ *
+ * @param data 		Start of TLS section. This is an output argument.
+ * @param size		Size of tdata + tbss section.
+ * @return 		Pointer to tcb_t structure.
+ */
+tcb_t *tls_alloc_variant_1(void **data, size_t size)
+{
+	tcb_t *result;
+
+	result = malloc(sizeof(tcb_t) + size);
+	*data = ((void *)result) + sizeof(tcb_t);
+	return result;
+}
+
+/** Free TLS variant I data structures.
+ *
+ * @param tcb		Pointer to TCB structure.
+ * @param size		This argument is ignored.
+ */
+void tls_free_variant_1(tcb_t *tcb, size_t size)
+{
+	free(tcb);
+}
+#endif
+
+#ifdef CONFIG_TLS_VARIANT_2
+/** Allocate TLS variant II data structures.
+ *
+ * @param data		Pointer to pointer to thread local data. This is
+ * 			actually an output argument.
+ * @param size		Size of thread local data.
+ * @return		Pointer to TCB structure.
+ */
+tcb_t * tls_alloc_variant_2(void **data, size_t size)
+{
+	tcb_t *tcb;
+	
+	size = ALIGN_UP(size, &_tls_alignment);
+	*data = memalign((uintptr_t) &_tls_alignment, sizeof(tcb_t) + size);
+
+	tcb = (tcb_t *) (*data + size);
+	tcb->self = tcb;
+
+	return tcb;
+}
+
+/** Free TLS variant II data structures.
+ *
+ * @param tcb		Pointer to TCB structure.
+ * @param size		Size of thread local data.
+ */
+void tls_free_variant_2(tcb_t *tcb, size_t size)
+{
+	size = ALIGN_UP(size, &_tls_alignment);
+	void *start = ((void *) tcb) - size;
+	free(start);
+}
+#endif
+
+/** @}
+ */
Index: uspace/lib/c/generic/udebug.c
===================================================================
--- uspace/lib/c/generic/udebug.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
+++ uspace/lib/c/generic/udebug.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2008 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 libc
+ * @{
+ */
+/** @file
+ */ 
+
+#include <udebug.h>
+#include <sys/types.h>
+#include <ipc/ipc.h>
+#include <async.h>
+
+int udebug_begin(int phoneid)
+{
+	return async_req_1_0(phoneid, IPC_M_DEBUG_ALL, UDEBUG_M_BEGIN);
+}
+
+int udebug_end(int phoneid)
+{
+	return async_req_1_0(phoneid, IPC_M_DEBUG_ALL, UDEBUG_M_END);
+}
+
+int udebug_set_evmask(int phoneid, udebug_evmask_t mask)
+{
+	return async_req_2_0(phoneid, IPC_M_DEBUG_ALL, UDEBUG_M_SET_EVMASK,
+		mask);
+}
+
+int udebug_thread_read(int phoneid, void *buffer, size_t n,
+	size_t *copied, size_t *needed)
+{
+	ipcarg_t a_copied, a_needed;
+	int rc;
+
+	rc = async_req_3_3(phoneid, IPC_M_DEBUG_ALL, UDEBUG_M_THREAD_READ,
+		(sysarg_t)buffer, n, NULL, &a_copied, &a_needed);
+
+	*copied = (size_t)a_copied;
+	*needed = (size_t)a_needed;
+
+	return rc;
+}
+
+int udebug_name_read(int phoneid, void *buffer, size_t n,
+	size_t *copied, size_t *needed)
+{
+	ipcarg_t a_copied, a_needed;
+	int rc;
+
+	rc = async_req_3_3(phoneid, IPC_M_DEBUG_ALL, UDEBUG_M_NAME_READ,
+		(sysarg_t)buffer, n, NULL, &a_copied, &a_needed);
+
+	*copied = (size_t)a_copied;
+	*needed = (size_t)a_needed;
+
+	return rc;
+}
+
+int udebug_areas_read(int phoneid, void *buffer, size_t n,
+	size_t *copied, size_t *needed)
+{
+	ipcarg_t a_copied, a_needed;
+	int rc;
+
+	rc = async_req_3_3(phoneid, IPC_M_DEBUG_ALL, UDEBUG_M_AREAS_READ,
+		(sysarg_t)buffer, n, NULL, &a_copied, &a_needed);
+
+	*copied = (size_t)a_copied;
+	*needed = (size_t)a_needed;
+
+	return rc;
+}
+
+int udebug_mem_read(int phoneid, void *buffer, uintptr_t addr, size_t n)
+{
+	return async_req_4_0(phoneid, IPC_M_DEBUG_ALL, UDEBUG_M_MEM_READ,
+	    (sysarg_t)buffer, addr, n);
+}
+
+int udebug_args_read(int phoneid, thash_t tid, sysarg_t *buffer)
+{
+	return async_req_3_0(phoneid, IPC_M_DEBUG_ALL, UDEBUG_M_ARGS_READ,
+	    tid, (sysarg_t)buffer);
+}
+
+int udebug_regs_read(int phoneid, thash_t tid, void *buffer)
+{
+	return async_req_3_0(phoneid, IPC_M_DEBUG_ALL, UDEBUG_M_REGS_READ,
+	    tid, (sysarg_t)buffer);
+}
+
+int udebug_go(int phoneid, thash_t tid, udebug_event_t *ev_type,
+    sysarg_t *val0, sysarg_t *val1)
+{
+	ipcarg_t a_ev_type;
+	int rc;
+
+	rc =  async_req_2_3(phoneid, IPC_M_DEBUG_ALL, UDEBUG_M_GO,
+	    tid, &a_ev_type, val0, val1);
+
+	*ev_type = a_ev_type;
+	return rc;
+}
+
+int udebug_stop(int phoneid, thash_t tid)
+{
+	return async_req_2_0(phoneid, IPC_M_DEBUG_ALL, UDEBUG_M_STOP,
+	    tid);
+}
+
+/** @}
+ */
Index: uspace/lib/c/generic/vfs/canonify.c
===================================================================
--- uspace/lib/c/generic/vfs/canonify.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
+++ uspace/lib/c/generic/vfs/canonify.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
@@ -0,0 +1,347 @@
+/*
+ * Copyright (c) 2008 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.
+ */
+
+/** @addtogroup libc 
+ * @{
+ */ 
+
+/**
+ * @file
+ * @brief
+ */
+
+#include <stdlib.h>
+#include <vfs/canonify.h>
+
+/** Token types used for tokenization of path. */
+typedef enum {
+	TK_INVALID,
+	TK_SLASH,
+	TK_DOT,
+	TK_DOTDOT,
+	TK_COMP,
+	TK_NUL
+} tokval_t;
+
+typedef struct {
+	tokval_t kind;
+	char *start;
+	char *stop;
+} token_t;
+
+/** Fake up the TK_SLASH token. */
+static token_t slash_token(char *start)
+{
+	token_t ret;
+	ret.kind = TK_SLASH;
+	ret.start = start;
+	ret.stop = start;
+	return ret;
+}
+
+/** Given a token, return the next token. */
+static token_t next_token(token_t *cur)
+{
+	token_t ret;
+
+	if (cur->stop[1] == '\0') {
+		ret.kind = TK_NUL;
+		ret.start = cur->stop + 1;
+		ret.stop = ret.start;
+		return ret;
+	}
+	if (cur->stop[1] == '/') {
+		ret.kind = TK_SLASH;
+		ret.start = cur->stop + 1;
+		ret.stop = ret.start;
+		return ret;
+	}
+	if (cur->stop[1] == '.' && (!cur->stop[2] || cur->stop[2] == '/')) {
+		ret.kind = TK_DOT;
+		ret.start = cur->stop + 1;
+		ret.stop = ret.start;
+		return ret;
+	}
+	if (cur->stop[1] == '.' && cur->stop[2] == '.' &&
+	    (!cur->stop[3] || cur->stop[3] == '/')) {
+		ret.kind = TK_DOTDOT;
+		ret.start = cur->stop + 1;
+		ret.stop = cur->stop + 2;
+		return ret;
+	}
+	unsigned i;
+	for (i = 1; cur->stop[i] && cur->stop[i] != '/'; i++)
+		;
+	ret.kind = TK_COMP;
+	ret.start = &cur->stop[1];
+	ret.stop = &cur->stop[i - 1];
+	return ret;
+}
+
+/** States used by canonify(). */
+typedef enum {
+	S_INI,
+	S_A,
+	S_B,
+	S_C,
+	S_ACCEPT,
+	S_RESTART,
+	S_REJECT
+} state_t;
+
+typedef struct {
+	state_t s;
+	void (* f)(token_t *, token_t *, token_t *);
+} change_state_t;
+
+/*
+ * Actions that can be performed when transitioning from one
+ * state of canonify() to another.
+ */
+static void set_first_slash(token_t *t, token_t *tfsl, token_t *tlcomp)
+{
+	*tfsl = *t;
+	*tlcomp = *t;
+}
+static void save_component(token_t *t, token_t *tfsl, token_t *tlcomp)
+{
+	*tlcomp = *t;
+}
+static void terminate_slash(token_t *t, token_t *tfsl, token_t *tlcomp)
+{
+	if (tfsl->stop[1])	/* avoid writing to a well-formatted path */
+		tfsl->stop[1] = '\0';
+}
+static void remove_trailing_slash(token_t *t, token_t *tfsl, token_t *tlcomp)
+{
+	t->start[-1] = '\0';
+}
+/** Eat the extra '/'.
+ *
+ * @param t		The current TK_SLASH token.
+ */
+static void shift_slash(token_t *t, token_t *tfsl, token_t *tlcomp)
+{
+	char *p = t->start;
+	char *q = t->stop + 1;
+	while ((*p++ = *q++))
+		;
+}
+/** Eat the extra '.'.
+ *
+ * @param t		The current TK_DOT token.
+ */
+static void shift_dot(token_t *t, token_t *tfsl, token_t *tlcomp)
+{
+	char *p = t->start;
+	char *q = t->stop + 1;
+	while ((*p++ = *q++))
+		;
+}
+/** Collapse the TK_COMP TK_SLASH TK_DOTDOT pattern.
+ *
+ * @param t		The current TK_DOTDOT token.
+ * @param tlcomp	The last TK_COMP token.
+ */
+static void shift_dotdot(token_t *t, token_t *tfsl, token_t *tlcomp)
+{
+	char *p = tlcomp->start;
+	char *q = t->stop + 1;
+	while ((*p++ = *q++))
+		;
+}
+
+/** Transition function for canonify(). */
+static change_state_t trans[4][6] = {
+	[S_INI] = {
+		[TK_SLASH] = {
+			.s = S_A,
+			.f = set_first_slash,
+		},
+		[TK_DOT] = {
+			.s = S_REJECT,
+			.f = NULL,
+		},
+		[TK_DOTDOT] = {
+			.s = S_REJECT,
+			.f = NULL,
+		},
+		[TK_COMP] = {
+			.s = S_REJECT,
+			.f = NULL,
+		},
+		[TK_NUL] = {
+			.s = S_REJECT,
+			.f = NULL,
+		},
+		[TK_INVALID] = {
+			.s = S_REJECT,
+			.f = NULL,
+		},
+	},
+	[S_A] = {
+		[TK_SLASH] = {
+			.s = S_A,
+			.f = set_first_slash,
+		},
+		[TK_DOT] = {
+			.s = S_A,
+			.f = NULL,
+		},
+		[TK_DOTDOT] = {
+			.s = S_A,
+			.f = NULL,
+		},
+		[TK_COMP] = {
+			.s = S_B,
+			.f = save_component,
+		},
+		[TK_NUL] = {
+			.s = S_ACCEPT,
+			.f = terminate_slash,
+		},
+		[TK_INVALID] = {
+			.s = S_REJECT,
+			.f = NULL,
+		},
+	},
+	[S_B] = {
+		[TK_SLASH] = {
+			.s = S_C,
+			.f = NULL,
+		},
+		[TK_DOT] = {
+			.s = S_REJECT,
+			.f = NULL,
+		},
+		[TK_DOTDOT] = {
+			.s = S_REJECT,
+			.f = NULL,
+		},
+		[TK_COMP] = {
+			.s = S_REJECT,
+			.f = NULL,
+		},
+		[TK_NUL] = {
+			.s = S_ACCEPT,
+			.f = NULL,
+		},
+		[TK_INVALID] = {
+			.s = S_REJECT,
+			.f = NULL,
+		},
+	},
+	[S_C] = {
+		[TK_SLASH] = {
+			.s = S_RESTART,
+			.f = shift_slash,
+		},
+		[TK_DOT] = {
+			.s = S_RESTART,
+			.f = shift_dot,
+		},
+		[TK_DOTDOT] = {
+			.s = S_RESTART,
+			.f = shift_dotdot,
+		},
+		[TK_COMP] = {
+			.s = S_B,
+			.f = save_component,
+		},
+		[TK_NUL] = {
+			.s = S_ACCEPT,
+			.f = remove_trailing_slash,
+		},
+		[TK_INVALID] = {
+			.s = S_REJECT,
+			.f = NULL,
+		},
+	}
+};
+
+/** Canonify a file system path.
+ *
+ * A file system path is canonical, if the following holds:
+ *
+ * 1) the path is absolute
+ *    (i.e. a/b/c is not canonical)
+ * 2) there is no trailing slash in the path if it has components
+ *    (i.e. /a/b/c/ is not canonical)
+ * 3) there is no extra slash in the path
+ *    (i.e. /a//b/c is not canonical)
+ * 4) there is no '.' component in the path
+ *    (i.e. /a/./b/c is not canonical)
+ * 5) there is no '..' component in the path
+ *    (i.e. /a/b/../c is not canonical)
+ *
+ * This function makes a potentially non-canonical file system path canonical.
+ * It works in-place and requires a NULL-terminated input string.
+ *
+ * @param path		Path to be canonified.
+ * @param lenp		Pointer where the length of the final path will be
+ *			stored. Can be NULL.
+ *
+ * @return		Canonified path or NULL on failure.
+ */
+char *canonify(char *path, size_t *lenp)
+{
+	state_t state;
+	token_t t;
+	token_t tfsl;		/* first slash */
+	token_t tlcomp;		/* last component */
+	if (*path != '/')
+		return NULL;
+	tfsl = slash_token(path);
+restart:
+	state = S_INI;
+	t = tfsl;
+	tlcomp = tfsl;
+	while (state != S_ACCEPT && state != S_RESTART && state != S_REJECT) {
+		if (trans[state][t.kind].f)
+			trans[state][t.kind].f(&t, &tfsl, &tlcomp);
+		state = trans[state][t.kind].s;
+		t = next_token(&t);
+	}
+	
+	switch (state) {
+	case S_RESTART:
+		goto restart;
+	case S_REJECT:
+		return NULL;
+	case S_ACCEPT:
+		if (lenp)
+			*lenp = (size_t)((tlcomp.stop - tfsl.start) + 1);
+		return tfsl.start; 
+	default:
+		abort();
+	}
+}
+
+/**
+ * @}
+ */
Index: uspace/lib/c/generic/vfs/vfs.c
===================================================================
--- uspace/lib/c/generic/vfs/vfs.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
+++ uspace/lib/c/generic/vfs/vfs.c	(revision 63f89665fc0283c1661274dc636d5a66e03d48b4)
@@ -0,0 +1,825 @@
+/*
+ * Copyright (c) 2008 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.
+ */
+
+/** @addtogroup libc
+ * @{
+ */
+/** @file
+ */
+
+#include <vfs/vfs.h>
+#include <vfs/canonify.h>
+#include <macros.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <ipc/ipc.h>
+#include <ipc/services.h>
+#include <async.h>
+#include <atomic.h>
+#include <futex.h>
+#include <errno.h>
+#include <str.h>
+#include <devmap.h>
+#include <ipc/vfs.h>
+#include <ipc/devmap.h>
+
+static int vfs_phone = -1;
+static futex_t vfs_phone_futex = FUTEX_INITIALIZER;
+static futex_t cwd_futex = FUTEX_INITIALIZER;
+
+static int cwd_fd = -1;
+static char *cwd_path = NULL;
+static size_t cwd_size = 0;
+
+char *absolutize(const char *path, size_t *retlen)
+{
+	char *ncwd_path;
+	char *ncwd_path_nc;
+
+	futex_down(&cwd_futex);
+	size_t size = str_size(path);
+	if (*path != '/') {
+		if (!cwd_path) {
+			futex_up(&cwd_futex);
+			return NULL;
+		}
+		ncwd_path_nc = malloc(cwd_size + 1 + size + 1);
+		if (!ncwd_path_nc) {
+			futex_up(&cwd_futex);
+			return NULL;
+		}
+		str_cpy(ncwd_path_nc, cwd_size + 1 + size + 1, cwd_path);
+		ncwd_path_nc[cwd_size] = '/';
+		ncwd_path_nc[cwd_size + 1] = '\0';
+	} else {
+		ncwd_path_nc = malloc(size + 1);
+		if (!ncwd_path_nc) {
+			futex_up(&cwd_futex);
+			return NULL;
+		}
+		ncwd_path_nc[0] = '\0';
+	}
+	str_append(ncwd_path_nc, cwd_size + 1 + size + 1, path);
+	ncwd_path = canonify(ncwd_path_nc, retlen);
+	if (!ncwd_path) {
+		futex_up(&cwd_futex);
+		free(ncwd_path_nc);
+		return NULL;
+	}
+	/*
+	 * We need to clone ncwd_path because canonify() works in-place and thus
+	 * the address in ncwd_path need not be the same as ncwd_path_nc, even
+	 * though they both point into the same dynamically allocated buffer.
+	 */
+	ncwd_path = str_dup(ncwd_path);
+	free(ncwd_path_nc);
+	if (!ncwd_path) {
+		futex_up(&cwd_futex);
+		return NULL;
+	}
+	futex_up(&cwd_futex);
+	return ncwd_path;
+}
+
+static void vfs_connect(void)
+{
+	while (vfs_phone < 0)
+		vfs_phone = ipc_connect_me_to_blocking(PHONE_NS, SERVICE_VFS, 0, 0);
+}
+
+int mount(const char *fs_name, const char *mp, const char *fqdn,
+    const char *opts, unsigned int flags)
+{
+	int null_id = -1;
+	char null[DEVMAP_NAME_MAXLEN];
+	
+	if (str_cmp(fqdn, "") == 0) {
+		/* No device specified, create a fresh
+		   null/%d device instead */
+		null_id = devmap_null_create();
+		
+		if (null_id == -1)
+			return ENOMEM;
+		
+		snprintf(null, DEVMAP_NAME_MAXLEN, "null/%d", null_id);
+		fqdn = null;
+	}
+	
+	dev_handle_t dev_handle;
+	int res = devmap_device_get_handle(fqdn, &dev_handle, flags);
+	if (res != EOK) {
+		if (null_id != -1)
+			devmap_null_destroy(null_id);
+		
+		return res;
+	}
+	
+	size_t mpa_size;
+	char *mpa = absolutize(mp, &mpa_size);
+	if (!mpa) {
+		if (null_id != -1)
+			devmap_null_destroy(null_id);
+		
+		return ENOMEM;
+	}
+	
+	futex_down(&vfs_phone_futex);
+	async_serialize_start();
+	vfs_connect();
+	
+	ipcarg_t rc_orig;
+	aid_t req = async_send_2(vfs_phone, VFS_IN_MOUNT, dev_handle, flags, NULL);
+	ipcarg_t rc = async_data_write_start(vfs_phone, (void *) mpa, mpa_size);
+	if (rc != EOK) {
+		async_wait_for(req, &rc_orig);
+		async_serialize_end();
+		futex_up(&vfs_phone_futex);
+		free(mpa);
+		
+		if (null_id != -1)
+			devmap_null_destroy(null_id);
+		
+		if (rc_orig == EOK)
+			return (int) rc;
+		else
+			return (int) rc_orig;
+	}
+	
+	rc = async_data_write_start(vfs_phone, (void *) opts, str_size(opts));
+	if (rc != EOK) {
+		async_wait_for(req, &rc_orig);
+		async_serialize_end();
+		futex_up(&vfs_phone_futex);
+		free(mpa);
+		
+		if (null_id != -1)
+			devmap_null_destroy(null_id);
+		
+		if (rc_orig == EOK)
+			return (int) rc;
+		else
+			return (int) rc_orig;
+	}
+	
+	rc = async_data_write_start(vfs_phone, (void *) fs_name, str_size(fs_name));
+	if (rc != EOK) {
+		async_wait_for(req, &rc_orig);
+		async_serialize_end();
+		futex_up(&vfs_phone_futex);
+		free(mpa);
+		
+		if (null_id != -1)
+			devmap_null_destroy(null_id);
+		
+		if (rc_orig == EOK)
+			return (int) rc;
+		else
+			return (int) rc_orig;
+	}
+	
+	/* Ask VFS whether it likes fs_name. */
+	rc = async_req_0_0(vfs_phone, IPC_M_PING);
+	if (rc != EOK) {
+		async_wait_for(req, &rc_orig);
+		async_serialize_end();
+		futex_up(&vfs_phone_futex);
+		free(mpa);
+		
+		if (null_id != -1)
+			devmap_null_destroy(null_id);
+		
+		if (rc_orig == EOK)
+			return (int) rc;
+		else
+			return (int) rc_orig;
+	}
+	
+	async_wait_for(req, &rc);
+	async_serialize_end();
+	futex_up(&vfs_phone_futex);
+	free(mpa);
+	
+	if ((rc != EOK) && (null_id != -1))
+		devmap_null_destroy(null_id);
+	
+	return (int) rc;
+}
+
+int unmount(const char *mp)
+{
+	ipcarg_t rc;
+	ipcarg_t rc_orig;
+	aid_t req;
+	size_t mpa_size;
+	char *mpa;
+	
+	mpa = absolutize(mp, &mpa_size);
+	if (!mpa)
+		return ENOMEM;
+	
+	futex_down(&vfs_phone_futex);
+	async_serialize_start();
+	vfs_connect();
+	
+	req = async_send_0(vfs_phone, VFS_IN_UNMOUNT, NULL);
+	rc = async_data_write_start(vfs_phone, (void *) mpa, mpa_size);
+	if (rc != EOK) {
+		async_wait_for(req, &rc_orig);
+		async_serialize_end();
+		futex_up(&vfs_phone_futex);
+		free(mpa);
+		if (rc_orig == EOK)
+			return (int) rc;
+		else
+			return (int) rc_orig;
+	}
+	
+
+	async_wait_for(req, &rc);
+	async_serialize_end();
+	futex_up(&vfs_phone_futex);
+	free(mpa);
+	
+	return (int) rc;
+}
+
+static int open_internal(const char *abs, size_t abs_size, int lflag, int oflag)
+{
+	futex_down(&vfs_phone_futex);
+	async_serialize_start();
+	vfs_connect();
+	
+	ipc_call_t answer;
+	aid_t req = async_send_3(vfs_phone, VFS_IN_OPEN, lflag, oflag, 0, &answer);
+	ipcarg_t rc = async_data_write_start(vfs_phone, abs, abs_size);
+	
+	if (rc != EOK) {
+		ipcarg_t rc_orig;
+		async_wait_for(req, &rc_orig);
+		
+		async_serialize_end();
+		futex_up(&vfs_phone_futex);
+		
+		if (rc_orig == EOK)
+			return (int) rc;
+		else
+			return (int) rc_orig;
+	}
+	
+	async_wait_for(req, &rc);
+	async_serialize_end();
+	futex_up(&vfs_phone_futex);
+	
+	if (rc != EOK)
+	    return (int) rc;
+	
+	return (int) IPC_GET_ARG1(answer);
+}
+
+int open(const char *path, int oflag, ...)
+{
+	size_t abs_size;
+	char *abs = absolutize(path, &abs_size);
+	if (!abs)
+		return ENOMEM;
+	
+	int ret = open_internal(abs, abs_size, L_FILE, oflag);
+	free(abs);
+	
+	return ret;
+}
+
+int open_node(fdi_node_t *node, int oflag)
+{
+	futex_down(&vfs_phone_futex);
+	async_serialize_start();
+	vfs_connect();
+	
+	ipc_call_t answer;
+	aid_t req = async_send_4(vfs_phone, VFS_IN_OPEN_NODE, node->fs_handle,
+	    node->dev_handle, node->index, oflag, &answer);
+	
+	ipcarg_t rc;
+	async_wait_for(req, &rc);
+	async_serialize_end();
+	futex_up(&vfs_phone_futex);
+	
+	if (rc != EOK)
+		return (int) rc;
+	
+	return (int) IPC_GET_ARG1(answer);
+}
+
+int close(int fildes)
+{
+	ipcarg_t rc;
+	
+	futex_down(&vfs_phone_futex);
+	async_serialize_start();
+	vfs_connect();
+	
+	rc = async_req_1_0(vfs_phone, VFS_IN_CLOSE, fildes);
+	
+	async_serialize_end();
+	futex_up(&vfs_phone_futex);
+	
+	return (int)rc;
+}
+
+ssize_t read(int fildes, void *buf, size_t nbyte) 
+{
+	ipcarg_t rc;
+	ipc_call_t answer;
+	aid_t req;
+
+	futex_down(&vfs_phone_futex);
+	async_serialize_start();
+	vfs_connect();
+	
+	req = async_send_1(vfs_phone, VFS_IN_READ, fildes, &answer);
+	rc = async_data_read_start(vfs_phone, (void *)buf, nbyte);
+	if (rc != EOK) {
+		ipcarg_t rc_orig;
+	
+		async_wait_for(req, &rc_orig);
+		async_serialize_end();
+		futex_up(&vfs_phone_futex);
+		if (rc_orig == EOK)
+			return (ssize_t) rc;
+		else
+			return (ssize_t) rc_orig;
+	}
+	async_wait_for(req, &rc);
+	async_serialize_end();
+	futex_up(&vfs_phone_futex);
+	if (rc == EOK)
+		return (ssize_t) IPC_GET_ARG1(answer);
+	else
+		return rc;
+}
+
+ssize_t write(int fildes, const void *buf, size_t nbyte) 
+{
+	ipcarg_t rc;
+	ipc_call_t answer;
+	aid_t req;
+
+	futex_down(&vfs_phone_futex);
+	async_serialize_start();
+	vfs_connect();
+	
+	req = async_send_1(vfs_phone, VFS_IN_WRITE, fildes, &answer);
+	rc = async_data_write_start(vfs_phone, (void *)buf, nbyte);
+	if (rc != EOK) {
+		ipcarg_t rc_orig;
+	
+		async_wait_for(req, &rc_orig);
+		async_serialize_end();
+		futex_up(&vfs_phone_futex);
+		if (rc_orig == EOK)
+			return (ssize_t) rc;
+		else
+			return (ssize_t) rc_orig;
+	}
+	async_wait_for(req, &rc);
+	async_serialize_end();
+	futex_up(&vfs_phone_futex);
+	if (rc == EOK)
+		return (ssize_t) IPC_GET_ARG1(answer);
+	else
+		return -1;
+}
+
+int fsync(int fildes)
+{
+	futex_down(&vfs_phone_futex);
+	async_serialize_start();
+	vfs_connect();
+	
+	ipcarg_t rc = async_req_1_0(vfs_phone, VFS_IN_SYNC, fildes);
+	
+	async_serialize_end();
+	futex_up(&vfs_phone_futex);
+	
+	return (int) rc;
+}
+
+off64_t lseek(int fildes, off64_t offset, int whence)
+{
+	futex_down(&vfs_phone_futex);
+	async_serialize_start();
+	vfs_connect();
+	
+	ipcarg_t newoff_lo;
+	ipcarg_t newoff_hi;
+	ipcarg_t rc = async_req_4_2(vfs_phone, VFS_IN_SEEK, fildes,
+	    LOWER32(offset), UPPER32(offset), whence,
+	    &newoff_lo, &newoff_hi);
+	
+	async_serialize_end();
+	futex_up(&vfs_phone_futex);
+	
+	if (rc != EOK)
+		return (off64_t) -1;
+	
+	return (off64_t) MERGE_LOUP32(newoff_lo, newoff_hi);
+}
+
+int ftruncate(int fildes, aoff64_t length)
+{
+	ipcarg_t rc;
+	
+	futex_down(&vfs_phone_futex);
+	async_serialize_start();
+	vfs_connect();
+	
+	rc = async_req_3_0(vfs_phone, VFS_IN_TRUNCATE, fildes,
+	    LOWER32(length), UPPER32(length));
+	async_serialize_end();
+	futex_up(&vfs_phone_futex);
+	
+	return (int) rc;
+}
+
+int fstat(int fildes, struct stat *stat)
+{
+	ipcarg_t rc;
+	aid_t req;
+
+	futex_down(&vfs_phone_futex);
+	async_serialize_start();
+	vfs_connect();
+	
+	req = async_send_1(vfs_phone, VFS_IN_FSTAT, fildes, NULL);
+	rc = async_data_read_start(vfs_phone, (void *) stat, sizeof(struct stat));
+	if (rc != EOK) {
+		ipcarg_t rc_orig;
+		
+		async_wait_for(req, &rc_orig);
+		async_serialize_end();
+		futex_up(&vfs_phone_futex);
+		if (rc_orig == EOK)
+			return (ssize_t) rc;
+		else
+			return (ssize_t) rc_orig;
+	}
+	async_wait_for(req, &rc);
+	async_serialize_end();
+	futex_up(&vfs_phone_futex);
+
+	return rc;
+}
+
+int stat(const char *path, struct stat *stat)
+{
+	ipcarg_t rc;
+	ipcarg_t rc_orig;
+	aid_t req;
+	
+	size_t pa_size;
+	char *pa = absolutize(path, &pa_size);
+	if (!pa)
+		return ENOMEM;
+	
+	futex_down(&vfs_phone_futex);
+	async_serialize_start();
+	vfs_connect();
+	
+	req = async_send_0(vfs_phone, VFS_IN_STAT, NULL);
+	rc = async_data_write_start(vfs_phone, pa, pa_size);
+	if (rc != EOK) {
+		async_wait_for(req, &rc_orig);
+		async_serialize_end();
+		futex_up(&vfs_phone_futex);
+		free(pa);
+		if (rc_orig == EOK)
+			return (int) rc;
+		else
+			return (int) rc_orig;
+	}
+	rc = async_data_read_start(vfs_phone, stat, sizeof(struct stat));
+	if (rc != EOK) {
+		async_wait_for(req, &rc_orig);
+		async_serialize_end();
+		futex_up(&vfs_phone_futex);
+		free(pa);
+		if (rc_orig == EOK)
+			return (int) rc;
+		else
+			return (int) rc_orig;
+	}
+	async_wait_for(req, &rc);
+	async_serialize_end();
+	futex_up(&vfs_phone_futex);
+	free(pa);
+	return rc;
+}
+
+DIR *opendir(const char *dirname)
+{
+	DIR *dirp = malloc(sizeof(DIR));
+	if (!dirp)
+		return NULL;
+	
+	size_t abs_size;
+	char *abs = absolutize(dirname, &abs_size);
+	if (!abs) {
+		free(dirp);
+		return NULL;
+	}
+	
+	int ret = open_internal(abs, abs_size, L_DIRECTORY, 0);
+	free(abs);
+	
+	if (ret < 0) {
+		free(dirp);
+		return NULL;
+	}
+	
+	dirp->fd = ret;
+	return dirp;
+}
+
+struct dirent *readdir(DIR *dirp)
+{
+	ssize_t len = read(dirp->fd, &dirp->res.d_name[0], NAME_MAX + 1);
+	if (len <= 0)
+		return NULL;
+	return &dirp->res;
+}
+
+void rewinddir(DIR *dirp)
+{
+	(void) lseek(dirp->fd, 0, SEEK_SET);
+}
+
+int closedir(DIR *dirp)
+{
+	(void) close(dirp->fd);
+	free(dirp);
+	return 0;
+}
+
+int mkdir(const char *path, mode_t mode)
+{
+	ipcarg_t rc;
+	aid_t req;
+	
+	size_t pa_size;
+	char *pa = absolutize(path, &pa_size);
+	if (!pa)
+		return ENOMEM;
+	
+	futex_down(&vfs_phone_futex);
+	async_serialize_start();
+	vfs_connect();
+	
+	req = async_send_1(vfs_phone, VFS_IN_MKDIR, mode, NULL);
+	rc = async_data_write_start(vfs_phone, pa, pa_size);
+	if (rc != EOK) {
+		ipcarg_t rc_orig;
+	
+		async_wait_for(req, &rc_orig);
+		async_serialize_end();
+		futex_up(&vfs_phone_futex);
+		free(pa);
+		if (rc_orig == EOK)
+			return (int) rc;
+		else
+			return (int) rc_orig;
+	}
+	async_wait_for(req, &rc);
+	async_serialize_end();
+	futex_up(&vfs_phone_futex);
+	free(pa);
+	return rc;
+}
+
+static int _unlink(const char *path, int lflag)
+{
+	ipcarg_t rc;
+	aid_t req;
+	
+	size_t pa_size;
+	char *pa = absolutize(path, &pa_size);
+	if (!pa)
+		return ENOMEM;
+
+	futex_down(&vfs_phone_futex);
+	async_serialize_start();
+	vfs_connect();
+	
+	req = async_send_0(vfs_phone, VFS_IN_UNLINK, NULL);
+	rc = async_data_write_start(vfs_phone, pa, pa_size);
+	if (rc != EOK) {
+		ipcarg_t rc_orig;
+
+		async_wait_for(req, &rc_orig);
+		async_serialize_end();
+		futex_up(&vfs_phone_futex);
+		free(pa);
+		if (rc_orig == EOK)
+			return (int) rc;
+		else
+			return (int) rc_orig;
+	}
+	async_wait_for(req, &rc);
+	async_serialize_end();
+	futex_up(&vfs_phone_futex);
+	free(pa);
+	return rc;
+}
+
+int unlink(const char *path)
+{
+	return _unlink(path, L_NONE);
+}
+
+int rmdir(const char *path)
+{
+	return _unlink(path, L_DIRECTORY);
+}
+
+int rename(const char *old, const char *new)
+{
+	ipcarg_t rc;
+	ipcarg_t rc_orig;
+	aid_t req;
+	
+	size_t olda_size;
+	char *olda = absolutize(old, &olda_size);
+	if (!olda)
+		return ENOMEM;
+
+	size_t newa_size;
+	char *newa = absolutize(new, &newa_size);
+	if (!newa) {
+		free(olda);
+		return ENOMEM;
+	}
+
+	futex_down(&vfs_phone_futex);
+	async_serialize_start();
+	vfs_connect();
+	
+	req = async_send_0(vfs_phone, VFS_IN_RENAME, NULL);
+	rc = async_data_write_start(vfs_phone, olda, olda_size);
+	if (rc != EOK) {
+		async_wait_for(req, &rc_orig);
+		async_serialize_end();
+		futex_up(&vfs_phone_futex);
+		free(olda);
+		free(newa);
+		if (rc_orig == EOK)
+			return (int) rc;
+		else
+			return (int) rc_orig;
+	}
+	rc = async_data_write_start(vfs_phone, newa, newa_size);
+	if (rc != EOK) {
+		async_wait_for(req, &rc_orig);
+		async_serialize_end();
+		futex_up(&vfs_phone_futex);
+		free(olda);
+		free(newa);
+		if (rc_orig == EOK)
+			return (int) rc;
+		else
+			return (int) rc_orig;
+	}
+	async_wait_for(req, &rc);
+	async_serialize_end();
+	futex_up(&vfs_phone_futex);
+	free(olda);
+	free(newa);
+	return rc;
+}
+
+int chdir(const char *path)
+{
+	size_t abs_size;
+	char *abs = absolutize(path, &abs_size);
+	if (!abs)
+		return ENOMEM;
+	
+	int fd = open_internal(abs, abs_size, L_DIRECTORY, O_DESC);
+	
+	if (fd < 0) {
+		free(abs);
+		return ENOENT;
+	}
+	
+	futex_down(&cwd_futex);
+	
+	if (cwd_fd >= 0)
+		close(cwd_fd);
+	
+	
+	if (cwd_path)
+		free(cwd_path);
+	
+	cwd_fd = fd;
+	cwd_path = abs;
+	cwd_size = abs_size;
+	
+	futex_up(&cwd_futex);
+	return EOK;
+}
+
+char *getcwd(char *buf, size_t size)
+{
+	if (size == 0)
+		return NULL;
+	
+	futex_down(&cwd_futex);
+	
+	if ((cwd_size == 0) || (size < cwd_size + 1)) {
+		futex_up(&cwd_futex);
+		return NULL;
+	}
+	
+	str_cpy(buf, size, cwd_path);
+	futex_up(&cwd_futex);
+	
+	return buf;
+}
+
+int fd_phone(int fildes)
+{
+	struct stat stat;
+	int rc;
+
+	rc = fstat(fildes, &stat);
+
+	if (!stat.device)
+		return -1;
+	
+	return devmap_device_connect(stat.device, 0);
+}
+
+int fd_node(int fildes, fdi_node_t *node)
+{
+	struct stat stat;
+	int rc;
+
+	rc = fstat(fildes, &stat);
+	
+	if (rc == EOK) {
+		node->fs_handle = stat.fs_handle;
+		node->dev_handle = stat.dev_handle;
+		node->index = stat.index;
+	}
+	
+	return rc;
+}
+
+int dup2(int oldfd, int newfd)
+{
+	futex_down(&vfs_phone_futex);
+	async_serialize_start();
+	vfs_connect();
+	
+	ipcarg_t ret;
+	ipcarg_t rc = async_req_2_1(vfs_phone, VFS_IN_DUP, oldfd, newfd, &ret);
+	
+	async_serialize_end();
+	futex_up(&vfs_phone_futex);
+	
+	if (rc == EOK)
+		return (int) ret;
+	
+	return (int) rc;
+}
+
+/** @}
+ */
