Index: uspace/Makefile
===================================================================
--- uspace/Makefile	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/Makefile	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -215,5 +215,6 @@
 	lib/c \
 	lib/softint \
-	lib/softfloat
+	lib/softfloat \
+	lib/untar
 
 LIBS = \
Index: uspace/Makefile.common
===================================================================
--- uspace/Makefile.common	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/Makefile.common	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -165,5 +165,5 @@
 ifneq ($(TEST_SOURCES),)
 	TEST_OUTPUTS = $(TEST_BINARY) $(TEST_BINARY).disasm
-	TEST_CFLAGS = -I$(LIB_PREFIX)/pcut/include -D__helenos__
+	TEST_CFLAGS = -I$(LIB_PREFIX)/pcut/include -D__helenos__ $(EXTRA_TEST_CFLAGS)
 	TEST_BINARY_LIBS = $(LIB_PREFIX)/pcut/libpcut.a
 	EXTRA_CLEAN += $(TEST_OUTPUTS) $(TEST_BINARY).map
@@ -190,4 +190,8 @@
 endif
 
+ifeq ($(CONFIG_UBSAN),y)
+	DEFAULT_CFLAGS += -fsanitize=undefined
+endif
+
 ifeq ($(COMPILER),clang)
 	DEFAULT_CFLAGS += \
Index: uspace/app/bdsh/cmds/modules/cat/cat.c
===================================================================
--- uspace/app/bdsh/cmds/modules/cat/cat.c	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/app/bdsh/cmds/modules/cat/cat.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -160,5 +160,5 @@
 		printf("%6u  ", lineno);
 	}
-	putchar(c);
+	putwchar(c);
 	last_char_was_newline = c == '\n';
 	if (paging_enabled) {
Index: uspace/app/bdsh/cmds/modules/printf/printf.c
===================================================================
--- uspace/app/bdsh/cmds/modules/printf/printf.c	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/app/bdsh/cmds/modules/printf/printf.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -172,9 +172,9 @@
 				break;
 			}
-			putchar(ch);
+			putwchar(ch);
 			break;
 
 		emit:
-			putchar(ch);
+			putwchar(ch);
 			esc_flag = false;
 		}
Index: uspace/app/bdsh/scli.h
===================================================================
--- uspace/app/bdsh/scli.h	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/app/bdsh/scli.h	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -31,4 +31,5 @@
 
 #include "config.h"
+#include <errno.h>
 #include <stdint.h>
 #include <stdio.h>
Index: uspace/app/edit/edit.c
===================================================================
--- uspace/app/edit/edit.c	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/app/edit/edit.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -671,5 +671,5 @@
 				default:
 					if (kev->c >= 32 && nc < max_len) {
-						putchar(kev->c);
+						putwchar(kev->c);
 						console_flush(con);
 						buffer[nc++] = kev->c;
Index: uspace/app/init/Makefile
===================================================================
--- uspace/app/init/Makefile	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/app/init/Makefile	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -32,6 +32,9 @@
 STATIC_NEEDED = y
 
+LIBS = untar block
+
 SOURCES = \
-	init.c
+	init.c \
+	untar.c
 
 include $(USPACE_PREFIX)/Makefile.common
Index: uspace/app/init/init.c
===================================================================
--- uspace/app/init/init.c	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/app/init/init.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -48,4 +48,5 @@
 #include <config.h>
 #include <io/logctl.h>
+#include "untar.h"
 #include "init.h"
 
@@ -83,5 +84,5 @@
 	switch (rc) {
 	case EOK:
-		if (dev != NULL)
+		if ((dev != NULL) && (str_cmp(dev, "") != 0))
 			printf("%s: %s mounted on %s (%s at %s)\n", NAME, desc, mntpt,
 			    fstype, dev);
@@ -107,10 +108,10 @@
 }
 
-/** Mount root filesystem
- *
- * The operation blocks until the root filesystem
+/** Mount root file system
+ *
+ * The operation blocks until the root file system
  * server is ready for mounting.
  *
- * @param[in] fstype Root filesystem type.
+ * @param[in] fstype Root file system type.
  *
  * @return True on success.
@@ -120,20 +121,35 @@
 static bool mount_root(const char *fstype)
 {
-	const char *opts = "";
-
-	if (str_cmp(fstype, "tmpfs") == 0)
-		opts = "restore";
-
-	errno_t rc = vfs_mount_path(ROOT_MOUNT_POINT, fstype, ROOT_DEVICE, opts,
+	const char *root_device = "";
+
+	if (str_cmp(fstype, "tmpfs") != 0)
+		root_device = ROOT_DEVICE;
+
+	errno_t rc = vfs_mount_path(ROOT_MOUNT_POINT, fstype, root_device, "",
 	    IPC_FLAG_BLOCKING, 0);
 	if (rc == EOK)
 		logctl_set_root();
-	return mount_report("Root filesystem", ROOT_MOUNT_POINT, fstype,
-	    ROOT_DEVICE, rc);
-}
-
-/** Mount locfs filesystem
- *
- * The operation blocks until the locfs filesystem
+
+	bool ret = mount_report("Root file system", ROOT_MOUNT_POINT, fstype,
+	    root_device, rc);
+
+	rc = vfs_cwd_set(ROOT_MOUNT_POINT);
+	if (rc != EOK) {
+		printf("%s: Unable to set current directory to %s (%s)\n",
+		    NAME, ROOT_MOUNT_POINT, str_error(ret));
+		return false;
+	}
+
+	if ((ret) && (str_cmp(fstype, "tmpfs") == 0)) {
+		printf("%s: Extracting root file system archive\n", NAME);
+		ret = bd_untar(ROOT_DEVICE);
+	}
+
+	return ret;
+}
+
+/** Mount locfs file system
+ *
+ * The operation blocks until the locfs file system
  * server is ready for mounting.
  *
@@ -146,5 +162,5 @@
 	errno_t rc = vfs_mount_path(LOCFS_MOUNT_POINT, LOCFS_FS_TYPE, "", "",
 	    IPC_FLAG_BLOCKING, 0);
-	return mount_report("Location service filesystem", LOCFS_MOUNT_POINT,
+	return mount_report("Location service file system", LOCFS_MOUNT_POINT,
 	    LOCFS_FS_TYPE, NULL, rc);
 }
@@ -301,5 +317,5 @@
 {
 	errno_t rc = vfs_mount_path(TMPFS_MOUNT_POINT, TMPFS_FS_TYPE, "", "", 0, 0);
-	return mount_report("Temporary filesystem", TMPFS_MOUNT_POINT,
+	return mount_report("Temporary file system", TMPFS_MOUNT_POINT,
 	    TMPFS_FS_TYPE, NULL, rc);
 }
Index: uspace/app/init/untar.c
===================================================================
--- uspace/app/init/untar.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
+++ uspace/app/init/untar.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2018 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 init
+ * @{
+ */
+/**
+ * @file
+ */
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <loc.h>
+#include <untar.h>
+#include <block.h>
+#include "init.h"
+#include "untar.h"
+
+typedef struct {
+	const char *dev;
+	
+	service_id_t sid;
+	aoff64_t offset;
+} tar_state_t;
+
+static int bd_tar_open(tar_file_t *tar)
+{
+	tar_state_t *state = (tar_state_t *) tar->data;
+
+	errno_t ret = loc_service_get_id(state->dev, &state->sid,
+	    IPC_FLAG_BLOCKING);
+	if (ret != EOK)
+		return ret;
+
+	ret = block_init(state->sid, 4096);
+	if (ret != EOK)
+		return ret;
+
+	state->offset = 0;
+	return EOK;
+}
+
+static void bd_tar_close(tar_file_t *tar)
+{
+	tar_state_t *state = (tar_state_t *) tar->data;
+	block_fini(state->sid);
+}
+
+static size_t bd_tar_read(tar_file_t *tar, void *data, size_t size)
+{
+	tar_state_t *state = (tar_state_t *) tar->data;
+
+	if (block_read_bytes_direct(state->sid, state->offset, size,
+	    data) != EOK)
+		return 0;
+
+	state->offset += size;
+	return size;
+}
+
+static void bd_tar_vreport(tar_file_t *tar, const char *fmt, va_list args)
+{
+	vprintf(fmt, args);
+}
+
+tar_file_t tar = {
+	.open = bd_tar_open,
+	.close = bd_tar_close,
+
+	.read = bd_tar_read,
+	.vreport = bd_tar_vreport
+};
+
+bool bd_untar(const char *dev)
+{
+	tar_state_t state;
+	state.dev = dev;
+
+	tar.data = (void *) &state;
+	return (untar(&tar) == EOK);
+}
+
+/** @}
+ */
Index: uspace/app/init/untar.h
===================================================================
--- uspace/app/init/untar.h	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
+++ uspace/app/init/untar.h	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2018 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 init
+ * @{
+ */
+/**
+ * @file
+ */
+
+#ifndef __UNTAR_H__
+#define __UNTAR_H__
+
+#include <stdbool.h>
+
+extern bool bd_untar(const char *);
+
+#endif
+
+/** @}
+ */
Index: uspace/app/kio/kio.c
===================================================================
--- uspace/app/kio/kio.c	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/app/kio/kio.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -122,9 +122,9 @@
 
 		for (size_t i = 0; i < item->length; i++)
-			putchar(item->data[i]);
+			putwchar(item->data[i]);
 
 		if (log != NULL) {
 			for (size_t i = 0; i < item->length; i++)
-				fputc(item->data[i], log);
+				fputwc(item->data[i], log);
 
 			fflush(log);
Index: uspace/app/sbi/src/os/os.h
===================================================================
--- uspace/app/sbi/src/os/os.h	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/app/sbi/src/os/os.h	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -30,4 +30,6 @@
 #define OS_H_
 
+#include <errno.h>
+
 char *os_str_acat(const char *a, const char *b);
 char *os_str_aslice(const char *str, size_t start, size_t length);
Index: uspace/app/untar/Makefile
===================================================================
--- uspace/app/untar/Makefile	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/app/untar/Makefile	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -30,7 +30,8 @@
 BINARY = untar
 
+LIBS = untar
+
 SOURCES = \
-	main.c \
-	tar.c
+	main.c
 
 include $(USPACE_PREFIX)/Makefile.common
Index: uspace/app/untar/main.c
===================================================================
--- uspace/app/untar/main.c	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/app/untar/main.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -33,88 +33,49 @@
  */
 
+#include <errno.h>
 #include <stdio.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <str_error.h>
-#include <vfs/vfs.h>
-#include "tar.h"
+#include <stdarg.h>
+#include <untar.h>
 
-static size_t get_block_count(size_t bytes)
+typedef struct {
+	const char *filename;
+	FILE *file;
+} tar_state_t;
+
+static int tar_open(tar_file_t *tar)
 {
-	return (bytes + TAR_BLOCK_SIZE - 1) / TAR_BLOCK_SIZE;
-}
+	tar_state_t *state = (tar_state_t *) tar->data;
 
-static errno_t skip_blocks(FILE *tarfile, size_t valid_data_size)
-{
-	size_t blocks_to_read = get_block_count(valid_data_size);
-	while (blocks_to_read > 0) {
-		uint8_t block[TAR_BLOCK_SIZE];
-		size_t actually_read = fread(block, TAR_BLOCK_SIZE, 1, tarfile);
-		if (actually_read != 1) {
-			return errno;
-		}
-		blocks_to_read--;
-	}
+	state->file = fopen(state->filename, "rb");
+	if (state->file == NULL)
+		return errno;
+
 	return EOK;
 }
 
-static errno_t handle_normal_file(const tar_header_t *header, FILE *tarfile)
+static void tar_close(tar_file_t *tar)
 {
-	// FIXME: create the directory first
-
-	FILE *file = fopen(header->filename, "wb");
-	if (file == NULL) {
-		fprintf(stderr, "Failed to create %s: %s.\n", header->filename,
-		    str_error(errno));
-		return errno;
-	}
-
-	errno_t rc = EOK;
-	size_t bytes_remaining = header->size;
-	size_t blocks = get_block_count(bytes_remaining);
-	while (blocks > 0) {
-		uint8_t block[TAR_BLOCK_SIZE];
-		size_t actually_read = fread(block, 1, TAR_BLOCK_SIZE, tarfile);
-		if (actually_read != TAR_BLOCK_SIZE) {
-			rc = errno;
-			fprintf(stderr, "Failed to read block for %s: %s.\n",
-			    header->filename, str_error(rc));
-			break;
-		}
-		size_t to_write = TAR_BLOCK_SIZE;
-		if (bytes_remaining < TAR_BLOCK_SIZE) {
-			to_write = bytes_remaining;
-		}
-		size_t actually_written = fwrite(block, 1, to_write, file);
-		if (actually_written != to_write) {
-			rc = errno;
-			fprintf(stderr, "Failed to write to %s: %s.\n",
-			    header->filename, str_error(rc));
-			break;
-		}
-		blocks--;
-		bytes_remaining -= TAR_BLOCK_SIZE;
-	}
-
-	fclose(file);
-
-	return rc;
+	tar_state_t *state = (tar_state_t *) tar->data;
+	fclose(state->file);
 }
 
-static errno_t handle_directory(const tar_header_t *header, FILE *tarfile)
+static size_t tar_read(tar_file_t *tar, void *data, size_t size)
 {
-	errno_t rc;
+	tar_state_t *state = (tar_state_t *) tar->data;
+	return fread(data, 1, size, state->file);
+}
 
-	rc = vfs_link_path(header->filename, KIND_DIRECTORY, NULL);
-	if (rc != EOK) {
-		if (rc != EEXIST) {
-			fprintf(stderr, "Failed to create directory %s: %s.\n",
-			    header->filename, str_error(rc));
-			return rc;
-		}
-	}
+static void tar_vreport(tar_file_t *tar, const char *fmt, va_list args)
+{
+	vfprintf(stderr, fmt, args);
+}
 
-	return skip_blocks(tarfile, header->size);
-}
+tar_file_t tar = {
+	.open = tar_open,
+	.close = tar_close,
+
+	.read = tar_read,
+	.vreport = tar_vreport
+};
 
 int main(int argc, char *argv[])
@@ -125,52 +86,9 @@
 	}
 
-	const char *filename = argv[1];
+	tar_state_t state;
+	state.filename = argv[1];
 
-	FILE *tarfile = fopen(filename, "rb");
-	if (tarfile == NULL) {
-		fprintf(stderr, "Failed to open `%s': %s.\n", filename, str_error(errno));
-		return 2;
-	}
-
-	while (true) {
-		size_t header_ok;
-		tar_header_raw_t header_raw;
-		tar_header_t header;
-		header_ok = fread(&header_raw, sizeof(header_raw), 1, tarfile);
-		if (header_ok != 1) {
-			break;
-		}
-		errno_t rc = tar_header_parse(&header, &header_raw);
-		if (rc == EEMPTY) {
-			continue;
-		}
-		if (rc != EOK) {
-			fprintf(stderr, "Failed parsing TAR header: %s.\n", str_error(rc));
-			break;
-		}
-
-		//printf(" ==> %s (%zuB, type %s)\n", header.filename,
-		//    header.size, tar_type_str(header.type));
-
-		switch (header.type) {
-		case TAR_TYPE_DIRECTORY:
-			rc = handle_directory(&header, tarfile);
-			break;
-		case TAR_TYPE_NORMAL:
-			rc = handle_normal_file(&header, tarfile);
-			break;
-		default:
-			rc = skip_blocks(tarfile, header.size);
-			break;
-		}
-		if (rc != EOK) {
-			break;
-		}
-
-	}
-
-	fclose(tarfile);
-
-	return 0;
+	tar.data = (void *) &state;
+	return untar(&tar);
 }
 
Index: uspace/app/untar/tar.c
===================================================================
--- uspace/app/untar/tar.c	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ 	(revision )
@@ -1,94 +1,0 @@
-/*
- * Copyright (c) 2013 Vojtech Horky
- * 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 untar
- * @{
- */
-/** @file
- */
-
-#include <str.h>
-#include <stdlib.h>
-#include <stddef.h>
-#include <errno.h>
-#include <assert.h>
-
-#include "tar.h"
-
-tar_type_t tar_type_parse(const char type)
-{
-	switch (type) {
-	case '0':
-	case 0:
-		return TAR_TYPE_NORMAL;
-	case '5':
-		return TAR_TYPE_DIRECTORY;
-	default:
-		return TAR_TYPE_UNKNOWN;
-	}
-}
-
-const char *tar_type_str(tar_type_t type)
-{
-	switch (type) {
-	case TAR_TYPE_UNKNOWN:
-		return "unknown";
-	case TAR_TYPE_NORMAL:
-		return "normal";
-	case TAR_TYPE_DIRECTORY:
-		return "directory";
-	default:
-		assert(false && "unexpected tar_type_t enum value");
-		return "?";
-	}
-}
-
-errno_t tar_header_parse(tar_header_t *parsed, const tar_header_raw_t *raw)
-{
-	errno_t rc;
-
-	if (str_length(raw->filename) == 0) {
-		return EEMPTY;
-	}
-
-	size_t size;
-	rc = str_size_t(raw->size, NULL, 8, true, &size);
-	if (rc != EOK) {
-		return rc;
-	}
-	parsed->size = size;
-
-	str_cpy(parsed->filename, 100, raw->filename);
-
-	parsed->type = tar_type_parse(raw->type);
-
-	return EOK;
-}
-
-/** @}
- */
Index: uspace/app/untar/tar.h
===================================================================
--- uspace/app/untar/tar.h	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ 	(revision )
@@ -1,79 +1,0 @@
-/*
- * Copyright (c) 2013 Vojtech Horky
- * 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 untar
- * @{
- */
-/** @file
- */
-#ifndef TAR_H_GUARD
-#define TAR_H_GUARD
-
-#define TAR_BLOCK_SIZE 512
-
-typedef struct tar_header_raw {
-	char filename[100];
-	char permissions[8];
-	char owner[8];
-	char group[8];
-	char size[12];
-	char modification_time[12];
-	char checksum[8];
-	char type;
-	char name[100];
-	char ustar_magic[6];
-	char ustar_version[2];
-	char ustar_owner_name[32];
-	char ustar_group_name[32];
-	char ustar_device_major[8];
-	char ustar_device_minor[8];
-	char ustar_prefix[155];
-	char ignored[12];
-} tar_header_raw_t;
-
-typedef enum tar_type {
-	TAR_TYPE_UNKNOWN,
-	TAR_TYPE_NORMAL,
-	TAR_TYPE_DIRECTORY
-} tar_type_t;
-
-typedef struct tar_header {
-	char filename[100];
-	size_t size;
-	tar_type_t type;
-} tar_header_t;
-
-
-extern errno_t tar_header_parse(tar_header_t *, const tar_header_raw_t *);
-extern tar_type_t tar_type_parse(const char);
-extern const char *tar_type_str(tar_type_t);
-
-#endif
-
-/** @}
- */
Index: uspace/dist/src/c/demos/edit/edit.c
===================================================================
--- uspace/dist/src/c/demos/edit/edit.c	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/dist/src/c/demos/edit/edit.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -671,5 +671,5 @@
 				default:
 					if (kev->c >= 32 && nc < max_len) {
-						putchar(kev->c);
+						putwchar(kev->c);
 						console_flush(con);
 						buffer[nc++] = kev->c;
Index: uspace/drv/bus/pci/pciintel/pci.c
===================================================================
--- uspace/drv/bus/pci/pciintel/pci.c	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/drv/bus/pci/pciintel/pci.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -63,5 +63,5 @@
 #define NAME "pciintel"
 
-#define CONF_ADDR_ENABLE	(1 << 31)
+#define CONF_ADDR_ENABLE	(((unsigned)1) << 31)
 #define CONF_ADDR(bus, dev, fn, reg) \
 	((bus << 16) | (dev << 11) | (fn << 8) | (reg & ~3))
Index: uspace/drv/bus/usb/xhci/trb_ring.c
===================================================================
--- uspace/drv/bus/usb/xhci/trb_ring.c	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/drv/bus/usb/xhci/trb_ring.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -365,5 +365,5 @@
 {
 	list_foreach(ring->segments, segments_link, trb_segment_t, segment)
-	    memset(segment->trb_storage, 0, sizeof(segment->trb_storage));
+		memset(segment->trb_storage, 0, sizeof(segment->trb_storage));
 
 	trb_segment_t *const segment = get_first_segment(&ring->segments);
Index: uspace/lib/bithenge/include/bithenge/os.h
===================================================================
--- uspace/lib/bithenge/include/bithenge/os.h	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/lib/bithenge/include/bithenge/os.h	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -33,4 +33,5 @@
 
 #ifdef __HELENOS__
+#include <stdint.h>
 typedef int64_t bithenge_int_t;
 #define BITHENGE_PRId PRId64
Index: uspace/lib/bithenge/src/helenos/common.h
===================================================================
--- uspace/lib/bithenge/src/helenos/common.h	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/lib/bithenge/src/helenos/common.h	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -73,12 +73,4 @@
 }
 
-static inline void *memchr(const void *s, int c, size_t n)
-{
-	for (size_t i = 0; i < n; i++)
-		if (((char *)s)[i] == c)
-			return (void *)(s + i);
-	return NULL;
-}
-
 static inline errno_t bithenge_parse_int(const char *start, bithenge_int_t *result)
 {
Index: uspace/lib/c/Makefile
===================================================================
--- uspace/lib/c/Makefile	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/lib/c/Makefile	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -41,4 +41,5 @@
 EXTRA_OUTPUT = $(LINKER_SCRIPTS)
 EXTRA_CLEAN = $(LINKER_SCRIPTS)
+EXTRA_TEST_CFLAGS = -Wno-deprecated-declarations
 LIBRARY = libc
 SOVERSION = 0.0
@@ -131,8 +132,15 @@
 	generic/double_to_str.c \
 	generic/malloc.c \
+	generic/stdio/scanf.c \
+	generic/stdio/sprintf.c \
+	generic/stdio/sscanf.c \
+	generic/stdio/sstream.c \
+	generic/stdio/vsprintf.c \
 	generic/sysinfo.c \
 	generic/ipc.c \
 	generic/ns.c \
-	generic/async.c \
+	generic/async/client.c \
+	generic/async/server.c \
+	generic/async/ports.c \
 	generic/loader.c \
 	generic/getopt.c \
@@ -144,4 +152,5 @@
 	generic/adt/prodcons.c \
 	generic/time.c \
+	generic/tmpfile.c \
 	generic/stdio.c \
 	generic/stdlib.c \
@@ -158,6 +167,8 @@
 	generic/stats.c \
 	generic/assert.c \
+	generic/bsearch.c \
 	generic/pio_trace.c \
 	generic/qsort.c \
+	generic/ubsan.c \
 	generic/uuid.c \
 	generic/vbd.c \
@@ -182,8 +193,12 @@
 	test/fibril/timer.c \
 	test/main.c \
+	test/mem.c \
 	test/io/table.c \
+	test/stdio/scanf.c \
 	test/odict.c \
 	test/qsort.c \
 	test/sprintf.c \
+	test/stdio.c \
+	test/stdlib.c \
 	test/str.c
 
Index: uspace/lib/c/arch/arm32/include/libarch/fibril_context.h
===================================================================
--- uspace/lib/c/arch/arm32/include/libarch/fibril_context.h	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/lib/c/arch/arm32/include/libarch/fibril_context.h	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -49,5 +49,5 @@
 	uint32_t r7;
 	uint32_t r8;
-	/* r9*/
+	/* r9 */
 	uint32_t tls;
 	uint32_t r10;
@@ -57,3 +57,2 @@
 
 #endif
-
Index: uspace/lib/c/generic/async.c
===================================================================
--- uspace/lib/c/generic/async.c	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ 	(revision )
@@ -1,3324 +1,0 @@
-/*
- * 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 of the synchronization
- * problems.
- *
- * Example of use (pseudo C):
- *
- * 1) Multithreaded client application
- *
- *   fibril_create(fibril1, ...);
- *   fibril_create(fibril2, ...);
- *   ...
- *
- *   int fibril1(void *arg)
- *   {
- *     conn = async_connect_me_to(...);
- *
- *     exch = async_exchange_begin(conn);
- *     c1 = async_send(exch);
- *     async_exchange_end(exch);
- *
- *     exch = async_exchange_begin(conn);
- *     c2 = async_send(exch);
- *     async_exchange_end(exch);
- *
- *     async_wait_for(c1);
- *     async_wait_for(c2);
- *     ...
- *   }
- *
- *
- * 2) Multithreaded server application
- *
- *   main()
- *   {
- *     async_manager();
- *   }
- *
- *   port_handler(ichandle, *icall)
- *   {
- *     if (want_refuse) {
- *       async_answer_0(ichandle, ELIMIT);
- *       return;
- *     }
- *     async_answer_0(ichandle, EOK);
- *
- *     chandle = async_get_call(&call);
- *     somehow_handle_the_call(chandle, call);
- *     async_answer_2(chandle, 1, 2, 3);
- *
- *     chandle = async_get_call(&call);
- *     ...
- *   }
- *
- */
-
-#define LIBC_ASYNC_C_
-#include <ipc/ipc.h>
-#include <async.h>
-#include "private/async.h"
-#undef LIBC_ASYNC_C_
-
-#include <ipc/irq.h>
-#include <ipc/event.h>
-#include <futex.h>
-#include <fibril.h>
-#include <adt/hash_table.h>
-#include <adt/hash.h>
-#include <adt/list.h>
-#include <assert.h>
-#include <errno.h>
-#include <sys/time.h>
-#include <libarch/barrier.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <mem.h>
-#include <stdlib.h>
-#include <macros.h>
-#include <as.h>
-#include <abi/mm/as.h>
-#include "private/libc.h"
-
-/** Session data */
-struct async_sess {
-	/** List of inactive exchanges */
-	list_t exch_list;
-
-	/** Session interface */
-	iface_t iface;
-
-	/** Exchange management style */
-	exch_mgmt_t mgmt;
-
-	/** Session identification */
-	cap_phone_handle_t phone;
-
-	/** First clone connection argument */
-	sysarg_t arg1;
-
-	/** Second clone connection argument */
-	sysarg_t arg2;
-
-	/** Third clone connection argument */
-	sysarg_t arg3;
-
-	/** Exchange mutex */
-	fibril_mutex_t mutex;
-
-	/** Number of opened exchanges */
-	atomic_t refcnt;
-
-	/** Mutex for stateful connections */
-	fibril_mutex_t remote_state_mtx;
-
-	/** Data for stateful connections */
-	void *remote_state_data;
-};
-
-/** Exchange data */
-struct async_exch {
-	/** Link into list of inactive exchanges */
-	link_t sess_link;
-
-	/** Link into global list of inactive exchanges */
-	link_t global_link;
-
-	/** Session pointer */
-	async_sess_t *sess;
-
-	/** Exchange identification */
-	cap_phone_handle_t phone;
-};
-
-/** Async framework global futex */
-futex_t async_futex = FUTEX_INITIALIZER;
-
-/** Number of threads waiting for IPC in the kernel. */
-atomic_t threads_in_ipc_wait = { 0 };
-
-/** Naming service session */
-async_sess_t *session_ns;
-
-/** Call data */
-typedef struct {
-	link_t link;
-
-	cap_call_handle_t chandle;
-	ipc_call_t call;
-} msg_t;
-
-/** Message data */
-typedef struct {
-	awaiter_t wdata;
-
-	/** If reply was received. */
-	bool done;
-
-	/** If the message / reply should be discarded on arrival. */
-	bool forget;
-
-	/** If already destroyed. */
-	bool destroyed;
-
-	/** Pointer to where the answer data is stored. */
-	ipc_call_t *dataptr;
-
-	errno_t retval;
-} amsg_t;
-
-/* Client connection data */
-typedef struct {
-	ht_link_t link;
-
-	task_id_t in_task_id;
-	atomic_t refcnt;
-	void *data;
-} client_t;
-
-/* Server connection data */
-typedef struct {
-	awaiter_t wdata;
-
-	/** Hash table link. */
-	ht_link_t link;
-
-	/** Incoming client task ID. */
-	task_id_t in_task_id;
-
-	/** Incoming phone hash. */
-	sysarg_t in_phone_hash;
-
-	/** Link to the client tracking structure. */
-	client_t *client;
-
-	/** Messages that should be delivered to this fibril. */
-	list_t msg_queue;
-
-	/** Identification of the opening call. */
-	cap_call_handle_t chandle;
-
-	/** Call data of the opening call. */
-	ipc_call_t call;
-
-	/** Identification of the closing call. */
-	cap_call_handle_t close_chandle;
-
-	/** Fibril function that will be used to handle the connection. */
-	async_port_handler_t handler;
-
-	/** Client data */
-	void *data;
-} connection_t;
-
-/** Interface data */
-typedef struct {
-	ht_link_t link;
-
-	/** Interface ID */
-	iface_t iface;
-
-	/** Futex protecting the hash table */
-	futex_t futex;
-
-	/** Interface ports */
-	hash_table_t port_hash_table;
-
-	/** Next available port ID */
-	port_id_t port_id_avail;
-} interface_t;
-
-/* Port data */
-typedef struct {
-	ht_link_t link;
-
-	/** Port ID */
-	port_id_t id;
-
-	/** Port connection handler */
-	async_port_handler_t handler;
-
-	/** Client data */
-	void *data;
-} port_t;
-
-/* Notification data */
-typedef struct {
-	ht_link_t link;
-
-	/** Notification method */
-	sysarg_t imethod;
-
-	/** Notification handler */
-	async_notification_handler_t handler;
-
-	/** Notification data */
-	void *data;
-} notification_t;
-
-/** Identifier of the incoming connection handled by the current fibril. */
-static fibril_local connection_t *fibril_connection;
-
-static void to_event_initialize(to_event_t *to)
-{
-	struct timeval tv = { 0, 0 };
-
-	to->inlist = false;
-	to->occurred = false;
-	link_initialize(&to->link);
-	to->expires = tv;
-}
-
-static void wu_event_initialize(wu_event_t *wu)
-{
-	wu->inlist = false;
-	link_initialize(&wu->link);
-}
-
-void awaiter_initialize(awaiter_t *aw)
-{
-	aw->fid = 0;
-	aw->active = false;
-	to_event_initialize(&aw->to_event);
-	wu_event_initialize(&aw->wu_event);
-}
-
-static amsg_t *amsg_create(void)
-{
-	amsg_t *msg = malloc(sizeof(amsg_t));
-	if (msg) {
-		msg->done = false;
-		msg->forget = false;
-		msg->destroyed = false;
-		msg->dataptr = NULL;
-		msg->retval = EINVAL;
-		awaiter_initialize(&msg->wdata);
-	}
-
-	return msg;
-}
-
-static void amsg_destroy(amsg_t *msg)
-{
-	assert(!msg->destroyed);
-	msg->destroyed = true;
-	free(msg);
-}
-
-static void *default_client_data_constructor(void)
-{
-	return NULL;
-}
-
-static void default_client_data_destructor(void *data)
-{
-}
-
-static async_client_data_ctor_t async_client_data_create =
-    default_client_data_constructor;
-static async_client_data_dtor_t async_client_data_destroy =
-    default_client_data_destructor;
-
-void async_set_client_data_constructor(async_client_data_ctor_t ctor)
-{
-	assert(async_client_data_create == default_client_data_constructor);
-	async_client_data_create = ctor;
-}
-
-void async_set_client_data_destructor(async_client_data_dtor_t dtor)
-{
-	assert(async_client_data_destroy == default_client_data_destructor);
-	async_client_data_destroy = dtor;
-}
-
-/** Default fallback fibril function.
- *
- * This fallback fibril function gets called on incomming connections that do
- * not have a specific handler defined.
- *
- * @param chandle  Handle of the incoming call.
- * @param call     Data of the incoming call.
- * @param arg      Local argument
- *
- */
-static void default_fallback_port_handler(cap_call_handle_t chandle,
-    ipc_call_t *call, void *arg)
-{
-	ipc_answer_0(chandle, ENOENT);
-}
-
-static async_port_handler_t fallback_port_handler =
-    default_fallback_port_handler;
-static void *fallback_port_data = NULL;
-
-static hash_table_t interface_hash_table;
-
-static size_t interface_key_hash(void *key)
-{
-	iface_t iface = *(iface_t *) key;
-	return iface;
-}
-
-static size_t interface_hash(const ht_link_t *item)
-{
-	interface_t *interface = hash_table_get_inst(item, interface_t, link);
-	return interface_key_hash(&interface->iface);
-}
-
-static bool interface_key_equal(void *key, const ht_link_t *item)
-{
-	iface_t iface = *(iface_t *) key;
-	interface_t *interface = hash_table_get_inst(item, interface_t, link);
-	return iface == interface->iface;
-}
-
-/** Operations for the port hash table. */
-static hash_table_ops_t interface_hash_table_ops = {
-	.hash = interface_hash,
-	.key_hash = interface_key_hash,
-	.key_equal = interface_key_equal,
-	.equal = NULL,
-	.remove_callback = NULL
-};
-
-static size_t port_key_hash(void *key)
-{
-	port_id_t port_id = *(port_id_t *) key;
-	return port_id;
-}
-
-static size_t port_hash(const ht_link_t *item)
-{
-	port_t *port = hash_table_get_inst(item, port_t, link);
-	return port_key_hash(&port->id);
-}
-
-static bool port_key_equal(void *key, const ht_link_t *item)
-{
-	port_id_t port_id = *(port_id_t *) key;
-	port_t *port = hash_table_get_inst(item, port_t, link);
-	return port_id == port->id;
-}
-
-/** Operations for the port hash table. */
-static hash_table_ops_t port_hash_table_ops = {
-	.hash = port_hash,
-	.key_hash = port_key_hash,
-	.key_equal = port_key_equal,
-	.equal = NULL,
-	.remove_callback = NULL
-};
-
-static interface_t *async_new_interface(iface_t iface)
-{
-	interface_t *interface =
-	    (interface_t *) malloc(sizeof(interface_t));
-	if (!interface)
-		return NULL;
-
-	bool ret = hash_table_create(&interface->port_hash_table, 0, 0,
-	    &port_hash_table_ops);
-	if (!ret) {
-		free(interface);
-		return NULL;
-	}
-
-	interface->iface = iface;
-	futex_initialize(&interface->futex, 1);
-	interface->port_id_avail = 0;
-
-	hash_table_insert(&interface_hash_table, &interface->link);
-
-	return interface;
-}
-
-static port_t *async_new_port(interface_t *interface,
-    async_port_handler_t handler, void *data)
-{
-	port_t *port = (port_t *) malloc(sizeof(port_t));
-	if (!port)
-		return NULL;
-
-	futex_down(&interface->futex);
-
-	port_id_t id = interface->port_id_avail;
-	interface->port_id_avail++;
-
-	port->id = id;
-	port->handler = handler;
-	port->data = data;
-
-	hash_table_insert(&interface->port_hash_table, &port->link);
-
-	futex_up(&interface->futex);
-
-	return port;
-}
-
-/** Mutex protecting inactive_exch_list and avail_phone_cv.
- *
- */
-static FIBRIL_MUTEX_INITIALIZE(async_sess_mutex);
-
-/** List of all currently inactive exchanges.
- *
- */
-static LIST_INITIALIZE(inactive_exch_list);
-
-/** Condition variable to wait for a phone to become available.
- *
- */
-static FIBRIL_CONDVAR_INITIALIZE(avail_phone_cv);
-
-errno_t async_create_port(iface_t iface, async_port_handler_t handler,
-    void *data, port_id_t *port_id)
-{
-	if ((iface & IFACE_MOD_MASK) == IFACE_MOD_CALLBACK)
-		return EINVAL;
-
-	interface_t *interface;
-
-	futex_down(&async_futex);
-
-	ht_link_t *link = hash_table_find(&interface_hash_table, &iface);
-	if (link)
-		interface = hash_table_get_inst(link, interface_t, link);
-	else
-		interface = async_new_interface(iface);
-
-	if (!interface) {
-		futex_up(&async_futex);
-		return ENOMEM;
-	}
-
-	port_t *port = async_new_port(interface, handler, data);
-	if (!port) {
-		futex_up(&async_futex);
-		return ENOMEM;
-	}
-
-	*port_id = port->id;
-
-	futex_up(&async_futex);
-
-	return EOK;
-}
-
-void async_set_fallback_port_handler(async_port_handler_t handler, void *data)
-{
-	assert(handler != NULL);
-
-	fallback_port_handler = handler;
-	fallback_port_data = data;
-}
-
-static hash_table_t client_hash_table;
-static hash_table_t conn_hash_table;
-static hash_table_t notification_hash_table;
-static LIST_INITIALIZE(timeout_list);
-
-static sysarg_t notification_avail = 0;
-
-static size_t client_key_hash(void *key)
-{
-	task_id_t in_task_id = *(task_id_t *) key;
-	return in_task_id;
-}
-
-static size_t client_hash(const ht_link_t *item)
-{
-	client_t *client = hash_table_get_inst(item, client_t, link);
-	return client_key_hash(&client->in_task_id);
-}
-
-static bool client_key_equal(void *key, const ht_link_t *item)
-{
-	task_id_t in_task_id = *(task_id_t *) key;
-	client_t *client = hash_table_get_inst(item, client_t, link);
-	return in_task_id == client->in_task_id;
-}
-
-/** Operations for the client hash table. */
-static hash_table_ops_t client_hash_table_ops = {
-	.hash = client_hash,
-	.key_hash = client_key_hash,
-	.key_equal = client_key_equal,
-	.equal = NULL,
-	.remove_callback = NULL
-};
-
-typedef struct {
-	task_id_t task_id;
-	sysarg_t phone_hash;
-} conn_key_t;
-
-/** Compute hash into the connection hash table
- *
- * The hash is based on the source task ID and the source phone hash. The task
- * ID is included in the hash because a phone hash alone might not be unique
- * while we still track connections for killed tasks due to kernel's recycling
- * of phone structures.
- *
- * @param key Pointer to the connection key structure.
- *
- * @return Index into the connection hash table.
- *
- */
-static size_t conn_key_hash(void *key)
-{
-	conn_key_t *ck = (conn_key_t *) key;
-
-	size_t hash = 0;
-	hash = hash_combine(hash, LOWER32(ck->task_id));
-	hash = hash_combine(hash, UPPER32(ck->task_id));
-	hash = hash_combine(hash, ck->phone_hash);
-	return hash;
-}
-
-static size_t conn_hash(const ht_link_t *item)
-{
-	connection_t *conn = hash_table_get_inst(item, connection_t, link);
-	return conn_key_hash(&(conn_key_t){
-		.task_id = conn->in_task_id,
-		.phone_hash = conn->in_phone_hash
-	});
-}
-
-static bool conn_key_equal(void *key, const ht_link_t *item)
-{
-	conn_key_t *ck = (conn_key_t *) key;
-	connection_t *conn = hash_table_get_inst(item, connection_t, link);
-	return ((ck->task_id == conn->in_task_id) &&
-	    (ck->phone_hash == conn->in_phone_hash));
-}
-
-/** Operations for the connection hash table. */
-static hash_table_ops_t conn_hash_table_ops = {
-	.hash = conn_hash,
-	.key_hash = conn_key_hash,
-	.key_equal = conn_key_equal,
-	.equal = NULL,
-	.remove_callback = NULL
-};
-
-static client_t *async_client_get(task_id_t client_id, bool create)
-{
-	client_t *client = NULL;
-
-	futex_down(&async_futex);
-	ht_link_t *link = hash_table_find(&client_hash_table, &client_id);
-	if (link) {
-		client = hash_table_get_inst(link, client_t, link);
-		atomic_inc(&client->refcnt);
-	} else if (create) {
-		client = malloc(sizeof(client_t));
-		if (client) {
-			client->in_task_id = client_id;
-			client->data = async_client_data_create();
-
-			atomic_set(&client->refcnt, 1);
-			hash_table_insert(&client_hash_table, &client->link);
-		}
-	}
-
-	futex_up(&async_futex);
-	return client;
-}
-
-static void async_client_put(client_t *client)
-{
-	bool destroy;
-
-	futex_down(&async_futex);
-
-	if (atomic_predec(&client->refcnt) == 0) {
-		hash_table_remove(&client_hash_table, &client->in_task_id);
-		destroy = true;
-	} else
-		destroy = false;
-
-	futex_up(&async_futex);
-
-	if (destroy) {
-		if (client->data)
-			async_client_data_destroy(client->data);
-
-		free(client);
-	}
-}
-
-/** Wrapper for client connection fibril.
- *
- * When a new connection arrives, a fibril with this implementing
- * function is created.
- *
- * @param arg Connection structure pointer.
- *
- * @return Always zero.
- *
- */
-static errno_t connection_fibril(void *arg)
-{
-	assert(arg);
-
-	/*
-	 * Setup fibril-local connection pointer.
-	 */
-	fibril_connection = (connection_t *) arg;
-
-	/*
-	 * Add our reference for the current connection in the client task
-	 * tracking structure. If this is the first reference, create and
-	 * hash in a new tracking structure.
-	 */
-
-	client_t *client = async_client_get(fibril_connection->in_task_id, true);
-	if (!client) {
-		ipc_answer_0(fibril_connection->chandle, ENOMEM);
-		return 0;
-	}
-
-	fibril_connection->client = client;
-
-	/*
-	 * Call the connection handler function.
-	 */
-	fibril_connection->handler(fibril_connection->chandle,
-	    &fibril_connection->call, fibril_connection->data);
-
-	/*
-	 * Remove the reference for this client task connection.
-	 */
-	async_client_put(client);
-
-	/*
-	 * Remove myself from the connection hash table.
-	 */
-	futex_down(&async_futex);
-	hash_table_remove(&conn_hash_table, &(conn_key_t){
-		.task_id = fibril_connection->in_task_id,
-		.phone_hash = fibril_connection->in_phone_hash
-	});
-	futex_up(&async_futex);
-
-	/*
-	 * Answer all remaining messages with EHANGUP.
-	 */
-	while (!list_empty(&fibril_connection->msg_queue)) {
-		msg_t *msg =
-		    list_get_instance(list_first(&fibril_connection->msg_queue),
-		    msg_t, link);
-
-		list_remove(&msg->link);
-		ipc_answer_0(msg->chandle, EHANGUP);
-		free(msg);
-	}
-
-	/*
-	 * If the connection was hung-up, answer the last call,
-	 * i.e. IPC_M_PHONE_HUNGUP.
-	 */
-	if (fibril_connection->close_chandle)
-		ipc_answer_0(fibril_connection->close_chandle, EOK);
-
-	free(fibril_connection);
-	return EOK;
-}
-
-/** Create a new fibril for a new connection.
- *
- * Create new fibril for connection, fill in connection structures and insert it
- * into the hash table, so that later we can easily do routing of messages to
- * particular fibrils.
- *
- * @param in_task_id     Identification of the incoming connection.
- * @param in_phone_hash  Identification of the incoming connection.
- * @param chandle        Handle of the opening IPC_M_CONNECT_ME_TO call.
- *                       If chandle is CAP_NIL, 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 handler        Connection handler.
- * @param data           Client argument to pass to the connection handler.
- *
- * @return  New fibril id or NULL on failure.
- *
- */
-static fid_t async_new_connection(task_id_t in_task_id, sysarg_t in_phone_hash,
-    cap_call_handle_t chandle, ipc_call_t *call, async_port_handler_t handler,
-    void *data)
-{
-	connection_t *conn = malloc(sizeof(*conn));
-	if (!conn) {
-		if (chandle != CAP_NIL)
-			ipc_answer_0(chandle, ENOMEM);
-
-		return (uintptr_t) NULL;
-	}
-
-	conn->in_task_id = in_task_id;
-	conn->in_phone_hash = in_phone_hash;
-	list_initialize(&conn->msg_queue);
-	conn->chandle = chandle;
-	conn->close_chandle = CAP_NIL;
-	conn->handler = handler;
-	conn->data = data;
-
-	if (call)
-		conn->call = *call;
-
-	/* We will activate the fibril ASAP */
-	conn->wdata.active = true;
-	conn->wdata.fid = fibril_create(connection_fibril, conn);
-
-	if (conn->wdata.fid == 0) {
-		free(conn);
-
-		if (chandle != CAP_NIL)
-			ipc_answer_0(chandle, ENOMEM);
-
-		return (uintptr_t) NULL;
-	}
-
-	/* Add connection to the connection hash table */
-
-	futex_down(&async_futex);
-	hash_table_insert(&conn_hash_table, &conn->link);
-	futex_up(&async_futex);
-
-	fibril_add_ready(conn->wdata.fid);
-
-	return conn->wdata.fid;
-}
-
-/** Wrapper for making IPC_M_CONNECT_TO_ME calls using the async framework.
- *
- * Ask through phone for a new connection to some service.
- *
- * @param exch    Exchange for sending the message.
- * @param iface   Callback interface.
- * @param arg1    User defined argument.
- * @param arg2    User defined argument.
- * @param handler Callback handler.
- * @param data    Handler data.
- * @param port_id ID of the newly created port.
- *
- * @return Zero on success or an error code.
- *
- */
-errno_t async_create_callback_port(async_exch_t *exch, iface_t iface, sysarg_t arg1,
-    sysarg_t arg2, async_port_handler_t handler, void *data, port_id_t *port_id)
-{
-	if ((iface & IFACE_MOD_CALLBACK) != IFACE_MOD_CALLBACK)
-		return EINVAL;
-
-	if (exch == NULL)
-		return ENOENT;
-
-	ipc_call_t answer;
-	aid_t req = async_send_3(exch, IPC_M_CONNECT_TO_ME, iface, arg1, arg2,
-	    &answer);
-
-	errno_t ret;
-	async_wait_for(req, &ret);
-	if (ret != EOK)
-		return (errno_t) ret;
-
-	sysarg_t phone_hash = IPC_GET_ARG5(answer);
-	interface_t *interface;
-
-	futex_down(&async_futex);
-
-	ht_link_t *link = hash_table_find(&interface_hash_table, &iface);
-	if (link)
-		interface = hash_table_get_inst(link, interface_t, link);
-	else
-		interface = async_new_interface(iface);
-
-	if (!interface) {
-		futex_up(&async_futex);
-		return ENOMEM;
-	}
-
-	port_t *port = async_new_port(interface, handler, data);
-	if (!port) {
-		futex_up(&async_futex);
-		return ENOMEM;
-	}
-
-	*port_id = port->id;
-
-	futex_up(&async_futex);
-
-	fid_t fid = async_new_connection(answer.in_task_id, phone_hash,
-	    CAP_NIL, NULL, handler, data);
-	if (fid == (uintptr_t) NULL)
-		return ENOMEM;
-
-	return EOK;
-}
-
-static size_t notification_key_hash(void *key)
-{
-	sysarg_t id = *(sysarg_t *) key;
-	return id;
-}
-
-static size_t notification_hash(const ht_link_t *item)
-{
-	notification_t *notification =
-	    hash_table_get_inst(item, notification_t, link);
-	return notification_key_hash(&notification->imethod);
-}
-
-static bool notification_key_equal(void *key, const ht_link_t *item)
-{
-	sysarg_t id = *(sysarg_t *) key;
-	notification_t *notification =
-	    hash_table_get_inst(item, notification_t, link);
-	return id == notification->imethod;
-}
-
-/** Operations for the notification hash table. */
-static hash_table_ops_t notification_hash_table_ops = {
-	.hash = notification_hash,
-	.key_hash = notification_key_hash,
-	.key_equal = notification_key_equal,
-	.equal = NULL,
-	.remove_callback = NULL
-};
-
-/** Sort in current fibril's timeout request.
- *
- * @param wd Wait data of the current fibril.
- *
- */
-void async_insert_timeout(awaiter_t *wd)
-{
-	assert(wd);
-
-	wd->to_event.occurred = false;
-	wd->to_event.inlist = true;
-
-	link_t *tmp = timeout_list.head.next;
-	while (tmp != &timeout_list.head) {
-		awaiter_t *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_insert_before(&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 chandle  Handle of the incoming call.
- * @param call     Data of the incoming call.
- *
- * @return False if the call doesn't match any connection.
- * @return True if the call was passed to the respective connection fibril.
- *
- */
-static bool route_call(cap_call_handle_t chandle, ipc_call_t *call)
-{
-	assert(call);
-
-	futex_down(&async_futex);
-
-	ht_link_t *link = hash_table_find(&conn_hash_table, &(conn_key_t){
-		.task_id = call->in_task_id,
-		.phone_hash = call->in_phone_hash
-	});
-	if (!link) {
-		futex_up(&async_futex);
-		return false;
-	}
-
-	connection_t *conn = hash_table_get_inst(link, connection_t, link);
-
-	msg_t *msg = malloc(sizeof(*msg));
-	if (!msg) {
-		futex_up(&async_futex);
-		return false;
-	}
-
-	msg->chandle = chandle;
-	msg->call = *call;
-	list_append(&msg->link, &conn->msg_queue);
-
-	if (IPC_GET_IMETHOD(*call) == IPC_M_PHONE_HUNGUP)
-		conn->close_chandle = chandle;
-
-	/* 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;
-}
-
-/** Process notification.
- *
- * @param call   Data of the incoming call.
- *
- */
-static void process_notification(ipc_call_t *call)
-{
-	async_notification_handler_t handler = NULL;
-	void *data = NULL;
-
-	assert(call);
-
-	futex_down(&async_futex);
-
-	ht_link_t *link = hash_table_find(&notification_hash_table,
-	    &IPC_GET_IMETHOD(*call));
-	if (link) {
-		notification_t *notification =
-		    hash_table_get_inst(link, notification_t, link);
-		handler = notification->handler;
-		data = notification->data;
-	}
-
-	futex_up(&async_futex);
-
-	if (handler)
-		handler(call, data);
-}
-
-/** Subscribe to IRQ notification.
- *
- * @param inr     IRQ number.
- * @param handler Notification handler.
- * @param data    Notification handler client data.
- * @param ucode   Top-half pseudocode handler.
- *
- * @param[out] handle  IRQ capability handle on success.
- *
- * @return An error code.
- *
- */
-errno_t async_irq_subscribe(int inr, async_notification_handler_t handler,
-    void *data, const irq_code_t *ucode, cap_irq_handle_t *handle)
-{
-	notification_t *notification =
-	    (notification_t *) malloc(sizeof(notification_t));
-	if (!notification)
-		return ENOMEM;
-
-	futex_down(&async_futex);
-
-	sysarg_t imethod = notification_avail;
-	notification_avail++;
-
-	notification->imethod = imethod;
-	notification->handler = handler;
-	notification->data = data;
-
-	hash_table_insert(&notification_hash_table, &notification->link);
-
-	futex_up(&async_futex);
-
-	cap_irq_handle_t ihandle;
-	errno_t rc = ipc_irq_subscribe(inr, imethod, ucode, &ihandle);
-	if (rc == EOK && handle != NULL) {
-		*handle = ihandle;
-	}
-	return rc;
-}
-
-/** Unsubscribe from IRQ notification.
- *
- * @param handle  IRQ capability handle.
- *
- * @return Zero on success or an error code.
- *
- */
-errno_t async_irq_unsubscribe(cap_irq_handle_t ihandle)
-{
-	// TODO: Remove entry from hash table
-	//       to avoid memory leak
-
-	return ipc_irq_unsubscribe(ihandle);
-}
-
-/** Subscribe to event notifications.
- *
- * @param evno    Event type to subscribe.
- * @param handler Notification handler.
- * @param data    Notification handler client data.
- *
- * @return Zero on success or an error code.
- *
- */
-errno_t async_event_subscribe(event_type_t evno,
-    async_notification_handler_t handler, void *data)
-{
-	notification_t *notification =
-	    (notification_t *) malloc(sizeof(notification_t));
-	if (!notification)
-		return ENOMEM;
-
-	futex_down(&async_futex);
-
-	sysarg_t imethod = notification_avail;
-	notification_avail++;
-
-	notification->imethod = imethod;
-	notification->handler = handler;
-	notification->data = data;
-
-	hash_table_insert(&notification_hash_table, &notification->link);
-
-	futex_up(&async_futex);
-
-	return ipc_event_subscribe(evno, imethod);
-}
-
-/** Subscribe to task event notifications.
- *
- * @param evno    Event type to subscribe.
- * @param handler Notification handler.
- * @param data    Notification handler client data.
- *
- * @return Zero on success or an error code.
- *
- */
-errno_t async_event_task_subscribe(event_task_type_t evno,
-    async_notification_handler_t handler, void *data)
-{
-	notification_t *notification =
-	    (notification_t *) malloc(sizeof(notification_t));
-	if (!notification)
-		return ENOMEM;
-
-	futex_down(&async_futex);
-
-	sysarg_t imethod = notification_avail;
-	notification_avail++;
-
-	notification->imethod = imethod;
-	notification->handler = handler;
-	notification->data = data;
-
-	hash_table_insert(&notification_hash_table, &notification->link);
-
-	futex_up(&async_futex);
-
-	return ipc_event_task_subscribe(evno, imethod);
-}
-
-/** Unmask event notifications.
- *
- * @param evno Event type to unmask.
- *
- * @return Value returned by the kernel.
- *
- */
-errno_t async_event_unmask(event_type_t evno)
-{
-	return ipc_event_unmask(evno);
-}
-
-/** Unmask task event notifications.
- *
- * @param evno Event type to unmask.
- *
- * @return Value returned by the kernel.
- *
- */
-errno_t async_event_task_unmask(event_task_type_t evno)
-{
-	return ipc_event_task_unmask(evno);
-}
-
-/** 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 handle of the incoming call is
- *          returned. If a timeout is specified, then a handle of the incoming
- *          call is returned unless the timeout expires prior to receiving a
- *          message. In that case zero CAP_NIL is returned.
- */
-cap_call_handle_t async_get_call_timeout(ipc_call_t *call, suseconds_t usecs)
-{
-	assert(call);
-	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) {
-		getuptime(&conn->wdata.to_event.expires);
-		tv_add_diff(&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_chandle) {
-			/*
-			 * 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_IMETHOD(*call, IPC_M_PHONE_HUNGUP);
-			futex_up(&async_futex);
-			return conn->close_chandle;
-		}
-
-		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 CAP_NIL;
-		}
-	}
-
-	msg_t *msg = list_get_instance(list_first(&conn->msg_queue),
-	    msg_t, link);
-	list_remove(&msg->link);
-
-	cap_call_handle_t chandle = msg->chandle;
-	*call = msg->call;
-	free(msg);
-
-	futex_up(&async_futex);
-	return chandle;
-}
-
-void *async_get_client_data(void)
-{
-	assert(fibril_connection);
-	return fibril_connection->client->data;
-}
-
-void *async_get_client_data_by_id(task_id_t client_id)
-{
-	client_t *client = async_client_get(client_id, false);
-	if (!client)
-		return NULL;
-
-	if (!client->data) {
-		async_client_put(client);
-		return NULL;
-	}
-
-	return client->data;
-}
-
-void async_put_client_data_by_id(task_id_t client_id)
-{
-	client_t *client = async_client_get(client_id, false);
-
-	assert(client);
-	assert(client->data);
-
-	/* Drop the reference we got in async_get_client_data_by_hash(). */
-	async_client_put(client);
-
-	/* Drop our own reference we got at the beginning of this function. */
-	async_client_put(client);
-}
-
-static port_t *async_find_port(iface_t iface, port_id_t port_id)
-{
-	port_t *port = NULL;
-
-	futex_down(&async_futex);
-
-	ht_link_t *link = hash_table_find(&interface_hash_table, &iface);
-	if (link) {
-		interface_t *interface =
-		    hash_table_get_inst(link, interface_t, link);
-
-		link = hash_table_find(&interface->port_hash_table, &port_id);
-		if (link)
-			port = hash_table_get_inst(link, port_t, link);
-	}
-
-	futex_up(&async_futex);
-
-	return port;
-}
-
-/** 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 chandle  Handle of the incoming call.
- * @param call     Data of the incoming call.
- *
- */
-static void handle_call(cap_call_handle_t chandle, ipc_call_t *call)
-{
-	assert(call);
-
-	/* Kernel notification */
-	if ((chandle == CAP_NIL) && (call->flags & IPC_CALL_NOTIF)) {
-		fibril_t *fibril = (fibril_t *) __tcb_get()->fibril_data;
-		unsigned oldsw = fibril->switches;
-
-		process_notification(call);
-
-		if (oldsw != fibril->switches) {
-			/*
-			 * The notification handler did not execute atomically
-			 * and so the current manager fibril assumed the role of
-			 * a notification fibril. While waiting for its
-			 * resources, it switched to another manager fibril that
-			 * had already existed or it created a new one. We
-			 * therefore know there is at least yet another
-			 * manager fibril that can take over. We now kill the
-			 * current 'notification' fibril to prevent fibril
-			 * population explosion.
-			 */
-			futex_down(&async_futex);
-			fibril_switch(FIBRIL_FROM_DEAD);
-		}
-
-		return;
-	}
-
-	/* New connection */
-	if (IPC_GET_IMETHOD(*call) == IPC_M_CONNECT_ME_TO) {
-		iface_t iface = (iface_t) IPC_GET_ARG1(*call);
-		sysarg_t in_phone_hash = IPC_GET_ARG5(*call);
-
-		async_port_handler_t handler = fallback_port_handler;
-		void *data = fallback_port_data;
-
-		// TODO: Currently ignores all ports but the first one
-		port_t *port = async_find_port(iface, 0);
-		if (port) {
-			handler = port->handler;
-			data = port->data;
-		}
-
-		async_new_connection(call->in_task_id, in_phone_hash, chandle,
-		    call, handler, data);
-		return;
-	}
-
-	/* Try to route the call through the connection hash table */
-	if (route_call(chandle, call))
-		return;
-
-	/* Unknown call from unknown phone - hang it up */
-	ipc_answer_0(chandle, EHANGUP);
-}
-
-/** Fire all timeouts that expired. */
-static void handle_expired_timeouts(void)
-{
-	struct timeval tv;
-	getuptime(&tv);
-
-	futex_down(&async_futex);
-
-	link_t *cur = list_first(&timeout_list);
-	while (cur != NULL) {
-		awaiter_t *waiter =
-		    list_get_instance(cur, awaiter_t, to_event.link);
-
-		if (tv_gt(&waiter->to_event.expires, &tv))
-			break;
-
-		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);
-		}
-
-		cur = list_first(&timeout_list);
-	}
-
-	futex_up(&async_futex);
-}
-
-/** Endless loop dispatching incoming calls and answers.
- *
- * @return Never returns.
- *
- */
-static errno_t 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;
-		unsigned int flags = SYNCH_FLAGS_NONE;
-		if (!list_empty(&timeout_list)) {
-			awaiter_t *waiter = list_get_instance(
-			    list_first(&timeout_list), awaiter_t, to_event.link);
-
-			struct timeval tv;
-			getuptime(&tv);
-
-			if (tv_gteq(&tv, &waiter->to_event.expires)) {
-				futex_up(&async_futex);
-				handle_expired_timeouts();
-				/*
-				 * Notice that even if the event(s) already
-				 * expired (and thus the other fibril was
-				 * supposed to be running already),
-				 * we check for incoming IPC.
-				 *
-				 * Otherwise, a fibril that continuously
-				 * creates (almost) expired events could
-				 * prevent IPC retrieval from the kernel.
-				 */
-				timeout = 0;
-				flags = SYNCH_FLAGS_NON_BLOCKING;
-
-			} else {
-				timeout = tv_sub_diff(&waiter->to_event.expires,
-				    &tv);
-				futex_up(&async_futex);
-			}
-		} else {
-			futex_up(&async_futex);
-			timeout = SYNCH_NO_TIMEOUT;
-		}
-
-		atomic_inc(&threads_in_ipc_wait);
-
-		ipc_call_t call;
-		errno_t rc = ipc_wait_cycle(&call, timeout, flags);
-
-		atomic_dec(&threads_in_ipc_wait);
-
-		assert(rc == EOK);
-
-		if (call.cap_handle == CAP_NIL) {
-			if ((call.flags &
-			    (IPC_CALL_NOTIF | IPC_CALL_ANSWERED)) == 0) {
-				/* Neither a notification nor an answer. */
-				handle_expired_timeouts();
-				continue;
-			}
-		}
-
-		if (call.flags & IPC_CALL_ANSWERED)
-			continue;
-
-		handle_call(call.cap_handle, &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 errno_t 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_generic(async_manager_fibril, NULL, PAGE_SIZE);
-	if (fid != 0)
-		fibril_add_manager(fid);
-}
-
-/** Remove one manager from manager list */
-void async_destroy_manager(void)
-{
-	fibril_remove_manager();
-}
-
-/** Initialize the async framework.
- *
- */
-void __async_init(void)
-{
-	if (!hash_table_create(&interface_hash_table, 0, 0,
-	    &interface_hash_table_ops))
-		abort();
-
-	if (!hash_table_create(&client_hash_table, 0, 0, &client_hash_table_ops))
-		abort();
-
-	if (!hash_table_create(&conn_hash_table, 0, 0, &conn_hash_table_ops))
-		abort();
-
-	if (!hash_table_create(&notification_hash_table, 0, 0,
-	    &notification_hash_table_ops))
-		abort();
-
-	session_ns = (async_sess_t *) malloc(sizeof(async_sess_t));
-	if (session_ns == NULL)
-		abort();
-
-	session_ns->iface = 0;
-	session_ns->mgmt = EXCHANGE_ATOMIC;
-	session_ns->phone = PHONE_NS;
-	session_ns->arg1 = 0;
-	session_ns->arg2 = 0;
-	session_ns->arg3 = 0;
-
-	fibril_mutex_initialize(&session_ns->remote_state_mtx);
-	session_ns->remote_state_data = NULL;
-
-	list_initialize(&session_ns->exch_list);
-	fibril_mutex_initialize(&session_ns->mutex);
-	atomic_set(&session_ns->refcnt, 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.
- *
- */
-void reply_received(void *arg, errno_t retval, ipc_call_t *data)
-{
-	assert(arg);
-
-	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->forget) {
-		assert(msg->wdata.active);
-		amsg_destroy(msg);
-	} else 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 exch    Exchange for sending the message.
- * @param imethod Service-defined interface and 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(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
-    sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, ipc_call_t *dataptr)
-{
-	if (exch == NULL)
-		return 0;
-
-	amsg_t *msg = amsg_create();
-	if (msg == NULL)
-		return 0;
-
-	msg->dataptr = dataptr;
-	msg->wdata.active = true;
-
-	ipc_call_async_4(exch->phone, imethod, arg1, arg2, arg3, arg4, msg,
-	    reply_received);
-
-	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 exch    Exchange for sending the message.
- * @param imethod Service-defined interface and 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(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
-    sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5,
-    ipc_call_t *dataptr)
-{
-	if (exch == NULL)
-		return 0;
-
-	amsg_t *msg = amsg_create();
-	if (msg == NULL)
-		return 0;
-
-	msg->dataptr = dataptr;
-	msg->wdata.active = true;
-
-	ipc_call_async_5(exch->phone, imethod, arg1, arg2, arg3, arg4, arg5,
-	    msg, reply_received);
-
-	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, errno_t *retval)
-{
-	assert(amsgid);
-
-	amsg_t *msg = (amsg_t *) amsgid;
-
-	futex_down(&async_futex);
-
-	assert(!msg->forget);
-	assert(!msg->destroyed);
-
-	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;
-
-	amsg_destroy(msg);
-}
-
-/** Wait for a message sent by the async framework, timeout variant.
- *
- * If the wait times out, the caller may choose to either wait again by calling
- * async_wait_for() or async_wait_timeout(), or forget the message via
- * async_forget().
- *
- * @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.
- *
- */
-errno_t async_wait_timeout(aid_t amsgid, errno_t *retval, suseconds_t timeout)
-{
-	assert(amsgid);
-
-	amsg_t *msg = (amsg_t *) amsgid;
-
-	futex_down(&async_futex);
-
-	assert(!msg->forget);
-	assert(!msg->destroyed);
-
-	if (msg->done) {
-		futex_up(&async_futex);
-		goto done;
-	}
-
-	/*
-	 * Negative timeout is converted to zero timeout to avoid
-	 * using tv_add with negative augmenter.
-	 */
-	if (timeout < 0)
-		timeout = 0;
-
-	getuptime(&msg->wdata.to_event.expires);
-	tv_add_diff(&msg->wdata.to_event.expires, timeout);
-
-	/*
-	 * Current fibril is inserted as waiting regardless of the
-	 * "size" of the timeout.
-	 *
-	 * Checking for msg->done and immediately bailing out when
-	 * timeout == 0 would mean that the manager fibril would never
-	 * run (consider single threaded program).
-	 * Thus the IPC answer would be never retrieved from the kernel.
-	 *
-	 * Notice that the actual delay would be very small because we
-	 * - switch to manager fibril
-	 * - the manager sees expired timeout
-	 * - and thus adds us back to ready queue
-	 * - manager switches back to some ready fibril
-	 *   (prior it, it checks for incoming IPC).
-	 *
-	 */
-	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;
-
-	amsg_destroy(msg);
-
-	return 0;
-}
-
-/** Discard the message / reply on arrival.
- *
- * The message will be marked to be discarded once the reply arrives in
- * reply_received(). It is not allowed to call async_wait_for() or
- * async_wait_timeout() on this message after a call to this function.
- *
- * @param amsgid  Hash of the message to forget.
- */
-void async_forget(aid_t amsgid)
-{
-	amsg_t *msg = (amsg_t *) amsgid;
-
-	assert(msg);
-	assert(!msg->forget);
-	assert(!msg->destroyed);
-
-	futex_down(&async_futex);
-
-	if (msg->done) {
-		amsg_destroy(msg);
-	} else {
-		msg->dataptr = NULL;
-		msg->forget = true;
-	}
-
-	futex_up(&async_futex);
-}
-
-/** 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)
-{
-	awaiter_t awaiter;
-	awaiter_initialize(&awaiter);
-
-	awaiter.fid = fibril_get_id();
-
-	getuptime(&awaiter.to_event.expires);
-	tv_add_diff(&awaiter.to_event.expires, timeout);
-
-	futex_down(&async_futex);
-
-	async_insert_timeout(&awaiter);
-
-	/* Leave the async_futex locked when entering this function */
-	fibril_switch(FIBRIL_TO_MANAGER);
-
-	/* Futex is up automatically after fibril_switch() */
-}
-
-/** Delay execution for the specified number of seconds
- *
- * @param sec Number of seconds to sleep
- */
-void async_sleep(unsigned int sec)
-{
-	/*
-	 * Sleep in 1000 second steps to support
-	 * full argument range
-	 */
-
-	while (sec > 0) {
-		unsigned int period = (sec > 1000) ? 1000 : sec;
-
-		async_usleep(period * 1000000);
-		sec -= period;
-	}
-}
-
-/** 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 exch    Exchange for sending the message.
- * @param imethod Interface and 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 an error code.
- *
- */
-errno_t async_req_fast(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
-    sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t *r1, sysarg_t *r2,
-    sysarg_t *r3, sysarg_t *r4, sysarg_t *r5)
-{
-	if (exch == NULL)
-		return ENOENT;
-
-	ipc_call_t result;
-	aid_t aid = async_send_4(exch, imethod, arg1, arg2, arg3, arg4,
-	    &result);
-
-	errno_t rc;
-	async_wait_for(aid, &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 exch    Exchange for sending the message.
- * @param imethod Interface and 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 an error code.
- *
- */
-errno_t async_req_slow(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
-    sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5, sysarg_t *r1,
-    sysarg_t *r2, sysarg_t *r3, sysarg_t *r4, sysarg_t *r5)
-{
-	if (exch == NULL)
-		return ENOENT;
-
-	ipc_call_t result;
-	aid_t aid = async_send_5(exch, imethod, arg1, arg2, arg3, arg4, arg5,
-	    &result);
-
-	errno_t rc;
-	async_wait_for(aid, &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;
-}
-
-void async_msg_0(async_exch_t *exch, sysarg_t imethod)
-{
-	if (exch != NULL)
-		ipc_call_async_0(exch->phone, imethod, NULL, NULL);
-}
-
-void async_msg_1(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1)
-{
-	if (exch != NULL)
-		ipc_call_async_1(exch->phone, imethod, arg1, NULL, NULL);
-}
-
-void async_msg_2(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
-    sysarg_t arg2)
-{
-	if (exch != NULL)
-		ipc_call_async_2(exch->phone, imethod, arg1, arg2, NULL, NULL);
-}
-
-void async_msg_3(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
-    sysarg_t arg2, sysarg_t arg3)
-{
-	if (exch != NULL)
-		ipc_call_async_3(exch->phone, imethod, arg1, arg2, arg3, NULL,
-		    NULL);
-}
-
-void async_msg_4(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
-    sysarg_t arg2, sysarg_t arg3, sysarg_t arg4)
-{
-	if (exch != NULL)
-		ipc_call_async_4(exch->phone, imethod, arg1, arg2, arg3, arg4,
-		    NULL, NULL);
-}
-
-void async_msg_5(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
-    sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5)
-{
-	if (exch != NULL)
-		ipc_call_async_5(exch->phone, imethod, arg1, arg2, arg3, arg4,
-		    arg5, NULL, NULL);
-}
-
-errno_t async_answer_0(cap_call_handle_t chandle, errno_t retval)
-{
-	return ipc_answer_0(chandle, retval);
-}
-
-errno_t async_answer_1(cap_call_handle_t chandle, errno_t retval, sysarg_t arg1)
-{
-	return ipc_answer_1(chandle, retval, arg1);
-}
-
-errno_t async_answer_2(cap_call_handle_t chandle, errno_t retval, sysarg_t arg1,
-    sysarg_t arg2)
-{
-	return ipc_answer_2(chandle, retval, arg1, arg2);
-}
-
-errno_t async_answer_3(cap_call_handle_t chandle, errno_t retval, sysarg_t arg1,
-    sysarg_t arg2, sysarg_t arg3)
-{
-	return ipc_answer_3(chandle, retval, arg1, arg2, arg3);
-}
-
-errno_t async_answer_4(cap_call_handle_t chandle, errno_t retval, sysarg_t arg1,
-    sysarg_t arg2, sysarg_t arg3, sysarg_t arg4)
-{
-	return ipc_answer_4(chandle, retval, arg1, arg2, arg3, arg4);
-}
-
-errno_t async_answer_5(cap_call_handle_t chandle, errno_t retval, sysarg_t arg1,
-    sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5)
-{
-	return ipc_answer_5(chandle, retval, arg1, arg2, arg3, arg4, arg5);
-}
-
-errno_t async_forward_fast(cap_call_handle_t chandle, async_exch_t *exch,
-    sysarg_t imethod, sysarg_t arg1, sysarg_t arg2, unsigned int mode)
-{
-	if (exch == NULL)
-		return ENOENT;
-
-	return ipc_forward_fast(chandle, exch->phone, imethod, arg1, arg2, mode);
-}
-
-errno_t async_forward_slow(cap_call_handle_t chandle, async_exch_t *exch,
-    sysarg_t imethod, sysarg_t arg1, sysarg_t arg2, sysarg_t arg3,
-    sysarg_t arg4, sysarg_t arg5, unsigned int mode)
-{
-	if (exch == NULL)
-		return ENOENT;
-
-	return ipc_forward_slow(chandle, exch->phone, imethod, arg1, arg2, arg3,
-	    arg4, arg5, mode);
-}
-
-/** Wrapper for making IPC_M_CONNECT_TO_ME calls using the async framework.
- *
- * Ask through phone for a new connection to some service.
- *
- * @param exch            Exchange for sending the message.
- * @param arg1            User defined argument.
- * @param arg2            User defined argument.
- * @param arg3            User defined argument.
- *
- * @return Zero on success or an error code.
- *
- */
-errno_t async_connect_to_me(async_exch_t *exch, sysarg_t arg1, sysarg_t arg2,
-    sysarg_t arg3)
-{
-	if (exch == NULL)
-		return ENOENT;
-
-	ipc_call_t answer;
-	aid_t req = async_send_3(exch, IPC_M_CONNECT_TO_ME, arg1, arg2, arg3,
-	    &answer);
-
-	errno_t rc;
-	async_wait_for(req, &rc);
-	if (rc != EOK)
-		return (errno_t) rc;
-
-	return EOK;
-}
-
-static errno_t async_connect_me_to_internal(cap_phone_handle_t phone,
-    sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t arg4,
-    cap_phone_handle_t *out_phone)
-{
-	ipc_call_t result;
-
-	// XXX: Workaround for GCC's inability to infer association between
-	// rc == EOK and *out_phone being assigned.
-	*out_phone = CAP_NIL;
-
-	amsg_t *msg = amsg_create();
-	if (!msg)
-		return ENOENT;
-
-	msg->dataptr = &result;
-	msg->wdata.active = true;
-
-	ipc_call_async_4(phone, IPC_M_CONNECT_ME_TO, arg1, arg2, arg3, arg4,
-	    msg, reply_received);
-
-	errno_t rc;
-	async_wait_for((aid_t) msg, &rc);
-
-	if (rc != EOK)
-		return rc;
-
-	*out_phone = (cap_phone_handle_t) IPC_GET_ARG5(result);
-	return EOK;
-}
-
-/** Wrapper for making IPC_M_CONNECT_ME_TO calls using the async framework.
- *
- * Ask through for a new connection to some service.
- *
- * @param mgmt Exchange management style.
- * @param exch Exchange for sending the message.
- * @param arg1 User defined argument.
- * @param arg2 User defined argument.
- * @param arg3 User defined argument.
- *
- * @return New session on success or NULL on error.
- *
- */
-async_sess_t *async_connect_me_to(exch_mgmt_t mgmt, async_exch_t *exch,
-    sysarg_t arg1, sysarg_t arg2, sysarg_t arg3)
-{
-	if (exch == NULL) {
-		errno = ENOENT;
-		return NULL;
-	}
-
-	async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
-	if (sess == NULL) {
-		errno = ENOMEM;
-		return NULL;
-	}
-
-	cap_phone_handle_t phone;
-	errno_t rc = async_connect_me_to_internal(exch->phone, arg1, arg2, arg3,
-	    0, &phone);
-	if (rc != EOK) {
-		errno = rc;
-		free(sess);
-		return NULL;
-	}
-
-	sess->iface = 0;
-	sess->mgmt = mgmt;
-	sess->phone = phone;
-	sess->arg1 = arg1;
-	sess->arg2 = arg2;
-	sess->arg3 = arg3;
-
-	fibril_mutex_initialize(&sess->remote_state_mtx);
-	sess->remote_state_data = NULL;
-
-	list_initialize(&sess->exch_list);
-	fibril_mutex_initialize(&sess->mutex);
-	atomic_set(&sess->refcnt, 0);
-
-	return sess;
-}
-
-/** 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 exch  Exchange for sending the message.
- * @param iface Connection interface.
- * @param arg2  User defined argument.
- * @param arg3  User defined argument.
- *
- * @return New session on success or NULL on error.
- *
- */
-async_sess_t *async_connect_me_to_iface(async_exch_t *exch, iface_t iface,
-    sysarg_t arg2, sysarg_t arg3)
-{
-	if (exch == NULL) {
-		errno = ENOENT;
-		return NULL;
-	}
-
-	async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
-	if (sess == NULL) {
-		errno = ENOMEM;
-		return NULL;
-	}
-
-	cap_phone_handle_t phone;
-	errno_t rc = async_connect_me_to_internal(exch->phone, iface, arg2,
-	    arg3, 0, &phone);
-	if (rc != EOK) {
-		errno = rc;
-		free(sess);
-		return NULL;
-	}
-
-	sess->iface = iface;
-	sess->phone = phone;
-	sess->arg1 = iface;
-	sess->arg2 = arg2;
-	sess->arg3 = arg3;
-
-	fibril_mutex_initialize(&sess->remote_state_mtx);
-	sess->remote_state_data = NULL;
-
-	list_initialize(&sess->exch_list);
-	fibril_mutex_initialize(&sess->mutex);
-	atomic_set(&sess->refcnt, 0);
-
-	return sess;
-}
-
-/** Set arguments for new connections.
- *
- * FIXME This is an ugly hack to work around the problem that parallel
- * exchanges are implemented using parallel connections. When we create
- * a callback session, the framework does not know arguments for the new
- * connections.
- *
- * The proper solution seems to be to implement parallel exchanges using
- * tagging.
- */
-void async_sess_args_set(async_sess_t *sess, sysarg_t arg1, sysarg_t arg2,
-    sysarg_t arg3)
-{
-	sess->arg1 = arg1;
-	sess->arg2 = arg2;
-	sess->arg3 = arg3;
-}
-
-/** 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 mgmt Exchange management style.
- * @param exch Exchange for sending the message.
- * @param arg1 User defined argument.
- * @param arg2 User defined argument.
- * @param arg3 User defined argument.
- *
- * @return New session on success or NULL on error.
- *
- */
-async_sess_t *async_connect_me_to_blocking(exch_mgmt_t mgmt, async_exch_t *exch,
-    sysarg_t arg1, sysarg_t arg2, sysarg_t arg3)
-{
-	if (exch == NULL) {
-		errno = ENOENT;
-		return NULL;
-	}
-
-	async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
-	if (sess == NULL) {
-		errno = ENOMEM;
-		return NULL;
-	}
-
-	cap_phone_handle_t phone;
-	errno_t rc = async_connect_me_to_internal(exch->phone, arg1, arg2, arg3,
-	    IPC_FLAG_BLOCKING, &phone);
-
-	if (rc != EOK) {
-		errno = rc;
-		free(sess);
-		return NULL;
-	}
-
-	sess->iface = 0;
-	sess->mgmt = mgmt;
-	sess->phone = phone;
-	sess->arg1 = arg1;
-	sess->arg2 = arg2;
-	sess->arg3 = arg3;
-
-	fibril_mutex_initialize(&sess->remote_state_mtx);
-	sess->remote_state_data = NULL;
-
-	list_initialize(&sess->exch_list);
-	fibril_mutex_initialize(&sess->mutex);
-	atomic_set(&sess->refcnt, 0);
-
-	return sess;
-}
-
-/** 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 exch  Exchange for sending the message.
- * @param iface Connection interface.
- * @param arg2  User defined argument.
- * @param arg3  User defined argument.
- *
- * @return New session on success or NULL on error.
- *
- */
-async_sess_t *async_connect_me_to_blocking_iface(async_exch_t *exch, iface_t iface,
-    sysarg_t arg2, sysarg_t arg3)
-{
-	if (exch == NULL) {
-		errno = ENOENT;
-		return NULL;
-	}
-
-	async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
-	if (sess == NULL) {
-		errno = ENOMEM;
-		return NULL;
-	}
-
-	cap_phone_handle_t phone;
-	errno_t rc = async_connect_me_to_internal(exch->phone, iface, arg2,
-	    arg3, IPC_FLAG_BLOCKING, &phone);
-	if (rc != EOK) {
-		errno = rc;
-		free(sess);
-		return NULL;
-	}
-
-	sess->iface = iface;
-	sess->phone = phone;
-	sess->arg1 = iface;
-	sess->arg2 = arg2;
-	sess->arg3 = arg3;
-
-	fibril_mutex_initialize(&sess->remote_state_mtx);
-	sess->remote_state_data = NULL;
-
-	list_initialize(&sess->exch_list);
-	fibril_mutex_initialize(&sess->mutex);
-	atomic_set(&sess->refcnt, 0);
-
-	return sess;
-}
-
-/** Connect to a task specified by id.
- *
- */
-async_sess_t *async_connect_kbox(task_id_t id)
-{
-	async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
-	if (sess == NULL) {
-		errno = ENOMEM;
-		return NULL;
-	}
-
-	cap_phone_handle_t phone;
-	errno_t rc = ipc_connect_kbox(id, &phone);
-	if (rc != EOK) {
-		errno = rc;
-		free(sess);
-		return NULL;
-	}
-
-	sess->iface = 0;
-	sess->mgmt = EXCHANGE_ATOMIC;
-	sess->phone = phone;
-	sess->arg1 = 0;
-	sess->arg2 = 0;
-	sess->arg3 = 0;
-
-	fibril_mutex_initialize(&sess->remote_state_mtx);
-	sess->remote_state_data = NULL;
-
-	list_initialize(&sess->exch_list);
-	fibril_mutex_initialize(&sess->mutex);
-	atomic_set(&sess->refcnt, 0);
-
-	return sess;
-}
-
-static errno_t async_hangup_internal(cap_phone_handle_t phone)
-{
-	return ipc_hangup(phone);
-}
-
-/** Wrapper for ipc_hangup.
- *
- * @param sess Session to hung up.
- *
- * @return Zero on success or an error code.
- *
- */
-errno_t async_hangup(async_sess_t *sess)
-{
-	async_exch_t *exch;
-
-	assert(sess);
-
-	if (atomic_get(&sess->refcnt) > 0)
-		return EBUSY;
-
-	fibril_mutex_lock(&async_sess_mutex);
-
-	errno_t rc = async_hangup_internal(sess->phone);
-
-	while (!list_empty(&sess->exch_list)) {
-		exch = (async_exch_t *)
-		    list_get_instance(list_first(&sess->exch_list),
-		    async_exch_t, sess_link);
-
-		list_remove(&exch->sess_link);
-		list_remove(&exch->global_link);
-		async_hangup_internal(exch->phone);
-		free(exch);
-	}
-
-	free(sess);
-
-	fibril_mutex_unlock(&async_sess_mutex);
-
-	return rc;
-}
-
-/** Interrupt one thread of this task from waiting for IPC. */
-void async_poke(void)
-{
-	ipc_poke();
-}
-
-/** Start new exchange in a session.
- *
- * @param session Session.
- *
- * @return New exchange or NULL on error.
- *
- */
-async_exch_t *async_exchange_begin(async_sess_t *sess)
-{
-	if (sess == NULL)
-		return NULL;
-
-	exch_mgmt_t mgmt = sess->mgmt;
-	if (sess->iface != 0)
-		mgmt = sess->iface & IFACE_EXCHANGE_MASK;
-
-	async_exch_t *exch = NULL;
-
-	fibril_mutex_lock(&async_sess_mutex);
-
-	if (!list_empty(&sess->exch_list)) {
-		/*
-		 * There are inactive exchanges in the session.
-		 */
-		exch = (async_exch_t *)
-		    list_get_instance(list_first(&sess->exch_list),
-		    async_exch_t, sess_link);
-
-		list_remove(&exch->sess_link);
-		list_remove(&exch->global_link);
-	} else {
-		/*
-		 * There are no available exchanges in the session.
-		 */
-
-		if ((mgmt == EXCHANGE_ATOMIC) ||
-		    (mgmt == EXCHANGE_SERIALIZE)) {
-			exch = (async_exch_t *) malloc(sizeof(async_exch_t));
-			if (exch != NULL) {
-				link_initialize(&exch->sess_link);
-				link_initialize(&exch->global_link);
-				exch->sess = sess;
-				exch->phone = sess->phone;
-			}
-		} else if (mgmt == EXCHANGE_PARALLEL) {
-			cap_phone_handle_t phone;
-			errno_t rc;
-
-		retry:
-			/*
-			 * Make a one-time attempt to connect a new data phone.
-			 */
-			rc = async_connect_me_to_internal(sess->phone, sess->arg1,
-			    sess->arg2, sess->arg3, 0, &phone);
-			if (rc == EOK) {
-				exch = (async_exch_t *) malloc(sizeof(async_exch_t));
-				if (exch != NULL) {
-					link_initialize(&exch->sess_link);
-					link_initialize(&exch->global_link);
-					exch->sess = sess;
-					exch->phone = phone;
-				} else
-					async_hangup_internal(phone);
-			} else if (!list_empty(&inactive_exch_list)) {
-				/*
-				 * We did not manage to connect a new phone. But we
-				 * can try to close some of the currently inactive
-				 * connections in other sessions and try again.
-				 */
-				exch = (async_exch_t *)
-				    list_get_instance(list_first(&inactive_exch_list),
-				    async_exch_t, global_link);
-
-				list_remove(&exch->sess_link);
-				list_remove(&exch->global_link);
-				async_hangup_internal(exch->phone);
-				free(exch);
-				goto retry;
-			} else {
-				/*
-				 * Wait for a phone to become available.
-				 */
-				fibril_condvar_wait(&avail_phone_cv, &async_sess_mutex);
-				goto retry;
-			}
-		}
-	}
-
-	fibril_mutex_unlock(&async_sess_mutex);
-
-	if (exch != NULL) {
-		atomic_inc(&sess->refcnt);
-
-		if (mgmt == EXCHANGE_SERIALIZE)
-			fibril_mutex_lock(&sess->mutex);
-	}
-
-	return exch;
-}
-
-/** Finish an exchange.
- *
- * @param exch Exchange to finish.
- *
- */
-void async_exchange_end(async_exch_t *exch)
-{
-	if (exch == NULL)
-		return;
-
-	async_sess_t *sess = exch->sess;
-	assert(sess != NULL);
-
-	exch_mgmt_t mgmt = sess->mgmt;
-	if (sess->iface != 0)
-		mgmt = sess->iface & IFACE_EXCHANGE_MASK;
-
-	atomic_dec(&sess->refcnt);
-
-	if (mgmt == EXCHANGE_SERIALIZE)
-		fibril_mutex_unlock(&sess->mutex);
-
-	fibril_mutex_lock(&async_sess_mutex);
-
-	list_append(&exch->sess_link, &sess->exch_list);
-	list_append(&exch->global_link, &inactive_exch_list);
-	fibril_condvar_signal(&avail_phone_cv);
-
-	fibril_mutex_unlock(&async_sess_mutex);
-}
-
-/** Wrapper for IPC_M_SHARE_IN calls using the async framework.
- *
- * @param exch  Exchange for sending the message.
- * @param size  Size of the destination address space area.
- * @param arg   User defined argument.
- * @param flags Storage for the received flags. Can be NULL.
- * @param dst   Address of the storage for the destination address space area
- *              base address. Cannot be NULL.
- *
- * @return Zero on success or an error code from errno.h.
- *
- */
-errno_t async_share_in_start(async_exch_t *exch, size_t size, sysarg_t arg,
-    unsigned int *flags, void **dst)
-{
-	if (exch == NULL)
-		return ENOENT;
-
-	sysarg_t _flags = 0;
-	sysarg_t _dst = (sysarg_t) -1;
-	errno_t res = async_req_2_4(exch, IPC_M_SHARE_IN, (sysarg_t) size,
-	    arg, NULL, &_flags, NULL, &_dst);
-
-	if (flags)
-		*flags = (unsigned int) _flags;
-
-	*dst = (void *) _dst;
-	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 chandle  Storage for the handle of the IPC_M_SHARE_IN call.
- * @param size     Destination address space area size.
- *
- * @return True on success, false on failure.
- *
- */
-bool async_share_in_receive(cap_call_handle_t *chandle, size_t *size)
-{
-	assert(chandle);
-	assert(size);
-
-	ipc_call_t data;
-	*chandle = async_get_call(&data);
-
-	if (IPC_GET_IMETHOD(data) != IPC_M_SHARE_IN)
-		return false;
-
-	*size = (size_t) IPC_GET_ARG1(data);
-	return true;
-}
-
-/** Wrapper for answering the IPC_M_SHARE_IN calls using the async framework.
- *
- * This wrapper only makes it more comfortable to answer IPC_M_SHARE_IN
- * calls so that the user doesn't have to remember the meaning of each IPC
- * argument.
- *
- * @param chandle  Handle 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.
- *
- */
-errno_t async_share_in_finalize(cap_call_handle_t chandle, void *src,
-    unsigned int flags)
-{
-	// FIXME: The source has no business deciding destination address.
-	return ipc_answer_3(chandle, EOK, (sysarg_t) src, (sysarg_t) flags,
-	    (sysarg_t) _end);
-}
-
-/** Wrapper for IPC_M_SHARE_OUT calls using the async framework.
- *
- * @param exch  Exchange for sending the message.
- * @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 an error code from errno.h.
- *
- */
-errno_t async_share_out_start(async_exch_t *exch, void *src, unsigned int flags)
-{
-	if (exch == NULL)
-		return ENOENT;
-
-	return async_req_3_0(exch, IPC_M_SHARE_OUT, (sysarg_t) src, 0,
-	    (sysarg_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 chandle  Storage for the hash of the IPC_M_SHARE_OUT call.
- * @param size     Storage for the source address space area size.
- * @param flags    Storage for the sharing flags.
- *
- * @return True on success, false on failure.
- *
- */
-bool async_share_out_receive(cap_call_handle_t *chandle, size_t *size,
-    unsigned int *flags)
-{
-	assert(chandle);
-	assert(size);
-	assert(flags);
-
-	ipc_call_t data;
-	*chandle = async_get_call(&data);
-
-	if (IPC_GET_IMETHOD(data) != IPC_M_SHARE_OUT)
-		return false;
-
-	*size = (size_t) IPC_GET_ARG2(data);
-	*flags = (unsigned int) IPC_GET_ARG3(data);
-	return true;
-}
-
-/** 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 chandle  Handle of the IPC_M_DATA_WRITE call to answer.
- * @param dst      Address of the storage for the destination address space area
- *                 base address.
- *
- * @return  Zero on success or a value from @ref errno.h on failure.
- *
- */
-errno_t async_share_out_finalize(cap_call_handle_t chandle, void **dst)
-{
-	return ipc_answer_2(chandle, EOK, (sysarg_t) _end, (sysarg_t) dst);
-}
-
-/** Start IPC_M_DATA_READ using the async framework.
- *
- * @param exch    Exchange for sending the message.
- * @param dst     Address of the beginning of the destination buffer.
- * @param size    Size of the destination buffer (in bytes).
- * @param dataptr Storage of call data (arg 2 holds actual data size).
- *
- * @return Hash of the sent message or 0 on error.
- *
- */
-aid_t async_data_read(async_exch_t *exch, void *dst, size_t size,
-    ipc_call_t *dataptr)
-{
-	return async_send_2(exch, IPC_M_DATA_READ, (sysarg_t) dst,
-	    (sysarg_t) size, dataptr);
-}
-
-/** Wrapper for IPC_M_DATA_READ calls using the async framework.
- *
- * @param exch Exchange for sending the message.
- * @param dst  Address of the beginning of the destination buffer.
- * @param size Size of the destination buffer.
- *
- * @return Zero on success or an error code from errno.h.
- *
- */
-errno_t async_data_read_start(async_exch_t *exch, void *dst, size_t size)
-{
-	if (exch == NULL)
-		return ENOENT;
-
-	return async_req_2_0(exch, IPC_M_DATA_READ, (sysarg_t) dst,
-	    (sysarg_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 chandle  Storage for the handle of the IPC_M_DATA_READ.
- * @param size     Storage for the maximum size. Can be NULL.
- *
- * @return True on success, false on failure.
- *
- */
-bool async_data_read_receive(cap_call_handle_t *chandle, size_t *size)
-{
-	ipc_call_t data;
-	return async_data_read_receive_call(chandle, &data, 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 chandle  Storage for the handle of the IPC_M_DATA_READ.
- * @param size     Storage for the maximum size. Can be NULL.
- *
- * @return True on success, false on failure.
- *
- */
-bool async_data_read_receive_call(cap_call_handle_t *chandle, ipc_call_t *data,
-    size_t *size)
-{
-	assert(chandle);
-	assert(data);
-
-	*chandle = async_get_call(data);
-
-	if (IPC_GET_IMETHOD(*data) != IPC_M_DATA_READ)
-		return false;
-
-	if (size)
-		*size = (size_t) IPC_GET_ARG2(*data);
-
-	return true;
-}
-
-/** 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 chandle  Handle 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.
- *
- */
-errno_t async_data_read_finalize(cap_call_handle_t chandle, const void *src,
-    size_t size)
-{
-	return ipc_answer_2(chandle, EOK, (sysarg_t) src, (sysarg_t) size);
-}
-
-/** Wrapper for forwarding any read request
- *
- */
-errno_t async_data_read_forward_fast(async_exch_t *exch, sysarg_t imethod,
-    sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t arg4,
-    ipc_call_t *dataptr)
-{
-	if (exch == NULL)
-		return ENOENT;
-
-	cap_call_handle_t chandle;
-	if (!async_data_read_receive(&chandle, NULL)) {
-		ipc_answer_0(chandle, EINVAL);
-		return EINVAL;
-	}
-
-	aid_t msg = async_send_fast(exch, imethod, arg1, arg2, arg3, arg4,
-	    dataptr);
-	if (msg == 0) {
-		ipc_answer_0(chandle, EINVAL);
-		return EINVAL;
-	}
-
-	errno_t retval = ipc_forward_fast(chandle, exch->phone, 0, 0, 0,
-	    IPC_FF_ROUTE_FROM_ME);
-	if (retval != EOK) {
-		async_forget(msg);
-		ipc_answer_0(chandle, retval);
-		return retval;
-	}
-
-	errno_t rc;
-	async_wait_for(msg, &rc);
-
-	return (errno_t) rc;
-}
-
-/** Wrapper for IPC_M_DATA_WRITE calls using the async framework.
- *
- * @param exch Exchange for sending the message.
- * @param src  Address of the beginning of the source buffer.
- * @param size Size of the source buffer.
- *
- * @return Zero on success or an error code from errno.h.
- *
- */
-errno_t async_data_write_start(async_exch_t *exch, const void *src, size_t size)
-{
-	if (exch == NULL)
-		return ENOENT;
-
-	return async_req_2_0(exch, IPC_M_DATA_WRITE, (sysarg_t) src,
-	    (sysarg_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 chandle  Storage for the handle of the IPC_M_DATA_WRITE.
- * @param size     Storage for the suggested size. May be NULL.
- *
- * @return  True on success, false on failure.
- *
- */
-bool async_data_write_receive(cap_call_handle_t *chandle, size_t *size)
-{
-	ipc_call_t data;
-	return async_data_write_receive_call(chandle, &data, 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 chandle  Storage for the handle of the IPC_M_DATA_WRITE.
- * @param data     Storage for the ipc call data.
- * @param size     Storage for the suggested size. May be NULL.
- *
- * @return True on success, false on failure.
- *
- */
-bool async_data_write_receive_call(cap_call_handle_t *chandle, ipc_call_t *data,
-    size_t *size)
-{
-	assert(chandle);
-	assert(data);
-
-	*chandle = async_get_call(data);
-
-	if (IPC_GET_IMETHOD(*data) != IPC_M_DATA_WRITE)
-		return false;
-
-	if (size)
-		*size = (size_t) IPC_GET_ARG2(*data);
-
-	return true;
-}
-
-/** 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 chandle  Handle 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.
- *
- */
-errno_t async_data_write_finalize(cap_call_handle_t chandle, void *dst,
-    size_t size)
-{
-	return ipc_answer_2(chandle, EOK, (sysarg_t) dst, (sysarg_t) 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.
- *
- */
-errno_t 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)
-{
-	assert(data);
-
-	cap_call_handle_t chandle;
-	size_t size;
-	if (!async_data_write_receive(&chandle, &size)) {
-		ipc_answer_0(chandle, EINVAL);
-		return EINVAL;
-	}
-
-	if (size < min_size) {
-		ipc_answer_0(chandle, EINVAL);
-		return EINVAL;
-	}
-
-	if ((max_size > 0) && (size > max_size)) {
-		ipc_answer_0(chandle, EINVAL);
-		return EINVAL;
-	}
-
-	if ((granularity > 0) && ((size % granularity) != 0)) {
-		ipc_answer_0(chandle, EINVAL);
-		return EINVAL;
-	}
-
-	void *arg_data;
-
-	if (nullterm)
-		arg_data = malloc(size + 1);
-	else
-		arg_data = malloc(size);
-
-	if (arg_data == NULL) {
-		ipc_answer_0(chandle, ENOMEM);
-		return ENOMEM;
-	}
-
-	errno_t rc = async_data_write_finalize(chandle, arg_data, size);
-	if (rc != EOK) {
-		free(arg_data);
-		return rc;
-	}
-
-	if (nullterm)
-		((char *) arg_data)[size] = 0;
-
-	*data = arg_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(errno_t retval)
-{
-	cap_call_handle_t chandle;
-	async_data_write_receive(&chandle, NULL);
-	ipc_answer_0(chandle, retval);
-}
-
-/** Wrapper for forwarding any data that is about to be received
- *
- */
-errno_t async_data_write_forward_fast(async_exch_t *exch, sysarg_t imethod,
-    sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t arg4,
-    ipc_call_t *dataptr)
-{
-	if (exch == NULL)
-		return ENOENT;
-
-	cap_call_handle_t chandle;
-	if (!async_data_write_receive(&chandle, NULL)) {
-		ipc_answer_0(chandle, EINVAL);
-		return EINVAL;
-	}
-
-	aid_t msg = async_send_fast(exch, imethod, arg1, arg2, arg3, arg4,
-	    dataptr);
-	if (msg == 0) {
-		ipc_answer_0(chandle, EINVAL);
-		return EINVAL;
-	}
-
-	errno_t retval = ipc_forward_fast(chandle, exch->phone, 0, 0, 0,
-	    IPC_FF_ROUTE_FROM_ME);
-	if (retval != EOK) {
-		async_forget(msg);
-		ipc_answer_0(chandle, retval);
-		return retval;
-	}
-
-	errno_t rc;
-	async_wait_for(msg, &rc);
-
-	return (errno_t) rc;
-}
-
-/** Wrapper for receiving the IPC_M_CONNECT_TO_ME calls.
- *
- * If the current call is IPC_M_CONNECT_TO_ME then a new
- * async session is created for the accepted phone.
- *
- * @param mgmt Exchange management style.
- *
- * @return New async session.
- * @return NULL on failure.
- *
- */
-async_sess_t *async_callback_receive(exch_mgmt_t mgmt)
-{
-	/* Accept the phone */
-	ipc_call_t call;
-	cap_call_handle_t chandle = async_get_call(&call);
-	cap_phone_handle_t phandle = (cap_handle_t) IPC_GET_ARG5(call);
-
-	if ((IPC_GET_IMETHOD(call) != IPC_M_CONNECT_TO_ME) ||
-	    !CAP_HANDLE_VALID((phandle))) {
-		async_answer_0(chandle, EINVAL);
-		return NULL;
-	}
-
-	async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
-	if (sess == NULL) {
-		async_answer_0(chandle, ENOMEM);
-		return NULL;
-	}
-
-	sess->iface = 0;
-	sess->mgmt = mgmt;
-	sess->phone = phandle;
-	sess->arg1 = 0;
-	sess->arg2 = 0;
-	sess->arg3 = 0;
-
-	fibril_mutex_initialize(&sess->remote_state_mtx);
-	sess->remote_state_data = NULL;
-
-	list_initialize(&sess->exch_list);
-	fibril_mutex_initialize(&sess->mutex);
-	atomic_set(&sess->refcnt, 0);
-
-	/* Acknowledge the connected phone */
-	async_answer_0(chandle, EOK);
-
-	return sess;
-}
-
-/** Wrapper for receiving the IPC_M_CONNECT_TO_ME calls.
- *
- * If the call is IPC_M_CONNECT_TO_ME then a new
- * async session is created. However, the phone is
- * not accepted automatically.
- *
- * @param mgmt   Exchange management style.
- * @param call   Call data.
- *
- * @return New async session.
- * @return NULL on failure.
- * @return NULL if the call is not IPC_M_CONNECT_TO_ME.
- *
- */
-async_sess_t *async_callback_receive_start(exch_mgmt_t mgmt, ipc_call_t *call)
-{
-	cap_phone_handle_t phandle = (cap_handle_t) IPC_GET_ARG5(*call);
-
-	if ((IPC_GET_IMETHOD(*call) != IPC_M_CONNECT_TO_ME) ||
-	    !CAP_HANDLE_VALID((phandle)))
-		return NULL;
-
-	async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
-	if (sess == NULL)
-		return NULL;
-
-	sess->iface = 0;
-	sess->mgmt = mgmt;
-	sess->phone = phandle;
-	sess->arg1 = 0;
-	sess->arg2 = 0;
-	sess->arg3 = 0;
-
-	fibril_mutex_initialize(&sess->remote_state_mtx);
-	sess->remote_state_data = NULL;
-
-	list_initialize(&sess->exch_list);
-	fibril_mutex_initialize(&sess->mutex);
-	atomic_set(&sess->refcnt, 0);
-
-	return sess;
-}
-
-errno_t async_state_change_start(async_exch_t *exch, sysarg_t arg1, sysarg_t arg2,
-    sysarg_t arg3, async_exch_t *other_exch)
-{
-	return async_req_5_0(exch, IPC_M_STATE_CHANGE_AUTHORIZE,
-	    arg1, arg2, arg3, 0, CAP_HANDLE_RAW(other_exch->phone));
-}
-
-bool async_state_change_receive(cap_call_handle_t *chandle, sysarg_t *arg1,
-    sysarg_t *arg2, sysarg_t *arg3)
-{
-	assert(chandle);
-
-	ipc_call_t call;
-	*chandle = async_get_call(&call);
-
-	if (IPC_GET_IMETHOD(call) != IPC_M_STATE_CHANGE_AUTHORIZE)
-		return false;
-
-	if (arg1)
-		*arg1 = IPC_GET_ARG1(call);
-	if (arg2)
-		*arg2 = IPC_GET_ARG2(call);
-	if (arg3)
-		*arg3 = IPC_GET_ARG3(call);
-
-	return true;
-}
-
-errno_t async_state_change_finalize(cap_call_handle_t chandle,
-    async_exch_t *other_exch)
-{
-	return ipc_answer_1(chandle, EOK, CAP_HANDLE_RAW(other_exch->phone));
-}
-
-/** Lock and get session remote state
- *
- * Lock and get the local replica of the remote state
- * in stateful sessions. The call should be paired
- * with async_remote_state_release*().
- *
- * @param[in] sess Stateful session.
- *
- * @return Local replica of the remote state.
- *
- */
-void *async_remote_state_acquire(async_sess_t *sess)
-{
-	fibril_mutex_lock(&sess->remote_state_mtx);
-	return sess->remote_state_data;
-}
-
-/** Update the session remote state
- *
- * Update the local replica of the remote state
- * in stateful sessions. The remote state must
- * be already locked.
- *
- * @param[in] sess  Stateful session.
- * @param[in] state New local replica of the remote state.
- *
- */
-void async_remote_state_update(async_sess_t *sess, void *state)
-{
-	assert(fibril_mutex_is_locked(&sess->remote_state_mtx));
-	sess->remote_state_data = state;
-}
-
-/** Release the session remote state
- *
- * Unlock the local replica of the remote state
- * in stateful sessions.
- *
- * @param[in] sess Stateful session.
- *
- */
-void async_remote_state_release(async_sess_t *sess)
-{
-	assert(fibril_mutex_is_locked(&sess->remote_state_mtx));
-
-	fibril_mutex_unlock(&sess->remote_state_mtx);
-}
-
-/** Release the session remote state and end an exchange
- *
- * Unlock the local replica of the remote state
- * in stateful sessions. This is convenience function
- * which gets the session pointer from the exchange
- * and also ends the exchange.
- *
- * @param[in] exch Stateful session's exchange.
- *
- */
-void async_remote_state_release_exchange(async_exch_t *exch)
-{
-	if (exch == NULL)
-		return;
-
-	async_sess_t *sess = exch->sess;
-	assert(fibril_mutex_is_locked(&sess->remote_state_mtx));
-
-	async_exchange_end(exch);
-	fibril_mutex_unlock(&sess->remote_state_mtx);
-}
-
-void *async_as_area_create(void *base, size_t size, unsigned int flags,
-    async_sess_t *pager, sysarg_t id1, sysarg_t id2, sysarg_t id3)
-{
-	as_area_pager_info_t pager_info = {
-		.pager = pager->phone,
-		.id1 = id1,
-		.id2 = id2,
-		.id3 = id3
-	};
-	return as_area_create(base, size, flags, &pager_info);
-}
-
-/** @}
- */
Index: uspace/lib/c/generic/async/client.c
===================================================================
--- uspace/lib/c/generic/async/client.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
+++ uspace/lib/c/generic/async/client.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -0,0 +1,1345 @@
+/*
+ * 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 of the synchronization
+ * problems.
+ *
+ * Example of use (pseudo C):
+ *
+ * 1) Multithreaded client application
+ *
+ *   fibril_create(fibril1, ...);
+ *   fibril_create(fibril2, ...);
+ *   ...
+ *
+ *   int fibril1(void *arg)
+ *   {
+ *     conn = async_connect_me_to(...);
+ *
+ *     exch = async_exchange_begin(conn);
+ *     c1 = async_send(exch);
+ *     async_exchange_end(exch);
+ *
+ *     exch = async_exchange_begin(conn);
+ *     c2 = async_send(exch);
+ *     async_exchange_end(exch);
+ *
+ *     async_wait_for(c1);
+ *     async_wait_for(c2);
+ *     ...
+ *   }
+ *
+ *
+ * 2) Multithreaded server application
+ *
+ *   main()
+ *   {
+ *     async_manager();
+ *   }
+ *
+ *   port_handler(ichandle, *icall)
+ *   {
+ *     if (want_refuse) {
+ *       async_answer_0(ichandle, ELIMIT);
+ *       return;
+ *     }
+ *     async_answer_0(ichandle, EOK);
+ *
+ *     chandle = async_get_call(&call);
+ *     somehow_handle_the_call(chandle, call);
+ *     async_answer_2(chandle, 1, 2, 3);
+ *
+ *     chandle = async_get_call(&call);
+ *     ...
+ *   }
+ *
+ */
+
+#define LIBC_ASYNC_C_
+#include <ipc/ipc.h>
+#include <async.h>
+#include "../private/async.h"
+#include "../private/ns.h"
+#undef LIBC_ASYNC_C_
+
+#include <ipc/irq.h>
+#include <ipc/event.h>
+#include <futex.h>
+#include <fibril.h>
+#include <adt/hash_table.h>
+#include <adt/hash.h>
+#include <adt/list.h>
+#include <assert.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <libarch/barrier.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <mem.h>
+#include <stdlib.h>
+#include <macros.h>
+#include <as.h>
+#include <abi/mm/as.h>
+#include "../private/libc.h"
+
+/** Naming service session */
+async_sess_t session_ns;
+
+/** Message data */
+typedef struct {
+	awaiter_t wdata;
+
+	/** If reply was received. */
+	bool done;
+
+	/** If the message / reply should be discarded on arrival. */
+	bool forget;
+
+	/** If already destroyed. */
+	bool destroyed;
+
+	/** Pointer to where the answer data is stored. */
+	ipc_call_t *dataptr;
+
+	errno_t retval;
+} amsg_t;
+
+static void to_event_initialize(to_event_t *to)
+{
+	struct timeval tv = { 0, 0 };
+
+	to->inlist = false;
+	to->occurred = false;
+	link_initialize(&to->link);
+	to->expires = tv;
+}
+
+static void wu_event_initialize(wu_event_t *wu)
+{
+	wu->inlist = false;
+	link_initialize(&wu->link);
+}
+
+void awaiter_initialize(awaiter_t *aw)
+{
+	aw->fid = 0;
+	aw->active = false;
+	to_event_initialize(&aw->to_event);
+	wu_event_initialize(&aw->wu_event);
+}
+
+static amsg_t *amsg_create(void)
+{
+	amsg_t *msg = malloc(sizeof(amsg_t));
+	if (msg) {
+		msg->done = false;
+		msg->forget = false;
+		msg->destroyed = false;
+		msg->dataptr = NULL;
+		msg->retval = EINVAL;
+		awaiter_initialize(&msg->wdata);
+	}
+
+	return msg;
+}
+
+static void amsg_destroy(amsg_t *msg)
+{
+	assert(!msg->destroyed);
+	msg->destroyed = true;
+	free(msg);
+}
+
+/** Mutex protecting inactive_exch_list and avail_phone_cv.
+ *
+ */
+static FIBRIL_MUTEX_INITIALIZE(async_sess_mutex);
+
+/** List of all currently inactive exchanges.
+ *
+ */
+static LIST_INITIALIZE(inactive_exch_list);
+
+/** Condition variable to wait for a phone to become available.
+ *
+ */
+static FIBRIL_CONDVAR_INITIALIZE(avail_phone_cv);
+
+/** Initialize the async framework.
+ *
+ */
+void __async_client_init(void)
+{
+	session_ns.iface = 0;
+	session_ns.mgmt = EXCHANGE_ATOMIC;
+	session_ns.phone = PHONE_NS;
+	session_ns.arg1 = 0;
+	session_ns.arg2 = 0;
+	session_ns.arg3 = 0;
+
+	fibril_mutex_initialize(&session_ns.remote_state_mtx);
+	session_ns.remote_state_data = NULL;
+
+	list_initialize(&session_ns.exch_list);
+	fibril_mutex_initialize(&session_ns.mutex);
+	atomic_set(&session_ns.refcnt, 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, errno_t retval, ipc_call_t *data)
+{
+	assert(arg);
+
+	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->forget) {
+		assert(msg->wdata.active);
+		amsg_destroy(msg);
+	} else 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 exch    Exchange for sending the message.
+ * @param imethod Service-defined interface and 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(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
+    sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, ipc_call_t *dataptr)
+{
+	if (exch == NULL)
+		return 0;
+
+	amsg_t *msg = amsg_create();
+	if (msg == NULL)
+		return 0;
+
+	msg->dataptr = dataptr;
+	msg->wdata.active = true;
+
+	ipc_call_async_4(exch->phone, imethod, arg1, arg2, arg3, arg4, msg,
+	    reply_received);
+
+	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 exch    Exchange for sending the message.
+ * @param imethod Service-defined interface and 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(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
+    sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5,
+    ipc_call_t *dataptr)
+{
+	if (exch == NULL)
+		return 0;
+
+	amsg_t *msg = amsg_create();
+	if (msg == NULL)
+		return 0;
+
+	msg->dataptr = dataptr;
+	msg->wdata.active = true;
+
+	ipc_call_async_5(exch->phone, imethod, arg1, arg2, arg3, arg4, arg5,
+	    msg, reply_received);
+
+	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, errno_t *retval)
+{
+	assert(amsgid);
+
+	amsg_t *msg = (amsg_t *) amsgid;
+
+	futex_down(&async_futex);
+
+	assert(!msg->forget);
+	assert(!msg->destroyed);
+
+	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;
+
+	amsg_destroy(msg);
+}
+
+/** Wait for a message sent by the async framework, timeout variant.
+ *
+ * If the wait times out, the caller may choose to either wait again by calling
+ * async_wait_for() or async_wait_timeout(), or forget the message via
+ * async_forget().
+ *
+ * @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.
+ *
+ */
+errno_t async_wait_timeout(aid_t amsgid, errno_t *retval, suseconds_t timeout)
+{
+	assert(amsgid);
+
+	amsg_t *msg = (amsg_t *) amsgid;
+
+	futex_down(&async_futex);
+
+	assert(!msg->forget);
+	assert(!msg->destroyed);
+
+	if (msg->done) {
+		futex_up(&async_futex);
+		goto done;
+	}
+
+	/*
+	 * Negative timeout is converted to zero timeout to avoid
+	 * using tv_add with negative augmenter.
+	 */
+	if (timeout < 0)
+		timeout = 0;
+
+	getuptime(&msg->wdata.to_event.expires);
+	tv_add_diff(&msg->wdata.to_event.expires, timeout);
+
+	/*
+	 * Current fibril is inserted as waiting regardless of the
+	 * "size" of the timeout.
+	 *
+	 * Checking for msg->done and immediately bailing out when
+	 * timeout == 0 would mean that the manager fibril would never
+	 * run (consider single threaded program).
+	 * Thus the IPC answer would be never retrieved from the kernel.
+	 *
+	 * Notice that the actual delay would be very small because we
+	 * - switch to manager fibril
+	 * - the manager sees expired timeout
+	 * - and thus adds us back to ready queue
+	 * - manager switches back to some ready fibril
+	 *   (prior it, it checks for incoming IPC).
+	 *
+	 */
+	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;
+
+	amsg_destroy(msg);
+
+	return 0;
+}
+
+/** Discard the message / reply on arrival.
+ *
+ * The message will be marked to be discarded once the reply arrives in
+ * reply_received(). It is not allowed to call async_wait_for() or
+ * async_wait_timeout() on this message after a call to this function.
+ *
+ * @param amsgid  Hash of the message to forget.
+ */
+void async_forget(aid_t amsgid)
+{
+	amsg_t *msg = (amsg_t *) amsgid;
+
+	assert(msg);
+	assert(!msg->forget);
+	assert(!msg->destroyed);
+
+	futex_down(&async_futex);
+
+	if (msg->done) {
+		amsg_destroy(msg);
+	} else {
+		msg->dataptr = NULL;
+		msg->forget = true;
+	}
+
+	futex_up(&async_futex);
+}
+
+/** 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)
+{
+	awaiter_t awaiter;
+	awaiter_initialize(&awaiter);
+
+	awaiter.fid = fibril_get_id();
+
+	getuptime(&awaiter.to_event.expires);
+	tv_add_diff(&awaiter.to_event.expires, timeout);
+
+	futex_down(&async_futex);
+
+	async_insert_timeout(&awaiter);
+
+	/* Leave the async_futex locked when entering this function */
+	fibril_switch(FIBRIL_TO_MANAGER);
+
+	/* Futex is up automatically after fibril_switch() */
+}
+
+/** Delay execution for the specified number of seconds
+ *
+ * @param sec Number of seconds to sleep
+ */
+void async_sleep(unsigned int sec)
+{
+	/*
+	 * Sleep in 1000 second steps to support
+	 * full argument range
+	 */
+
+	while (sec > 0) {
+		unsigned int period = (sec > 1000) ? 1000 : sec;
+
+		async_usleep(period * 1000000);
+		sec -= period;
+	}
+}
+
+/** 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 exch    Exchange for sending the message.
+ * @param imethod Interface and 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 an error code.
+ *
+ */
+errno_t async_req_fast(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
+    sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t *r1, sysarg_t *r2,
+    sysarg_t *r3, sysarg_t *r4, sysarg_t *r5)
+{
+	if (exch == NULL)
+		return ENOENT;
+
+	ipc_call_t result;
+	aid_t aid = async_send_4(exch, imethod, arg1, arg2, arg3, arg4,
+	    &result);
+
+	errno_t rc;
+	async_wait_for(aid, &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 exch    Exchange for sending the message.
+ * @param imethod Interface and 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 an error code.
+ *
+ */
+errno_t async_req_slow(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
+    sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5, sysarg_t *r1,
+    sysarg_t *r2, sysarg_t *r3, sysarg_t *r4, sysarg_t *r5)
+{
+	if (exch == NULL)
+		return ENOENT;
+
+	ipc_call_t result;
+	aid_t aid = async_send_5(exch, imethod, arg1, arg2, arg3, arg4, arg5,
+	    &result);
+
+	errno_t rc;
+	async_wait_for(aid, &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;
+}
+
+void async_msg_0(async_exch_t *exch, sysarg_t imethod)
+{
+	if (exch != NULL)
+		ipc_call_async_0(exch->phone, imethod, NULL, NULL);
+}
+
+void async_msg_1(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1)
+{
+	if (exch != NULL)
+		ipc_call_async_1(exch->phone, imethod, arg1, NULL, NULL);
+}
+
+void async_msg_2(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
+    sysarg_t arg2)
+{
+	if (exch != NULL)
+		ipc_call_async_2(exch->phone, imethod, arg1, arg2, NULL, NULL);
+}
+
+void async_msg_3(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
+    sysarg_t arg2, sysarg_t arg3)
+{
+	if (exch != NULL)
+		ipc_call_async_3(exch->phone, imethod, arg1, arg2, arg3, NULL,
+		    NULL);
+}
+
+void async_msg_4(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
+    sysarg_t arg2, sysarg_t arg3, sysarg_t arg4)
+{
+	if (exch != NULL)
+		ipc_call_async_4(exch->phone, imethod, arg1, arg2, arg3, arg4,
+		    NULL, NULL);
+}
+
+void async_msg_5(async_exch_t *exch, sysarg_t imethod, sysarg_t arg1,
+    sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5)
+{
+	if (exch != NULL)
+		ipc_call_async_5(exch->phone, imethod, arg1, arg2, arg3, arg4,
+		    arg5, NULL, NULL);
+}
+
+static errno_t async_connect_me_to_internal(cap_phone_handle_t phone,
+    sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t arg4,
+    cap_phone_handle_t *out_phone)
+{
+	ipc_call_t result;
+
+	// XXX: Workaround for GCC's inability to infer association between
+	// rc == EOK and *out_phone being assigned.
+	*out_phone = CAP_NIL;
+
+	amsg_t *msg = amsg_create();
+	if (!msg)
+		return ENOENT;
+
+	msg->dataptr = &result;
+	msg->wdata.active = true;
+
+	ipc_call_async_4(phone, IPC_M_CONNECT_ME_TO, arg1, arg2, arg3, arg4,
+	    msg, reply_received);
+
+	errno_t rc;
+	async_wait_for((aid_t) msg, &rc);
+
+	if (rc != EOK)
+		return rc;
+
+	*out_phone = (cap_phone_handle_t) IPC_GET_ARG5(result);
+	return EOK;
+}
+
+/** Wrapper for making IPC_M_CONNECT_ME_TO calls using the async framework.
+ *
+ * Ask through for a new connection to some service.
+ *
+ * @param mgmt Exchange management style.
+ * @param exch Exchange for sending the message.
+ * @param arg1 User defined argument.
+ * @param arg2 User defined argument.
+ * @param arg3 User defined argument.
+ *
+ * @return New session on success or NULL on error.
+ *
+ */
+async_sess_t *async_connect_me_to(exch_mgmt_t mgmt, async_exch_t *exch,
+    sysarg_t arg1, sysarg_t arg2, sysarg_t arg3)
+{
+	if (exch == NULL) {
+		errno = ENOENT;
+		return NULL;
+	}
+
+	async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
+	if (sess == NULL) {
+		errno = ENOMEM;
+		return NULL;
+	}
+
+	cap_phone_handle_t phone;
+	errno_t rc = async_connect_me_to_internal(exch->phone, arg1, arg2, arg3,
+	    0, &phone);
+	if (rc != EOK) {
+		errno = rc;
+		free(sess);
+		return NULL;
+	}
+
+	sess->iface = 0;
+	sess->mgmt = mgmt;
+	sess->phone = phone;
+	sess->arg1 = arg1;
+	sess->arg2 = arg2;
+	sess->arg3 = arg3;
+
+	fibril_mutex_initialize(&sess->remote_state_mtx);
+	sess->remote_state_data = NULL;
+
+	list_initialize(&sess->exch_list);
+	fibril_mutex_initialize(&sess->mutex);
+	atomic_set(&sess->refcnt, 0);
+
+	return sess;
+}
+
+/** 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 exch  Exchange for sending the message.
+ * @param iface Connection interface.
+ * @param arg2  User defined argument.
+ * @param arg3  User defined argument.
+ *
+ * @return New session on success or NULL on error.
+ *
+ */
+async_sess_t *async_connect_me_to_iface(async_exch_t *exch, iface_t iface,
+    sysarg_t arg2, sysarg_t arg3)
+{
+	if (exch == NULL) {
+		errno = ENOENT;
+		return NULL;
+	}
+
+	async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
+	if (sess == NULL) {
+		errno = ENOMEM;
+		return NULL;
+	}
+
+	cap_phone_handle_t phone;
+	errno_t rc = async_connect_me_to_internal(exch->phone, iface, arg2,
+	    arg3, 0, &phone);
+	if (rc != EOK) {
+		errno = rc;
+		free(sess);
+		return NULL;
+	}
+
+	sess->iface = iface;
+	sess->phone = phone;
+	sess->arg1 = iface;
+	sess->arg2 = arg2;
+	sess->arg3 = arg3;
+
+	fibril_mutex_initialize(&sess->remote_state_mtx);
+	sess->remote_state_data = NULL;
+
+	list_initialize(&sess->exch_list);
+	fibril_mutex_initialize(&sess->mutex);
+	atomic_set(&sess->refcnt, 0);
+
+	return sess;
+}
+
+/** Set arguments for new connections.
+ *
+ * FIXME This is an ugly hack to work around the problem that parallel
+ * exchanges are implemented using parallel connections. When we create
+ * a callback session, the framework does not know arguments for the new
+ * connections.
+ *
+ * The proper solution seems to be to implement parallel exchanges using
+ * tagging.
+ */
+void async_sess_args_set(async_sess_t *sess, sysarg_t arg1, sysarg_t arg2,
+    sysarg_t arg3)
+{
+	sess->arg1 = arg1;
+	sess->arg2 = arg2;
+	sess->arg3 = arg3;
+}
+
+/** 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 mgmt Exchange management style.
+ * @param exch Exchange for sending the message.
+ * @param arg1 User defined argument.
+ * @param arg2 User defined argument.
+ * @param arg3 User defined argument.
+ *
+ * @return New session on success or NULL on error.
+ *
+ */
+async_sess_t *async_connect_me_to_blocking(exch_mgmt_t mgmt, async_exch_t *exch,
+    sysarg_t arg1, sysarg_t arg2, sysarg_t arg3)
+{
+	if (exch == NULL) {
+		errno = ENOENT;
+		return NULL;
+	}
+
+	async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
+	if (sess == NULL) {
+		errno = ENOMEM;
+		return NULL;
+	}
+
+	cap_phone_handle_t phone;
+	errno_t rc = async_connect_me_to_internal(exch->phone, arg1, arg2, arg3,
+	    IPC_FLAG_BLOCKING, &phone);
+
+	if (rc != EOK) {
+		errno = rc;
+		free(sess);
+		return NULL;
+	}
+
+	sess->iface = 0;
+	sess->mgmt = mgmt;
+	sess->phone = phone;
+	sess->arg1 = arg1;
+	sess->arg2 = arg2;
+	sess->arg3 = arg3;
+
+	fibril_mutex_initialize(&sess->remote_state_mtx);
+	sess->remote_state_data = NULL;
+
+	list_initialize(&sess->exch_list);
+	fibril_mutex_initialize(&sess->mutex);
+	atomic_set(&sess->refcnt, 0);
+
+	return sess;
+}
+
+/** 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 exch  Exchange for sending the message.
+ * @param iface Connection interface.
+ * @param arg2  User defined argument.
+ * @param arg3  User defined argument.
+ *
+ * @return New session on success or NULL on error.
+ *
+ */
+async_sess_t *async_connect_me_to_blocking_iface(async_exch_t *exch, iface_t iface,
+    sysarg_t arg2, sysarg_t arg3)
+{
+	if (exch == NULL) {
+		errno = ENOENT;
+		return NULL;
+	}
+
+	async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
+	if (sess == NULL) {
+		errno = ENOMEM;
+		return NULL;
+	}
+
+	cap_phone_handle_t phone;
+	errno_t rc = async_connect_me_to_internal(exch->phone, iface, arg2,
+	    arg3, IPC_FLAG_BLOCKING, &phone);
+	if (rc != EOK) {
+		errno = rc;
+		free(sess);
+		return NULL;
+	}
+
+	sess->iface = iface;
+	sess->phone = phone;
+	sess->arg1 = iface;
+	sess->arg2 = arg2;
+	sess->arg3 = arg3;
+
+	fibril_mutex_initialize(&sess->remote_state_mtx);
+	sess->remote_state_data = NULL;
+
+	list_initialize(&sess->exch_list);
+	fibril_mutex_initialize(&sess->mutex);
+	atomic_set(&sess->refcnt, 0);
+
+	return sess;
+}
+
+/** Connect to a task specified by id.
+ *
+ */
+async_sess_t *async_connect_kbox(task_id_t id)
+{
+	async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
+	if (sess == NULL) {
+		errno = ENOMEM;
+		return NULL;
+	}
+
+	cap_phone_handle_t phone;
+	errno_t rc = ipc_connect_kbox(id, &phone);
+	if (rc != EOK) {
+		errno = rc;
+		free(sess);
+		return NULL;
+	}
+
+	sess->iface = 0;
+	sess->mgmt = EXCHANGE_ATOMIC;
+	sess->phone = phone;
+	sess->arg1 = 0;
+	sess->arg2 = 0;
+	sess->arg3 = 0;
+
+	fibril_mutex_initialize(&sess->remote_state_mtx);
+	sess->remote_state_data = NULL;
+
+	list_initialize(&sess->exch_list);
+	fibril_mutex_initialize(&sess->mutex);
+	atomic_set(&sess->refcnt, 0);
+
+	return sess;
+}
+
+static errno_t async_hangup_internal(cap_phone_handle_t phone)
+{
+	return ipc_hangup(phone);
+}
+
+/** Wrapper for ipc_hangup.
+ *
+ * @param sess Session to hung up.
+ *
+ * @return Zero on success or an error code.
+ *
+ */
+errno_t async_hangup(async_sess_t *sess)
+{
+	async_exch_t *exch;
+
+	assert(sess);
+
+	if (atomic_get(&sess->refcnt) > 0)
+		return EBUSY;
+
+	fibril_mutex_lock(&async_sess_mutex);
+
+	errno_t rc = async_hangup_internal(sess->phone);
+
+	while (!list_empty(&sess->exch_list)) {
+		exch = (async_exch_t *)
+		    list_get_instance(list_first(&sess->exch_list),
+		    async_exch_t, sess_link);
+
+		list_remove(&exch->sess_link);
+		list_remove(&exch->global_link);
+		async_hangup_internal(exch->phone);
+		free(exch);
+	}
+
+	free(sess);
+
+	fibril_mutex_unlock(&async_sess_mutex);
+
+	return rc;
+}
+
+/** Start new exchange in a session.
+ *
+ * @param session Session.
+ *
+ * @return New exchange or NULL on error.
+ *
+ */
+async_exch_t *async_exchange_begin(async_sess_t *sess)
+{
+	if (sess == NULL)
+		return NULL;
+
+	exch_mgmt_t mgmt = sess->mgmt;
+	if (sess->iface != 0)
+		mgmt = sess->iface & IFACE_EXCHANGE_MASK;
+
+	async_exch_t *exch = NULL;
+
+	fibril_mutex_lock(&async_sess_mutex);
+
+	if (!list_empty(&sess->exch_list)) {
+		/*
+		 * There are inactive exchanges in the session.
+		 */
+		exch = (async_exch_t *)
+		    list_get_instance(list_first(&sess->exch_list),
+		    async_exch_t, sess_link);
+
+		list_remove(&exch->sess_link);
+		list_remove(&exch->global_link);
+	} else {
+		/*
+		 * There are no available exchanges in the session.
+		 */
+
+		if ((mgmt == EXCHANGE_ATOMIC) ||
+		    (mgmt == EXCHANGE_SERIALIZE)) {
+			exch = (async_exch_t *) malloc(sizeof(async_exch_t));
+			if (exch != NULL) {
+				link_initialize(&exch->sess_link);
+				link_initialize(&exch->global_link);
+				exch->sess = sess;
+				exch->phone = sess->phone;
+			}
+		} else if (mgmt == EXCHANGE_PARALLEL) {
+			cap_phone_handle_t phone;
+			errno_t rc;
+
+		retry:
+			/*
+			 * Make a one-time attempt to connect a new data phone.
+			 */
+			rc = async_connect_me_to_internal(sess->phone, sess->arg1,
+			    sess->arg2, sess->arg3, 0, &phone);
+			if (rc == EOK) {
+				exch = (async_exch_t *) malloc(sizeof(async_exch_t));
+				if (exch != NULL) {
+					link_initialize(&exch->sess_link);
+					link_initialize(&exch->global_link);
+					exch->sess = sess;
+					exch->phone = phone;
+				} else
+					async_hangup_internal(phone);
+			} else if (!list_empty(&inactive_exch_list)) {
+				/*
+				 * We did not manage to connect a new phone. But we
+				 * can try to close some of the currently inactive
+				 * connections in other sessions and try again.
+				 */
+				exch = (async_exch_t *)
+				    list_get_instance(list_first(&inactive_exch_list),
+				    async_exch_t, global_link);
+
+				list_remove(&exch->sess_link);
+				list_remove(&exch->global_link);
+				async_hangup_internal(exch->phone);
+				free(exch);
+				goto retry;
+			} else {
+				/*
+				 * Wait for a phone to become available.
+				 */
+				fibril_condvar_wait(&avail_phone_cv, &async_sess_mutex);
+				goto retry;
+			}
+		}
+	}
+
+	fibril_mutex_unlock(&async_sess_mutex);
+
+	if (exch != NULL) {
+		atomic_inc(&sess->refcnt);
+
+		if (mgmt == EXCHANGE_SERIALIZE)
+			fibril_mutex_lock(&sess->mutex);
+	}
+
+	return exch;
+}
+
+/** Finish an exchange.
+ *
+ * @param exch Exchange to finish.
+ *
+ */
+void async_exchange_end(async_exch_t *exch)
+{
+	if (exch == NULL)
+		return;
+
+	async_sess_t *sess = exch->sess;
+	assert(sess != NULL);
+
+	exch_mgmt_t mgmt = sess->mgmt;
+	if (sess->iface != 0)
+		mgmt = sess->iface & IFACE_EXCHANGE_MASK;
+
+	atomic_dec(&sess->refcnt);
+
+	if (mgmt == EXCHANGE_SERIALIZE)
+		fibril_mutex_unlock(&sess->mutex);
+
+	fibril_mutex_lock(&async_sess_mutex);
+
+	list_append(&exch->sess_link, &sess->exch_list);
+	list_append(&exch->global_link, &inactive_exch_list);
+	fibril_condvar_signal(&avail_phone_cv);
+
+	fibril_mutex_unlock(&async_sess_mutex);
+}
+
+/** Wrapper for IPC_M_SHARE_IN calls using the async framework.
+ *
+ * @param exch  Exchange for sending the message.
+ * @param size  Size of the destination address space area.
+ * @param arg   User defined argument.
+ * @param flags Storage for the received flags. Can be NULL.
+ * @param dst   Address of the storage for the destination address space area
+ *              base address. Cannot be NULL.
+ *
+ * @return Zero on success or an error code from errno.h.
+ *
+ */
+errno_t async_share_in_start(async_exch_t *exch, size_t size, sysarg_t arg,
+    unsigned int *flags, void **dst)
+{
+	if (exch == NULL)
+		return ENOENT;
+
+	sysarg_t _flags = 0;
+	sysarg_t _dst = (sysarg_t) -1;
+	errno_t res = async_req_2_4(exch, IPC_M_SHARE_IN, (sysarg_t) size,
+	    arg, NULL, &_flags, NULL, &_dst);
+
+	if (flags)
+		*flags = (unsigned int) _flags;
+
+	*dst = (void *) _dst;
+	return res;
+}
+
+/** Wrapper for IPC_M_SHARE_OUT calls using the async framework.
+ *
+ * @param exch  Exchange for sending the message.
+ * @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 an error code from errno.h.
+ *
+ */
+errno_t async_share_out_start(async_exch_t *exch, void *src, unsigned int flags)
+{
+	if (exch == NULL)
+		return ENOENT;
+
+	return async_req_3_0(exch, IPC_M_SHARE_OUT, (sysarg_t) src, 0,
+	    (sysarg_t) flags);
+}
+
+/** Start IPC_M_DATA_READ using the async framework.
+ *
+ * @param exch    Exchange for sending the message.
+ * @param dst     Address of the beginning of the destination buffer.
+ * @param size    Size of the destination buffer (in bytes).
+ * @param dataptr Storage of call data (arg 2 holds actual data size).
+ *
+ * @return Hash of the sent message or 0 on error.
+ *
+ */
+aid_t async_data_read(async_exch_t *exch, void *dst, size_t size,
+    ipc_call_t *dataptr)
+{
+	return async_send_2(exch, IPC_M_DATA_READ, (sysarg_t) dst,
+	    (sysarg_t) size, dataptr);
+}
+
+/** Wrapper for IPC_M_DATA_READ calls using the async framework.
+ *
+ * @param exch Exchange for sending the message.
+ * @param dst  Address of the beginning of the destination buffer.
+ * @param size Size of the destination buffer.
+ *
+ * @return Zero on success or an error code from errno.h.
+ *
+ */
+errno_t async_data_read_start(async_exch_t *exch, void *dst, size_t size)
+{
+	if (exch == NULL)
+		return ENOENT;
+
+	return async_req_2_0(exch, IPC_M_DATA_READ, (sysarg_t) dst,
+	    (sysarg_t) size);
+}
+
+/** Wrapper for IPC_M_DATA_WRITE calls using the async framework.
+ *
+ * @param exch Exchange for sending the message.
+ * @param src  Address of the beginning of the source buffer.
+ * @param size Size of the source buffer.
+ *
+ * @return Zero on success or an error code from errno.h.
+ *
+ */
+errno_t async_data_write_start(async_exch_t *exch, const void *src, size_t size)
+{
+	if (exch == NULL)
+		return ENOENT;
+
+	return async_req_2_0(exch, IPC_M_DATA_WRITE, (sysarg_t) src,
+	    (sysarg_t) size);
+}
+
+errno_t async_state_change_start(async_exch_t *exch, sysarg_t arg1, sysarg_t arg2,
+    sysarg_t arg3, async_exch_t *other_exch)
+{
+	return async_req_5_0(exch, IPC_M_STATE_CHANGE_AUTHORIZE,
+	    arg1, arg2, arg3, 0, CAP_HANDLE_RAW(other_exch->phone));
+}
+
+/** Lock and get session remote state
+ *
+ * Lock and get the local replica of the remote state
+ * in stateful sessions. The call should be paired
+ * with async_remote_state_release*().
+ *
+ * @param[in] sess Stateful session.
+ *
+ * @return Local replica of the remote state.
+ *
+ */
+void *async_remote_state_acquire(async_sess_t *sess)
+{
+	fibril_mutex_lock(&sess->remote_state_mtx);
+	return sess->remote_state_data;
+}
+
+/** Update the session remote state
+ *
+ * Update the local replica of the remote state
+ * in stateful sessions. The remote state must
+ * be already locked.
+ *
+ * @param[in] sess  Stateful session.
+ * @param[in] state New local replica of the remote state.
+ *
+ */
+void async_remote_state_update(async_sess_t *sess, void *state)
+{
+	assert(fibril_mutex_is_locked(&sess->remote_state_mtx));
+	sess->remote_state_data = state;
+}
+
+/** Release the session remote state
+ *
+ * Unlock the local replica of the remote state
+ * in stateful sessions.
+ *
+ * @param[in] sess Stateful session.
+ *
+ */
+void async_remote_state_release(async_sess_t *sess)
+{
+	assert(fibril_mutex_is_locked(&sess->remote_state_mtx));
+
+	fibril_mutex_unlock(&sess->remote_state_mtx);
+}
+
+/** Release the session remote state and end an exchange
+ *
+ * Unlock the local replica of the remote state
+ * in stateful sessions. This is convenience function
+ * which gets the session pointer from the exchange
+ * and also ends the exchange.
+ *
+ * @param[in] exch Stateful session's exchange.
+ *
+ */
+void async_remote_state_release_exchange(async_exch_t *exch)
+{
+	if (exch == NULL)
+		return;
+
+	async_sess_t *sess = exch->sess;
+	assert(fibril_mutex_is_locked(&sess->remote_state_mtx));
+
+	async_exchange_end(exch);
+	fibril_mutex_unlock(&sess->remote_state_mtx);
+}
+
+void *async_as_area_create(void *base, size_t size, unsigned int flags,
+    async_sess_t *pager, sysarg_t id1, sysarg_t id2, sysarg_t id3)
+{
+	as_area_pager_info_t pager_info = {
+		.pager = pager->phone,
+		.id1 = id1,
+		.id2 = id2,
+		.id3 = id3
+	};
+	return as_area_create(base, size, flags, &pager_info);
+}
+
+/** @}
+ */
Index: uspace/lib/c/generic/async/ports.c
===================================================================
--- uspace/lib/c/generic/async/ports.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
+++ uspace/lib/c/generic/async/ports.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -0,0 +1,299 @@
+/*
+ * 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.
+ */
+
+#define LIBC_ASYNC_C_
+#include <ipc/ipc.h>
+#include <async.h>
+#include "../private/async.h"
+#undef LIBC_ASYNC_C_
+
+#include <ipc/irq.h>
+#include <ipc/event.h>
+#include <futex.h>
+#include <fibril.h>
+#include <adt/hash_table.h>
+#include <adt/hash.h>
+#include <adt/list.h>
+#include <assert.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <libarch/barrier.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <mem.h>
+#include <stdlib.h>
+#include <macros.h>
+#include <as.h>
+#include <abi/mm/as.h>
+#include "../private/libc.h"
+
+/** Interface data */
+typedef struct {
+	ht_link_t link;
+
+	/** Interface ID */
+	iface_t iface;
+
+	/** Interface ports */
+	hash_table_t port_hash_table;
+
+	/** Next available port ID */
+	port_id_t port_id_avail;
+} interface_t;
+
+/* Port data */
+typedef struct {
+	ht_link_t link;
+
+	/** Port ID */
+	port_id_t id;
+
+	/** Port connection handler */
+	async_port_handler_t handler;
+
+	/** Client data */
+	void *data;
+} port_t;
+
+/** Default fallback fibril function.
+ *
+ * This fallback fibril function gets called on incomming connections that do
+ * not have a specific handler defined.
+ *
+ * @param chandle  Handle of the incoming call.
+ * @param call     Data of the incoming call.
+ * @param arg      Local argument
+ *
+ */
+static void default_fallback_port_handler(cap_call_handle_t chandle,
+    ipc_call_t *call, void *arg)
+{
+	ipc_answer_0(chandle, ENOENT);
+}
+
+static async_port_handler_t fallback_port_handler =
+    default_fallback_port_handler;
+static void *fallback_port_data = NULL;
+
+/** Futex guarding the interface hash table. */
+static futex_t interface_futex = FUTEX_INITIALIZER;
+static hash_table_t interface_hash_table;
+
+static size_t interface_key_hash(void *key)
+{
+	iface_t iface = *(iface_t *) key;
+	return iface;
+}
+
+static size_t interface_hash(const ht_link_t *item)
+{
+	interface_t *interface = hash_table_get_inst(item, interface_t, link);
+	return interface_key_hash(&interface->iface);
+}
+
+static bool interface_key_equal(void *key, const ht_link_t *item)
+{
+	iface_t iface = *(iface_t *) key;
+	interface_t *interface = hash_table_get_inst(item, interface_t, link);
+	return iface == interface->iface;
+}
+
+/** Operations for the port hash table. */
+static hash_table_ops_t interface_hash_table_ops = {
+	.hash = interface_hash,
+	.key_hash = interface_key_hash,
+	.key_equal = interface_key_equal,
+	.equal = NULL,
+	.remove_callback = NULL
+};
+
+static size_t port_key_hash(void *key)
+{
+	port_id_t port_id = *(port_id_t *) key;
+	return port_id;
+}
+
+static size_t port_hash(const ht_link_t *item)
+{
+	port_t *port = hash_table_get_inst(item, port_t, link);
+	return port_key_hash(&port->id);
+}
+
+static bool port_key_equal(void *key, const ht_link_t *item)
+{
+	port_id_t port_id = *(port_id_t *) key;
+	port_t *port = hash_table_get_inst(item, port_t, link);
+	return port_id == port->id;
+}
+
+/** Operations for the port hash table. */
+static hash_table_ops_t port_hash_table_ops = {
+	.hash = port_hash,
+	.key_hash = port_key_hash,
+	.key_equal = port_key_equal,
+	.equal = NULL,
+	.remove_callback = NULL
+};
+
+static interface_t *async_new_interface(iface_t iface)
+{
+	interface_t *interface =
+	    (interface_t *) malloc(sizeof(interface_t));
+	if (!interface)
+		return NULL;
+
+	bool ret = hash_table_create(&interface->port_hash_table, 0, 0,
+	    &port_hash_table_ops);
+	if (!ret) {
+		free(interface);
+		return NULL;
+	}
+
+	interface->iface = iface;
+	interface->port_id_avail = 0;
+
+	hash_table_insert(&interface_hash_table, &interface->link);
+
+	return interface;
+}
+
+static port_t *async_new_port(interface_t *interface,
+    async_port_handler_t handler, void *data)
+{
+	// TODO: Move the malloc out of critical section.
+	port_t *port = (port_t *) malloc(sizeof(port_t));
+	if (!port)
+		return NULL;
+
+	port_id_t id = interface->port_id_avail;
+	interface->port_id_avail++;
+
+	port->id = id;
+	port->handler = handler;
+	port->data = data;
+
+	hash_table_insert(&interface->port_hash_table, &port->link);
+
+	return port;
+}
+
+errno_t async_create_port_internal(iface_t iface, async_port_handler_t handler,
+    void *data, port_id_t *port_id)
+{
+	interface_t *interface;
+
+	futex_lock(&interface_futex);
+
+	ht_link_t *link = hash_table_find(&interface_hash_table, &iface);
+	if (link)
+		interface = hash_table_get_inst(link, interface_t, link);
+	else
+		interface = async_new_interface(iface);
+
+	if (!interface) {
+		futex_unlock(&interface_futex);
+		return ENOMEM;
+	}
+
+	port_t *port = async_new_port(interface, handler, data);
+	if (!port) {
+		futex_unlock(&interface_futex);
+		return ENOMEM;
+	}
+
+	*port_id = port->id;
+
+	futex_unlock(&interface_futex);
+
+	return EOK;
+}
+
+errno_t async_create_port(iface_t iface, async_port_handler_t handler,
+    void *data, port_id_t *port_id)
+{
+	if ((iface & IFACE_MOD_MASK) == IFACE_MOD_CALLBACK)
+		return EINVAL;
+
+	return async_create_port_internal(iface, handler, data, port_id);
+}
+
+void async_set_fallback_port_handler(async_port_handler_t handler, void *data)
+{
+	assert(handler != NULL);
+
+	fallback_port_handler = handler;
+	fallback_port_data = data;
+}
+
+static port_t *async_find_port(iface_t iface, port_id_t port_id)
+{
+	port_t *port = NULL;
+
+	futex_lock(&interface_futex);
+
+	ht_link_t *link = hash_table_find(&interface_hash_table, &iface);
+	if (link) {
+		interface_t *interface =
+		    hash_table_get_inst(link, interface_t, link);
+
+		link = hash_table_find(&interface->port_hash_table, &port_id);
+		if (link)
+			port = hash_table_get_inst(link, port_t, link);
+	}
+
+	futex_unlock(&interface_futex);
+
+	return port;
+}
+
+async_port_handler_t async_get_port_handler(iface_t iface, port_id_t port_id,
+    void **data)
+{
+	assert(data);
+
+	async_port_handler_t handler = fallback_port_handler;
+	*data = fallback_port_data;
+
+	port_t *port = async_find_port(iface, port_id);
+	if (port) {
+		handler = port->handler;
+		*data = port->data;
+	}
+
+	return handler;
+}
+
+/** Initialize the async framework.
+ *
+ */
+void __async_ports_init(void)
+{
+	if (!hash_table_create(&interface_hash_table, 0, 0,
+	    &interface_hash_table_ops))
+		abort();
+}
Index: uspace/lib/c/generic/async/server.c
===================================================================
--- uspace/lib/c/generic/async/server.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
+++ uspace/lib/c/generic/async/server.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -0,0 +1,1887 @@
+/*
+ * 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 of the synchronization
+ * problems.
+ *
+ * Example of use (pseudo C):
+ *
+ * 1) Multithreaded client application
+ *
+ *   fibril_create(fibril1, ...);
+ *   fibril_create(fibril2, ...);
+ *   ...
+ *
+ *   int fibril1(void *arg)
+ *   {
+ *     conn = async_connect_me_to(...);
+ *
+ *     exch = async_exchange_begin(conn);
+ *     c1 = async_send(exch);
+ *     async_exchange_end(exch);
+ *
+ *     exch = async_exchange_begin(conn);
+ *     c2 = async_send(exch);
+ *     async_exchange_end(exch);
+ *
+ *     async_wait_for(c1);
+ *     async_wait_for(c2);
+ *     ...
+ *   }
+ *
+ *
+ * 2) Multithreaded server application
+ *
+ *   main()
+ *   {
+ *     async_manager();
+ *   }
+ *
+ *   port_handler(ichandle, *icall)
+ *   {
+ *     if (want_refuse) {
+ *       async_answer_0(ichandle, ELIMIT);
+ *       return;
+ *     }
+ *     async_answer_0(ichandle, EOK);
+ *
+ *     chandle = async_get_call(&call);
+ *     somehow_handle_the_call(chandle, call);
+ *     async_answer_2(chandle, 1, 2, 3);
+ *
+ *     chandle = async_get_call(&call);
+ *     ...
+ *   }
+ *
+ */
+
+#define LIBC_ASYNC_C_
+#include <ipc/ipc.h>
+#include <async.h>
+#include "../private/async.h"
+#undef LIBC_ASYNC_C_
+
+#include <ipc/irq.h>
+#include <ipc/event.h>
+#include <futex.h>
+#include <fibril.h>
+#include <adt/hash_table.h>
+#include <adt/hash.h>
+#include <adt/list.h>
+#include <assert.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <libarch/barrier.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <mem.h>
+#include <stdlib.h>
+#include <macros.h>
+#include <as.h>
+#include <abi/mm/as.h>
+#include "../private/libc.h"
+
+/** Async framework global futex */
+futex_t async_futex = FUTEX_INITIALIZER;
+
+/** Number of threads waiting for IPC in the kernel. */
+static atomic_t threads_in_ipc_wait = { 0 };
+
+/** Call data */
+typedef struct {
+	link_t link;
+
+	cap_call_handle_t chandle;
+	ipc_call_t call;
+} msg_t;
+
+/* Client connection data */
+typedef struct {
+	ht_link_t link;
+
+	task_id_t in_task_id;
+	atomic_t refcnt;
+	void *data;
+} client_t;
+
+/* Server connection data */
+typedef struct {
+	awaiter_t wdata;
+
+	/** Hash table link. */
+	ht_link_t link;
+
+	/** Incoming client task ID. */
+	task_id_t in_task_id;
+
+	/** Incoming phone hash. */
+	sysarg_t in_phone_hash;
+
+	/** Link to the client tracking structure. */
+	client_t *client;
+
+	/** Messages that should be delivered to this fibril. */
+	list_t msg_queue;
+
+	/** Identification of the opening call. */
+	cap_call_handle_t chandle;
+
+	/** Call data of the opening call. */
+	ipc_call_t call;
+
+	/** Identification of the closing call. */
+	cap_call_handle_t close_chandle;
+
+	/** Fibril function that will be used to handle the connection. */
+	async_port_handler_t handler;
+
+	/** Client data */
+	void *data;
+} connection_t;
+
+/* Notification data */
+typedef struct {
+	/** notification_hash_table link */
+	ht_link_t htlink;
+
+	/** notification_queue link */
+	link_t qlink;
+
+	/** Notification method */
+	sysarg_t imethod;
+
+	/** Notification handler */
+	async_notification_handler_t handler;
+
+	/** Notification handler argument */
+	void *arg;
+
+	/** Data of the most recent notification. */
+	ipc_call_t calldata;
+
+	/**
+	 * How many notifications with this `imethod` arrived since it was last
+	 * handled. If `count` > 1, `calldata` only holds the data for the most
+	 * recent such notification, all the older data being lost.
+	 *
+	 * `async_spawn_notification_handler()` can be used to increase the
+	 * number of notifications that can be processed simultaneously,
+	 * reducing the likelihood of losing them when the handler blocks.
+	 */
+	long count;
+} notification_t;
+
+/** Identifier of the incoming connection handled by the current fibril. */
+static fibril_local connection_t *fibril_connection;
+
+static void *default_client_data_constructor(void)
+{
+	return NULL;
+}
+
+static void default_client_data_destructor(void *data)
+{
+}
+
+static async_client_data_ctor_t async_client_data_create =
+    default_client_data_constructor;
+static async_client_data_dtor_t async_client_data_destroy =
+    default_client_data_destructor;
+
+void async_set_client_data_constructor(async_client_data_ctor_t ctor)
+{
+	assert(async_client_data_create == default_client_data_constructor);
+	async_client_data_create = ctor;
+}
+
+void async_set_client_data_destructor(async_client_data_dtor_t dtor)
+{
+	assert(async_client_data_destroy == default_client_data_destructor);
+	async_client_data_destroy = dtor;
+}
+
+static futex_t client_futex = FUTEX_INITIALIZER;
+static hash_table_t client_hash_table;
+
+// TODO: lockfree notification_queue?
+static futex_t notification_futex = FUTEX_INITIALIZER;
+static hash_table_t notification_hash_table;
+static LIST_INITIALIZE(notification_queue);
+static FIBRIL_SEMAPHORE_INITIALIZE(notification_semaphore, 0);
+
+static sysarg_t notification_avail = 0;
+
+/* The remaining structures are guarded by async_futex. */
+static hash_table_t conn_hash_table;
+static LIST_INITIALIZE(timeout_list);
+
+static size_t client_key_hash(void *key)
+{
+	task_id_t in_task_id = *(task_id_t *) key;
+	return in_task_id;
+}
+
+static size_t client_hash(const ht_link_t *item)
+{
+	client_t *client = hash_table_get_inst(item, client_t, link);
+	return client_key_hash(&client->in_task_id);
+}
+
+static bool client_key_equal(void *key, const ht_link_t *item)
+{
+	task_id_t in_task_id = *(task_id_t *) key;
+	client_t *client = hash_table_get_inst(item, client_t, link);
+	return in_task_id == client->in_task_id;
+}
+
+/** Operations for the client hash table. */
+static hash_table_ops_t client_hash_table_ops = {
+	.hash = client_hash,
+	.key_hash = client_key_hash,
+	.key_equal = client_key_equal,
+	.equal = NULL,
+	.remove_callback = NULL
+};
+
+typedef struct {
+	task_id_t task_id;
+	sysarg_t phone_hash;
+} conn_key_t;
+
+/** Compute hash into the connection hash table
+ *
+ * The hash is based on the source task ID and the source phone hash. The task
+ * ID is included in the hash because a phone hash alone might not be unique
+ * while we still track connections for killed tasks due to kernel's recycling
+ * of phone structures.
+ *
+ * @param key Pointer to the connection key structure.
+ *
+ * @return Index into the connection hash table.
+ *
+ */
+static size_t conn_key_hash(void *key)
+{
+	conn_key_t *ck = (conn_key_t *) key;
+
+	size_t hash = 0;
+	hash = hash_combine(hash, LOWER32(ck->task_id));
+	hash = hash_combine(hash, UPPER32(ck->task_id));
+	hash = hash_combine(hash, ck->phone_hash);
+	return hash;
+}
+
+static size_t conn_hash(const ht_link_t *item)
+{
+	connection_t *conn = hash_table_get_inst(item, connection_t, link);
+	return conn_key_hash(&(conn_key_t){
+		.task_id = conn->in_task_id,
+		.phone_hash = conn->in_phone_hash
+	});
+}
+
+static bool conn_key_equal(void *key, const ht_link_t *item)
+{
+	conn_key_t *ck = (conn_key_t *) key;
+	connection_t *conn = hash_table_get_inst(item, connection_t, link);
+	return ((ck->task_id == conn->in_task_id) &&
+	    (ck->phone_hash == conn->in_phone_hash));
+}
+
+/** Operations for the connection hash table. */
+static hash_table_ops_t conn_hash_table_ops = {
+	.hash = conn_hash,
+	.key_hash = conn_key_hash,
+	.key_equal = conn_key_equal,
+	.equal = NULL,
+	.remove_callback = NULL
+};
+
+static client_t *async_client_get(task_id_t client_id, bool create)
+{
+	client_t *client = NULL;
+
+	futex_lock(&client_futex);
+	ht_link_t *link = hash_table_find(&client_hash_table, &client_id);
+	if (link) {
+		client = hash_table_get_inst(link, client_t, link);
+		atomic_inc(&client->refcnt);
+	} else if (create) {
+		// TODO: move the malloc out of critical section
+		client = malloc(sizeof(client_t));
+		if (client) {
+			client->in_task_id = client_id;
+			client->data = async_client_data_create();
+
+			atomic_set(&client->refcnt, 1);
+			hash_table_insert(&client_hash_table, &client->link);
+		}
+	}
+
+	futex_unlock(&client_futex);
+	return client;
+}
+
+static void async_client_put(client_t *client)
+{
+	bool destroy;
+
+	futex_lock(&client_futex);
+
+	if (atomic_predec(&client->refcnt) == 0) {
+		hash_table_remove(&client_hash_table, &client->in_task_id);
+		destroy = true;
+	} else
+		destroy = false;
+
+	futex_unlock(&client_futex);
+
+	if (destroy) {
+		if (client->data)
+			async_client_data_destroy(client->data);
+
+		free(client);
+	}
+}
+
+/** Wrapper for client connection fibril.
+ *
+ * When a new connection arrives, a fibril with this implementing
+ * function is created.
+ *
+ * @param arg Connection structure pointer.
+ *
+ * @return Always zero.
+ *
+ */
+static errno_t connection_fibril(void *arg)
+{
+	assert(arg);
+
+	/*
+	 * Setup fibril-local connection pointer.
+	 */
+	fibril_connection = (connection_t *) arg;
+
+	/*
+	 * Add our reference for the current connection in the client task
+	 * tracking structure. If this is the first reference, create and
+	 * hash in a new tracking structure.
+	 */
+
+	client_t *client = async_client_get(fibril_connection->in_task_id, true);
+	if (!client) {
+		ipc_answer_0(fibril_connection->chandle, ENOMEM);
+		return 0;
+	}
+
+	fibril_connection->client = client;
+
+	/*
+	 * Call the connection handler function.
+	 */
+	fibril_connection->handler(fibril_connection->chandle,
+	    &fibril_connection->call, fibril_connection->data);
+
+	/*
+	 * Remove the reference for this client task connection.
+	 */
+	async_client_put(client);
+
+	/*
+	 * Remove myself from the connection hash table.
+	 */
+	futex_down(&async_futex);
+	hash_table_remove(&conn_hash_table, &(conn_key_t){
+		.task_id = fibril_connection->in_task_id,
+		.phone_hash = fibril_connection->in_phone_hash
+	});
+	futex_up(&async_futex);
+
+	/*
+	 * Answer all remaining messages with EHANGUP.
+	 */
+	while (!list_empty(&fibril_connection->msg_queue)) {
+		msg_t *msg =
+		    list_get_instance(list_first(&fibril_connection->msg_queue),
+		    msg_t, link);
+
+		list_remove(&msg->link);
+		ipc_answer_0(msg->chandle, EHANGUP);
+		free(msg);
+	}
+
+	/*
+	 * If the connection was hung-up, answer the last call,
+	 * i.e. IPC_M_PHONE_HUNGUP.
+	 */
+	if (fibril_connection->close_chandle)
+		ipc_answer_0(fibril_connection->close_chandle, EOK);
+
+	free(fibril_connection);
+	return EOK;
+}
+
+/** Create a new fibril for a new connection.
+ *
+ * Create new fibril for connection, fill in connection structures and insert it
+ * into the hash table, so that later we can easily do routing of messages to
+ * particular fibrils.
+ *
+ * @param in_task_id     Identification of the incoming connection.
+ * @param in_phone_hash  Identification of the incoming connection.
+ * @param chandle        Handle of the opening IPC_M_CONNECT_ME_TO call.
+ *                       If chandle is CAP_NIL, 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 handler        Connection handler.
+ * @param data           Client argument to pass to the connection handler.
+ *
+ * @return  New fibril id or NULL on failure.
+ *
+ */
+static fid_t async_new_connection(task_id_t in_task_id, sysarg_t in_phone_hash,
+    cap_call_handle_t chandle, ipc_call_t *call, async_port_handler_t handler,
+    void *data)
+{
+	connection_t *conn = malloc(sizeof(*conn));
+	if (!conn) {
+		if (chandle != CAP_NIL)
+			ipc_answer_0(chandle, ENOMEM);
+
+		return (uintptr_t) NULL;
+	}
+
+	conn->in_task_id = in_task_id;
+	conn->in_phone_hash = in_phone_hash;
+	list_initialize(&conn->msg_queue);
+	conn->chandle = chandle;
+	conn->close_chandle = CAP_NIL;
+	conn->handler = handler;
+	conn->data = data;
+
+	if (call)
+		conn->call = *call;
+
+	/* We will activate the fibril ASAP */
+	conn->wdata.active = true;
+	conn->wdata.fid = fibril_create(connection_fibril, conn);
+
+	if (conn->wdata.fid == 0) {
+		free(conn);
+
+		if (chandle != CAP_NIL)
+			ipc_answer_0(chandle, ENOMEM);
+
+		return (uintptr_t) NULL;
+	}
+
+	/* Add connection to the connection hash table */
+
+	futex_down(&async_futex);
+	hash_table_insert(&conn_hash_table, &conn->link);
+	futex_up(&async_futex);
+
+	fibril_add_ready(conn->wdata.fid);
+
+	return conn->wdata.fid;
+}
+
+/** Wrapper for making IPC_M_CONNECT_TO_ME calls using the async framework.
+ *
+ * Ask through phone for a new connection to some service.
+ *
+ * @param exch    Exchange for sending the message.
+ * @param iface   Callback interface.
+ * @param arg1    User defined argument.
+ * @param arg2    User defined argument.
+ * @param handler Callback handler.
+ * @param data    Handler data.
+ * @param port_id ID of the newly created port.
+ *
+ * @return Zero on success or an error code.
+ *
+ */
+errno_t async_create_callback_port(async_exch_t *exch, iface_t iface, sysarg_t arg1,
+    sysarg_t arg2, async_port_handler_t handler, void *data, port_id_t *port_id)
+{
+	if ((iface & IFACE_MOD_CALLBACK) != IFACE_MOD_CALLBACK)
+		return EINVAL;
+
+	if (exch == NULL)
+		return ENOENT;
+
+	ipc_call_t answer;
+	aid_t req = async_send_3(exch, IPC_M_CONNECT_TO_ME, iface, arg1, arg2,
+	    &answer);
+
+	errno_t rc;
+	async_wait_for(req, &rc);
+	if (rc != EOK)
+		return rc;
+
+	rc = async_create_port_internal(iface, handler, data, port_id);
+	if (rc != EOK)
+		return rc;
+
+	sysarg_t phone_hash = IPC_GET_ARG5(answer);
+	fid_t fid = async_new_connection(answer.in_task_id, phone_hash,
+	    CAP_NIL, NULL, handler, data);
+	if (fid == (uintptr_t) NULL)
+		return ENOMEM;
+
+	return EOK;
+}
+
+static size_t notification_key_hash(void *key)
+{
+	sysarg_t id = *(sysarg_t *) key;
+	return id;
+}
+
+static size_t notification_hash(const ht_link_t *item)
+{
+	notification_t *notification =
+	    hash_table_get_inst(item, notification_t, htlink);
+	return notification_key_hash(&notification->imethod);
+}
+
+static bool notification_key_equal(void *key, const ht_link_t *item)
+{
+	sysarg_t id = *(sysarg_t *) key;
+	notification_t *notification =
+	    hash_table_get_inst(item, notification_t, htlink);
+	return id == notification->imethod;
+}
+
+/** Operations for the notification hash table. */
+static hash_table_ops_t notification_hash_table_ops = {
+	.hash = notification_hash,
+	.key_hash = notification_key_hash,
+	.key_equal = notification_key_equal,
+	.equal = NULL,
+	.remove_callback = NULL
+};
+
+/** Sort in current fibril's timeout request.
+ *
+ * @param wd Wait data of the current fibril.
+ *
+ */
+void async_insert_timeout(awaiter_t *wd)
+{
+	assert(wd);
+
+	wd->to_event.occurred = false;
+	wd->to_event.inlist = true;
+
+	link_t *tmp = timeout_list.head.next;
+	while (tmp != &timeout_list.head) {
+		awaiter_t *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_insert_before(&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 chandle  Handle of the incoming call.
+ * @param call     Data of the incoming call.
+ *
+ * @return False if the call doesn't match any connection.
+ * @return True if the call was passed to the respective connection fibril.
+ *
+ */
+static bool route_call(cap_call_handle_t chandle, ipc_call_t *call)
+{
+	assert(call);
+
+	futex_down(&async_futex);
+
+	ht_link_t *link = hash_table_find(&conn_hash_table, &(conn_key_t){
+		.task_id = call->in_task_id,
+		.phone_hash = call->in_phone_hash
+	});
+	if (!link) {
+		futex_up(&async_futex);
+		return false;
+	}
+
+	connection_t *conn = hash_table_get_inst(link, connection_t, link);
+
+	msg_t *msg = malloc(sizeof(*msg));
+	if (!msg) {
+		futex_up(&async_futex);
+		return false;
+	}
+
+	msg->chandle = chandle;
+	msg->call = *call;
+	list_append(&msg->link, &conn->msg_queue);
+
+	if (IPC_GET_IMETHOD(*call) == IPC_M_PHONE_HUNGUP)
+		conn->close_chandle = chandle;
+
+	/* 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;
+}
+
+/** Function implementing the notification handler fibril. Never returns. */
+static errno_t notification_fibril_func(void *arg)
+{
+	(void) arg;
+
+	while (true) {
+		fibril_semaphore_down(&notification_semaphore);
+
+		futex_lock(&notification_futex);
+
+		/*
+		 * The semaphore ensures that if we get this far,
+		 * the queue must be non-empty.
+		 */
+		assert(!list_empty(&notification_queue));
+
+		notification_t *notification = list_get_instance(
+		    list_first(&notification_queue), notification_t, qlink);
+		list_remove(&notification->qlink);
+
+		async_notification_handler_t handler = notification->handler;
+		void *arg = notification->arg;
+		ipc_call_t calldata = notification->calldata;
+		long count = notification->count;
+
+		notification->count = 0;
+
+		futex_unlock(&notification_futex);
+
+		// FIXME: Pass count to the handler. It might be important.
+		(void) count;
+
+		if (handler)
+			handler(&calldata, arg);
+	}
+
+	/* Not reached. */
+	return EOK;
+}
+
+/**
+ * Creates a new dedicated fibril for handling notifications.
+ * By default, there is one such fibril. This function can be used to
+ * create more in order to increase the number of notification that can
+ * be processed concurrently.
+ *
+ * Currently, there is no way to destroy those fibrils after they are created.
+ */
+errno_t async_spawn_notification_handler(void)
+{
+	fid_t f = fibril_create(notification_fibril_func, NULL);
+	if (f == 0)
+		return ENOMEM;
+
+	fibril_add_ready(f);
+	return EOK;
+}
+
+/** Queue notification.
+ *
+ * @param call   Data of the incoming call.
+ *
+ */
+static void queue_notification(ipc_call_t *call)
+{
+	assert(call);
+
+	futex_lock(&notification_futex);
+
+	ht_link_t *link = hash_table_find(&notification_hash_table,
+	    &IPC_GET_IMETHOD(*call));
+	if (!link) {
+		/* Invalid notification. */
+		// TODO: Make sure this can't happen and turn it into assert.
+		futex_unlock(&notification_futex);
+		return;
+	}
+
+	notification_t *notification =
+	    hash_table_get_inst(link, notification_t, htlink);
+
+	notification->count++;
+	notification->calldata = *call;
+
+	if (link_in_use(&notification->qlink)) {
+		/* Notification already queued. */
+		futex_unlock(&notification_futex);
+		return;
+	}
+
+	list_append(&notification->qlink, &notification_queue);
+	futex_unlock(&notification_futex);
+
+	fibril_semaphore_up(&notification_semaphore);
+}
+
+/**
+ * Creates a new notification structure and inserts it into the hash table.
+ *
+ * @param handler  Function to call when notification is received.
+ * @param arg      Argument for the handler function.
+ * @return         The newly created notification structure.
+ */
+static notification_t *notification_create(async_notification_handler_t handler, void *arg)
+{
+	notification_t *notification = calloc(1, sizeof(notification_t));
+	if (!notification)
+		return NULL;
+
+	notification->handler = handler;
+	notification->arg = arg;
+
+	fid_t fib = 0;
+
+	futex_lock(&notification_futex);
+
+	if (notification_avail == 0) {
+		/* Attempt to create the first handler fibril. */
+		fib = fibril_create(notification_fibril_func, NULL);
+		if (fib == 0) {
+			futex_unlock(&notification_futex);
+			free(notification);
+			return NULL;
+		}
+	}
+
+	sysarg_t imethod = notification_avail;
+	notification_avail++;
+
+	notification->imethod = imethod;
+	hash_table_insert(&notification_hash_table, &notification->htlink);
+
+	futex_unlock(&notification_futex);
+
+	if (imethod == 0) {
+		assert(fib);
+		fibril_add_ready(fib);
+	}
+
+	return notification;
+}
+
+/** Subscribe to IRQ notification.
+ *
+ * @param inr     IRQ number.
+ * @param handler Notification handler.
+ * @param data    Notification handler client data.
+ * @param ucode   Top-half pseudocode handler.
+ *
+ * @param[out] handle  IRQ capability handle on success.
+ *
+ * @return An error code.
+ *
+ */
+errno_t async_irq_subscribe(int inr, async_notification_handler_t handler,
+    void *data, const irq_code_t *ucode, cap_irq_handle_t *handle)
+{
+	notification_t *notification = notification_create(handler, data);
+	if (!notification)
+		return ENOMEM;
+
+	cap_irq_handle_t ihandle;
+	errno_t rc = ipc_irq_subscribe(inr, notification->imethod, ucode,
+	    &ihandle);
+	if (rc == EOK && handle != NULL) {
+		*handle = ihandle;
+	}
+	return rc;
+}
+
+/** Unsubscribe from IRQ notification.
+ *
+ * @param handle  IRQ capability handle.
+ *
+ * @return Zero on success or an error code.
+ *
+ */
+errno_t async_irq_unsubscribe(cap_irq_handle_t ihandle)
+{
+	// TODO: Remove entry from hash table
+	//       to avoid memory leak
+
+	return ipc_irq_unsubscribe(ihandle);
+}
+
+/** Subscribe to event notifications.
+ *
+ * @param evno    Event type to subscribe.
+ * @param handler Notification handler.
+ * @param data    Notification handler client data.
+ *
+ * @return Zero on success or an error code.
+ *
+ */
+errno_t async_event_subscribe(event_type_t evno,
+    async_notification_handler_t handler, void *data)
+{
+	notification_t *notification = notification_create(handler, data);
+	if (!notification)
+		return ENOMEM;
+
+	return ipc_event_subscribe(evno, notification->imethod);
+}
+
+/** Subscribe to task event notifications.
+ *
+ * @param evno    Event type to subscribe.
+ * @param handler Notification handler.
+ * @param data    Notification handler client data.
+ *
+ * @return Zero on success or an error code.
+ *
+ */
+errno_t async_event_task_subscribe(event_task_type_t evno,
+    async_notification_handler_t handler, void *data)
+{
+	notification_t *notification = notification_create(handler, data);
+	if (!notification)
+		return ENOMEM;
+
+	return ipc_event_task_subscribe(evno, notification->imethod);
+}
+
+/** Unmask event notifications.
+ *
+ * @param evno Event type to unmask.
+ *
+ * @return Value returned by the kernel.
+ *
+ */
+errno_t async_event_unmask(event_type_t evno)
+{
+	return ipc_event_unmask(evno);
+}
+
+/** Unmask task event notifications.
+ *
+ * @param evno Event type to unmask.
+ *
+ * @return Value returned by the kernel.
+ *
+ */
+errno_t async_event_task_unmask(event_task_type_t evno)
+{
+	return ipc_event_task_unmask(evno);
+}
+
+/** 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 handle of the incoming call is
+ *          returned. If a timeout is specified, then a handle of the incoming
+ *          call is returned unless the timeout expires prior to receiving a
+ *          message. In that case zero CAP_NIL is returned.
+ */
+cap_call_handle_t async_get_call_timeout(ipc_call_t *call, suseconds_t usecs)
+{
+	assert(call);
+	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) {
+		getuptime(&conn->wdata.to_event.expires);
+		tv_add_diff(&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_chandle) {
+			/*
+			 * 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_IMETHOD(*call, IPC_M_PHONE_HUNGUP);
+			futex_up(&async_futex);
+			return conn->close_chandle;
+		}
+
+		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 CAP_NIL;
+		}
+	}
+
+	msg_t *msg = list_get_instance(list_first(&conn->msg_queue),
+	    msg_t, link);
+	list_remove(&msg->link);
+
+	cap_call_handle_t chandle = msg->chandle;
+	*call = msg->call;
+	free(msg);
+
+	futex_up(&async_futex);
+	return chandle;
+}
+
+void *async_get_client_data(void)
+{
+	assert(fibril_connection);
+	return fibril_connection->client->data;
+}
+
+void *async_get_client_data_by_id(task_id_t client_id)
+{
+	client_t *client = async_client_get(client_id, false);
+	if (!client)
+		return NULL;
+
+	if (!client->data) {
+		async_client_put(client);
+		return NULL;
+	}
+
+	return client->data;
+}
+
+void async_put_client_data_by_id(task_id_t client_id)
+{
+	client_t *client = async_client_get(client_id, false);
+
+	assert(client);
+	assert(client->data);
+
+	/* Drop the reference we got in async_get_client_data_by_hash(). */
+	async_client_put(client);
+
+	/* Drop our own reference we got at the beginning of this function. */
+	async_client_put(client);
+}
+
+/** 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 chandle  Handle of the incoming call.
+ * @param call     Data of the incoming call.
+ *
+ */
+static void handle_call(cap_call_handle_t chandle, ipc_call_t *call)
+{
+	assert(call);
+
+	/* Kernel notification */
+	if ((chandle == CAP_NIL) && (call->flags & IPC_CALL_NOTIF)) {
+		queue_notification(call);
+		return;
+	}
+
+	/* New connection */
+	if (IPC_GET_IMETHOD(*call) == IPC_M_CONNECT_ME_TO) {
+		iface_t iface = (iface_t) IPC_GET_ARG1(*call);
+		sysarg_t in_phone_hash = IPC_GET_ARG5(*call);
+
+		// TODO: Currently ignores all ports but the first one.
+		void *data;
+		async_port_handler_t handler =
+		    async_get_port_handler(iface, 0, &data);
+
+		async_new_connection(call->in_task_id, in_phone_hash, chandle,
+		    call, handler, data);
+		return;
+	}
+
+	/* Try to route the call through the connection hash table */
+	if (route_call(chandle, call))
+		return;
+
+	/* Unknown call from unknown phone - hang it up */
+	ipc_answer_0(chandle, EHANGUP);
+}
+
+/** Fire all timeouts that expired. */
+static void handle_expired_timeouts(void)
+{
+	struct timeval tv;
+	getuptime(&tv);
+
+	futex_down(&async_futex);
+
+	link_t *cur = list_first(&timeout_list);
+	while (cur != NULL) {
+		awaiter_t *waiter =
+		    list_get_instance(cur, awaiter_t, to_event.link);
+
+		if (tv_gt(&waiter->to_event.expires, &tv))
+			break;
+
+		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);
+		}
+
+		cur = list_first(&timeout_list);
+	}
+
+	futex_up(&async_futex);
+}
+
+/** Endless loop dispatching incoming calls and answers.
+ *
+ * @return Never returns.
+ *
+ */
+static errno_t 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;
+		unsigned int flags = SYNCH_FLAGS_NONE;
+		if (!list_empty(&timeout_list)) {
+			awaiter_t *waiter = list_get_instance(
+			    list_first(&timeout_list), awaiter_t, to_event.link);
+
+			struct timeval tv;
+			getuptime(&tv);
+
+			if (tv_gteq(&tv, &waiter->to_event.expires)) {
+				futex_up(&async_futex);
+				handle_expired_timeouts();
+				/*
+				 * Notice that even if the event(s) already
+				 * expired (and thus the other fibril was
+				 * supposed to be running already),
+				 * we check for incoming IPC.
+				 *
+				 * Otherwise, a fibril that continuously
+				 * creates (almost) expired events could
+				 * prevent IPC retrieval from the kernel.
+				 */
+				timeout = 0;
+				flags = SYNCH_FLAGS_NON_BLOCKING;
+
+			} else {
+				timeout = tv_sub_diff(&waiter->to_event.expires,
+				    &tv);
+				futex_up(&async_futex);
+			}
+		} else {
+			futex_up(&async_futex);
+			timeout = SYNCH_NO_TIMEOUT;
+		}
+
+		atomic_inc(&threads_in_ipc_wait);
+
+		ipc_call_t call;
+		errno_t rc = ipc_wait_cycle(&call, timeout, flags);
+
+		atomic_dec(&threads_in_ipc_wait);
+
+		assert(rc == EOK);
+
+		if (call.cap_handle == CAP_NIL) {
+			if ((call.flags &
+			    (IPC_CALL_NOTIF | IPC_CALL_ANSWERED)) == 0) {
+				/* Neither a notification nor an answer. */
+				handle_expired_timeouts();
+				continue;
+			}
+		}
+
+		if (call.flags & IPC_CALL_ANSWERED)
+			continue;
+
+		handle_call(call.cap_handle, &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 errno_t 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_generic(async_manager_fibril, NULL, PAGE_SIZE);
+	if (fid != 0)
+		fibril_add_manager(fid);
+}
+
+/** Remove one manager from manager list */
+void async_destroy_manager(void)
+{
+	fibril_remove_manager();
+}
+
+/** Initialize the async framework.
+ *
+ */
+void __async_server_init(void)
+{
+	if (!hash_table_create(&client_hash_table, 0, 0, &client_hash_table_ops))
+		abort();
+
+	if (!hash_table_create(&conn_hash_table, 0, 0, &conn_hash_table_ops))
+		abort();
+
+	if (!hash_table_create(&notification_hash_table, 0, 0,
+	    &notification_hash_table_ops))
+		abort();
+}
+
+errno_t async_answer_0(cap_call_handle_t chandle, errno_t retval)
+{
+	return ipc_answer_0(chandle, retval);
+}
+
+errno_t async_answer_1(cap_call_handle_t chandle, errno_t retval, sysarg_t arg1)
+{
+	return ipc_answer_1(chandle, retval, arg1);
+}
+
+errno_t async_answer_2(cap_call_handle_t chandle, errno_t retval, sysarg_t arg1,
+    sysarg_t arg2)
+{
+	return ipc_answer_2(chandle, retval, arg1, arg2);
+}
+
+errno_t async_answer_3(cap_call_handle_t chandle, errno_t retval, sysarg_t arg1,
+    sysarg_t arg2, sysarg_t arg3)
+{
+	return ipc_answer_3(chandle, retval, arg1, arg2, arg3);
+}
+
+errno_t async_answer_4(cap_call_handle_t chandle, errno_t retval, sysarg_t arg1,
+    sysarg_t arg2, sysarg_t arg3, sysarg_t arg4)
+{
+	return ipc_answer_4(chandle, retval, arg1, arg2, arg3, arg4);
+}
+
+errno_t async_answer_5(cap_call_handle_t chandle, errno_t retval, sysarg_t arg1,
+    sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5)
+{
+	return ipc_answer_5(chandle, retval, arg1, arg2, arg3, arg4, arg5);
+}
+
+errno_t async_forward_fast(cap_call_handle_t chandle, async_exch_t *exch,
+    sysarg_t imethod, sysarg_t arg1, sysarg_t arg2, unsigned int mode)
+{
+	if (exch == NULL)
+		return ENOENT;
+
+	return ipc_forward_fast(chandle, exch->phone, imethod, arg1, arg2, mode);
+}
+
+errno_t async_forward_slow(cap_call_handle_t chandle, async_exch_t *exch,
+    sysarg_t imethod, sysarg_t arg1, sysarg_t arg2, sysarg_t arg3,
+    sysarg_t arg4, sysarg_t arg5, unsigned int mode)
+{
+	if (exch == NULL)
+		return ENOENT;
+
+	return ipc_forward_slow(chandle, exch->phone, imethod, arg1, arg2, arg3,
+	    arg4, arg5, mode);
+}
+
+/** Wrapper for making IPC_M_CONNECT_TO_ME calls using the async framework.
+ *
+ * Ask through phone for a new connection to some service.
+ *
+ * @param exch            Exchange for sending the message.
+ * @param arg1            User defined argument.
+ * @param arg2            User defined argument.
+ * @param arg3            User defined argument.
+ *
+ * @return Zero on success or an error code.
+ *
+ */
+errno_t async_connect_to_me(async_exch_t *exch, sysarg_t arg1, sysarg_t arg2,
+    sysarg_t arg3)
+{
+	if (exch == NULL)
+		return ENOENT;
+
+	ipc_call_t answer;
+	aid_t req = async_send_3(exch, IPC_M_CONNECT_TO_ME, arg1, arg2, arg3,
+	    &answer);
+
+	errno_t rc;
+	async_wait_for(req, &rc);
+	if (rc != EOK)
+		return (errno_t) rc;
+
+	return EOK;
+}
+
+/** Interrupt one thread of this task from waiting for IPC. */
+void async_poke(void)
+{
+	if (atomic_get(&threads_in_ipc_wait) > 0)
+		ipc_poke();
+}
+
+/** 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 chandle  Storage for the handle of the IPC_M_SHARE_IN call.
+ * @param size     Destination address space area size.
+ *
+ * @return True on success, false on failure.
+ *
+ */
+bool async_share_in_receive(cap_call_handle_t *chandle, size_t *size)
+{
+	assert(chandle);
+	assert(size);
+
+	ipc_call_t data;
+	*chandle = async_get_call(&data);
+
+	if (IPC_GET_IMETHOD(data) != IPC_M_SHARE_IN)
+		return false;
+
+	*size = (size_t) IPC_GET_ARG1(data);
+	return true;
+}
+
+/** Wrapper for answering the IPC_M_SHARE_IN calls using the async framework.
+ *
+ * This wrapper only makes it more comfortable to answer IPC_M_SHARE_IN
+ * calls so that the user doesn't have to remember the meaning of each IPC
+ * argument.
+ *
+ * @param chandle  Handle 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.
+ *
+ */
+errno_t async_share_in_finalize(cap_call_handle_t chandle, void *src,
+    unsigned int flags)
+{
+	// FIXME: The source has no business deciding destination address.
+	return ipc_answer_3(chandle, EOK, (sysarg_t) src, (sysarg_t) flags,
+	    (sysarg_t) _end);
+}
+
+/** 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 chandle  Storage for the hash of the IPC_M_SHARE_OUT call.
+ * @param size     Storage for the source address space area size.
+ * @param flags    Storage for the sharing flags.
+ *
+ * @return True on success, false on failure.
+ *
+ */
+bool async_share_out_receive(cap_call_handle_t *chandle, size_t *size,
+    unsigned int *flags)
+{
+	assert(chandle);
+	assert(size);
+	assert(flags);
+
+	ipc_call_t data;
+	*chandle = async_get_call(&data);
+
+	if (IPC_GET_IMETHOD(data) != IPC_M_SHARE_OUT)
+		return false;
+
+	*size = (size_t) IPC_GET_ARG2(data);
+	*flags = (unsigned int) IPC_GET_ARG3(data);
+	return true;
+}
+
+/** 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 chandle  Handle of the IPC_M_DATA_WRITE call to answer.
+ * @param dst      Address of the storage for the destination address space area
+ *                 base address.
+ *
+ * @return  Zero on success or a value from @ref errno.h on failure.
+ *
+ */
+errno_t async_share_out_finalize(cap_call_handle_t chandle, void **dst)
+{
+	return ipc_answer_2(chandle, EOK, (sysarg_t) _end, (sysarg_t) dst);
+}
+
+/** 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 chandle  Storage for the handle of the IPC_M_DATA_READ.
+ * @param size     Storage for the maximum size. Can be NULL.
+ *
+ * @return True on success, false on failure.
+ *
+ */
+bool async_data_read_receive(cap_call_handle_t *chandle, size_t *size)
+{
+	ipc_call_t data;
+	return async_data_read_receive_call(chandle, &data, 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 chandle  Storage for the handle of the IPC_M_DATA_READ.
+ * @param size     Storage for the maximum size. Can be NULL.
+ *
+ * @return True on success, false on failure.
+ *
+ */
+bool async_data_read_receive_call(cap_call_handle_t *chandle, ipc_call_t *data,
+    size_t *size)
+{
+	assert(chandle);
+	assert(data);
+
+	*chandle = async_get_call(data);
+
+	if (IPC_GET_IMETHOD(*data) != IPC_M_DATA_READ)
+		return false;
+
+	if (size)
+		*size = (size_t) IPC_GET_ARG2(*data);
+
+	return true;
+}
+
+/** 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 chandle  Handle 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.
+ *
+ */
+errno_t async_data_read_finalize(cap_call_handle_t chandle, const void *src,
+    size_t size)
+{
+	return ipc_answer_2(chandle, EOK, (sysarg_t) src, (sysarg_t) size);
+}
+
+/** Wrapper for forwarding any read request
+ *
+ */
+errno_t async_data_read_forward_fast(async_exch_t *exch, sysarg_t imethod,
+    sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t arg4,
+    ipc_call_t *dataptr)
+{
+	if (exch == NULL)
+		return ENOENT;
+
+	cap_call_handle_t chandle;
+	if (!async_data_read_receive(&chandle, NULL)) {
+		ipc_answer_0(chandle, EINVAL);
+		return EINVAL;
+	}
+
+	aid_t msg = async_send_fast(exch, imethod, arg1, arg2, arg3, arg4,
+	    dataptr);
+	if (msg == 0) {
+		ipc_answer_0(chandle, EINVAL);
+		return EINVAL;
+	}
+
+	errno_t retval = ipc_forward_fast(chandle, exch->phone, 0, 0, 0,
+	    IPC_FF_ROUTE_FROM_ME);
+	if (retval != EOK) {
+		async_forget(msg);
+		ipc_answer_0(chandle, retval);
+		return retval;
+	}
+
+	errno_t rc;
+	async_wait_for(msg, &rc);
+
+	return (errno_t) rc;
+}
+
+/** 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 chandle  Storage for the handle of the IPC_M_DATA_WRITE.
+ * @param size     Storage for the suggested size. May be NULL.
+ *
+ * @return  True on success, false on failure.
+ *
+ */
+bool async_data_write_receive(cap_call_handle_t *chandle, size_t *size)
+{
+	ipc_call_t data;
+	return async_data_write_receive_call(chandle, &data, 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 chandle  Storage for the handle of the IPC_M_DATA_WRITE.
+ * @param data     Storage for the ipc call data.
+ * @param size     Storage for the suggested size. May be NULL.
+ *
+ * @return True on success, false on failure.
+ *
+ */
+bool async_data_write_receive_call(cap_call_handle_t *chandle, ipc_call_t *data,
+    size_t *size)
+{
+	assert(chandle);
+	assert(data);
+
+	*chandle = async_get_call(data);
+
+	if (IPC_GET_IMETHOD(*data) != IPC_M_DATA_WRITE)
+		return false;
+
+	if (size)
+		*size = (size_t) IPC_GET_ARG2(*data);
+
+	return true;
+}
+
+/** 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 chandle  Handle 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.
+ *
+ */
+errno_t async_data_write_finalize(cap_call_handle_t chandle, void *dst,
+    size_t size)
+{
+	return ipc_answer_2(chandle, EOK, (sysarg_t) dst, (sysarg_t) 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.
+ *
+ */
+errno_t 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)
+{
+	assert(data);
+
+	cap_call_handle_t chandle;
+	size_t size;
+	if (!async_data_write_receive(&chandle, &size)) {
+		ipc_answer_0(chandle, EINVAL);
+		return EINVAL;
+	}
+
+	if (size < min_size) {
+		ipc_answer_0(chandle, EINVAL);
+		return EINVAL;
+	}
+
+	if ((max_size > 0) && (size > max_size)) {
+		ipc_answer_0(chandle, EINVAL);
+		return EINVAL;
+	}
+
+	if ((granularity > 0) && ((size % granularity) != 0)) {
+		ipc_answer_0(chandle, EINVAL);
+		return EINVAL;
+	}
+
+	void *arg_data;
+
+	if (nullterm)
+		arg_data = malloc(size + 1);
+	else
+		arg_data = malloc(size);
+
+	if (arg_data == NULL) {
+		ipc_answer_0(chandle, ENOMEM);
+		return ENOMEM;
+	}
+
+	errno_t rc = async_data_write_finalize(chandle, arg_data, size);
+	if (rc != EOK) {
+		free(arg_data);
+		return rc;
+	}
+
+	if (nullterm)
+		((char *) arg_data)[size] = 0;
+
+	*data = arg_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(errno_t retval)
+{
+	cap_call_handle_t chandle;
+	async_data_write_receive(&chandle, NULL);
+	ipc_answer_0(chandle, retval);
+}
+
+/** Wrapper for forwarding any data that is about to be received
+ *
+ */
+errno_t async_data_write_forward_fast(async_exch_t *exch, sysarg_t imethod,
+    sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t arg4,
+    ipc_call_t *dataptr)
+{
+	if (exch == NULL)
+		return ENOENT;
+
+	cap_call_handle_t chandle;
+	if (!async_data_write_receive(&chandle, NULL)) {
+		ipc_answer_0(chandle, EINVAL);
+		return EINVAL;
+	}
+
+	aid_t msg = async_send_fast(exch, imethod, arg1, arg2, arg3, arg4,
+	    dataptr);
+	if (msg == 0) {
+		ipc_answer_0(chandle, EINVAL);
+		return EINVAL;
+	}
+
+	errno_t retval = ipc_forward_fast(chandle, exch->phone, 0, 0, 0,
+	    IPC_FF_ROUTE_FROM_ME);
+	if (retval != EOK) {
+		async_forget(msg);
+		ipc_answer_0(chandle, retval);
+		return retval;
+	}
+
+	errno_t rc;
+	async_wait_for(msg, &rc);
+
+	return (errno_t) rc;
+}
+
+/** Wrapper for receiving the IPC_M_CONNECT_TO_ME calls.
+ *
+ * If the current call is IPC_M_CONNECT_TO_ME then a new
+ * async session is created for the accepted phone.
+ *
+ * @param mgmt Exchange management style.
+ *
+ * @return New async session.
+ * @return NULL on failure.
+ *
+ */
+async_sess_t *async_callback_receive(exch_mgmt_t mgmt)
+{
+	/* Accept the phone */
+	ipc_call_t call;
+	cap_call_handle_t chandle = async_get_call(&call);
+	cap_phone_handle_t phandle = (cap_handle_t) IPC_GET_ARG5(call);
+
+	if ((IPC_GET_IMETHOD(call) != IPC_M_CONNECT_TO_ME) ||
+	    !CAP_HANDLE_VALID((phandle))) {
+		async_answer_0(chandle, EINVAL);
+		return NULL;
+	}
+
+	async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
+	if (sess == NULL) {
+		async_answer_0(chandle, ENOMEM);
+		return NULL;
+	}
+
+	sess->iface = 0;
+	sess->mgmt = mgmt;
+	sess->phone = phandle;
+	sess->arg1 = 0;
+	sess->arg2 = 0;
+	sess->arg3 = 0;
+
+	fibril_mutex_initialize(&sess->remote_state_mtx);
+	sess->remote_state_data = NULL;
+
+	list_initialize(&sess->exch_list);
+	fibril_mutex_initialize(&sess->mutex);
+	atomic_set(&sess->refcnt, 0);
+
+	/* Acknowledge the connected phone */
+	async_answer_0(chandle, EOK);
+
+	return sess;
+}
+
+/** Wrapper for receiving the IPC_M_CONNECT_TO_ME calls.
+ *
+ * If the call is IPC_M_CONNECT_TO_ME then a new
+ * async session is created. However, the phone is
+ * not accepted automatically.
+ *
+ * @param mgmt   Exchange management style.
+ * @param call   Call data.
+ *
+ * @return New async session.
+ * @return NULL on failure.
+ * @return NULL if the call is not IPC_M_CONNECT_TO_ME.
+ *
+ */
+async_sess_t *async_callback_receive_start(exch_mgmt_t mgmt, ipc_call_t *call)
+{
+	cap_phone_handle_t phandle = (cap_handle_t) IPC_GET_ARG5(*call);
+
+	if ((IPC_GET_IMETHOD(*call) != IPC_M_CONNECT_TO_ME) ||
+	    !CAP_HANDLE_VALID((phandle)))
+		return NULL;
+
+	async_sess_t *sess = (async_sess_t *) malloc(sizeof(async_sess_t));
+	if (sess == NULL)
+		return NULL;
+
+	sess->iface = 0;
+	sess->mgmt = mgmt;
+	sess->phone = phandle;
+	sess->arg1 = 0;
+	sess->arg2 = 0;
+	sess->arg3 = 0;
+
+	fibril_mutex_initialize(&sess->remote_state_mtx);
+	sess->remote_state_data = NULL;
+
+	list_initialize(&sess->exch_list);
+	fibril_mutex_initialize(&sess->mutex);
+	atomic_set(&sess->refcnt, 0);
+
+	return sess;
+}
+
+bool async_state_change_receive(cap_call_handle_t *chandle, sysarg_t *arg1,
+    sysarg_t *arg2, sysarg_t *arg3)
+{
+	assert(chandle);
+
+	ipc_call_t call;
+	*chandle = async_get_call(&call);
+
+	if (IPC_GET_IMETHOD(call) != IPC_M_STATE_CHANGE_AUTHORIZE)
+		return false;
+
+	if (arg1)
+		*arg1 = IPC_GET_ARG1(call);
+	if (arg2)
+		*arg2 = IPC_GET_ARG2(call);
+	if (arg3)
+		*arg3 = IPC_GET_ARG3(call);
+
+	return true;
+}
+
+errno_t async_state_change_finalize(cap_call_handle_t chandle,
+    async_exch_t *other_exch)
+{
+	return ipc_answer_1(chandle, EOK, CAP_HANDLE_RAW(other_exch->phone));
+}
+
+/** @}
+ */
Index: uspace/lib/c/generic/bsearch.c
===================================================================
--- uspace/lib/c/generic/bsearch.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
+++ uspace/lib/c/generic/bsearch.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2018 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup libc
+ * @{
+ */
+
+/**
+ * @file
+ * @brief Binary search.
+ */
+
+#include <bsearch.h>
+#include <stddef.h>
+
+/** Binary search.
+ *
+ * @param key Key to search for
+ * @param base Array of objects
+ * @param nmemb Number of objects in array
+ * @param size Size of each object
+ * @param compar Comparison function
+ */
+void *bsearch(const void *key, const void *base, size_t nmemb, size_t size,
+    int (*compar)(const void *, const void *))
+{
+	size_t pividx;
+	const void *pivot;
+	int r;
+
+	while (nmemb != 0) {
+		pividx = nmemb / 2;
+		pivot = base + size * pividx;
+
+		r = compar(key, pivot);
+		if (r == 0)
+			return (void *)pivot;
+
+		if (r < 0) {
+			/* Now only look at members preceding pivot */
+			nmemb = pividx;
+		} else {
+			/* Now only look at members following pivot */
+			nmemb = nmemb - pividx - 1;
+			base += size * (pividx + 1);
+		}
+	}
+
+	return NULL;
+}
+
+/** @}
+ */
Index: uspace/lib/c/generic/elf/elf.c
===================================================================
--- uspace/lib/c/generic/elf/elf.c	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/lib/c/generic/elf/elf.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -27,7 +27,7 @@
  */
 
+#include <assert.h>
 #include <elf/elf.h>
-
-#include <assert.h>
+#include <stdbool.h>
 #include <stdlib.h>
 
Index: uspace/lib/c/generic/fibril.c
===================================================================
--- uspace/lib/c/generic/fibril.c	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/lib/c/generic/fibril.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -72,4 +72,7 @@
 static void fibril_main(void)
 {
+	/* fibril_futex is locked when a fibril is first started. */
+	futex_unlock(&fibril_futex);
+
 	fibril_t *fibril = __tcb_get()->fibril_data;
 
@@ -112,6 +115,4 @@
 
 	fibril->waits_for = NULL;
-
-	fibril->switches = 0;
 
 	/*
@@ -203,5 +204,4 @@
 		break;
 	case FIBRIL_TO_MANAGER:
-		srcf->switches++;
 		/*
 		 * Don't put the current fibril into any list, it should
@@ -211,6 +211,4 @@
 	}
 
-	futex_unlock(&fibril_futex);
-
 #ifdef FUTEX_UPGRADABLE
 	if (stype == FIBRIL_FROM_DEAD) {
@@ -223,4 +221,7 @@
 
 	/* Restored by another fibril! */
+
+	/* Must be after context_swap()! */
+	futex_unlock(&fibril_futex);
 
 	if (srcf->clean_after_me) {
Index: uspace/lib/c/generic/fibril_synch.c
===================================================================
--- uspace/lib/c/generic/fibril_synch.c	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/lib/c/generic/fibril_synch.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -55,6 +55,5 @@
 	 * fibril back to fruitful work.
 	 */
-	if (atomic_get(&threads_in_ipc_wait) > 0)
-		async_poke();
+	async_poke();
 }
 
@@ -630,4 +629,77 @@
 }
 
+/**
+ * Initialize a semaphore with initial count set to the provided value.
+ *
+ * @param sem    Semaphore to initialize.
+ * @param count  Initial count. Must not be negative.
+ */
+void fibril_semaphore_initialize(fibril_semaphore_t *sem, long count)
+{
+	/*
+	 * Negative count denotes the length of waitlist,
+	 * so it makes no sense as an initial value.
+	 */
+	assert(count >= 0);
+	sem->count = count;
+	list_initialize(&sem->waiters);
+}
+
+/**
+ * Produce one token.
+ * If there are fibrils waiting for tokens, this operation satisfies
+ * exactly one waiting `fibril_semaphore_down()`.
+ * This operation never blocks the fibril.
+ *
+ * @param sem  Semaphore to use.
+ */
+void fibril_semaphore_up(fibril_semaphore_t *sem)
+{
+	futex_down(&async_futex);
+	sem->count++;
+
+	if (sem->count > 0) {
+		futex_up(&async_futex);
+		return;
+	}
+
+	link_t *tmp = list_first(&sem->waiters);
+	assert(tmp);
+	list_remove(tmp);
+
+	futex_up(&async_futex);
+
+	awaiter_t *wdp = list_get_instance(tmp, awaiter_t, wu_event.link);
+	fibril_add_ready(wdp->fid);
+	optimize_execution_power();
+}
+
+/**
+ * Consume one token.
+ * If there are no available tokens (count <= 0), this operation blocks until
+ * another fibril produces a token using `fibril_semaphore_up()`.
+ *
+ * @param sem  Semaphore to use.
+ */
+void fibril_semaphore_down(fibril_semaphore_t *sem)
+{
+	futex_down(&async_futex);
+	sem->count--;
+
+	if (sem->count >= 0) {
+		futex_up(&async_futex);
+		return;
+	}
+
+	awaiter_t wdata;
+	awaiter_initialize(&wdata);
+
+	wdata.fid = fibril_get_id();
+	list_append(&wdata.wu_event.link, &sem->waiters);
+	fibril_switch(FIBRIL_TO_MANAGER);
+
+	/* async_futex not held after fibril_switch() */
+}
+
 /** @}
  */
Index: uspace/lib/c/generic/io/chargrid.c
===================================================================
--- uspace/lib/c/generic/io/chargrid.c	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/lib/c/generic/io/chargrid.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -140,5 +140,5 @@
  *
  */
-sysarg_t chargrid_putchar(chargrid_t *scrbuf, wchar_t ch, bool update)
+sysarg_t chargrid_putwchar(chargrid_t *scrbuf, wchar_t ch, bool update)
 {
 	assert(scrbuf->col < scrbuf->cols);
@@ -199,5 +199,5 @@
 
 	for (sysarg_t i = 0; i < spaces; i++)
-		flush += chargrid_putchar(scrbuf, ' ', true) - 1;
+		flush += chargrid_putwchar(scrbuf, ' ', true) - 1;
 
 	return flush;
@@ -228,10 +228,10 @@
 		scrbuf->row--;
 
-		chargrid_putchar(scrbuf, ' ', false);
+		chargrid_putwchar(scrbuf, ' ', false);
 		return 2;
 	}
 
 	scrbuf->col--;
-	chargrid_putchar(scrbuf, ' ', false);
+	chargrid_putwchar(scrbuf, ' ', false);
 	return 1;
 }
Index: uspace/lib/c/generic/io/io.c
===================================================================
--- uspace/lib/c/generic/io/io.c	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/lib/c/generic/io/io.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -46,4 +46,5 @@
 #include <ipc/loc.h>
 #include <adt/list.h>
+#include <wchar.h>
 #include "../private/io.h"
 #include "../private/stdio.h"
@@ -51,4 +52,27 @@
 static void _ffillbuf(FILE *stream);
 static void _fflushbuf(FILE *stream);
+
+static size_t stdio_kio_read(void *, size_t, size_t, FILE *);
+static size_t stdio_kio_write(const void *, size_t, size_t, FILE *);
+static int stdio_kio_flush(FILE *);
+
+static size_t stdio_vfs_read(void *, size_t, size_t, FILE *);
+static size_t stdio_vfs_write(const void *, size_t, size_t, FILE *);
+
+static int stdio_vfs_flush(FILE *);
+
+/** KIO stream ops */
+static __stream_ops_t stdio_kio_ops = {
+	.read = stdio_kio_read,
+	.write = stdio_kio_write,
+	.flush = stdio_kio_flush
+};
+
+/** VFS stream ops */
+static __stream_ops_t stdio_vfs_ops = {
+	.read = stdio_vfs_read,
+	.write = stdio_vfs_write,
+	.flush = stdio_vfs_flush
+};
 
 static FILE stdin_null = {
@@ -57,5 +81,6 @@
 	.error = true,
 	.eof = true,
-	.kio = false,
+	.ops = &stdio_vfs_ops,
+	.arg = NULL,
 	.sess = NULL,
 	.btype = _IONBF,
@@ -72,5 +97,6 @@
 	.error = false,
 	.eof = false,
-	.kio = true,
+	.ops = &stdio_kio_ops,
+	.arg = NULL,
 	.sess = NULL,
 	.btype = _IOLBF,
@@ -87,5 +113,6 @@
 	.error = false,
 	.eof = false,
-	.kio = true,
+	.ops = &stdio_kio_ops,
+	.arg = NULL,
 	.sess = NULL,
 	.btype = _IONBF,
@@ -327,5 +354,6 @@
 	stream->error = false;
 	stream->eof = false;
-	stream->kio = false;
+	stream->ops = &stdio_vfs_ops;
+	stream->arg = NULL;
 	stream->sess = NULL;
 	stream->need_sync = false;
@@ -351,5 +379,6 @@
 	stream->error = false;
 	stream->eof = false;
-	stream->kio = false;
+	stream->ops = &stdio_vfs_ops;
+	stream->arg = NULL;
 	stream->sess = NULL;
 	stream->need_sync = false;
@@ -434,19 +463,5 @@
 static size_t _fread(void *buf, size_t size, size_t nmemb, FILE *stream)
 {
-	errno_t rc;
-	size_t nread;
-
-	if (size == 0 || nmemb == 0)
-		return 0;
-
-	rc = vfs_read(stream->fd, &stream->pos, buf, size * nmemb, &nread);
-	if (rc != EOK) {
-		errno = rc;
-		stream->error = true;
-	} else if (nread == 0) {
-		stream->eof = true;
-	}
-
-	return (nread / size);
+	return stream->ops->read(buf, size, nmemb, stream);
 }
 
@@ -463,5 +478,4 @@
 static size_t _fwrite(const void *buf, size_t size, size_t nmemb, FILE *stream)
 {
-	errno_t rc;
 	size_t nwritten;
 
@@ -469,18 +483,5 @@
 		return 0;
 
-	if (stream->kio) {
-		rc = kio_write(buf, size * nmemb, &nwritten);
-		if (rc != EOK) {
-			stream->error = true;
-			nwritten = 0;
-		}
-	} else {
-		rc = vfs_write(stream->fd, &stream->pos, buf, size * nmemb,
-		    &nwritten);
-		if (rc != EOK) {
-			errno = rc;
-			stream->error = true;
-		}
-	}
+	nwritten = stream->ops->write(buf, size, nmemb, stream);
 
 	if (nwritten > 0)
@@ -702,22 +703,40 @@
 }
 
-int fputc(wchar_t c, FILE *stream)
+wint_t fputwc(wchar_t wc, 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)
+	if (chr_encode(wc, buf, &sz, STR_BOUNDS(1)) != EOK) {
+		errno = EILSEQ;
+		return WEOF;
+	}
+
+	size_t wr = fwrite(buf, 1, sz, stream);
+	if (wr < sz)
+		return WEOF;
+
+	return wc;
+}
+
+wint_t putwchar(wchar_t wc)
+{
+	return fputwc(wc, stdout);
+}
+
+int fputc(int c, FILE *stream)
+{
+	unsigned char b;
+	size_t wr;
+
+	b = (unsigned char) c;
+	wr = fwrite(&b, sizeof(b), 1, stream);
+	if (wr < 1)
+		return EOF;
+
+	return b;
+}
+
+int putchar(int c)
 {
 	return fputc(c, stdout);
@@ -886,24 +905,13 @@
 	}
 
-	if (stream->kio) {
-		kio_update();
-		return 0;
-	}
-
-	if ((stream->fd >= 0) && (stream->need_sync)) {
-		errno_t rc;
-
+	if (stream->need_sync) {
 		/**
 		 * Better than syncing always, but probably still not the
 		 * right thing to do.
 		 */
+		if (stream->ops->flush(stream) == EOF)
+			return EOF;
+
 		stream->need_sync = false;
-		rc = vfs_sync(stream->fd);
-		if (rc != EOK) {
-			errno = rc;
-			return EOF;
-		}
-
-		return 0;
 	}
 
@@ -929,5 +937,5 @@
 int fileno(FILE *stream)
 {
-	if (stream->kio) {
+	if (stream->ops != &stdio_vfs_ops) {
 		errno = EBADF;
 		return EOF;
@@ -959,4 +967,84 @@
 }
 
+/** Read from KIO stream. */
+static size_t stdio_kio_read(void *buf, size_t size, size_t nmemb, FILE *stream)
+{
+	stream->eof = true;
+	return 0;
+}
+
+/** Write to KIO stream. */
+static size_t stdio_kio_write(const void *buf, size_t size, size_t nmemb,
+    FILE *stream)
+{
+	errno_t rc;
+	size_t nwritten;
+
+	rc = kio_write(buf, size * nmemb, &nwritten);
+	if (rc != EOK) {
+		stream->error = true;
+		nwritten = 0;
+	}
+
+	return nwritten / size;
+}
+
+/** Flush KIO stream. */
+static int stdio_kio_flush(FILE *stream)
+{
+	kio_update();
+	return 0;
+}
+
+/** Read from VFS stream. */
+static size_t stdio_vfs_read(void *buf, size_t size, size_t nmemb, FILE *stream)
+{
+	errno_t rc;
+	size_t nread;
+
+	if (size == 0 || nmemb == 0)
+		return 0;
+
+	rc = vfs_read(stream->fd, &stream->pos, buf, size * nmemb, &nread);
+	if (rc != EOK) {
+		errno = rc;
+		stream->error = true;
+	} else if (nread == 0) {
+		stream->eof = true;
+	}
+
+	return (nread / size);
+}
+
+/** Write to VFS stream. */
+static size_t stdio_vfs_write(const void *buf, size_t size, size_t nmemb,
+    FILE *stream)
+{
+	errno_t rc;
+	size_t nwritten;
+
+	rc = vfs_write(stream->fd, &stream->pos, buf, size * nmemb, &nwritten);
+	if (rc != EOK) {
+		errno = rc;
+		stream->error = true;
+	}
+
+	return nwritten / size;
+}
+
+/** Flush VFS stream. */
+static int stdio_vfs_flush(FILE *stream)
+{
+	errno_t rc;
+
+	rc = vfs_sync(stream->fd);
+	if (rc != EOK) {
+		errno = rc;
+		return EOF;
+	}
+
+	return 0;
+}
+
 /** @}
  */
Index: uspace/lib/c/generic/io/printf_core.c
===================================================================
--- uspace/lib/c/generic/io/printf_core.c	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/lib/c/generic/io/printf_core.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -39,4 +39,5 @@
 #include <stdio.h>
 #include <stddef.h>
+#include <stdlib.h>
 #include <io/printf_core.h>
 #include <ctype.h>
Index: uspace/lib/c/generic/io/snprintf.c
===================================================================
--- uspace/lib/c/generic/io/snprintf.c	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/lib/c/generic/io/snprintf.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -35,5 +35,4 @@
 #include <stdarg.h>
 #include <stdio.h>
-#include <io/printf_core.h>
 
 /** Print formatted to the given buffer with limited size.
Index: uspace/lib/c/generic/io/vprintf.c
===================================================================
--- uspace/lib/c/generic/io/vprintf.c	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/lib/c/generic/io/vprintf.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -54,5 +54,5 @@
 
 	while (offset < size) {
-		if (fputc(str[chars], (FILE *) stream) <= 0)
+		if (fputwc(str[chars], (FILE *) stream) <= 0)
 			break;
 
Index: uspace/lib/c/generic/libc.c
===================================================================
--- uspace/lib/c/generic/libc.c	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/lib/c/generic/libc.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -95,5 +95,7 @@
 #endif
 
-	__async_init();
+	__async_server_init();
+	__async_client_init();
+	__async_ports_init();
 
 	/* The basic run-time environment is setup */
@@ -128,5 +130,5 @@
 }
 
-void exit(int status)
+void __libc_exit(int status)
 {
 	if (env_setup) {
@@ -143,5 +145,5 @@
 }
 
-void abort(void)
+void __libc_abort(void)
 {
 	__SYSCALL1(SYS_TASK_EXIT, true);
Index: uspace/lib/c/generic/mem.c
===================================================================
--- uspace/lib/c/generic/mem.c	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/lib/c/generic/mem.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -1,5 +1,5 @@
 /*
  * Copyright (c) 2005 Martin Decky
- * Copyright (c) 2008 Jiri Svoboda
+ * Copyright (c) 2018 Jiri Svoboda
  * All rights reserved.
  *
@@ -252,4 +252,27 @@
 }
 
+/** Search memory area.
+ *
+ * @param s Memory area
+ * @param c Character (byte) to search for
+ * @param n Size of memory area in bytes
+ *
+ * @return Pointer to the first occurrence of @a c in the first @a n
+ *         bytes of @a s or @c NULL if not found.
+ */
+void *memchr(const void *s, int c, size_t n)
+{
+	uint8_t *u = (uint8_t *) s;
+	unsigned char uc = (unsigned char) c;
+	size_t i;
+
+	for (i = 0; i < n; i++) {
+		if (u[i] == uc)
+			return (void *) &u[i];
+	}
+
+	return NULL;
+}
+
 /** @}
  */
Index: uspace/lib/c/generic/ns.c
===================================================================
--- uspace/lib/c/generic/ns.c	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/lib/c/generic/ns.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -155,5 +155,5 @@
 
 	if (sess_ns == NULL) {
-		exch = async_exchange_begin(session_ns);
+		exch = async_exchange_begin(&session_ns);
 		sess_ns = async_connect_me_to_iface(exch, 0, 0, 0);
 		async_exchange_end(exch);
Index: uspace/lib/c/generic/private/async.h
===================================================================
--- uspace/lib/c/generic/private/async.h	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/lib/c/generic/private/async.h	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -81,9 +81,65 @@
 } awaiter_t;
 
+/** Session data */
+struct async_sess {
+	/** List of inactive exchanges */
+	list_t exch_list;
+
+	/** Session interface */
+	iface_t iface;
+
+	/** Exchange management style */
+	exch_mgmt_t mgmt;
+
+	/** Session identification */
+	cap_phone_handle_t phone;
+
+	/** First clone connection argument */
+	sysarg_t arg1;
+
+	/** Second clone connection argument */
+	sysarg_t arg2;
+
+	/** Third clone connection argument */
+	sysarg_t arg3;
+
+	/** Exchange mutex */
+	fibril_mutex_t mutex;
+
+	/** Number of opened exchanges */
+	atomic_t refcnt;
+
+	/** Mutex for stateful connections */
+	fibril_mutex_t remote_state_mtx;
+
+	/** Data for stateful connections */
+	void *remote_state_data;
+};
+
+/** Exchange data */
+struct async_exch {
+	/** Link into list of inactive exchanges */
+	link_t sess_link;
+
+	/** Link into global list of inactive exchanges */
+	link_t global_link;
+
+	/** Session pointer */
+	async_sess_t *sess;
+
+	/** Exchange identification */
+	cap_phone_handle_t phone;
+};
+
 extern void awaiter_initialize(awaiter_t *);
 
-extern void __async_init(void);
+extern void __async_server_init(void);
+extern void __async_client_init(void);
+extern void __async_ports_init(void);
 extern void async_insert_timeout(awaiter_t *);
-extern void reply_received(void *, errno_t, ipc_call_t *);
+
+extern errno_t async_create_port_internal(iface_t, async_port_handler_t,
+    void *, port_id_t *);
+extern async_port_handler_t async_get_port_handler(iface_t, port_id_t, void **);
 
 #endif
Index: uspace/lib/c/generic/private/libc.h
===================================================================
--- uspace/lib/c/generic/private/libc.h	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/lib/c/generic/private/libc.h	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -38,4 +38,6 @@
 extern unsigned char _end[];
 extern void __libc_main(void *) __attribute__((noreturn));
+extern void __libc_exit(int) __attribute__((noreturn));
+extern void __libc_abort(void) __attribute__((noreturn));
 extern int main(int, char *[]);
 
Index: uspace/lib/c/generic/private/ns.h
===================================================================
--- uspace/lib/c/generic/private/ns.h	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/lib/c/generic/private/ns.h	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -38,5 +38,5 @@
 #include <async.h>
 
-extern async_sess_t *session_ns;
+extern async_sess_t session_ns;
 
 #endif
Index: uspace/lib/c/generic/private/sstream.h
===================================================================
--- uspace/lib/c/generic/private/sstream.h	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
+++ uspace/lib/c/generic/private/sstream.h	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2018 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup libc
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBC_PRIVATE_SSTREAM_H_
+#define LIBC_PRIVATE_SSTREAM_H_
+
+#include <stdio.h>
+
+extern void __sstream_init(const char *, FILE *);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/c/generic/private/stdio.h
===================================================================
--- uspace/lib/c/generic/private/stdio.h	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/lib/c/generic/private/stdio.h	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -39,7 +39,19 @@
 #include <stdio.h>
 #include <async.h>
+#include <stddef.h>
 
 /** Maximum characters that can be pushed back by ungetc() */
 #define UNGETC_MAX 1
+
+/** Stream operations */
+typedef struct {
+	/** Read from stream */
+	size_t (*read)(void *buf, size_t size, size_t nmemb, FILE *stream);
+	/** Write to stream */
+	size_t (*write)(const void *buf, size_t size, size_t nmemb,
+	    FILE *stream);
+	/** Flush stream */
+	int (*flush)(FILE *stream);
+} __stream_ops_t;
 
 struct _IO_FILE {
@@ -47,6 +59,12 @@
 	link_t link;
 
+	/** Stream operations */
+	__stream_ops_t *ops;
+
 	/** Underlying file descriptor. */
 	int fd;
+
+	/** Instance argument */
+	void *arg;
 
 	/** File position. */
@@ -58,7 +76,4 @@
 	/** End-of-file indicator. */
 	int eof;
-
-	/** KIO indicator */
-	int kio;
 
 	/** Session to the file provider */
Index: uspace/lib/c/generic/private/stdlib.h
===================================================================
--- uspace/lib/c/generic/private/stdlib.h	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
+++ uspace/lib/c/generic/private/stdlib.h	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2018 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup libc
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBC_PRIVATE_STDLIB_H_
+#define LIBC_PRIVATE_STDLIB_H_
+
+#include <adt/list.h>
+
+/** Exit handler list entry */
+typedef struct {
+	/** Link to exit handler list */
+	link_t llist;
+	/** Exit handler function */
+	void (*func)(void);
+} __exit_handler_t;
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/c/generic/stdio.c
===================================================================
--- uspace/lib/c/generic/stdio.c	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/lib/c/generic/stdio.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2017 Jiri Svoboda
+ * Copyright (c) 2018 Jiri Svoboda
  * All rights reserved.
  *
@@ -34,6 +34,50 @@
 
 #include <errno.h>
+#include <stdbool.h>
 #include <stdio.h>
+#include <str.h>
+#include <str_error.h>
+#include <tmpfile.h>
 #include <vfs/vfs.h>
+
+/** Static buffer for tmpnam function */
+static char tmpnam_buf[L_tmpnam];
+
+/** Get stream position.
+ *
+ * @param stream Stream
+ * @param pos Place to store position
+ *
+ * @return Zero on success, non-zero on failure
+ */
+int fgetpos(FILE *stream, fpos_t *pos)
+{
+	off64_t p;
+
+	p = ftell64(stream);
+	if (p < 0)
+		return -1;
+
+	pos->pos = p;
+	return 0;
+}
+
+/** Get stream position.
+ *
+ * @param stream Stream
+ * @param pos Position
+ *
+ * @return Zero on sucess, non-zero on failure
+ */
+int fsetpos(FILE *stream, const fpos_t *pos)
+{
+	int rc;
+
+	rc = fseek64(stream, pos->pos, SEEK_SET);
+	if (rc < 0)
+		return -1;
+
+	return 0;
+}
 
 /** Rename file or directory (C standard) */
@@ -44,4 +88,8 @@
 	rc = vfs_rename_path(old_path, new_path);
 	if (rc != EOK) {
+		/*
+		 * Note that ISO C leaves the value of errno undefined,
+		 * whereas according to UN*X standards, it is set.
+		 */
 		errno = rc;
 		return -1;
@@ -58,4 +106,8 @@
 	rc = vfs_unlink_path(path);
 	if (rc != EOK) {
+		/*
+		 * Note that ISO C leaves the value of errno undefined,
+		 * whereas according to UN*X standards, it is set.
+		 */
 		errno = rc;
 		return -1;
@@ -65,4 +117,60 @@
 }
 
+/** Create a temporary file.
+ *
+ * @return Open stream descriptor or @c NULL on error. In that case
+ *         errno is set (UN*X). Note that ISO C leaves the value of errno
+ *         undefined.
+ */
+FILE *tmpfile(void)
+{
+	int file;
+	FILE *stream;
+
+	file = __tmpfile();
+	if (file < 0) {
+		printf("file is < 0\n");
+		errno = EEXIST;
+		return NULL;
+	}
+
+	stream = fdopen(file, "w+");
+	if (stream == NULL) {
+		printf("stream = NULL\n");
+		vfs_put(file);
+		errno = EACCES;
+		return NULL;
+	}
+
+	return stream;
+}
+
+/** Create name for a temporary file.
+ *
+ * @param s Pointer to array of at least L_tmpnam bytes or @c NULL.
+ * @return The pointer @a s or pointer to internal static buffer on success,
+ *         @c NULL on error.
+ */
+char *tmpnam(char *s)
+{
+	char *p;
+
+	p = (s != NULL) ? s : tmpnam_buf;
+	return __tmpnam(p);
+}
+
+/** Print error message and string representation of @c errno.
+ *
+ * @param s Error message
+ */
+void perror(const char *s)
+{
+	if (s != NULL && *s != '\0')
+		fprintf(stderr, "%s: %s\n", s, str_error(errno));
+	else
+		fprintf(stderr, "%s\n", str_error(errno));
+}
+
+
 /** @}
  */
Index: uspace/lib/c/generic/stdio/scanf.c
===================================================================
--- uspace/lib/c/generic/stdio/scanf.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
+++ uspace/lib/c/generic/stdio/scanf.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -0,0 +1,1389 @@
+/*
+ * Copyright (c) 2018 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup libc
+ * @{
+ */
+/**
+ * @file
+ * @brief Formatted input (scanf family)
+ */
+
+#include <assert.h>
+#include <_bits/ssize_t.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+typedef enum {
+	/** No length modifier */
+	lm_none,
+	/** 'hh' */
+	lm_hh,
+	/** 'h' */
+	lm_h,
+	/** 'l' */
+	lm_l,
+	/** 'll' */
+	lm_ll,
+	/** 'j' */
+	lm_j,
+	/** 'z' */
+	lm_z,
+	/** 't' */
+	lm_t,
+	/** 'L' */
+	lm_L
+} lenmod_t;
+
+typedef enum {
+	/** Unknown */
+	cs_unknown,
+	/** 'd' */
+	cs_decimal,
+	/** 'i' */
+	cs_int,
+	/** 'o' */
+	cs_octal,
+	/** 'u' */
+	cs_udecimal,
+	/** 'x', 'X' */
+	cs_hex,
+	/** 'a', 'e', 'f', 'g', 'A', 'E', 'F', 'G' */
+	cs_float,
+	/** 'c' */
+	cs_char,
+	/** 's' */
+	cs_str,
+	/* [...] */
+	cs_set,
+	/* 'p' */
+	cs_ptr,
+	/* 'n' */
+	cs_numchar,
+	/* '%' */
+	cs_percent
+} cvtspcr_t;
+
+/** Conversion specification */
+typedef struct {
+	/** Suppress assignment */
+	bool noassign;
+	/** Allocate memory for string (GNU extension) */
+	bool memalloc;
+	/** @c true if width field is valid */
+	bool have_width;
+	/** Width */
+	size_t width;
+	/** Length modifier */
+	lenmod_t lenmod;
+	/** Conversion specifier */
+	cvtspcr_t spcr;
+	/** Scan set (if spc == cs_set) */
+	const char *scanset;
+} cvtspec_t;
+
+/** Buffer for writing strings */
+typedef struct {
+	/** Buffer */
+	char *buf;
+	/** Place where to store pointer to the buffer */
+	char **pptr;
+	/** Allocating memory for caller */
+	bool memalloc;
+	/** Current size of allocated buffer */
+	size_t size;
+} strbuf_t;
+
+/** Wrapper needed to pass va_list around by reference in a portable fashion */
+typedef struct {
+	va_list ap;
+} va_encaps_t;
+
+static int digit_value(char digit)
+{
+	switch (digit) {
+	case '0':
+		return 0;
+	case '1':
+		return 1;
+	case '2':
+		return 2;
+	case '3':
+		return 3;
+	case '4':
+		return 4;
+	case '5':
+		return 5;
+	case '6':
+		return 6;
+	case '7':
+		return 7;
+	case '8':
+		return 8;
+	case '9':
+		return 9;
+	case 'a':
+	case 'A':
+		return 10;
+	case 'b':
+	case 'B':
+		return 11;
+	case 'c':
+	case 'C':
+		return 12;
+	case 'd':
+	case 'D':
+		return 13;
+	case 'e':
+	case 'E':
+		return 14;
+	case 'f':
+	case 'F':
+		return 15;
+	default:
+		assert(false);
+	}
+}
+
+static void cvtspec_parse(const char **fmt, cvtspec_t *spec)
+{
+	spec->scanset = NULL;
+	spec->width = 0;
+
+	/* Assignment suppresion */
+
+	if (**fmt == '*') {
+		spec->noassign = true;
+		++(*fmt);
+	} else {
+		spec->noassign = false;
+	}
+
+	/* Memory allocation */
+
+	if (**fmt == 'm') {
+		spec->memalloc = true;
+		++(*fmt);
+	} else {
+		spec->memalloc = false;
+	}
+
+	/* Width specifier */
+
+	if (isdigit(**fmt)) {
+		spec->have_width = true;
+		assert(**fmt != '0');
+		spec->width = 0;
+		while (isdigit(**fmt)) {
+			spec->width *= 10;
+			spec->width += digit_value(**fmt);
+			++(*fmt);
+		}
+	} else {
+		spec->have_width = false;
+	}
+
+	/* Length modifier */
+
+	switch (**fmt) {
+	case 'h':
+		++(*fmt);
+		if (**fmt == 'h') {
+			spec->lenmod = lm_hh;
+			++(*fmt);
+		} else {
+			spec->lenmod = lm_h;
+		}
+		break;
+	case 'l':
+		++(*fmt);
+		if (**fmt == 'l') {
+			spec->lenmod = lm_ll;
+			++(*fmt);
+		} else {
+			spec->lenmod = lm_l;
+		}
+		break;
+	case 'j':
+		++(*fmt);
+		spec->lenmod = lm_j;
+		break;
+	case 'z':
+		++(*fmt);
+		spec->lenmod = lm_z;
+		break;
+	case 't':
+		++(*fmt);
+		spec->lenmod = lm_t;
+		break;
+	case 'L':
+		++(*fmt);
+		spec->lenmod = lm_L;
+		break;
+	default:
+		spec->lenmod = lm_none;
+		break;
+	}
+
+	/* Conversion specifier */
+
+	switch (**fmt) {
+	case 'd':
+		++(*fmt);
+		spec->spcr = cs_decimal;
+		break;
+	case 'i':
+		++(*fmt);
+		spec->spcr = cs_int;
+		break;
+	case 'o':
+		++(*fmt);
+		spec->spcr = cs_octal;
+		break;
+	case 'u':
+		++(*fmt);
+		spec->spcr = cs_udecimal;
+		break;
+	case 'x':
+	case 'X':
+		++(*fmt);
+		spec->spcr = cs_hex;
+		break;
+	case 'a':
+	case 'e':
+	case 'f':
+	case 'g':
+	case 'A':
+	case 'E':
+	case 'F':
+	case 'G':
+		++(*fmt);
+		spec->spcr = cs_float;
+		break;
+	case 'c':
+		++(*fmt);
+		spec->spcr = cs_char;
+		break;
+	case 's':
+		++(*fmt);
+		spec->spcr = cs_str;
+		break;
+	case '[':
+		++(*fmt);
+		spec->spcr = cs_set;
+		spec->scanset = *fmt;
+		while (**fmt != ']' && **fmt != '\0')
+			++(*fmt);
+		if (**fmt == ']')
+			++(*fmt);
+		break;
+	case 'p':
+		++(*fmt);
+		spec->spcr = cs_ptr;
+		break;
+	case 'n':
+		++(*fmt);
+		spec->spcr = cs_numchar;
+		break;
+	case '%':
+		++(*fmt);
+		spec->spcr = cs_percent;
+		break;
+	default:
+		assert(false);
+		spec->spcr = cs_unknown;
+		break;
+	}
+}
+
+/** Initialize string buffer.
+ *
+ * String buffer is used to write characters from a string conversion.
+ * The buffer can be provided by caller or dynamically allocated
+ * (and grown).
+ *
+ * Initialize the string buffer @a strbuf. If @a spec->noassign,is true,
+ * set the buffer pointer to NULL. Otherwise, if @a spec->memalloc is true,
+ * allocate a buffer and read an argument of type char ** designating
+ * a place where the pointer should be stored. If @a spec->memalloc is false,
+ * read an argument of type char * and use it as a destination to write
+ * the characters to.
+ *
+ * @param strbuf String buffer to initialize
+ * @param cvtspec Conversion specification (noassign, memalloc)
+ * @param ap Argument list to read pointer from
+ *
+ * @return EOK on success, ENOMEM if out of memory
+ */
+static int strbuf_init(strbuf_t *strbuf, cvtspec_t *spec, va_encaps_t *va)
+{
+	if (spec->noassign) {
+		strbuf->memalloc = false;
+		strbuf->buf = NULL;
+		strbuf->pptr = NULL;
+		strbuf->size = 0;
+		return EOK;
+	}
+
+	if (spec->memalloc) {
+		/* Allocate memory for caller */
+		strbuf->memalloc = true;
+		strbuf->size = 1;
+		strbuf->buf = malloc(strbuf->size);
+		if (strbuf->buf == NULL)
+			return ENOMEM;
+
+		/*
+		 * Save pointer to allocated buffer to caller-provided
+		 * location
+		 */
+		strbuf->pptr = va_arg(va->ap, char **);
+		*strbuf->pptr = strbuf->buf;
+	} else {
+		/* Caller-provided buffer */
+		strbuf->memalloc = false;
+		strbuf->size = 0;
+		strbuf->buf = va_arg(va->ap, char *);
+		strbuf->pptr = NULL;
+	}
+
+	return EOK;
+}
+
+/** Write character at the specified position in a string buffer.
+ *
+ * The buffer is enlarged if necessary.
+ *
+ * @param strbuf String buffer
+ * @param idx Character position (starting from 0)
+ * @param c Character to write
+ *
+ * @return EOK on sucess, ENOMEM if out of memory
+ */
+static int strbuf_write(strbuf_t *strbuf, size_t idx, char c)
+{
+	if (strbuf->memalloc && idx >= strbuf->size) {
+		/* Enlarge buffer */
+		strbuf->size = strbuf->size * 2;
+		strbuf->buf = realloc(strbuf->buf, strbuf->size);
+		*strbuf->pptr = strbuf->buf;
+		if (strbuf->buf == NULL)
+			return ENOMEM;
+	}
+
+	if (strbuf->buf != NULL)
+		strbuf->buf[idx] = c;
+
+	return EOK;
+}
+
+/** Get character from stream, keeping count of number of characters read.
+ *
+ * @param f Stream
+ * @param numchar Pointer to counter of characters read
+ * @return Character on success, EOF on error
+ */
+static int __fgetc(FILE *f, int *numchar)
+{
+	int c;
+
+	c = fgetc(f);
+	if (c == EOF)
+		return EOF;
+
+	++(*numchar);
+	return c;
+}
+
+/** Unget character to stream, keeping count of number of characters read.
+ *
+ * @param c Character
+ * @param f Stream
+ * @param numchar Pointer to counter of characters read
+ *
+ * @return @a c on success, EOF on failure
+ */
+static int __ungetc(int c, FILE *f, int *numchar)
+{
+	int rc;
+
+	rc = ungetc(c, f);
+	if (rc == EOF)
+		return EOF;
+
+	--(*numchar);
+	return rc;
+}
+
+/* Skip whitespace in input stream */
+static int vfscanf_skip_ws(FILE *f, int *numchar)
+{
+	int c;
+
+	c = __fgetc(f, numchar);
+	if (c == EOF)
+		return EIO;
+
+	while (isspace(c)) {
+		c = __fgetc(f, numchar);
+		if (c == EOF)
+			return EIO;
+	}
+
+	if (c == EOF)
+		return EIO;
+
+	__ungetc(c, f, numchar);
+	return EOK;
+}
+
+/* Match whitespace. */
+static int vfscanf_match_ws(FILE *f, int *numchar, const char **fmt)
+{
+	int rc;
+
+	rc = vfscanf_skip_ws(f, numchar);
+	if (rc == EOF)
+		return EIO;
+
+	++(*fmt);
+	return EOK;
+}
+
+/** Read intmax_t integer from file.
+ *
+ * @param f Input file
+ * @param numchar Pointer to counter of characters read
+ * @param base Numeric base (0 means detect using prefix)
+ * @param width Maximum field with in characters
+ * @param dest Place to store result
+ * @return EOK on success, EIO on I/O error, EINVAL if input is not valid
+ */
+static int __fstrtoimax(FILE *f, int *numchar, int base, size_t width,
+    intmax_t *dest)
+{
+	int c;
+	intmax_t v;
+	int digit;
+	int sign;
+
+	c = __fgetc(f, numchar);
+	if (c == EOF)
+		return EIO;
+
+	if (c == '+' || c == '-') {
+		/* Sign */
+		sign = (c == '-') ? -1 : +1;
+		c = __fgetc(f, numchar);
+		if (c == EOF)
+			return EIO;
+		--width;
+	} else {
+		sign = 1;
+	}
+
+	if (!isdigit(c) || width < 1) {
+		__ungetc(c, f, numchar);
+		return EINVAL;
+	}
+
+	if (base == 0) {
+		/* Base prefix */
+		if (c == '0') {
+			c = __fgetc(f, numchar);
+			if (c == EOF)
+				return EIO;
+			--width;
+
+			if (width > 0 && (c == 'x' || c == 'X')) {
+				--width;
+				c = __fgetc(f, numchar);
+				if (c == EOF)
+					return EIO;
+				if (width > 0 && isxdigit(c)) {
+					base = 16;
+				} else {
+					*dest = 0;
+					return EOK;
+				}
+			} else {
+				base = 8;
+				if (width == 0) {
+					*dest = 0;
+					return EOK;
+				}
+			}
+		} else {
+			base = 10;
+		}
+	}
+
+	/* Value */
+	v = 0;
+	do {
+		digit = digit_value(c);
+		if (digit >= base)
+			break;
+
+		v = v * base + digit;
+		c = __fgetc(f, numchar);
+		--width;
+	} while (width > 0 && isxdigit(c));
+
+	if (c != EOF)
+		__ungetc(c, f, numchar);
+
+	*dest = sign * v;
+	return EOK;
+}
+
+/** Read uintmax_t unsigned integer from file.
+ *
+ * @param f Input file
+ * @param numchar Pointer to counter of characters read
+ * @param base Numeric base (0 means detect using prefix)
+ * @param width Maximum field with in characters
+ * @param dest Place to store result
+ * @return EOK on success, EIO on I/O error, EINVAL if input is not valid
+ */
+static int __fstrtoumax(FILE *f, int *numchar, int base, size_t width,
+    uintmax_t *dest)
+{
+	int c;
+	uintmax_t v;
+	int digit;
+
+	c = __fgetc(f, numchar);
+	if (c == EOF)
+		return EIO;
+
+	if (!isdigit(c) || width < 1) {
+		__ungetc(c, f, numchar);
+		return EINVAL;
+	}
+
+	if (base == 0) {
+		/* Base prefix */
+		if (c == '0') {
+			c = __fgetc(f, numchar);
+			if (c == EOF)
+				return EIO;
+			--width;
+
+			if (width > 0 && (c == 'x' || c == 'X')) {
+				--width;
+				c = __fgetc(f, numchar);
+				if (c == EOF)
+					return EIO;
+				if (width > 0 && isxdigit(c)) {
+					base = 16;
+				} else {
+					*dest = 0;
+					return EOK;
+				}
+			} else {
+				base = 8;
+				if (width == 0) {
+					*dest = 0;
+					return EOK;
+				}
+			}
+		} else {
+			base = 10;
+		}
+	}
+
+	/* Value */
+	v = 0;
+	do {
+		digit = digit_value(c);
+		if (digit >= base)
+			break;
+
+		v = v * base + digit;
+		c = __fgetc(f, numchar);
+		--width;
+	} while (width > 0 && isxdigit(c));
+
+	if (c != EOF)
+		__ungetc(c, f, numchar);
+
+	*dest = v;
+	return EOK;
+}
+
+/** Read long double from file.
+ *
+ * @param f Input file
+ * @param numchar Pointer to counter of characters read
+ * @param width Maximum field with in characters
+ * @param dest Place to store result
+ * @return EOK on success, EIO on I/O error, EINVAL if input is not valid
+ */
+static int __fstrtold(FILE *f, int *numchar, size_t width, long double *dest)
+{
+	int c;
+	long double v;
+	int digit;
+	int sign;
+	int base;
+	int efactor;
+	int eadd;
+	int eadj;
+	int exp;
+	int expsign;
+
+	c = __fgetc(f, numchar);
+	if (c == EOF)
+		return EIO;
+
+	if (c == '+' || c == '-') {
+		/* Sign */
+		sign = (c == '-') ? -1 : +1;
+		c = __fgetc(f, numchar);
+		if (c == EOF)
+			return EIO;
+		--width;
+	} else {
+		sign = 1;
+	}
+
+	if (!isdigit(c) || width < 1) {
+		__ungetc(c, f, numchar);
+		return EINVAL;
+	}
+
+	/*
+	 * Default is base 10
+	 */
+
+	/* Significand is in base 10 */
+	base = 10;
+	/* e+1 multiplies number by ten */
+	efactor = 10;
+	/* Adjust exp. by one for each fractional digit */
+	eadd = 1;
+
+	/* Base prefix */
+	if (c == '0') {
+		c = __fgetc(f, numchar);
+		if (c == EOF)
+			return EIO;
+		--width;
+
+		if (width > 0 && (c == 'x' || c == 'X')) {
+			--width;
+			c = __fgetc(f, numchar);
+
+			if (width > 0 && isxdigit(c)) {
+				/* Significand is in base 16 */
+				base = 16;
+				/* p+1 multiplies number by two */
+				efactor = 2;
+				/*
+				 * Adjust exponent by 4 for each
+				 * fractional digit
+				 */
+				eadd = 4;
+			} else {
+				*dest = 0;
+				return EOK;
+			}
+		}
+	}
+
+	/* Value */
+	v = 0;
+	do {
+		digit = digit_value(c);
+		if (digit >= base)
+			break;
+
+		v = v * base + digit;
+		c = __fgetc(f, numchar);
+		--width;
+	} while (width > 0 && isxdigit(c));
+
+	/* Decimal-point */
+	eadj = 0;
+
+	if (c == '.' && width > 1) {
+		c = __fgetc(f, numchar);
+		if (c == EOF)
+			return EIO;
+
+		--width;
+
+		/* Fractional part */
+		while (width > 0 && isxdigit(c)) {
+			digit = digit_value(c);
+			if (digit >= base)
+				break;
+
+			v = v * base + digit;
+			c = __fgetc(f, numchar);
+			--width;
+			eadj -= eadd;
+		}
+	}
+
+	exp = 0;
+
+	/* Exponent */
+	if ((width > 1 && base == 10 && (c == 'e' || c == 'E')) ||
+	    (width > 1 && base == 16 && (c == 'p' || c == 'P'))) {
+		c = __fgetc(f, numchar);
+		if (c == EOF)
+			return EIO;
+
+		--width;
+
+		if (width > 1 && (c == '+' || c == '-')) {
+			/* Exponent sign */
+			if (c == '+') {
+				expsign = 1;
+			} else {
+				expsign = -1;
+			}
+
+			c = __fgetc(f, numchar);
+			if (c == EOF)
+				return EIO;
+
+			--width;
+		} else {
+			expsign = 1;
+		}
+
+		while (width > 0 && isdigit(c)) {
+			digit = digit_value(c);
+			if (digit >= 10)
+				break;
+
+			exp = exp * 10 + digit;
+			c = __fgetc(f, numchar);
+			--width;
+		}
+
+		exp = exp * expsign;
+	}
+
+	exp += eadj;
+
+	/* Adjust v for value of exponent */
+
+	while (exp > 0) {
+		v = v * efactor;
+		--exp;
+	}
+
+	while (exp < 0) {
+		v = v / efactor;
+		++exp;
+	}
+
+	if (c != EOF)
+		__ungetc(c, f, numchar);
+
+	*dest = sign * v;
+	return EOK;
+}
+
+/* Read characters from stream */
+static int __fgetchars(FILE *f, int *numchar, size_t width, strbuf_t *strbuf,
+    size_t *nread)
+{
+	size_t cnt;
+	int c;
+	int rc;
+
+	for (cnt = 0; cnt < width; cnt++) {
+		c = __fgetc(f, numchar);
+		if (c == EOF) {
+			*nread = cnt;
+			return EIO;
+		}
+
+		rc = strbuf_write(strbuf, cnt, c);
+		if (rc != EOK) {
+			*nread = cnt;
+			return rc;
+		}
+	}
+
+	*nread = cnt;
+	return EOK;
+}
+
+/* Read non-whitespace string from stream */
+static int __fgetstr(FILE *f, int *numchar, size_t width, strbuf_t *strbuf,
+    size_t *nread)
+{
+	size_t cnt;
+	int c;
+	int rc;
+	int rc2;
+
+	rc = EOK;
+
+	for (cnt = 0; cnt < width; cnt++) {
+		c = __fgetc(f, numchar);
+		if (c == EOF) {
+			rc = EIO;
+			break;
+		}
+
+		if (isspace(c)) {
+			__ungetc(c, f, numchar);
+			break;
+		}
+
+		rc = strbuf_write(strbuf, cnt, c);
+		if (rc != EOK) {
+			*nread = cnt;
+			return rc;
+		}
+	}
+
+	/* Null-terminate */
+	rc2 = strbuf_write(strbuf, cnt, '\0');
+	if (rc2 != EOK) {
+		*nread = cnt;
+		return rc2;
+	}
+
+	*nread = cnt;
+	return rc;
+}
+
+/** Determine if character is in scanset.
+ *
+ * Note that we support ranges, although that is a GNU extension.
+ *
+ * @param c Character
+ * @param scanset Pointer to scanset
+ * @return @c true iff @a c is in scanset @a scanset.
+ */
+static bool is_in_scanset(char c, const char *scanset)
+{
+	const char *p = scanset;
+	bool inverted = false;
+	char startc;
+	char endc;
+
+	/* Inverted scanset */
+	if (*p == '^') {
+		inverted = true;
+		++p;
+	}
+
+	/*
+	 * Either ']' or '-' at beginning or after '^' loses special meaning.
+	 * However, '-' after ']' (or vice versa) does not.
+	 */
+	if (*p == ']') {
+		/* ']' character in scanset */
+		if (c == ']')
+			return !inverted;
+		++p;
+	} else if (*p == '-') {
+		/* '-' character in scanset */
+		if (c == '-')
+			return !inverted;
+		++p;
+	}
+
+	/* Remaining characters */
+	while (*p != '\0' && *p != ']') {
+		/* '-' is a range unless it's the last character in scanset */
+		if (*p == '-' && p[1] != ']' && p[1] != '\0') {
+			startc = p[-1];
+			endc = p[1];
+
+			if (c >= startc && c <= endc)
+				return !inverted;
+
+			p += 2;
+			continue;
+		}
+
+		if (*p == c)
+			return !inverted;
+		++p;
+	}
+
+	return inverted;
+}
+
+/* Read string of characters from scanset from stream */
+static int __fgetscanstr(FILE *f, int *numchar, size_t width,
+    const char *scanset, strbuf_t *strbuf, size_t *nread)
+{
+	size_t cnt;
+	int c;
+	int rc;
+	int rc2;
+
+	rc = EOK;
+
+	for (cnt = 0; cnt < width; cnt++) {
+		c = __fgetc(f, numchar);
+		if (c == EOF) {
+			rc = EIO;
+			break;
+		}
+
+		if (!is_in_scanset(c, scanset)) {
+			__ungetc(c, f, numchar);
+			break;
+		}
+
+		rc = strbuf_write(strbuf, cnt, c);
+		if (rc != EOK) {
+			*nread = cnt;
+			break;
+		}
+	}
+
+	/* Null-terminate */
+	rc2 = strbuf_write(strbuf, cnt, '\0');
+	if (rc2 != EOK) {
+		*nread = cnt;
+		return rc2;
+	}
+
+	*nread = cnt;
+	return rc;
+}
+
+/** Perform a single conversion. */
+static int vfscanf_cvt(FILE *f, const char **fmt, va_encaps_t *va,
+    int *numchar, unsigned *ncvt)
+{
+	int rc;
+	int c;
+	intmax_t ival;
+	uintmax_t uval;
+	long double fval = 0.0;
+	int *iptr;
+	short *sptr;
+	signed char *scptr;
+	long *lptr;
+	long long *llptr;
+	ssize_t *ssptr;
+	ptrdiff_t *pdptr;
+	intmax_t *imptr;
+	unsigned *uptr;
+	unsigned short *usptr;
+	unsigned char *ucptr;
+	unsigned long *ulptr;
+	unsigned long long *ullptr;
+	float *fptr;
+	double *dptr;
+	long double *ldptr;
+	size_t *szptr;
+	ptrdiff_t *updptr;
+	uintmax_t *umptr;
+	void **pptr;
+	size_t width;
+	cvtspec_t cvtspec;
+	strbuf_t strbuf;
+	size_t nread;
+
+	++(*fmt);
+
+	cvtspec_parse(fmt, &cvtspec);
+
+	if (cvtspec.spcr != cs_set && cvtspec.spcr != cs_char &&
+	    cvtspec.spcr != cs_numchar) {
+		/* Skip whitespace */
+		rc = vfscanf_skip_ws(f, numchar);
+		if (rc == EIO)
+			return EIO;
+
+		assert(rc == EOK);
+	}
+
+	width = cvtspec.have_width ? cvtspec.width : SIZE_MAX;
+
+	switch (cvtspec.spcr) {
+	case cs_percent:
+		/* Match % character */
+		c = __fgetc(f, numchar);
+		if (c == EOF)
+			return EIO;
+		if (c != '%') {
+			__ungetc(c, f, numchar);
+			return EINVAL;
+		}
+		break;
+	case cs_decimal:
+		/* Decimal integer */
+		rc = __fstrtoimax(f, numchar, 10, width, &ival);
+		if (rc != EOK)
+			return rc;
+		break;
+	case cs_udecimal:
+		/* Decimal unsigned integer */
+		rc = __fstrtoumax(f, numchar, 10, width, &uval);
+		if (rc != EOK)
+			return rc;
+		break;
+	case cs_octal:
+		/* Octal unsigned integer */
+		rc = __fstrtoumax(f, numchar, 8, width, &uval);
+		if (rc != EOK)
+			return rc;
+		break;
+	case cs_hex:
+		/* Hexadecimal unsigned integer */
+		rc = __fstrtoumax(f, numchar, 16, width, &uval);
+		if (rc != EOK)
+			return rc;
+		break;
+	case cs_float:
+		/* Floating-point value */
+		rc = __fstrtold(f, numchar, width, &fval);
+		if (rc != EOK)
+			return rc;
+		break;
+	case cs_int:
+		/* Signed integer with base detection */
+		rc = __fstrtoimax(f, numchar, 0, width, &ival);
+		if (rc != EOK)
+			return rc;
+		break;
+	case cs_ptr:
+		/* Unsigned integer with base detection (need 0xXXXXX) */
+		rc = __fstrtoumax(f, numchar, 0, width, &uval);
+		if (rc != EOK)
+			return rc;
+		break;
+	case cs_char:
+		/* Characters */
+		rc = strbuf_init(&strbuf, &cvtspec, va);
+		if (rc != EOK)
+			return rc;
+
+		width = cvtspec.have_width ? cvtspec.width : 1;
+		rc = __fgetchars(f, numchar, width, &strbuf, &nread);
+		if (rc != EOK) {
+			if (rc == ENOMEM)
+				return EIO;
+
+			assert(rc == EIO);
+			/* If we have at least one char, we succeeded */
+			if (nread > 0)
+				++(*ncvt);
+			return EIO;
+		}
+		break;
+	case cs_str:
+		/* Non-whitespace string */
+		rc = strbuf_init(&strbuf, &cvtspec, va);
+		if (rc != EOK)
+			return rc;
+
+		width = cvtspec.have_width ? cvtspec.width : SIZE_MAX;
+		rc = __fgetstr(f, numchar, width, &strbuf, &nread);
+		if (rc != EOK) {
+			if (rc == ENOMEM)
+				return EIO;
+
+			assert(rc == EIO);
+			/* If we have at least one char, we succeeded */
+			if (nread > 0)
+				++(*ncvt);
+			return EIO;
+		}
+		break;
+	case cs_set:
+		/* String of characters from scanset */
+		rc = strbuf_init(&strbuf, &cvtspec, va);
+		if (rc != EOK)
+			return rc;
+
+		width = cvtspec.have_width ? cvtspec.width : SIZE_MAX;
+		rc = __fgetscanstr(f, numchar, width, cvtspec.scanset,
+		    &strbuf, &nread);
+		if (rc != EOK) {
+			if (rc == ENOMEM)
+				return EIO;
+
+			assert(rc == EIO);
+			/* If we have at least one char, we succeeded */
+			if (nread > 0)
+				++(*ncvt);
+			return EIO;
+		}
+		break;
+	case cs_numchar:
+		break;
+	default:
+		return EINVAL;
+	}
+
+	/* Assignment */
+
+	if (cvtspec.noassign)
+		goto skip_assign;
+
+	switch (cvtspec.spcr) {
+	case cs_percent:
+		break;
+	case cs_decimal:
+	case cs_int:
+		switch (cvtspec.lenmod) {
+		case lm_none:
+			iptr = va_arg(va->ap, int *);
+			*iptr = ival;
+			break;
+		case lm_hh:
+			scptr = va_arg(va->ap, signed char *);
+			*scptr = ival;
+			break;
+		case lm_h:
+			sptr = va_arg(va->ap, short *);
+			*sptr = ival;
+			break;
+		case lm_l:
+			lptr = va_arg(va->ap, long *);
+			*lptr = ival;
+			break;
+		case lm_ll:
+			llptr = va_arg(va->ap, long long *);
+			*llptr = ival;
+			break;
+		case lm_j:
+			imptr = va_arg(va->ap, intmax_t *);
+			*imptr = ival;
+			break;
+		case lm_z:
+			ssptr = va_arg(va->ap, ssize_t *);
+			*ssptr = ival;
+			break;
+		case lm_t:
+			pdptr = va_arg(va->ap, ptrdiff_t *);
+			*pdptr = ival;
+			break;
+		default:
+			assert(false);
+		}
+
+		++(*ncvt);
+		break;
+	case cs_udecimal:
+	case cs_octal:
+	case cs_hex:
+		switch (cvtspec.lenmod) {
+		case lm_none:
+			uptr = va_arg(va->ap, unsigned *);
+			*uptr = uval;
+			break;
+		case lm_hh:
+			ucptr = va_arg(va->ap, unsigned char *);
+			*ucptr = uval;
+			break;
+		case lm_h:
+			usptr = va_arg(va->ap, unsigned short *);
+			*usptr = uval;
+			break;
+		case lm_l:
+			ulptr = va_arg(va->ap, unsigned long *);
+			*ulptr = uval;
+			break;
+		case lm_ll:
+			ullptr = va_arg(va->ap, unsigned long long *);
+			*ullptr = uval;
+			break;
+		case lm_j:
+			umptr = va_arg(va->ap, uintmax_t *);
+			*umptr = uval;
+			break;
+		case lm_z:
+			szptr = va_arg(va->ap, size_t *);
+			*szptr = uval;
+			break;
+		case lm_t:
+			updptr = va_arg(va->ap, ptrdiff_t *);
+			*updptr = uval;
+			break;
+		default:
+			assert(false);
+		}
+
+		++(*ncvt);
+		break;
+	case cs_float:
+		switch (cvtspec.lenmod) {
+		case lm_none:
+			fptr = va_arg(va->ap, float *);
+			*fptr = fval;
+			break;
+		case lm_l:
+			dptr = va_arg(va->ap, double *);
+			*dptr = fval;
+			break;
+		case lm_L:
+			ldptr = va_arg(va->ap, long double *);
+			*ldptr = fval;
+			break;
+		default:
+			assert(false);
+		}
+
+		++(*ncvt);
+		break;
+	case cs_ptr:
+		pptr = va_arg(va->ap, void *);
+		*pptr = (void *)(uintptr_t)uval;
+		++(*ncvt);
+		break;
+	case cs_char:
+		++(*ncvt);
+		break;
+	case cs_str:
+		++(*ncvt);
+		break;
+	case cs_set:
+		++(*ncvt);
+		break;
+	case cs_numchar:
+		/* Store number of characters read so far. */
+		iptr = va_arg(va->ap, int *);
+		*iptr = *numchar;
+		/* No incrementing of ncvt */
+		break;
+	default:
+		return EINVAL;
+	}
+
+skip_assign:
+
+	return EOK;
+}
+
+int vfscanf(FILE *f, const char *fmt, va_list ap)
+{
+	const char *cp;
+	int c;
+	unsigned ncvt;
+	int numchar;
+	bool input_error = false;
+	va_encaps_t va;
+	int rc;
+
+	va_copy(va.ap, ap);
+
+	ncvt = 0;
+	numchar = 0;
+	cp = fmt;
+	while (*cp != '\0') {
+		if (isspace(*cp)) {
+			/* Whitespace */
+			rc = vfscanf_match_ws(f, &numchar, &cp);
+			if (rc == EIO) {
+				input_error = true;
+				break;
+			}
+
+			assert(rc == EOK);
+		} else if (*cp == '%') {
+			/* Conversion specification */
+			rc = vfscanf_cvt(f, &cp, &va, &numchar,
+			    &ncvt);
+			if (rc == EIO) {
+				/* Input error */
+				input_error = true;
+				break;
+			}
+
+			/* Other error */
+			if (rc != EOK)
+				break;
+		} else {
+			/* Match specific character */
+			c = __fgetc(f, &numchar);
+			if (c == EOF) {
+				input_error = true;
+				break;
+			}
+
+			if (c != *cp) {
+				__ungetc(c, f, &numchar);
+				break;
+			}
+
+			++cp;
+		}
+	}
+
+	if (input_error && ncvt == 0)
+		return EOF;
+
+	return ncvt;
+}
+
+int fscanf(FILE *f, const char *fmt, ...)
+{
+	va_list args;
+	int rc;
+
+	va_start(args, fmt);
+	rc = vfscanf(f, fmt, args);
+	va_end(args);
+
+	return rc;
+}
+
+int vscanf(const char *fmt, va_list ap)
+{
+	return vfscanf(stdin, fmt, ap);
+}
+
+int scanf(const char *fmt, ...)
+{
+	va_list args;
+	int rc;
+
+	va_start(args, fmt);
+	rc = vscanf(fmt, args);
+	va_end(args);
+
+	return rc;
+}
+
+/** @}
+ */
Index: uspace/lib/c/generic/stdio/sprintf.c
===================================================================
--- uspace/lib/c/generic/stdio/sprintf.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
+++ uspace/lib/c/generic/stdio/sprintf.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2018 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup libc
+ * @{
+ */
+/** @file
+ */
+
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+
+/** Print formatted to string.
+ *
+ * This function is unsafe, thus it is marked as deprecated. It should never
+ * be used in native HelenOS code.
+ *
+ * @param str Buffer to write to
+ * @param fmt Format string
+ *
+ * @return Number of characters printed on success, negative value on failure
+ */
+int sprintf(char *s, const char *fmt, ...)
+{
+	int rc;
+
+	va_list args;
+	va_start(args, fmt);
+	rc = vsnprintf(s, SIZE_MAX, fmt, args);
+	va_end(args);
+
+	return rc;
+}
+
+/** @}
+ */
Index: uspace/lib/c/generic/stdio/sscanf.c
===================================================================
--- uspace/lib/c/generic/stdio/sscanf.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
+++ uspace/lib/c/generic/stdio/sscanf.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2018 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup libc
+ * @{
+ */
+/**
+ * @file
+ * @brief sscanf, vsscanf
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+#include "../private/stdio.h"
+#include "../private/sstream.h"
+
+int vsscanf(const char *s, const char *fmt, va_list ap)
+{
+	FILE f;
+
+	__sstream_init(s, &f);
+	return vfscanf(&f, fmt, ap);
+}
+
+int sscanf(const char *s, const char *fmt, ...)
+{
+	va_list args;
+	int rc;
+
+	va_start(args, fmt);
+	rc = vsscanf(s, fmt, args);
+	va_end(args);
+
+	return rc;
+}
+
+/** @}
+ */
Index: uspace/lib/c/generic/stdio/sstream.c
===================================================================
--- uspace/lib/c/generic/stdio/sstream.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
+++ uspace/lib/c/generic/stdio/sstream.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2018 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup libc
+ * @{
+ */
+/** @file
+ */
+
+#include <stdio.h>
+#include <str.h>
+#include <errno.h>
+#include <adt/list.h>
+#include <wchar.h>
+#include "../private/stdio.h"
+#include "../private/sstream.h"
+
+static size_t stdio_str_read(void *, size_t, size_t, FILE *);
+static size_t stdio_str_write(const void *, size_t, size_t, FILE *);
+static int stdio_str_flush(FILE *);
+
+static __stream_ops_t stdio_str_ops = {
+	.read = stdio_str_read,
+	.write = stdio_str_write,
+	.flush = stdio_str_flush
+};
+
+/** Read from string stream. */
+static size_t stdio_str_read(void *buf, size_t size, size_t nmemb, FILE *stream)
+{
+	size_t nread;
+	char *cp = (char *)stream->arg;
+	char *bp = (char *)buf;
+
+	nread = 0;
+	while (nread < size * nmemb) {
+		if (*cp == '\0') {
+			stream->eof = true;
+			break;
+		}
+
+		bp[nread] = *cp;
+		++nread;
+		++cp;
+		stream->arg = (void *)cp;
+	}
+
+	return (nread / size);
+}
+
+/** Write to string stream. */
+static size_t stdio_str_write(const void *buf, size_t size, size_t nmemb,
+    FILE *stream)
+{
+	return 0;
+}
+
+/** Flush string stream. */
+static int stdio_str_flush(FILE *stream)
+{
+	return EOF;
+}
+
+/** Initialize string stream.
+ *
+ * @param str String used as backend for reading
+ * @param stream Stream to initialize
+ */
+void __sstream_init(const char *str, FILE *stream)
+{
+	memset(stream, 0, sizeof(FILE));
+	stream->ops = &stdio_str_ops;
+	stream->arg = (void *)str;
+}
+
+/** @}
+ */
Index: uspace/lib/c/generic/stdio/vsprintf.c
===================================================================
--- uspace/lib/c/generic/stdio/vsprintf.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
+++ uspace/lib/c/generic/stdio/vsprintf.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2018 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup libc
+ * @{
+ */
+/** @file
+ */
+
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+
+/** Print formatted to string with arguments passed as va_list.
+ *
+ * This function is unsafe, thus it is marked as deprecated. It should never
+ * be used in native HelenOS code.
+ *
+ * @param str Buffer to write to
+ * @param fmt Format string
+ * @param ap Arguments
+ *
+ * @return Number of characters printed on success, negative value on failure
+ */
+int vsprintf(char *s, const char *fmt, va_list ap)
+{
+	return vsnprintf(s, SIZE_MAX, fmt, ap);
+}
+
+/** @}
+ */
Index: uspace/lib/c/generic/stdlib.c
===================================================================
--- uspace/lib/c/generic/stdlib.c	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/lib/c/generic/stdlib.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -1,4 +1,5 @@
 /*
  * Copyright (c) 2006 Ondrej Palkovsky
+ * Copyright (c) 2018 Jiri Svoboda
  * All rights reserved.
  *
@@ -33,8 +34,19 @@
  */
 
+#include <adt/list.h>
+#include <fibril_synch.h>
 #include <stdlib.h>
+#include "private/libc.h"
+#include "private/stdlib.h"
 
 static int glbl_seed = 1;
 
+static LIST_INITIALIZE(exit_handlers);
+static FIBRIL_MUTEX_INITIALIZE(exit_handlers_lock);
+
+static LIST_INITIALIZE(quick_exit_handlers);
+static FIBRIL_MUTEX_INITIALIZE(quick_exit_handlers_lock);
+
+
 int rand(void)
 {
@@ -47,4 +59,251 @@
 }
 
+/** Register exit handler.
+ *
+ * @param func Function to be called during program terimnation
+ * @return Zero on success, nonzero on failure
+ */
+int atexit(void (*func)(void))
+{
+	__exit_handler_t *entry;
+
+	entry = malloc(sizeof(__exit_handler_t));
+	if (entry == NULL)
+		return -1;
+
+	entry->func = func;
+
+	fibril_mutex_lock(&exit_handlers_lock);
+	list_prepend(&entry->llist, &exit_handlers);
+	fibril_mutex_unlock(&exit_handlers_lock);
+	return 0;
+}
+
+/** Terminate program with exit status.
+ *
+ * @param status Exit status
+ */
+void exit(int status)
+{
+	link_t *link;
+	__exit_handler_t *eh;
+
+	/* Call exit handlers */
+	fibril_mutex_lock(&exit_handlers_lock);
+	while (!list_empty(&exit_handlers)) {
+		link = list_first(&exit_handlers);
+		list_remove(link);
+		fibril_mutex_unlock(&exit_handlers_lock);
+
+		eh = list_get_instance(link, __exit_handler_t, llist);
+		eh->func();
+		free(eh);
+		fibril_mutex_lock(&exit_handlers_lock);
+	}
+
+	fibril_mutex_unlock(&exit_handlers_lock);
+
+	_Exit(status);
+}
+
+/** Register quick exit handler.
+ *
+ * @param func Function to be called during quick program terimnation
+ * @return Zero on success, nonzero on failure
+ */
+int at_quick_exit(void (*func)(void))
+{
+	__exit_handler_t *entry;
+
+	entry = malloc(sizeof(__exit_handler_t));
+	if (entry == NULL)
+		return -1;
+
+	entry->func = func;
+
+	fibril_mutex_lock(&exit_handlers_lock);
+	list_prepend(&entry->llist, &exit_handlers);
+	fibril_mutex_unlock(&exit_handlers_lock);
+	return 0;
+}
+
+/** Quickly terminate program with exit status.
+ *
+ * @param status Exit status
+ */
+void quick_exit(int status)
+{
+	link_t *link;
+	__exit_handler_t *eh;
+
+	/* Call quick exit handlers */
+	fibril_mutex_lock(&quick_exit_handlers_lock);
+	while (!list_empty(&quick_exit_handlers)) {
+		link = list_first(&quick_exit_handlers);
+		list_remove(link);
+		fibril_mutex_unlock(&quick_exit_handlers_lock);
+
+		eh = list_get_instance(link, __exit_handler_t, llist);
+		eh->func();
+		free(eh);
+		fibril_mutex_lock(&quick_exit_handlers_lock);
+	}
+
+	fibril_mutex_unlock(&quick_exit_handlers_lock);
+
+	_Exit(status);
+}
+
+void _Exit(int status)
+{
+	__libc_exit(status);
+}
+
+/** Abnormal program termination */
+void abort(void)
+{
+	__libc_abort();
+}
+
+/** Get environment list entry.
+ *
+ * Note that this function is not reentrant. The returned string is only
+ * guaranteed to be valid until the next call to @c getenv.
+ *
+ * @param name Entry name
+ * @return Pointer to string or @c NULL if not found
+ */
+char *getenv(const char *name)
+{
+	(void) name;
+	return NULL;
+}
+
+/** Execute command.
+ *
+ * @param string Command to execute or @c NULL
+ *
+ * @return If @a string is @c NULL, return zero (no command processor
+ *         available). If @a string is not @c NULL, return 1 (failure).
+ */
+int system(const char *string)
+{
+	if (string == NULL)
+		return 0;
+
+	return 1;
+}
+
+/** Compute the absolute value of an integer.
+ *
+ * If the result cannot be represented, the behavior is undefined.
+ *
+ * @param j Integer
+ * @return The absolute value of @a j
+ */
+int abs(int j)
+{
+	int aj;
+
+	if (j < 0) {
+		aj = -j;
+		assert(aj >= 0);
+	} else {
+		aj = j;
+	}
+
+	return aj;
+}
+
+/** Compute the absolute value of a long integer.
+ *
+ * If the result cannot be represented, the behavior is undefined.
+ *
+ * @param j Long integer
+ * @return The absolute value of @a j
+ */
+long labs(long j)
+{
+	long aj;
+
+	if (j < 0) {
+		aj = -j;
+		assert(aj >= 0);
+	} else {
+		aj = j;
+	}
+
+	return aj;
+}
+
+/** Compute the absolute value of a long long integer.
+ *
+ * If the result cannot be represented, the behavior is undefined.
+ *
+ * @param j Long long integer
+ * @return The absolute value of @a j
+ */
+long long llabs(long long j)
+{
+	long long aj;
+
+	if (j < 0) {
+		aj = -j;
+		assert(aj >= 0);
+	} else {
+		aj = j;
+	}
+
+	return aj;
+}
+
+/** Compute quotient and remainder of int division.
+ *
+ * @param numer Numerator
+ * @param denom Denominator
+ * @return Structure containing quotient and remainder
+ */
+div_t div(int numer, int denom)
+{
+	div_t d;
+
+	d.quot = numer / denom;
+	d.rem = numer % denom;
+
+	return d;
+}
+
+/** Compute quotient and remainder of long division.
+ *
+ * @param numer Numerator
+ * @param denom Denominator
+ * @return Structure containing quotient and remainder
+ */
+ldiv_t ldiv(long numer, long denom)
+{
+	ldiv_t d;
+
+	d.quot = numer / denom;
+	d.rem = numer % denom;
+
+	return d;
+}
+
+/** Compute quotient and remainder of long long division.
+ *
+ * @param numer Numerator
+ * @param denom Denominator
+ * @return Structure containing quotient and remainder
+ */
+lldiv_t lldiv(long long numer, long long denom)
+{
+	lldiv_t d;
+
+	d.quot = numer / denom;
+	d.rem = numer % denom;
+
+	return d;
+}
+
 /** @}
  */
Index: uspace/lib/c/generic/strtol.c
===================================================================
--- uspace/lib/c/generic/strtol.c	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/lib/c/generic/strtol.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -37,9 +37,11 @@
  */
 
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
 #include <inttypes.h>
+#include <limits.h>
+#include <stdbool.h>
 #include <stdlib.h>
-#include <limits.h>
-#include <ctype.h>
-#include <assert.h>
 
 // TODO: unit tests
Index: uspace/lib/c/generic/tmpfile.c
===================================================================
--- uspace/lib/c/generic/tmpfile.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
+++ uspace/lib/c/generic/tmpfile.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2018 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup libc
+ * @{
+ */
+/** @file Temporary files
+ */
+
+#include <errno.h>
+#include <fibril_synch.h>
+#include <stdio.h>
+#include <str.h>
+#include <tmpfile.h>
+#include <vfs/vfs.h>
+
+static size_t tmpfile_cnt = 0;
+static FIBRIL_MUTEX_INITIALIZE(tmpfile_lock);
+
+/** Create and open file suitable as temporary file based on template.
+ *
+ * This is designed to allow creating temporary files compatible with POSIX
+ * mk(s)temp and tempnam, as well as for the use of ISO C tmpfile, tmpnam.
+ *
+ * @param templ
+ * @param create If @c false, only construct file name
+ *
+ * @return If @a create is true, open file descriptor on success (and
+ *         @a *templ is filled in with actual file name),
+ *         if @a create is false, zero on success. -1 on failure.
+ */
+int __tmpfile_templ(char *templ, bool create)
+{
+	size_t tsize;
+	char *p;
+	int file;
+	errno_t rc;
+
+	tsize = str_size(templ);
+	if (tsize < 6)
+		return -1;
+
+	p = templ + tsize - 6;
+	if (str_cmp(p, "XXXXXX") != 0)
+		return -1;
+
+	fibril_mutex_lock(&tmpfile_lock);
+
+	while (tmpfile_cnt < 1000000) {
+		snprintf(p, 6 + 1, "%06zu", tmpfile_cnt);
+		if (create) {
+			/* Try creating file */
+			rc = vfs_lookup(templ, WALK_MUST_CREATE |
+			    WALK_REGULAR, &file);
+			if (rc == EOK) {
+				rc = vfs_open(file, MODE_READ | MODE_WRITE);
+				if (rc == EOK) {
+					++tmpfile_cnt;
+					fibril_mutex_unlock(&tmpfile_lock);
+					return file;
+				}
+
+				vfs_put(file);
+			}
+		} else {
+			/* Test if file exists */
+			rc = vfs_lookup(templ, 0, &file);
+			if (rc != EOK) {
+				++tmpfile_cnt;
+				fibril_mutex_unlock(&tmpfile_lock);
+				return 0;
+			}
+
+			vfs_put(file);
+		}
+
+		++tmpfile_cnt;
+	}
+
+	fibril_mutex_unlock(&tmpfile_lock);
+	return -1;
+}
+
+/** Create and open temporary (unnamed) file.
+ *
+ * @return Open file descriptor on success, -1 on failure.
+ */
+int __tmpfile(void)
+{
+	int file;
+	char namebuf[L_tmpnam];
+
+	str_cpy(namebuf, L_tmpnam, "/tmp/tmp.XXXXXX");
+
+	file = __tmpfile_templ(namebuf, true);
+	if (file < 0)
+		return -1;
+
+	(void) vfs_unlink_path(namebuf);
+	return file;
+}
+
+/** Construct temporary file name.
+ *
+ * @param namebuf Buffer that can hold at least L_tmpnam bytes
+ * @return @a namebuf on success, @c NULL on failure
+ */
+char *__tmpnam(char *namebuf)
+{
+	int rc;
+
+	str_cpy(namebuf, L_tmpnam, "/tmp/tmp.XXXXXX");
+
+	rc = __tmpfile_templ(namebuf, false);
+	if (rc < 0)
+		return NULL;
+
+	return namebuf;
+}
+
+/** @}
+ */
Index: uspace/lib/c/generic/ubsan.c
===================================================================
--- uspace/lib/c/generic/ubsan.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
+++ uspace/lib/c/generic/ubsan.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -0,0 +1,220 @@
+// SPDX-License-Identifier: BSD-2-Clause
+/*
+ * Copyright (c) 2016, Linaro Limited
+ */
+
+/* Modified for HelenOS use by Jiří Zárevúcky. */
+
+#include <stdbool.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <mem.h>
+#include <io/kio.h>
+
+#define PRINTF(...) kio_printf(__VA_ARGS__)
+#define ubsan_panic() abort()
+
+struct source_location {
+	const char *file_name;
+	uint32_t line;
+	uint32_t column;
+};
+
+struct type_descriptor {
+	uint16_t type_kind;
+	uint16_t type_info;
+	char type_name[];
+};
+
+struct type_mismatch_data {
+	struct source_location loc;
+	struct type_descriptor *type;
+	unsigned long alignment;
+	unsigned char type_check_kind;
+};
+
+struct overflow_data {
+	struct source_location loc;
+	struct type_descriptor *type;
+};
+
+struct shift_out_of_bounds_data {
+	struct source_location loc;
+	struct type_descriptor *lhs_type;
+	struct type_descriptor *rhs_type;
+};
+
+struct out_of_bounds_data {
+	struct source_location loc;
+	struct type_descriptor *array_type;
+	struct type_descriptor *index_type;
+};
+
+struct unreachable_data {
+	struct source_location loc;
+};
+
+struct vla_bound_data {
+	struct source_location loc;
+	struct type_descriptor *type;
+};
+
+struct invalid_value_data {
+	struct source_location loc;
+	struct type_descriptor *type;
+};
+
+struct nonnull_arg_data {
+	struct source_location loc;
+};
+
+struct nonnull_return_data {
+	struct source_location loc;
+	struct source_location attr_loc;
+};
+
+/*
+ * When compiling with -fsanitize=undefined the compiler expects functions
+ * with the following signatures. The functions are never called directly,
+ * only when undefined behavior is detected in instrumented code.
+ */
+void __ubsan_handle_type_mismatch(struct type_mismatch_data *data, unsigned long ptr);
+void __ubsan_handle_add_overflow(struct overflow_data *data, unsigned long lhs, unsigned long rhs);
+void __ubsan_handle_sub_overflow(struct overflow_data *data, unsigned long lhs, unsigned long rhs);
+void __ubsan_handle_mul_overflow(struct overflow_data *data, unsigned long lhs, unsigned long rhs);
+void __ubsan_handle_negate_overflow(struct overflow_data *data, unsigned long old_val);
+void __ubsan_handle_divrem_overflow(struct overflow_data *data, unsigned long lhs, unsigned long rhs);
+void __ubsan_handle_shift_out_of_bounds(struct shift_out_of_bounds_data *data, unsigned long lhs, unsigned long rhs);
+void __ubsan_handle_out_of_bounds(struct out_of_bounds_data *data, unsigned long idx);
+void __ubsan_handle_unreachable(struct unreachable_data *data);
+void __ubsan_handle_missing_return(struct unreachable_data *data);
+void __ubsan_handle_vla_bound_not_positive(struct vla_bound_data *data, unsigned long bound);
+void __ubsan_handle_load_invalid_value(struct invalid_value_data *data, unsigned long val);
+#if __GCC_VERSION < 60000
+void __ubsan_handle_nonnull_arg(struct nonnull_arg_data *data, size_t arg_no);
+#else
+void __ubsan_handle_nonnull_arg(struct nonnull_arg_data *data);
+#endif
+void __ubsan_handle_nonnull_return(struct nonnull_return_data *data);
+
+static void print_loc(const char *func, struct source_location *loc)
+{
+	const char *f = func;
+	const char func_prefix[] = "__ubsan_handle";
+
+	if (!memcmp(f, func_prefix, sizeof(func_prefix) - 1))
+		f += sizeof(func_prefix);
+
+	PRINTF("Undefined behavior %s at %s:%" PRIu32 " col %" PRIu32 "\n",
+	    f, loc->file_name, loc->line, loc->column);
+}
+
+void __ubsan_handle_type_mismatch(struct type_mismatch_data *data,
+    unsigned long ptr)
+{
+	print_loc(__func__, &data->loc);
+	ubsan_panic();
+}
+
+void __ubsan_handle_add_overflow(struct overflow_data *data,
+    unsigned long lhs,
+    unsigned long rhs)
+{
+	print_loc(__func__, &data->loc);
+	ubsan_panic();
+}
+
+void __ubsan_handle_sub_overflow(struct overflow_data *data,
+    unsigned long lhs,
+    unsigned long rhs)
+{
+	print_loc(__func__, &data->loc);
+	ubsan_panic();
+}
+
+void __ubsan_handle_mul_overflow(struct overflow_data *data,
+    unsigned long lhs,
+    unsigned long rhs)
+{
+	print_loc(__func__, &data->loc);
+	ubsan_panic();
+}
+
+void __ubsan_handle_negate_overflow(struct overflow_data *data,
+    unsigned long old_val)
+{
+	print_loc(__func__, &data->loc);
+	ubsan_panic();
+}
+
+void __ubsan_handle_divrem_overflow(struct overflow_data *data,
+    unsigned long lhs,
+    unsigned long rhs)
+{
+	print_loc(__func__, &data->loc);
+	ubsan_panic();
+}
+
+void __ubsan_handle_shift_out_of_bounds(struct shift_out_of_bounds_data *data,
+    unsigned long lhs,
+    unsigned long rhs)
+{
+	print_loc(__func__, &data->loc);
+	PRINTF("LHS type: %s, value: %lu, RHS type: %s, value: %lu\n",
+	    data->lhs_type->type_name, lhs, data->rhs_type->type_name, rhs);
+	ubsan_panic();
+}
+
+void __ubsan_handle_out_of_bounds(struct out_of_bounds_data *data,
+    unsigned long idx)
+{
+	print_loc(__func__, &data->loc);
+	ubsan_panic();
+}
+
+void __ubsan_handle_unreachable(struct unreachable_data *data)
+{
+	print_loc(__func__, &data->loc);
+	ubsan_panic();
+}
+
+void __ubsan_handle_missing_return(struct unreachable_data *data)
+{
+	print_loc(__func__, &data->loc);
+	ubsan_panic();
+}
+
+void __ubsan_handle_vla_bound_not_positive(struct vla_bound_data *data,
+    unsigned long bound)
+{
+	print_loc(__func__, &data->loc);
+	ubsan_panic();
+}
+
+void __ubsan_handle_load_invalid_value(struct invalid_value_data *data,
+    unsigned long val)
+{
+	print_loc(__func__, &data->loc);
+	ubsan_panic();
+}
+
+#if __GCC_VERSION < 60000
+void __ubsan_handle_nonnull_arg(struct nonnull_arg_data *data, size_t arg_no)
+{
+	print_loc(__func__, &data->loc);
+	ubsan_panic();
+}
+#else
+void __ubsan_handle_nonnull_arg(struct nonnull_arg_data *data)
+{
+	print_loc(__func__, &data->loc);
+	ubsan_panic();
+}
+#endif
+
+void __ubsan_handle_nonnull_return(struct nonnull_return_data *data)
+{
+	print_loc(__func__, &data->loc);
+	ubsan_panic();
+}
Index: uspace/lib/c/generic/vfs/vfs.c
===================================================================
--- uspace/lib/c/generic/vfs/vfs.c	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/lib/c/generic/vfs/vfs.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -285,12 +285,12 @@
 {
 	size_t abs_size;
-	char *abs = vfs_absolutize(path, &abs_size);
-	if (!abs)
+	char *abs_path = vfs_absolutize(path, &abs_size);
+	if (abs_path == NULL)
 		return ENOMEM;
 
 	int fd;
-	errno_t rc = vfs_lookup(abs, WALK_DIRECTORY, &fd);
+	errno_t rc = vfs_lookup(abs_path, WALK_DIRECTORY, &fd);
 	if (rc != EOK) {
-		free(abs);
+		free(abs_path);
 		return rc;
 	}
@@ -305,5 +305,5 @@
 
 	cwd_fd = fd;
-	cwd_path = abs;
+	cwd_path = abs_path;
 	cwd_size = abs_size;
 
Index: uspace/lib/c/include/adt/hash_table.h
===================================================================
--- uspace/lib/c/include/adt/hash_table.h	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/lib/c/include/adt/hash_table.h	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -102,5 +102,4 @@
     void *);
 
-
 #endif
 
Index: uspace/lib/c/include/adt/list.h
===================================================================
--- uspace/lib/c/include/adt/list.h	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/lib/c/include/adt/list.h	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -414,4 +414,18 @@
 }
 
+static inline void *list_pop_internal(list_t *list, ptrdiff_t offset)
+{
+	link_t *tmp = list_first(list);
+	if (tmp == NULL)
+		return NULL;
+
+	list_remove(tmp);
+	return ((void *) tmp) - offset;
+}
+
+#define list_pop(list, type, member) \
+	((type *) list_pop_internal(list, \
+	    (list_link_to_void(&(((type *) NULL)->member)) - NULL)))
+
 #endif
 
Index: uspace/lib/c/include/async.h
===================================================================
--- uspace/lib/c/include/async.h	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/lib/c/include/async.h	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -107,6 +107,4 @@
 typedef struct async_sess async_sess_t;
 typedef struct async_exch async_exch_t;
-
-extern atomic_t threads_in_ipc_wait;
 
 #define async_manager() \
@@ -494,4 +492,6 @@
     sysarg_t, sysarg_t, sysarg_t);
 
+errno_t async_spawn_notification_handler(void);
+
 #endif
 
Index: uspace/lib/c/include/bsearch.h
===================================================================
--- uspace/lib/c/include/bsearch.h	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
+++ uspace/lib/c/include/bsearch.h	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2018 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup libc
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBC_BSEARCH_H_
+#define LIBC_BSEARCH_H_
+
+#include <stddef.h>
+
+extern void *bsearch(const void *, const void *, size_t, size_t,
+    int (*)(const void *, const void *));
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/c/include/cc.h
===================================================================
--- uspace/lib/c/include/cc.h	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/lib/c/include/cc.h	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -44,5 +44,4 @@
 #endif
 
-
 #endif
 
Index: uspace/lib/c/include/fibril.h
===================================================================
--- uspace/lib/c/include/fibril.h	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/lib/c/include/fibril.h	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -72,6 +72,4 @@
 
 	fibril_owner_info_t *waits_for;
-
-	unsigned int switches;
 } fibril_t;
 
Index: uspace/lib/c/include/fibril_synch.h
===================================================================
--- uspace/lib/c/include/fibril_synch.h	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/lib/c/include/fibril_synch.h	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -67,6 +67,6 @@
 typedef struct {
 	fibril_owner_info_t oi;  /**< Keep this the first thing. */
-	unsigned writers;
-	unsigned readers;
+	unsigned int writers;
+	unsigned int readers;
 	list_t waiters;
 } fibril_rwlock_t;
@@ -143,4 +143,24 @@
 } fibril_timer_t;
 
+/** A counting semaphore for fibrils. */
+typedef struct {
+	long int count;
+	list_t waiters;
+} fibril_semaphore_t;
+
+#define FIBRIL_SEMAPHORE_INITIALIZER(name, cnt) \
+	{ \
+		.count = (cnt), \
+		.waiters = { \
+			.head = { \
+				.next = &(name).waiters.head, \
+				.prev = &(name).waiters.head, \
+			} \
+		} \
+	}
+
+#define FIBRIL_SEMAPHORE_INITIALIZE(name, cnt) \
+	fibril_semaphore_t name = FIBRIL_SEMAPHORE_INITIALIZER(name, cnt)
+
 extern void fibril_mutex_initialize(fibril_mutex_t *);
 extern void fibril_mutex_lock(fibril_mutex_t *);
@@ -174,4 +194,8 @@
 extern fibril_timer_state_t fibril_timer_clear_locked(fibril_timer_t *);
 
+extern void fibril_semaphore_initialize(fibril_semaphore_t *, long);
+extern void fibril_semaphore_up(fibril_semaphore_t *);
+extern void fibril_semaphore_down(fibril_semaphore_t *);
+
 #endif
 
Index: uspace/lib/c/include/io/chargrid.h
===================================================================
--- uspace/lib/c/include/io/chargrid.h	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/lib/c/include/io/chargrid.h	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -79,5 +79,5 @@
 extern sysarg_t chargrid_get_top_row(chargrid_t *);
 
-extern sysarg_t chargrid_putchar(chargrid_t *, wchar_t, bool);
+extern sysarg_t chargrid_putwchar(chargrid_t *, wchar_t, bool);
 extern sysarg_t chargrid_newline(chargrid_t *);
 extern sysarg_t chargrid_tabstop(chargrid_t *, sysarg_t);
Index: uspace/lib/c/include/io/pixel.h
===================================================================
--- uspace/lib/c/include/io/pixel.h	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/lib/c/include/io/pixel.h	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -50,6 +50,6 @@
 
 #define PIXEL(a, r, g, b) \
-	((((a) & 0xff) << 24) | (((r) & 0xff) << 16) | \
-	(((g) & 0xff) << 8) | ((b) & 0xff))
+	((((unsigned)(a) & 0xff) << 24) | (((unsigned)(r) & 0xff) << 16) | \
+	(((unsigned)(g) & 0xff) << 8) | ((unsigned)(b) & 0xff))
 
 typedef uint32_t pixel_t;
Index: uspace/lib/c/include/ipc/common.h
===================================================================
--- uspace/lib/c/include/ipc/common.h	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/lib/c/include/ipc/common.h	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -50,5 +50,5 @@
 	task_id_t in_task_id;
 	sysarg_t in_phone_hash;
-	unsigned flags;
+	unsigned int flags;
 	struct async_call *label;
 	cap_call_handle_t cap_handle;
Index: uspace/lib/c/include/iso646.h
===================================================================
--- uspace/lib/c/include/iso646.h	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
+++ uspace/lib/c/include/iso646.h	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2011 Jiri Zarevucky
+ * 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 Alternative spellings.
+ */
+
+#ifndef LIBC_ISO646_H_
+#define LIBC_ISO646_H_
+
+#define and &&
+#define and_eq &=
+#define bitand &
+#define bitor |
+#define compl ˜
+#define not !
+#define not_eq !=
+#define or ||
+#define or_eq |=
+#define xor ^
+#define xor_eq ^=
+
+#endif /* LIBC_ISO646_H_ */
+
+/** @}
+ */
Index: uspace/lib/c/include/macros.h
===================================================================
--- uspace/lib/c/include/macros.h	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/lib/c/include/macros.h	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -38,5 +38,5 @@
 #define min(a, b)  ((a) < (b) ? (a) : (b))
 #define max(a, b)  ((a) > (b) ? (a) : (b))
-#define abs(a)     ((a) >= 0 ? (a) : -(a))
+#define mabs(a)    ((a) >= 0 ? (a) : -(a))
 
 #define ARRAY_SIZE(array)   (sizeof(array) / sizeof(array[0]))
Index: uspace/lib/c/include/mem.h
===================================================================
--- uspace/lib/c/include/mem.h	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/lib/c/include/mem.h	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -1,4 +1,5 @@
 /*
  * Copyright (c) 2005 Martin Decky
+ * Copyright (c) 2018 Jiri Svoboda
  * All rights reserved.
  *
@@ -49,4 +50,6 @@
 extern int memcmp(const void *, const void *, size_t)
     __attribute__((nonnull(1, 2)));
+extern void *memchr(const void *, int, size_t)
+    __attribute__((nonnull(1)));
 
 #endif
Index: uspace/lib/c/include/stdbool.h
===================================================================
--- uspace/lib/c/include/stdbool.h	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/lib/c/include/stdbool.h	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -41,5 +41,5 @@
 #define true   1
 
-#define __bool_true_false_are_defined 1
+#define __bool_true_false_are_defined  1
 
 #endif
Index: uspace/lib/c/include/stdio.h
===================================================================
--- uspace/lib/c/include/stdio.h	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/lib/c/include/stdio.h	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -1,4 +1,5 @@
 /*
  * Copyright (c) 2005 Martin Decky
+ * Copyright (c) 2018 Jiri Svoboda
  * All rights reserved.
  *
@@ -36,25 +37,11 @@
 #define LIBC_STDIO_H_
 
+#include <offset.h>
 #include <stdarg.h>
 #include <io/verify.h>
+#include <_bits/NULL.h>
 #include <_bits/size_t.h>
 #include <_bits/wchar_t.h>
-
-#define EOF  (-1)
-
-#ifndef SEEK_SET
-#define SEEK_SET  0
-#endif
-
-#ifndef SEEK_CUR
-#define SEEK_CUR  1
-#endif
-
-#ifndef SEEK_END
-#define SEEK_END  2
-#endif
-
-/** Default size for stream I/O buffers */
-#define BUFSIZ  4096
+#include <_bits/wint_t.h>
 
 /** Forward declaration */
@@ -62,4 +49,44 @@
 typedef struct _IO_FILE FILE;
 
+/** File position */
+typedef struct {
+	off64_t pos;
+} fpos_t;
+
+#ifndef _HELENOS_SOURCE
+#define _IONBF 0
+#define _IOLBF 1
+#define _IOFBF 2
+#endif
+
+/** Default size for stream I/O buffers */
+#define BUFSIZ  4096
+
+#define EOF  (-1)
+
+/** Max number of files that is guaranteed to be able to open at the same time */
+#define FOPEN_MAX VFS_MAX_OPEN_FILES
+
+/** Recommended size of fixed-size array for holding file names. */
+#define FILENAME_MAX 4096
+
+/** Length of "/tmp/tmp.XXXXXX" + 1 */
+#define L_tmpnam 16
+
+#ifndef SEEK_SET
+#define SEEK_SET  0
+#endif
+
+#ifndef SEEK_CUR
+#define SEEK_CUR  1
+#endif
+
+#ifndef SEEK_END
+#define SEEK_END  2
+#endif
+
+/** Minimum number of unique temporary file names */
+#define TMP_MAX 1000000
+
 extern FILE *stdin;
 extern FILE *stdout;
@@ -67,19 +94,23 @@
 
 /* Character and string input functions */
+#define getc fgetc
 extern int fgetc(FILE *);
 extern char *fgets(char *, int, FILE *);
+extern char *gets(char *, size_t) __attribute__((deprecated));
 
 extern int getchar(void);
 
 /* Character and string output functions */
-extern int fputc(wchar_t, FILE *);
+#define putc fputc
+extern int fputc(int, FILE *);
 extern int fputs(const char *, FILE *);
 
-// FIXME: putchar and fputc are byte-oriented.
-// They shouldn't accept wide characters.
-extern int putchar(wchar_t);
+extern int putchar(int);
 extern int puts(const char *);
 
 extern int ungetc(int, FILE *);
+
+extern wint_t fputwc(wchar_t, FILE *);
+extern wint_t putwchar(wchar_t);
 
 /* Formatted string output functions */
@@ -94,8 +125,22 @@
 extern int snprintf(char *, size_t, const char *, ...)
     _HELENOS_PRINTF_ATTRIBUTE(3, 4);
+#if defined(_HELENOS_SOURCE) || defined(_GNU_SOURCE)
 extern int vasprintf(char **, const char *, va_list);
 extern int asprintf(char **, const char *, ...)
     _HELENOS_PRINTF_ATTRIBUTE(2, 3);
+#endif
 extern int vsnprintf(char *, size_t, const char *, va_list);
+
+extern int sprintf(char *, const char *, ...)
+    __attribute__((deprecated)) _HELENOS_PRINTF_ATTRIBUTE(2, 3);
+extern int vsprintf(char *, const char *, va_list) __attribute__((deprecated));
+
+/* Formatted input */
+extern int scanf(const char *, ...);
+extern int vscanf(const char *, va_list);
+extern int fscanf(FILE *, const char *, ...);
+extern int vfscanf(FILE *, const char *, va_list);
+extern int sscanf(const char *, const char *, ...);
+extern int vsscanf(const char *, const char *, va_list);
 
 /* File stream functions */
@@ -107,4 +152,7 @@
 extern size_t fwrite(const void *, size_t, size_t, FILE *);
 
+extern int fgetpos(FILE *, fpos_t *);
+extern int fsetpos(FILE *, const fpos_t *);
+
 extern int fseek(FILE *, long, int);
 extern void rewind(FILE *);
@@ -116,16 +164,15 @@
 extern void clearerr(FILE *);
 
+extern void perror(const char *);
+
 extern void setvbuf(FILE *, void *, int, size_t);
 extern void setbuf(FILE *, void *);
 
 /* Misc file functions */
+extern int remove(const char *);
 extern int rename(const char *, const char *);
-extern int remove(const char *);
-
-#ifndef _HELENOS_SOURCE
-#define _IONBF 0
-#define _IOLBF 1
-#define _IOFBF 2
-#endif
+
+extern FILE *tmpfile(void);
+extern char *tmpnam(char *s) __attribute__((deprecated));
 
 #ifdef _HELENOS_SOURCE
@@ -158,5 +205,4 @@
 extern FILE *fdopen(int, const char *);
 extern int fileno(FILE *);
-extern char *gets(char *, size_t);
 
 #include <offset.h>
Index: uspace/lib/c/include/stdlib.h
===================================================================
--- uspace/lib/c/include/stdlib.h	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/lib/c/include/stdlib.h	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -36,15 +36,58 @@
 #define LIBC_STDLIB_H_
 
+#include <_bits/size_t.h>
+#include <_bits/wchar_t.h>
+#include <bsearch.h>
 #include <malloc.h>
 #include <qsort.h>
-#include <stacktrace.h>
+
+/** Type returned by the div function */
+typedef struct {
+	/** Quotient */
+	int quot;
+	/** Remainder */
+	int rem;
+} div_t;
+
+/** Type returned by the ldiv function */
+typedef struct {
+	/** Quotient */
+	long quot;
+	/** Remainder */
+	long rem;
+} ldiv_t;
+
+/** Type returned by the lldiv function */
+typedef struct {
+	/** Quotient */
+	long long quot;
+	/** Remainder */
+	long long rem;
+} lldiv_t;
+
+
+#define EXIT_FAILURE 1
+#define EXIT_SUCCESS 0
 
 #define RAND_MAX  714025
 
+#define MB_CUR_MAX 4
+
 extern int rand(void);
-extern void srand(unsigned int seed);
+extern void srand(unsigned int);
 
 extern void abort(void) __attribute__((noreturn));
+extern int atexit(void (*)(void));
 extern void exit(int) __attribute__((noreturn));
+extern void _Exit(int) __attribute__((noreturn));
+extern int at_quick_exit(void (*)(void));
+extern void quick_exit(int);
+
+extern char *getenv(const char *);
+extern int system(const char *);
+
+extern int abs(int);
+extern long labs(long);
+extern long long llabs(long long);
 
 extern int atoi(const char *);
@@ -57,4 +100,8 @@
 extern unsigned long long strtoull(const char *__restrict__, char **__restrict__, int);
 
+extern div_t div(int, int);
+extern ldiv_t ldiv(long, long);
+extern lldiv_t lldiv(long long, long long);
+
 #endif
 
Index: uspace/lib/c/include/tmpfile.h
===================================================================
--- uspace/lib/c/include/tmpfile.h	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
+++ uspace/lib/c/include/tmpfile.h	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2018 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef LIBC_TMPFILE_H_
+#define LIBC_TMPFILE_H_
+
+#include <stdbool.h>
+
+extern int __tmpfile_templ(char *, bool);
+extern int __tmpfile(void);
+extern char *__tmpnam(char *);
+
+#endif
Index: uspace/lib/c/include/vfs/vfs.h
===================================================================
--- uspace/lib/c/include/vfs/vfs.h	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/lib/c/include/vfs/vfs.h	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -45,5 +45,5 @@
 #include <offset.h>
 
-#define MAX_OPEN_FILES	128
+#define VFS_MAX_OPEN_FILES  128
 
 enum vfs_change_state_type {
Index: uspace/lib/c/include/wchar.h
===================================================================
--- uspace/lib/c/include/wchar.h	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/lib/c/include/wchar.h	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -44,4 +44,5 @@
 #include <_bits/wchar_t.h>
 #include <_bits/wint_t.h>
+#include <_bits/WEOF.h>
 
 #include <_bits/NULL.h>
Index: uspace/lib/c/test/main.c
===================================================================
--- uspace/lib/c/test/main.c	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/lib/c/test/main.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -34,7 +34,11 @@
 PCUT_IMPORT(circ_buf);
 PCUT_IMPORT(fibril_timer);
+PCUT_IMPORT(mem);
 PCUT_IMPORT(odict);
 PCUT_IMPORT(qsort);
+PCUT_IMPORT(scanf);
 PCUT_IMPORT(sprintf);
+PCUT_IMPORT(stdio);
+PCUT_IMPORT(stdlib);
 PCUT_IMPORT(str);
 PCUT_IMPORT(table);
Index: uspace/lib/c/test/mem.c
===================================================================
--- uspace/lib/c/test/mem.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
+++ uspace/lib/c/test/mem.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2018 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <mem.h>
+#include <pcut/pcut.h>
+
+PCUT_INIT;
+
+PCUT_TEST_SUITE(mem);
+
+/** memcpy function */
+PCUT_TEST(memcpy)
+{
+	char buf[5];
+	void *p;
+
+	p = memcpy(buf, "abc\0d", 5);
+	PCUT_ASSERT_TRUE(p == buf);
+
+	PCUT_ASSERT_INT_EQUALS('a', buf[0]);
+	PCUT_ASSERT_INT_EQUALS('b', buf[1]);
+	PCUT_ASSERT_INT_EQUALS('c', buf[2]);
+	PCUT_ASSERT_INT_EQUALS('\0', buf[3]);
+	PCUT_ASSERT_INT_EQUALS('d', buf[4]);
+}
+
+/** memmove function */
+PCUT_TEST(memmove)
+{
+	char buf[] = "abc\0d";
+	void *p;
+
+	p = memmove(buf, buf + 1, 4);
+	PCUT_ASSERT_TRUE(p == buf);
+
+	PCUT_ASSERT_INT_EQUALS('b', buf[0]);
+	PCUT_ASSERT_INT_EQUALS('c', buf[1]);
+	PCUT_ASSERT_INT_EQUALS('\0', buf[2]);
+	PCUT_ASSERT_INT_EQUALS('d', buf[3]);
+	PCUT_ASSERT_INT_EQUALS('d', buf[4]);
+}
+
+/** memcmp function */
+PCUT_TEST(memcmp)
+{
+	const char *s1 = "ab" "\0" "1d";
+	const char *s2 = "ab" "\0" "2d";
+	int c;
+
+	c = memcmp(s1, s2, 3);
+	PCUT_ASSERT_INT_EQUALS(0, c);
+
+	c = memcmp(s1, s2, 4);
+	PCUT_ASSERT_TRUE(c < 0);
+
+	c = memcmp(s2, s1, 4);
+	PCUT_ASSERT_TRUE(c > 0);
+}
+
+/** memchr function */
+PCUT_TEST(memchr)
+{
+	const char *s = "abc\0d";
+	void *p;
+
+	p = memchr(s, 'd', 5);
+	PCUT_ASSERT_TRUE(p == s + 4);
+
+	p = memchr(s, '\0', 5);
+	PCUT_ASSERT_TRUE(p == s + 3);
+
+	p = memchr(s, 'd', 4);
+	PCUT_ASSERT_TRUE(p == NULL);
+}
+
+/** memset function */
+PCUT_TEST(memset)
+{
+	char buf[5];
+	void *p;
+
+	buf[0] = buf[1] = buf[2] = buf[3] = buf[4] = 'a';
+	p = memset(buf, 'x', 5);
+	PCUT_ASSERT_TRUE(p == buf);
+
+	PCUT_ASSERT_INT_EQUALS('x', buf[0]);
+	PCUT_ASSERT_INT_EQUALS('x', buf[1]);
+	PCUT_ASSERT_INT_EQUALS('x', buf[2]);
+	PCUT_ASSERT_INT_EQUALS('x', buf[3]);
+	PCUT_ASSERT_INT_EQUALS('x', buf[4]);
+}
+
+PCUT_EXPORT(mem);
Index: uspace/lib/c/test/stdio.c
===================================================================
--- uspace/lib/c/test/stdio.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
+++ uspace/lib/c/test/stdio.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -0,0 +1,227 @@
+/*
+ * Copyright (c) 2018 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup libc
+ * @{
+ */
+/**
+ * @file
+ * @brief Test stdio module
+ */
+
+#include <errno.h>
+#include <pcut/pcut.h>
+#include <stdio.h>
+#include <str.h>
+#include <tmpfile.h>
+#include <vfs/vfs.h>
+
+PCUT_INIT;
+
+PCUT_TEST_SUITE(stdio);
+
+/** remove function */
+PCUT_TEST(remove)
+{
+	char buf[L_tmpnam];
+	char *p;
+	FILE *f;
+	int rc;
+
+	/* Generate unique file name */
+	p = tmpnam(buf);
+	PCUT_ASSERT_NOT_NULL(p);
+
+	/* Removing it should fail */
+	rc = remove(buf);
+	PCUT_ASSERT_TRUE(rc != 0);
+
+	f = fopen(buf, "w");
+	PCUT_ASSERT_NOT_NULL(f);
+	fclose(f);
+
+	/* Remove for the first time */
+	rc = remove(buf);
+	PCUT_ASSERT_INT_EQUALS(0, rc);
+
+	/* Should fail the second time */
+	rc = remove(buf);
+	PCUT_ASSERT_TRUE(rc != 0);
+}
+
+/** rename function */
+PCUT_TEST(rename)
+{
+	char buf1[L_tmpnam];
+	char buf2[L_tmpnam];
+	char *p;
+	FILE *f;
+	int rc;
+
+	/* Generate first unique file name */
+	p = tmpnam(buf1);
+	PCUT_ASSERT_NOT_NULL(p);
+
+	/* Generate second unique file name */
+	p = tmpnam(buf2);
+	PCUT_ASSERT_NOT_NULL(p);
+
+	f = fopen(buf1, "w");
+	PCUT_ASSERT_NOT_NULL(f);
+	fclose(f);
+
+	/* Rename to the second name */
+	rc = rename(buf1, buf2);
+	PCUT_ASSERT_INT_EQUALS(0, rc);
+
+	/* First name should no longer exist */
+	rc = remove(buf1);
+	PCUT_ASSERT_TRUE(rc != 0);
+
+	/* Second can be removed */
+	rc = remove(buf2);
+	PCUT_ASSERT_INT_EQUALS(0, rc);
+}
+
+/** tmpfile function */
+PCUT_TEST(tmpfile)
+{
+	FILE *f;
+	char c;
+	size_t n;
+
+	f = tmpfile();
+	PCUT_ASSERT_NOT_NULL(f);
+
+	c = 'x';
+	n = fwrite(&c, sizeof(c), 1, f);
+	PCUT_ASSERT_INT_EQUALS(1, n);
+
+	rewind(f);
+	c = '\0';
+	n = fread(&c, sizeof(c), 1, f);
+	PCUT_ASSERT_INT_EQUALS(1, n);
+	PCUT_ASSERT_INT_EQUALS('x', c);
+
+	fclose(f);
+}
+
+/** tmpnam function with buffer argument */
+PCUT_TEST(tmpnam_buf)
+{
+	char buf[L_tmpnam];
+	char *p;
+	FILE *f;
+
+	p = tmpnam(buf);
+	PCUT_ASSERT_NOT_NULL(p);
+
+	f = fopen(p, "w+");
+	PCUT_ASSERT_NOT_NULL(f);
+	(void) remove(p);
+	fclose(f);
+}
+
+/** tmpnam function called twice */
+PCUT_TEST(tmpnam_twice)
+{
+	char buf1[L_tmpnam];
+	char buf2[L_tmpnam];
+	char *p;
+
+	p = tmpnam(buf1);
+	PCUT_ASSERT_NOT_NULL(p);
+
+	p = tmpnam(buf2);
+	PCUT_ASSERT_NOT_NULL(p);
+
+	/* We must get two distinct names */
+	PCUT_ASSERT_TRUE(str_cmp(buf1, buf2) != 0);
+}
+
+/** tmpnam function with NULL argument */
+PCUT_TEST(tmpnam_null)
+{
+	char *p;
+	FILE *f;
+
+	p = tmpnam(NULL);
+	PCUT_ASSERT_NOT_NULL(p);
+
+	f = fopen(p, "w+");
+	PCUT_ASSERT_NOT_NULL(f);
+	(void) remove(p);
+	fclose(f);
+}
+
+/** fgetpos and fsetpos functions */
+PCUT_TEST(fgetpos_setpos)
+{
+	fpos_t pos;
+	int rc;
+	FILE *f;
+
+	f = tmpfile();
+	PCUT_ASSERT_NOT_NULL(f);
+
+	rc = fputs("abc", f);
+	PCUT_ASSERT_TRUE(rc >= 0);
+
+	rc = fgetpos(f, &pos);
+	PCUT_ASSERT_TRUE(rc >= 0);
+
+	rewind(f);
+
+	rc = fsetpos(f, &pos);
+	PCUT_ASSERT_TRUE(rc >= 0);
+
+	(void) fclose(f);
+}
+
+/** perror function with NULL as argument */
+PCUT_TEST(perror_null_msg)
+{
+	errno = EINVAL;
+	perror(NULL);
+}
+
+/** perror function with empty string as argument */
+PCUT_TEST(perror_empty_msg)
+{
+	errno = EINVAL;
+	perror("");
+}
+
+/** perror function with a message argument */
+PCUT_TEST(perror_msg)
+{
+	errno = EINVAL;
+	perror("This is a test");
+}
+
+PCUT_EXPORT(stdio);
Index: uspace/lib/c/test/stdio/scanf.c
===================================================================
--- uspace/lib/c/test/stdio/scanf.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
+++ uspace/lib/c/test/stdio/scanf.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -0,0 +1,1311 @@
+/*
+ * Copyright (c) 2018 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup libc
+ * @{
+ */
+/**
+ * @file
+ * @brief Test formatted input (scanf family)
+ */
+
+#include <mem.h>
+#include <pcut/pcut.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+PCUT_INIT;
+
+PCUT_TEST_SUITE(scanf);
+
+enum {
+	chars_size = 10
+};
+
+/** Empty format string */
+PCUT_TEST(empty_fmt)
+{
+	int rc;
+
+	rc = sscanf("42", "");
+	PCUT_ASSERT_INT_EQUALS(0, rc);
+}
+
+/** Decimal integer */
+PCUT_TEST(dec_int)
+{
+	int rc;
+	int i;
+
+	rc = sscanf("42", "%d", &i);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(i == 42);
+}
+
+/** Two integers */
+PCUT_TEST(int_int)
+{
+	int rc;
+	int i, j;
+
+	rc = sscanf("42 43", "%d%d", &i, &j);
+	PCUT_ASSERT_INT_EQUALS(2, rc);
+	PCUT_ASSERT_TRUE(i == 42);
+	PCUT_ASSERT_TRUE(j == 43);
+}
+
+/** Decimal signed char */
+PCUT_TEST(dec_sign_char)
+{
+	int rc;
+	signed char sc;
+
+	rc = sscanf("42", "%hhd", &sc);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(sc == 42);
+}
+
+/** Decimal short */
+PCUT_TEST(dec_short)
+{
+	int rc;
+	short si;
+
+	rc = sscanf("42", "%hd", &si);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(si == 42);
+}
+
+/** Decimal long */
+PCUT_TEST(dec_long)
+{
+	int rc;
+	long li;
+
+	rc = sscanf("42", "%ld", &li);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(li == 42);
+}
+
+/** Decimal long long */
+PCUT_TEST(dec_long_long)
+{
+	int rc;
+	long long lli;
+
+	rc = sscanf("42", "%lld", &lli);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(lli == 42);
+}
+
+/** Decimal intmax_t */
+PCUT_TEST(dec_intmax)
+{
+	int rc;
+	intmax_t imax;
+
+	rc = sscanf("42", "%jd", &imax);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(imax == 42);
+}
+
+/** Decimal size_t-sized */
+PCUT_TEST(dec_size_t_size)
+{
+	int rc;
+	size_t szi;
+
+	rc = sscanf("42", "%zd", &szi);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(szi == 42);
+}
+
+/** Decimal ptrdiff_t-sized */
+PCUT_TEST(dec_ptrdiff_t_size)
+{
+	int rc;
+	ptrdiff_t pdi;
+
+	rc = sscanf("42", "%td", &pdi);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(pdi == 42);
+}
+
+/** Decimal integer followed by hexadecimal digit */
+PCUT_TEST(dec_int_hexdigit)
+{
+	int rc;
+	int i;
+
+	rc = sscanf("42a", "%d", &i);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(i == 42);
+}
+
+/** Decimal integer - detect no prefix */
+PCUT_TEST(int_noprefix)
+{
+	int rc;
+	int i;
+
+	rc = sscanf("42", "%i", &i);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(i == 42);
+}
+
+/** Prefixed octal integer followed by decimal digit */
+PCUT_TEST(octal_decimal_digit)
+{
+	int rc;
+	int i;
+
+	rc = sscanf("019", "%i", &i);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(i == 1);
+}
+
+/** Prefixed hexadecimal integer followed by other character */
+PCUT_TEST(hex_other_char)
+{
+	int rc;
+	int i;
+
+	rc = sscanf("0xag", "%i", &i);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(i == 10);
+}
+
+/** Decimal integer with '+' sign */
+PCUT_TEST(positive_dec)
+{
+	int rc;
+	int i;
+
+	rc = sscanf("+42", "%d", &i);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(i == 42);
+}
+
+/** Decimal integer with '-' sign */
+PCUT_TEST(negative_dec)
+{
+	int rc;
+	int i;
+
+	rc = sscanf("-42", "%d", &i);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(i == -42);
+}
+
+/** Hexadecimal integer with prefix and '-' sign */
+PCUT_TEST(negative_hex)
+{
+	int rc;
+	int i;
+
+	rc = sscanf("-0xa", "%i", &i);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(i == -10);
+}
+
+/** Decimal unsigned integer */
+PCUT_TEST(dec_unsigned)
+{
+	int rc;
+	unsigned u;
+
+	rc = sscanf("42", "%u", &u);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(u == 42);
+}
+
+/** Decimal unsigned char */
+PCUT_TEST(dec_unsigned_char)
+{
+	int rc;
+	unsigned char uc;
+
+	rc = sscanf("42", "%hhu", &uc);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(uc == 42);
+}
+
+/** Decimal unsigned short */
+PCUT_TEST(dec_unsigned_short)
+{
+	int rc;
+	unsigned short su;
+
+	rc = sscanf("42", "%hu", &su);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(su == 42);
+}
+
+/** Decimal unsigned long */
+PCUT_TEST(dec_unsigned_long)
+{
+	int rc;
+	unsigned long lu;
+
+	rc = sscanf("42", "%lu", &lu);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(lu == 42);
+}
+
+/** Decimal unsigned long long */
+PCUT_TEST(dec_unsigned_long_long)
+{
+	int rc;
+	unsigned long long llu;
+
+	rc = sscanf("42", "%llu", &llu);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(llu == 42);
+}
+
+/** Decimal uintmax_t */
+PCUT_TEST(dec_unitmax)
+{
+	int rc;
+	uintmax_t umax;
+
+	rc = sscanf("42", "%ju", &umax);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(umax == 42);
+}
+
+/** Decimal size_t */
+PCUT_TEST(dec_unsigned_size)
+{
+	int rc;
+	size_t szu;
+
+	rc = sscanf("42", "%zu", &szu);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(szu == 42);
+}
+
+/** Decimal ptrdiff_t-sized unsigned int*/
+PCUT_TEST(dec_unsigned_ptrdiff)
+{
+	int rc;
+	ptrdiff_t pdu;
+
+	rc = sscanf("42", "%tu", &pdu);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(pdu == 42);
+}
+
+/** Octal unsigned integer */
+PCUT_TEST(octal_unsigned)
+{
+	int rc;
+	unsigned u;
+
+	rc = sscanf("52", "%o", &u);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(u == 052);
+}
+
+/** Hexadecimal unsigned integer */
+PCUT_TEST(hex_unsigned)
+{
+	int rc;
+	unsigned u;
+
+	rc = sscanf("2a", "%x", &u);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(u == 0x2a);
+}
+
+/** Hexadecimal unsigned integer unsing alternate specifier */
+PCUT_TEST(hex_unsigned_cap_x)
+{
+	int rc;
+	unsigned u;
+
+	rc = sscanf("2a", "%X", &u);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(u == 0x2a);
+}
+
+/** Uppercase hexadecimal unsigned integer */
+PCUT_TEST(uppercase_hex_unsigned)
+{
+	int rc;
+	unsigned u;
+
+	rc = sscanf("2A", "%x", &u);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(u == 0x2a);
+}
+
+/** Make sure %x does not match 0x prefix */
+PCUT_TEST(hex_not_match_0x)
+{
+	int rc;
+	unsigned u;
+
+	rc = sscanf("0x1", "%x", &u);
+
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(u == 0);
+}
+
+/** Skipping whitespace */
+PCUT_TEST(skipws)
+{
+	int rc;
+	int i;
+
+	rc = sscanf(" \t\n42", "%d", &i);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(i == 42);
+}
+
+/** Percentile conversion */
+PCUT_TEST(percentile)
+{
+	int rc;
+	int i;
+
+	rc = sscanf(" \t\n%42", "%%%d", &i);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(i == 42);
+}
+
+/** Matching specific character */
+PCUT_TEST(match_spec_char)
+{
+	int rc;
+	int i;
+
+	rc = sscanf("x42", "x%d", &i);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(i == 42);
+}
+
+/** Matching specific character should not skip whitespace */
+PCUT_TEST(match_char_noskipws)
+{
+	int rc;
+	int i;
+
+	rc = sscanf(" x42", "x%d", &i);
+	PCUT_ASSERT_INT_EQUALS(0, rc);
+}
+
+/** Skipping whitespace + match specific character */
+PCUT_TEST(skipws_match_char)
+{
+	int rc;
+	int i;
+
+	rc = sscanf(" x42", "\t\nx%d", &i);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(i == 42);
+}
+
+/** Decimal with limited, but sufficient width */
+PCUT_TEST(dec_sufficient_lim_width)
+{
+	int rc;
+	int i;
+
+	rc = sscanf("42", "%2d", &i);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(i == 42);
+}
+
+/** Decimal with limited, smaller width */
+PCUT_TEST(dec_smaller_width)
+{
+	int rc;
+	int i;
+
+	rc = sscanf("42", "%1d", &i);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(i == 4);
+}
+
+/** Integer with hex prefix, format with limited, sufficient width */
+PCUT_TEST(int_hex_limited_width)
+{
+	int rc;
+	int i;
+
+	rc = sscanf("0x1", "%3i", &i);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(i == 1);
+}
+
+/** Integer with hex prefix, format with limited, smaller width */
+PCUT_TEST(int_hex_small_width)
+{
+	int rc;
+	int i;
+
+	rc = sscanf("0x1", "%2i", &i);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(i == 0);
+}
+
+/** Integer with octal prefix, format with limited, sufficient width */
+PCUT_TEST(int_oct_limited_width)
+{
+	int rc;
+	int i;
+
+	rc = sscanf("012", "%3i", &i);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(i == 012);
+}
+
+/** Integer with octal prefix, format with limited, smaller width */
+PCUT_TEST(int_oct_smaller_width)
+{
+	int rc;
+	int i;
+
+	rc = sscanf("012", "%2i", &i);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(i == 01);
+}
+
+/** Integer with octal prefix, format with width allowing just for 0 */
+PCUT_TEST(int_oct_tiny_width)
+{
+	int rc;
+	int i;
+
+	rc = sscanf("012", "%1i", &i);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(i == 0);
+}
+
+/** Pointer */
+PCUT_TEST(pointer)
+{
+	int rc;
+	void *ptr;
+
+	rc = sscanf("0xABCDEF88", "%p", &ptr);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(ptr == (void *)0xABCDEF88);
+}
+
+/** Single character */
+PCUT_TEST(single_char)
+{
+	int rc;
+	char c;
+
+	rc = sscanf("x", "%c", &c);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(c == 'x');
+}
+
+/** Single whitespace character */
+PCUT_TEST(single_ws_char)
+{
+	int rc;
+	char c;
+
+	rc = sscanf("\t", "%c", &c);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(c == '\t');
+}
+
+/** Multiple characters */
+PCUT_TEST(chars)
+{
+	int rc;
+	char chars[chars_size];
+
+	memset(chars, 'X', chars_size);
+	rc = sscanf("abc", "%3c", chars);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(chars[0] == 'a');
+	PCUT_ASSERT_TRUE(chars[1] == 'b');
+	PCUT_ASSERT_TRUE(chars[2] == 'c');
+	PCUT_ASSERT_TRUE(chars[3] == 'X');
+}
+
+/** Fewer characters than requested */
+PCUT_TEST(fewer_chars)
+{
+	int rc;
+	char chars[chars_size];
+
+	memset(chars, 'X', chars_size);
+	rc = sscanf("abc", "%5c", chars);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(chars[0] == 'a');
+	PCUT_ASSERT_TRUE(chars[1] == 'b');
+	PCUT_ASSERT_TRUE(chars[2] == 'c');
+	PCUT_ASSERT_TRUE(chars[3] == 'X');
+}
+
+/** Reading characters but no found */
+PCUT_TEST(chars_not_found)
+{
+	int rc;
+	char chars[chars_size];
+
+	memset(chars, 'X', chars_size);
+	rc = sscanf("", "%5c", chars);
+	PCUT_ASSERT_INT_EQUALS(EOF, rc);
+	PCUT_ASSERT_TRUE(chars[0] == 'X');
+}
+
+/** Multiple characters with suppressed assignment */
+PCUT_TEST(chars_noassign)
+{
+	int rc;
+	int n;
+
+	rc = sscanf("abc", "%*3c%n", &n);
+	PCUT_ASSERT_INT_EQUALS(0, rc);
+	PCUT_ASSERT_INT_EQUALS(3, n);
+}
+
+/** Multiple characters with memory allocation */
+PCUT_TEST(chars_malloc)
+{
+	int rc;
+	char *cp;
+
+	cp = NULL;
+	rc = sscanf("abc", "%m3c", &cp);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_NOT_NULL(cp);
+	PCUT_ASSERT_TRUE(cp[0] == 'a');
+	PCUT_ASSERT_TRUE(cp[1] == 'b');
+	PCUT_ASSERT_TRUE(cp[2] == 'c');
+	free(cp);
+}
+
+/** String of non-whitespace characters, unlimited width */
+PCUT_TEST(str)
+{
+	int rc;
+	char chars[chars_size];
+
+	memset(chars, 'X', chars_size);
+	rc = sscanf(" abc d", "%s", chars);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(chars[0] == 'a');
+	PCUT_ASSERT_TRUE(chars[1] == 'b');
+	PCUT_ASSERT_TRUE(chars[2] == 'c');
+	PCUT_ASSERT_TRUE(chars[3] == '\0');
+	PCUT_ASSERT_TRUE(chars[4] == 'X');
+}
+
+/** String of non-whitespace characters, until the end */
+PCUT_TEST(str_till_end)
+{
+	int rc;
+	char chars[chars_size];
+
+	memset(chars, 'X', chars_size);
+	rc = sscanf(" abc", "%s", chars);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(chars[0] == 'a');
+	PCUT_ASSERT_TRUE(chars[1] == 'b');
+	PCUT_ASSERT_TRUE(chars[2] == 'c');
+	PCUT_ASSERT_TRUE(chars[3] == '\0');
+	PCUT_ASSERT_TRUE(chars[4] == 'X');
+}
+
+/** String of non-whitespace characters, large enough width */
+PCUT_TEST(str_large_width)
+{
+	int rc;
+	char chars[chars_size];
+
+	memset(chars, 'X', chars_size);
+	rc = sscanf(" abc d", "%5s", chars);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(chars[0] == 'a');
+	PCUT_ASSERT_TRUE(chars[1] == 'b');
+	PCUT_ASSERT_TRUE(chars[2] == 'c');
+	PCUT_ASSERT_TRUE(chars[3] == '\0');
+	PCUT_ASSERT_TRUE(chars[4] == 'X');
+}
+
+/** Want string of non-whitespace, but got only whitespace */
+PCUT_TEST(str_not_found)
+{
+	int rc;
+	char chars[chars_size];
+
+	memset(chars, 'X', chars_size);
+	rc = sscanf(" ", "%s", chars);
+	PCUT_ASSERT_INT_EQUALS(EOF, rc);
+	PCUT_ASSERT_TRUE(chars[0] == 'X');
+}
+
+/** String of non-whitespace characters, small width */
+PCUT_TEST(str_small_width)
+{
+	int rc;
+	char chars[chars_size];
+
+	memset(chars, 'X', chars_size);
+	rc = sscanf(" abc", "%2s", chars);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(chars[0] == 'a');
+	PCUT_ASSERT_TRUE(chars[1] == 'b');
+	PCUT_ASSERT_TRUE(chars[2] == '\0');
+	PCUT_ASSERT_TRUE(chars[3] == 'X');
+}
+
+/** String of non-whitespace characters, assignment suppression */
+PCUT_TEST(str_noassign)
+{
+	int rc;
+	int n;
+
+	rc = sscanf(" abc d", "%*s%n", &n);
+	PCUT_ASSERT_INT_EQUALS(0, rc);
+	PCUT_ASSERT_INT_EQUALS(4, n);
+}
+
+/** String of non-whitespace characters, memory allocation */
+PCUT_TEST(str_malloc)
+{
+	int rc;
+	char *cp;
+
+	rc = sscanf(" abc d", "%ms", &cp);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_NOT_NULL(cp);
+	PCUT_ASSERT_TRUE(cp[0] == 'a');
+	PCUT_ASSERT_TRUE(cp[1] == 'b');
+	PCUT_ASSERT_TRUE(cp[2] == 'c');
+	PCUT_ASSERT_TRUE(cp[3] == '\0');
+	free(cp);
+}
+
+/** Set conversion without width specified terminating before the end  */
+PCUT_TEST(set_convert)
+{
+	int rc;
+	char chars[chars_size];
+	int i;
+
+	memset(chars, 'X', chars_size);
+	rc = sscanf("abcd42", "%[abc]d%d", chars, &i);
+	PCUT_ASSERT_INT_EQUALS(2, rc);
+	PCUT_ASSERT_TRUE(chars[0] == 'a');
+	PCUT_ASSERT_TRUE(chars[1] == 'b');
+	PCUT_ASSERT_TRUE(chars[2] == 'c');
+	PCUT_ASSERT_TRUE(chars[3] == '\0');
+	PCUT_ASSERT_TRUE(chars[4] == 'X');
+	PCUT_ASSERT_TRUE(i == 42);
+}
+
+/** Set conversion without width specified, until the end */
+PCUT_TEST(set_till_end)
+{
+	int rc;
+	char chars[chars_size];
+
+	memset(chars, 'X', chars_size);
+	rc = sscanf("abc", "%[abc]", chars);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(chars[0] == 'a');
+	PCUT_ASSERT_TRUE(chars[1] == 'b');
+	PCUT_ASSERT_TRUE(chars[2] == 'c');
+	PCUT_ASSERT_TRUE(chars[3] == '\0');
+	PCUT_ASSERT_TRUE(chars[4] == 'X');
+}
+
+/** Set conversion with larger width */
+PCUT_TEST(set_large_width)
+{
+	int rc;
+	char chars[chars_size];
+
+	memset(chars, 'X', chars_size);
+	rc = sscanf("abcd", "%5[abc]", chars);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(chars[0] == 'a');
+	PCUT_ASSERT_TRUE(chars[1] == 'b');
+	PCUT_ASSERT_TRUE(chars[2] == 'c');
+	PCUT_ASSERT_TRUE(chars[3] == '\0');
+	PCUT_ASSERT_TRUE(chars[4] == 'X');
+}
+
+/** Set conversion with smaller width */
+PCUT_TEST(set_small_width)
+{
+	int rc;
+	char chars[chars_size];
+
+	memset(chars, 'X', chars_size);
+	rc = sscanf("abcd", "%3[abcd]", chars);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(chars[0] == 'a');
+	PCUT_ASSERT_TRUE(chars[1] == 'b');
+	PCUT_ASSERT_TRUE(chars[2] == 'c');
+	PCUT_ASSERT_TRUE(chars[3] == '\0');
+	PCUT_ASSERT_TRUE(chars[4] == 'X');
+}
+
+/** Set conversion with negated scanset */
+PCUT_TEST(set_inverted)
+{
+	int rc;
+	char chars[chars_size];
+
+	memset(chars, 'X', chars_size);
+	rc = sscanf("abcd", "%[^d]", chars);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(chars[0] == 'a');
+	PCUT_ASSERT_TRUE(chars[1] == 'b');
+	PCUT_ASSERT_TRUE(chars[2] == 'c');
+	PCUT_ASSERT_TRUE(chars[3] == '\0');
+	PCUT_ASSERT_TRUE(chars[4] == 'X');
+}
+
+/** Set conversion with ']' in scanset */
+PCUT_TEST(set_with_rbr)
+{
+	int rc;
+	char chars[chars_size];
+
+	memset(chars, 'X', chars_size);
+	rc = sscanf("]bcd", "%[]bc]", chars);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(chars[0] == ']');
+	PCUT_ASSERT_TRUE(chars[1] == 'b');
+	PCUT_ASSERT_TRUE(chars[2] == 'c');
+	PCUT_ASSERT_TRUE(chars[3] == '\0');
+	PCUT_ASSERT_TRUE(chars[4] == 'X');
+}
+
+/** Set conversion with ']' in inverted scanset */
+PCUT_TEST(set_inverted_with_rbr)
+{
+	int rc;
+	char chars[chars_size];
+
+	memset(chars, 'X', chars_size);
+	rc = sscanf("abc]", "%[^]def]", chars);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(chars[0] == 'a');
+	PCUT_ASSERT_TRUE(chars[1] == 'b');
+	PCUT_ASSERT_TRUE(chars[2] == 'c');
+	PCUT_ASSERT_TRUE(chars[3] == '\0');
+	PCUT_ASSERT_TRUE(chars[4] == 'X');
+}
+
+/** Set conversion with leading '-' in scanset */
+PCUT_TEST(set_with_leading_dash)
+{
+	int rc;
+	char chars[chars_size];
+
+	memset(chars, 'X', chars_size);
+	rc = sscanf("a-bc[", "%[-abc]", chars);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(chars[0] == 'a');
+	PCUT_ASSERT_TRUE(chars[1] == '-');
+	PCUT_ASSERT_TRUE(chars[2] == 'b');
+	PCUT_ASSERT_TRUE(chars[3] == 'c');
+	PCUT_ASSERT_TRUE(chars[4] == '\0');
+	PCUT_ASSERT_TRUE(chars[5] == 'X');
+}
+
+/** Set conversion with trailing '-' in scanset */
+PCUT_TEST(set_with_trailing_dash)
+{
+	int rc;
+	char chars[chars_size];
+
+	memset(chars, 'X', chars_size);
+	rc = sscanf("a-bc]", "%[abc-]", chars);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(chars[0] == 'a');
+	PCUT_ASSERT_TRUE(chars[1] == '-');
+	PCUT_ASSERT_TRUE(chars[2] == 'b');
+	PCUT_ASSERT_TRUE(chars[3] == 'c');
+	PCUT_ASSERT_TRUE(chars[4] == '\0');
+	PCUT_ASSERT_TRUE(chars[5] == 'X');
+}
+
+/** Set conversion with leading '-' in inverted scanset */
+PCUT_TEST(set_inverted_with_leading_dash)
+{
+	int rc;
+	char chars[chars_size];
+
+	memset(chars, 'X', chars_size);
+	rc = sscanf("def-", "%[^-abc]", chars);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(chars[0] == 'd');
+	PCUT_ASSERT_TRUE(chars[1] == 'e');
+	PCUT_ASSERT_TRUE(chars[2] == 'f');
+	PCUT_ASSERT_TRUE(chars[3] == '\0');
+	PCUT_ASSERT_TRUE(chars[4] == 'X');
+}
+
+/** ']' after '^' in scanset does not lose meaning of scanset delimiter */
+PCUT_TEST(set_inverted_with_only_dash)
+{
+	int rc;
+	char chars[chars_size];
+
+	memset(chars, 'X', chars_size);
+	rc = sscanf("abc-", "%[^-]", chars);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(chars[0] == 'a');
+	PCUT_ASSERT_TRUE(chars[1] == 'b');
+	PCUT_ASSERT_TRUE(chars[2] == 'c');
+	PCUT_ASSERT_TRUE(chars[3] == '\0');
+	PCUT_ASSERT_TRUE(chars[4] == 'X');
+}
+
+/** '^' after '-' in scanset does not have special meaning */
+PCUT_TEST(set_inverted_with_dash_caret)
+{
+	int rc;
+	char chars[chars_size];
+
+	memset(chars, 'X', chars_size);
+	rc = sscanf("-^a", "%[-^a]", chars);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(chars[0] == '-');
+	PCUT_ASSERT_TRUE(chars[1] == '^');
+	PCUT_ASSERT_TRUE(chars[2] == 'a');
+	PCUT_ASSERT_TRUE(chars[3] == '\0');
+	PCUT_ASSERT_TRUE(chars[4] == 'X');
+}
+
+/** Set conversion with range (GNU extension) */
+PCUT_TEST(set_with_range)
+{
+	int rc;
+	char chars[chars_size];
+
+	memset(chars, 'X', chars_size);
+	rc = sscanf("abc]", "%[a-c]", chars);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(chars[0] == 'a');
+	PCUT_ASSERT_TRUE(chars[1] == 'b');
+	PCUT_ASSERT_TRUE(chars[2] == 'c');
+	PCUT_ASSERT_TRUE(chars[3] == '\0');
+	PCUT_ASSERT_TRUE(chars[4] == 'X');
+}
+
+/** Set conversion with range (GNU extension) in inverted scanset */
+PCUT_TEST(set_inverted_with_range)
+{
+	int rc;
+	char chars[chars_size];
+
+	memset(chars, 'X', chars_size);
+	rc = sscanf("defb", "%[^a-c]", chars);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(chars[0] == 'd');
+	PCUT_ASSERT_TRUE(chars[1] == 'e');
+	PCUT_ASSERT_TRUE(chars[2] == 'f');
+	PCUT_ASSERT_TRUE(chars[3] == '\0');
+	PCUT_ASSERT_TRUE(chars[4] == 'X');
+}
+
+/** Set conversion with assignment suppression */
+PCUT_TEST(set_noassign)
+{
+	int rc;
+	int n;
+
+	rc = sscanf("abcd42", "%*[abc]%n", &n);
+	PCUT_ASSERT_INT_EQUALS(0, rc);
+	PCUT_ASSERT_INT_EQUALS(3, n);
+}
+
+/** Set conversion with memory allocation */
+PCUT_TEST(set_malloc)
+{
+	int rc;
+	char *cp;
+
+	cp = NULL;
+	rc = sscanf("abcd42", "%m[abcd]", &cp);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_NOT_NULL(cp);
+	PCUT_ASSERT_TRUE(cp[0] == 'a');
+	PCUT_ASSERT_TRUE(cp[1] == 'b');
+	PCUT_ASSERT_TRUE(cp[2] == 'c');
+	PCUT_ASSERT_TRUE(cp[3] == 'd');
+	PCUT_ASSERT_TRUE(cp[4] == '\0');
+	free(cp);
+}
+
+/** Decimal integer with suppressed assignment */
+PCUT_TEST(dec_int_noassign)
+{
+	int rc;
+	int n;
+
+	rc = sscanf("42", "%*d%n", &n);
+	PCUT_ASSERT_INT_EQUALS(0, rc);
+	PCUT_ASSERT_INT_EQUALS(2, n);
+}
+
+/** Count of characters read */
+PCUT_TEST(count_chars)
+{
+	int rc;
+	char chars[chars_size];
+	int n;
+
+	memset(chars, 'X', chars_size);
+	rc = sscanf("abcd", "%3c%n", chars, &n);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(chars[0] == 'a');
+	PCUT_ASSERT_TRUE(chars[1] == 'b');
+	PCUT_ASSERT_TRUE(chars[2] == 'c');
+	PCUT_ASSERT_TRUE(chars[3] == 'X');
+	PCUT_ASSERT_INT_EQUALS(3, n);
+}
+
+/** Float with just integer part */
+PCUT_TEST(float_intpart_only)
+{
+	int rc;
+	float f;
+
+	rc = sscanf("42", "%f", &f);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(f == 42.0);
+}
+
+/** Double with just integer part */
+PCUT_TEST(double_intpart_only)
+{
+	int rc;
+	double d;
+
+	rc = sscanf("42", "%lf", &d);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(d == 42.0);
+}
+
+/** Long double with just integer part */
+PCUT_TEST(ldouble_intpart_only)
+{
+	int rc;
+	long double ld;
+
+	rc = sscanf("42", "%Lf", &ld);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(ld == 42.0);
+}
+
+/** Float with just hexadecimal integer part */
+PCUT_TEST(float_hex_intpart_only)
+{
+	int rc;
+	float f;
+
+	rc = sscanf("0x2a", "%f", &f);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(f == 0x2a.0p0);
+}
+
+/** Float with sign and integer part */
+PCUT_TEST(float_sign_intpart)
+{
+	int rc;
+	float f;
+
+	rc = sscanf("-42", "%f", &f);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(f == -42.0);
+}
+
+/** Float with integer and fractional part */
+PCUT_TEST(float_intpart_fract)
+{
+	int rc;
+	float f;
+
+	rc = sscanf("4.2", "%f", &f);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	/* 1/10 is not exactly representable in binary floating point */
+	PCUT_ASSERT_TRUE(f > 4.199);
+	PCUT_ASSERT_TRUE(f < 4.201);
+}
+
+/** Float with integer part and unsigned exponent */
+PCUT_TEST(float_intpart_exp)
+{
+	int rc;
+	float f;
+
+	rc = sscanf("42e1", "%f", &f);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(f == 420.0);
+}
+
+
+/** Float with integer part and positive exponent */
+PCUT_TEST(float_intpart_posexp)
+{
+	int rc;
+	float f;
+	rc = sscanf("42e+1", "%f", &f);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(f == 420.0);
+}
+
+/** Float with integer part and negative exponent */
+PCUT_TEST(float_intpart_negexp)
+{
+	int rc;
+	float f;
+
+	rc = sscanf("42e-1", "%f", &f);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	/* 1/10 is not exactly representable in binary floating point */
+	PCUT_ASSERT_TRUE(f > 4.199);
+	PCUT_ASSERT_TRUE(f < 4.201);
+}
+
+/** Float with integer, fractional parts and unsigned exponent */
+PCUT_TEST(float_intpart_fract_exp)
+{
+	int rc;
+	float f;
+
+	rc = sscanf("4.2e1", "%f", &f);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(f == 42.0);
+}
+
+/** Hexadecimal float with integer and fractional part */
+PCUT_TEST(hexfloat_intpart_fract)
+{
+	int rc;
+	float f;
+
+	rc = sscanf("0x2.a", "%f", &f);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(f == 0x2.ap0);
+}
+
+/** Hexadecimal float with integer part and unsigned exponent */
+PCUT_TEST(hexfloat_intpart_exp)
+{
+	int rc;
+	float f;
+
+	rc = sscanf("0x2ap1", "%f", &f);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(f == 0x2ap1);
+}
+
+/** Hexadecimal float with integer part and negative exponent */
+PCUT_TEST(hexfloat_intpart_negexp)
+{
+	int rc;
+	float f;
+
+	rc = sscanf("0x2ap-1", "%f", &f);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(f == 0x2ap-1);
+}
+
+/** Hexadecimal float with integer, fractional parts and unsigned exponent */
+PCUT_TEST(hexfloat_intpart_fract_exp)
+{
+	int rc;
+	float f;
+
+	rc = sscanf("0x2.ap4", "%f", &f);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(f == 0x2.ap4);
+}
+
+/** Float with just integer part and limited width */
+PCUT_TEST(float_intpart_limwidth)
+{
+	int rc;
+	float f;
+
+	rc = sscanf("1234", "%3f", &f);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(f == 123.0);
+}
+
+/** Float with integer, fractional part and limited width */
+PCUT_TEST(float_intpart_fract_limwidth)
+{
+	int rc;
+	float f;
+
+	rc = sscanf("12.34", "%4f", &f);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	/* 1/10 is not exactly representable in binary floating point */
+	PCUT_ASSERT_TRUE(f > 12.29);
+	PCUT_ASSERT_TRUE(f < 12.31);
+}
+
+/** Float with width only enough to cover an integral part */
+PCUT_TEST(float_width_for_only_intpart)
+{
+	int rc;
+	float f;
+
+	rc = sscanf("12.34", "%3f", &f);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(f == 12.0);
+}
+
+/** Float with width too small to cover the exponent number */
+PCUT_TEST(float_width_small_for_expnum)
+{
+	int rc;
+	float f;
+
+	rc = sscanf("12.34e+2", "%7f", &f);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	/* 1/10 is not exactly representable in binary floating point */
+	PCUT_ASSERT_TRUE(f > 12.339);
+	PCUT_ASSERT_TRUE(f < 12.341);
+}
+
+/** Float with width too small to cover the exponent sign and number */
+PCUT_TEST(float_width_small_for_expsignum)
+{
+	int rc;
+	float f;
+
+	rc = sscanf("12.34e+2", "%6f", &f);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	/* 1/10 is not exactly representable in binary floating point */
+	PCUT_ASSERT_TRUE(f > 12.339);
+	PCUT_ASSERT_TRUE(f < 12.341);
+}
+
+/** Float with width too small to cover the exponent part */
+PCUT_TEST(float_width_small_for_exp)
+{
+	int rc;
+	float f;
+
+	rc = sscanf("12.34e+2", "%5f", &f);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	/* 1/10 is not exactly representable in binary floating point */
+	PCUT_ASSERT_TRUE(f > 12.339);
+	PCUT_ASSERT_TRUE(f < 12.341);
+}
+
+/** Float using alternate form 'F' */
+PCUT_TEST(float_cap_f)
+{
+	int rc;
+	float f;
+
+	rc = sscanf("42e1", "%F", &f);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(f == 420.0);
+}
+
+/** Float using alternate form 'a' */
+PCUT_TEST(float_a)
+{
+	int rc;
+	float f;
+
+	rc = sscanf("42e1", "%a", &f);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(f == 420.0);
+}
+
+/** Float using alternate form 'e' */
+PCUT_TEST(float_e)
+{
+	int rc;
+	float f;
+
+	rc = sscanf("42e1", "%e", &f);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(f == 420.0);
+}
+
+/** Float using alternate form 'g' */
+PCUT_TEST(float_g)
+{
+	int rc;
+	float f;
+
+	rc = sscanf("42e1", "%g", &f);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(f == 420.0);
+}
+
+/** Float using alternate form 'A' */
+PCUT_TEST(float_cap_a)
+{
+	int rc;
+	float f;
+
+	rc = sscanf("42e1", "%A", &f);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(f == 420.0);
+}
+
+/** Float using alternate form 'E' */
+PCUT_TEST(float_cap_e)
+{
+	int rc;
+	float f;
+
+	rc = sscanf("42e1", "%E", &f);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(f == 420.0);
+}
+
+/** Float using alternate form 'G' */
+PCUT_TEST(float_cap_g)
+{
+	int rc;
+	float f;
+
+	rc = sscanf("42e1", "%G", &f);
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+	PCUT_ASSERT_TRUE(f == 420.0);
+}
+
+PCUT_EXPORT(scanf);
Index: uspace/lib/c/test/stdlib.c
===================================================================
--- uspace/lib/c/test/stdlib.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
+++ uspace/lib/c/test/stdlib.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -0,0 +1,447 @@
+/*
+ * Copyright (c) 2018 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup libc
+ * @{
+ */
+/**
+ * @file
+ * @brief Test formatted input (scanf family)
+ */
+
+#include <pcut/pcut.h>
+#include <stdlib.h>
+
+PCUT_INIT;
+
+PCUT_TEST_SUITE(stdlib);
+
+PCUT_TEST(decls)
+{
+	/* Make sure size_t is defined */
+	size_t sz = 0;
+	(void) sz;
+
+	/* Make sure wchar_t is defined */
+	wchar_t wc = L'\0';
+	(void) wc;
+
+	/* Make sure EXIT_FAILURE and EXIT_SUCCESS are defined */
+	if (0)
+		exit(EXIT_FAILURE);
+	if (0)
+		exit(EXIT_SUCCESS);
+
+	/* Make sure NULL is defined */
+	void *ptr = NULL;
+	(void) ptr;
+}
+
+/** atoi function */
+PCUT_TEST(atoi)
+{
+	int i;
+
+	i = atoi(" \t42");
+	PCUT_ASSERT_TRUE(i == 42);
+}
+
+/** atol function */
+PCUT_TEST(atol)
+{
+	long li;
+
+	li = atol(" \t42");
+	PCUT_ASSERT_TRUE(li == 42);
+}
+
+/** atoll function */
+PCUT_TEST(atoll)
+{
+	long long lli;
+
+	lli = atoll(" \t42");
+	PCUT_ASSERT_TRUE(lli == 42);
+}
+
+/** strtol function */
+PCUT_TEST(strtol)
+{
+	long li;
+	char *ep;
+
+	li = strtol(" \t42x", &ep, 10);
+	PCUT_ASSERT_TRUE(li == 42);
+	PCUT_ASSERT_TRUE(*ep == 'x');
+}
+
+/** strtol function with auto-detected base 10 */
+PCUT_TEST(strtol_dec_auto)
+{
+	long li;
+	char *ep;
+
+	li = strtol(" \t42x", &ep, 0);
+	PCUT_ASSERT_TRUE(li == 42);
+	PCUT_ASSERT_TRUE(*ep == 'x');
+}
+
+/** strtol function with octal number */
+PCUT_TEST(strtol_oct)
+{
+	long li;
+	char *ep;
+
+	li = strtol(" \t052x", &ep, 8);
+	PCUT_ASSERT_TRUE(li == 052);
+	PCUT_ASSERT_TRUE(*ep == 'x');
+}
+
+/** strtol function with octal number with prefix */
+#include <stdio.h>
+PCUT_TEST(strtol_oct_prefix)
+{
+	long li;
+	char *ep;
+
+	li = strtol(" \t052x", &ep, 0);
+	printf("li=%ld (0%lo)\n", li, li);
+	PCUT_ASSERT_TRUE(li == 052);
+	PCUT_ASSERT_TRUE(*ep == 'x');
+}
+
+/** strtol function with hex number */
+PCUT_TEST(strtol_hex)
+{
+	long li;
+	char *ep;
+
+	li = strtol(" \t2ax", &ep, 16);
+	PCUT_ASSERT_TRUE(li == 0x2a);
+	PCUT_ASSERT_TRUE(*ep == 'x');
+}
+
+/** strtol function with hex number with hex prefix */
+PCUT_TEST(strtol_hex_prefixed)
+{
+	long li;
+	char *ep;
+
+	li = strtol(" \t0x2ax", &ep, 0);
+	PCUT_ASSERT_TRUE(li == 0x2a);
+	PCUT_ASSERT_TRUE(*ep == 'x');
+}
+
+/** strtol function with base 16 and number with 0x prefix */
+PCUT_TEST(strtol_base16_prefix)
+{
+	long li;
+	char *ep;
+
+	li = strtol(" \t0x1y", &ep, 16);
+	printf("li=%ld\n", li);
+	PCUT_ASSERT_TRUE(li == 1);
+	PCUT_ASSERT_TRUE(*ep == 'y');
+}
+
+/** strtol function with base 36 number */
+PCUT_TEST(strtol_base36)
+{
+	long li;
+	char *ep;
+
+	li = strtol(" \tz1.", &ep, 36);
+	PCUT_ASSERT_TRUE(li == 35 * 36 + 1);
+	PCUT_ASSERT_TRUE(*ep == '.');
+}
+
+/** rand function */
+PCUT_TEST(rand)
+{
+	int i;
+	int r;
+
+	for (i = 0; i < 100; i++) {
+		r = rand();
+		PCUT_ASSERT_TRUE(r >= 0);
+		PCUT_ASSERT_TRUE(r <= RAND_MAX);
+	}
+
+	PCUT_ASSERT_TRUE(RAND_MAX >= 32767);
+}
+
+/** srand function */
+PCUT_TEST(srand)
+{
+	int r1;
+	int r2;
+
+	srand(1);
+	r1 = rand();
+	srand(1);
+	r2 = rand();
+
+	PCUT_ASSERT_INT_EQUALS(r2, r1);
+
+	srand(42);
+	r1 = rand();
+	srand(42);
+	r2 = rand();
+
+	PCUT_ASSERT_INT_EQUALS(r2, r1);
+}
+
+/** Just make sure we have memory allocation function prototypes */
+PCUT_TEST(malloc)
+{
+	void *p;
+
+#if 0
+	// TODO
+	p = aligned_alloc(4, 8);
+	PCUT_ASSERT_NOT_NULL(p);
+	free(p);
+#endif
+	p = calloc(4, 4);
+	PCUT_ASSERT_NOT_NULL(p);
+	free(p);
+
+	p = malloc(4);
+	PCUT_ASSERT_NOT_NULL(p);
+	p = realloc(p, 2);
+	PCUT_ASSERT_NOT_NULL(p);
+	free(p);
+}
+
+/** Just check abort() is defined */
+PCUT_TEST(abort)
+{
+	if (0)
+		abort();
+}
+
+static void dummy_exit_handler(void)
+{
+}
+
+/** atexit function */
+PCUT_TEST(atexit)
+{
+	int rc;
+
+	rc = atexit(dummy_exit_handler);
+	PCUT_ASSERT_INT_EQUALS(0, rc);
+}
+
+/** exit function -- just make sure it is declared */
+PCUT_TEST(exit)
+{
+	if (0)
+		exit(0);
+}
+
+/** at_quick_exit function */
+PCUT_TEST(at_quick_exit)
+{
+	int rc;
+
+	rc = at_quick_exit(dummy_exit_handler);
+	PCUT_ASSERT_INT_EQUALS(0, rc);
+}
+
+/** quick_exit function -- just make sure it is declared */
+PCUT_TEST(quick_exit)
+{
+	if (0)
+		quick_exit(0);
+}
+
+/** getenv function */
+PCUT_TEST(getenv)
+{
+	char *s;
+
+	s = getenv("FOO");
+	PCUT_ASSERT_NULL(s);
+}
+
+/** Test availability of command processor */
+PCUT_TEST(system_null)
+{
+	int rc;
+
+	rc = system(NULL);
+	PCUT_ASSERT_INT_EQUALS(0, rc);
+}
+
+/** Test running a command */
+PCUT_TEST(system_cmd)
+{
+	int rc;
+
+	/* This should fail as system is just a stub */
+	rc = system("/app/bdsh");
+	PCUT_ASSERT_INT_EQUALS(1, rc);
+}
+
+/** Comparison function for bsearch test */
+static int test_compar(const void *a, const void *b)
+{
+	const int *ia, *ib;
+
+	ia = (const int *)a;
+	ib = (const int *)b;
+
+	return *ia - *ib;
+}
+
+PCUT_TEST(bsearch)
+{
+	int numbers[] = { 1, 2, 6, 7, 7, 10, 100, 120 };
+	int k;
+	void *r;
+
+	k = 0;
+	r = bsearch(&k, numbers, sizeof(numbers) / sizeof(int), sizeof(int),
+	    test_compar);
+	PCUT_ASSERT_NULL(r);
+
+	k = 1;
+	r = bsearch(&k, numbers, sizeof(numbers) / sizeof(int), sizeof(int),
+	    test_compar);
+	PCUT_ASSERT_NOT_NULL(r);
+	PCUT_ASSERT_INT_EQUALS(1, *(int *)r);
+
+	k = 3;
+	r = bsearch(&k, numbers, sizeof(numbers) / sizeof(int), sizeof(int),
+	    test_compar);
+	PCUT_ASSERT_NULL(r);
+
+	k = 6;
+	r = bsearch(&k, numbers, sizeof(numbers) / sizeof(int), sizeof(int),
+	    test_compar);
+	PCUT_ASSERT_NOT_NULL(r);
+	PCUT_ASSERT_INT_EQUALS(6, *(int *)r);
+
+	k = 7;
+	r = bsearch(&k, numbers, sizeof(numbers) / sizeof(int), sizeof(int),
+	    test_compar);
+	PCUT_ASSERT_NOT_NULL(r);
+	PCUT_ASSERT_INT_EQUALS(7, *(int *)r);
+
+	k = 200;
+	r = bsearch(&k, numbers, sizeof(numbers) / sizeof(int), sizeof(int),
+	    test_compar);
+	PCUT_ASSERT_NULL(r);
+}
+
+/** abs function of positive number */
+PCUT_TEST(abs_pos)
+{
+	int i;
+
+	i = abs(1);
+	PCUT_ASSERT_TRUE(i == 1);
+}
+
+/** abs function of negative number */
+PCUT_TEST(abs_neg)
+{
+	int i;
+
+	i = abs(-1);
+	PCUT_ASSERT_TRUE(i == 1);
+}
+
+/** labs function of positive number */
+PCUT_TEST(labs_pos)
+{
+	long li;
+
+	li = labs(1);
+	PCUT_ASSERT_TRUE(li == 1);
+}
+
+/** labs function of negative number */
+PCUT_TEST(labs_neg)
+{
+	long li;
+
+	li = labs(-1);
+	PCUT_ASSERT_TRUE(li == 1);
+}
+
+/** llabs function of positive number */
+PCUT_TEST(llabs_pos)
+{
+	long long lli;
+
+	lli = llabs(1);
+	PCUT_ASSERT_TRUE(lli == 1);
+}
+
+/** llabs function of negative number */
+PCUT_TEST(llabs_neg)
+{
+	long long lli;
+
+	lli = llabs(-1);
+	PCUT_ASSERT_TRUE(lli == 1);
+}
+
+/** Integer division */
+PCUT_TEST(div_func)
+{
+	div_t d;
+
+	d = div(41, 7);
+	PCUT_ASSERT_INT_EQUALS(5, d.quot);
+	PCUT_ASSERT_INT_EQUALS(6, d.rem);
+}
+
+/** Long integer division */
+PCUT_TEST(ldiv_func)
+{
+	ldiv_t d;
+
+	d = ldiv(41, 7);
+	PCUT_ASSERT_INT_EQUALS(5, d.quot);
+	PCUT_ASSERT_INT_EQUALS(6, d.rem);
+}
+
+/** Long long integer division */
+PCUT_TEST(lldiv_func)
+{
+	lldiv_t d;
+
+	d = lldiv(41, 7);
+	PCUT_ASSERT_INT_EQUALS(5, d.quot);
+	PCUT_ASSERT_INT_EQUALS(6, d.rem);
+}
+
+PCUT_EXPORT(stdlib);
Index: uspace/lib/clui/tinput.c
===================================================================
--- uspace/lib/clui/tinput.c	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/lib/clui/tinput.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -145,5 +145,5 @@
 
 	for (p = 0; p < pad; p++)
-		putchar(' ');
+		putwchar(' ');
 
 	console_flush(ti->console);
@@ -182,5 +182,5 @@
 	tinput_console_set_lpos(ti, ti->text_coord + ti->nc);
 	console_flush(ti->console);
-	putchar('\n');
+	putwchar('\n');
 }
 
Index: uspace/lib/fmtutil/fmtutil.c
===================================================================
--- uspace/lib/fmtutil/fmtutil.c	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/lib/fmtutil/fmtutil.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -95,14 +95,14 @@
 		for (i = 0; i < width; i++) {
 			if (i < len)
-				putchar(wstr[i]);
+				putwchar(wstr[i]);
 			else
-				putchar(' ');
+				putwchar(' ');
 		}
 	} else if (mode == ALIGN_RIGHT) {
 		for (i = 0; i < width; i++) {
 			if (i < width - len)
-				putchar(' ');
+				putwchar(' ');
 			else
-				putchar(wstr[i - (width - len)]);
+				putwchar(wstr[i - (width - len)]);
 		}
 	} else if (mode == ALIGN_CENTER) {
@@ -110,7 +110,7 @@
 		for (i = 0; i < width; i++) {
 			if ((i < padding) || ((i - padding) >= len))
-				putchar(' ');
+				putwchar(' ');
 			else
-				putchar(wstr[i - padding]);
+				putwchar(wstr[i - padding]);
 		}
 	} else if (mode == ALIGN_JUSTIFY) {
@@ -146,10 +146,10 @@
 				    (words - 1)));
 				for (j = 0; j < spaces; j++) {
-					putchar(' ');
+					putwchar(' ');
 				}
 				done_chars += spaces;
 			}
 			while (i < len && wstr[i] != ' ') {
-				putchar(wstr[i++]);
+				putwchar(wstr[i++]);
 				done_chars++;
 			}
@@ -158,5 +158,5 @@
 	skip_words:
 		while (done_chars < width) {
-			putchar(' ');
+			putwchar(' ');
 			done_chars++;
 		}
Index: uspace/lib/gui/terminal.c
===================================================================
--- uspace/lib/gui/terminal.c	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/lib/gui/terminal.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -456,5 +456,5 @@
 		break;
 	default:
-		updated = chargrid_putchar(term->frontbuf, ch, true);
+		updated = chargrid_putwchar(term->frontbuf, ch, true);
 	}
 
Index: uspace/lib/posix/Makefile
===================================================================
--- uspace/lib/posix/Makefile	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/lib/posix/Makefile	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -70,5 +70,4 @@
 	src/signal.c \
 	src/stdio.c \
-	src/stdio/scanf.c \
 	src/stdlib.c \
 	src/stdlib/strtold.c \
@@ -83,5 +82,9 @@
 TEST_SOURCES = \
 	test/main.c \
-	test/scanf.c
+	test/stdio.c \
+	test/stdlib.c \
+	test/unistd.c
+
+EXTRA_TEST_CFLAGS = -Wno-deprecated-declarations
 
 EXPORT_CPPFLAGS = \
Index: uspace/lib/posix/include/posix/assert.h
===================================================================
--- uspace/lib/posix/include/posix/assert.h	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/lib/posix/include/posix/assert.h	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -1,1 +1,44 @@
+/*
+ * Copyright (c) 2018 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup libposix
+ * @{
+ */
+
+#ifndef POSIX_ASSERT_H_
+#define POSIX_ASSERT_H_
+
+/*
+ * Just a pass-through to libc assert.
+ */
 #include "libc/assert.h"
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/posix/include/posix/dirent.h
===================================================================
--- uspace/lib/posix/include/posix/dirent.h	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/lib/posix/include/posix/dirent.h	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -36,5 +36,8 @@
 #define POSIX_DIRENT_H_
 
-#include <libc/dirent.h>
+/*
+ * Just a pass-through to libc dirent.
+ */
+#include "libc/dirent.h"
 
 #endif
Index: uspace/lib/posix/include/posix/errno.h
===================================================================
--- uspace/lib/posix/include/posix/errno.h	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/lib/posix/include/posix/errno.h	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -1,1 +1,44 @@
+/*
+ * Copyright (c) 2018 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup libposix
+ * @{
+ */
+
+#ifndef POSIX_ERRNO_H_
+#define POSIX_ERRNO_H_
+
+/*
+ * Just a pass-through to libc errno.
+ */
 #include "libc/errno.h"
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/posix/include/posix/inttypes.h
===================================================================
--- uspace/lib/posix/include/posix/inttypes.h	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/lib/posix/include/posix/inttypes.h	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -36,5 +36,7 @@
 #define POSIX_INTTYPES_H_
 
-#include "stdint.h"
+/*
+ * Just a pass-through to libc inttypes.
+ */
 #include "libc/inttypes.h"
 
Index: uspace/lib/posix/include/posix/iso646.h
===================================================================
--- uspace/lib/posix/include/posix/iso646.h	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/lib/posix/include/posix/iso646.h	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2011 Jiri Zarevucky
+ * Copyright (c) 2018 Jiri Svoboda
  * All rights reserved.
  *
@@ -30,23 +30,14 @@
  * @{
  */
-/** @file Alternative spellings.
- */
 
 #ifndef POSIX_ISO646_H_
 #define POSIX_ISO646_H_
 
-#define and &&
-#define and_eq &=
-#define bitand &
-#define bitor |
-#define compl ˜
-#define not !
-#define not_eq !=
-#define or ||
-#define or_eq |=
-#define xor ^
-#define xor_eq ^=
+/*
+ * Just a pass-through to libc iso646.
+ */
+#include "libc/iso646.h"
 
-#endif /* POSIX_ISO646_H_ */
+#endif
 
 /** @}
Index: uspace/lib/posix/include/posix/limits.h
===================================================================
--- uspace/lib/posix/include/posix/limits.h	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/lib/posix/include/posix/limits.h	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -36,5 +36,4 @@
 #define POSIX_LIMITS_H_
 
-#include "posix/stdint.h"
 #include "libc/limits.h"
 
Index: uspace/lib/posix/include/posix/malloc.h
===================================================================
--- uspace/lib/posix/include/posix/malloc.h	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/lib/posix/include/posix/malloc.h	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -33,5 +33,8 @@
 #define POSIX_MALLOC_H_
 
-/* Empty. Just to satisfy preprocessor. */
+/*
+ * Just a pass-through to libc malloc.
+ */
+#include "libc/malloc.h"
 
 #endif /* POSIX_MALLOC_H_ */
Index: uspace/lib/posix/include/posix/stdarg.h
===================================================================
--- uspace/lib/posix/include/posix/stdarg.h	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/lib/posix/include/posix/stdarg.h	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -34,10 +34,8 @@
 #define POSIX_STDARG_H_
 
-typedef __builtin_va_list va_list;
-
-#define va_start(ap, last)  __builtin_va_start(ap, last)
-#define va_arg(ap, type)    __builtin_va_arg(ap, type)
-#define va_end(ap)          __builtin_va_end(ap)
-#define va_copy(dst, src)   __builtin_va_copy(dst, src)
+/*
+ * Just a pass-through to libc stdarg.
+ */
+#include "libc/stdarg.h"
 
 #endif
Index: uspace/lib/posix/include/posix/stdbool.h
===================================================================
--- uspace/lib/posix/include/posix/stdbool.h	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/lib/posix/include/posix/stdbool.h	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -33,11 +33,11 @@
  */
 
-#ifndef POSIX_BOOL_H_
-#define POSIX_BOOL_H_
+#ifndef POSIX_STDBOOL_H_
+#define POSIX_STDBOOL_H_
 
-#define bool _Bool
-#define false  0
-#define true   1
-#define __bool_true_false_are_defined 1
+/*
+ * Just a pass-through to libc stdbool.
+ */
+#include "libc/stdbool.h"
 
 #endif
Index: uspace/lib/posix/include/posix/stddef.h
===================================================================
--- uspace/lib/posix/include/posix/stddef.h	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/lib/posix/include/posix/stddef.h	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -36,9 +36,8 @@
 #define POSIX_STDDEF_H_
 
-#include "sys/types.h"
-
-#include <_bits/NULL.h>
-
-#define offsetof(type,member) ((size_t) &(((type *) 0)->member))
+/*
+ * Just a pass-through to libc stddef.
+ */
+#include "libc/stddef.h"
 
 #endif /* POSIX_STDDEF_H_ */
Index: uspace/lib/posix/include/posix/stdint.h
===================================================================
--- uspace/lib/posix/include/posix/stdint.h	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/lib/posix/include/posix/stdint.h	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2011 Petr Koupy
+ * Copyright (c) 2018 Jiri Svoboda
  * All rights reserved.
  *
@@ -30,23 +30,14 @@
  * @{
  */
-/** @file Integer types.
- */
 
 #ifndef POSIX_STDINT_H_
 #define POSIX_STDINT_H_
 
+/*
+ * Just a pass-through to libc stdint.
+ */
 #include "libc/stdint.h"
 
-#undef OFF64_MAX
-#undef OFF64_MIN
-#define OFF64_MAX  INT64_MAX
-#define OFF64_MIN  INT64_MIN
-
-#undef AOFF64_MAX
-#undef AOFF64_MIN
-#define AOFF64_MAX  UINT64_MAX
-#define AOFF64_MIN  UINT64_MIN
-
-#endif /* POSIX_STDINT_H_ */
+#endif
 
 /** @}
Index: uspace/lib/posix/include/posix/stdio.h
===================================================================
--- uspace/lib/posix/include/posix/stdio.h	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/lib/posix/include/posix/stdio.h	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -40,5 +40,4 @@
 
 #include "stddef.h"
-#include "unistd.h"
 #include "libc/io/verify.h"
 #include "sys/types.h"
@@ -46,71 +45,8 @@
 #include "limits.h"
 
-/*
- * These are the same as in HelenOS libc.
- * It would be possible to directly include <stdio.h> but
- * it is better not to pollute POSIX namespace with other functions
- * defined in that header.
- *
- * Because libposix is always linked with libc, providing only these
- * forward declarations ought to be enough.
- */
-#define EOF (-1)
-
-/** Size of buffers used in stdio header. */
-#define BUFSIZ  4096
-
-/** Maximum size in bytes of the longest filename. */
-#define FILENAME_MAX 4096
-
-typedef struct _IO_FILE FILE;
-
-extern FILE *stdin;
-extern FILE *stdout;
-extern FILE *stderr;
-
-extern int fgetc(FILE *);
-extern char *fgets(char *, int, FILE *);
-
-extern int getchar(void);
-extern char *gets(char *, size_t);
-
-extern int fputc(wchar_t, FILE *);
-extern int fputs(const char *, FILE *);
-
-extern int putchar(wchar_t);
-extern int puts(const char *);
-
-extern int fprintf(FILE *, const char *, ...) _HELENOS_PRINTF_ATTRIBUTE(2, 3);
-extern int vfprintf(FILE *, const char *, va_list);
-
-extern int printf(const char *, ...) _HELENOS_PRINTF_ATTRIBUTE(1, 2);
-extern int vprintf(const char *, va_list);
-
-extern int snprintf(char *, size_t, const char *, ...) _HELENOS_PRINTF_ATTRIBUTE(3, 4);
-#ifdef _GNU_SOURCE
-extern int vasprintf(char **, const char *, va_list);
-extern int asprintf(char **, const char *, ...) _HELENOS_PRINTF_ATTRIBUTE(2, 3);
-#endif
-extern int vsnprintf(char *, size_t, const char *, va_list);
-
-extern FILE *fopen(const char *, const char *);
 extern FILE *fdopen(int, const char *);
-extern int fclose(FILE *);
-
-extern size_t fread(void *, size_t, size_t, FILE *);
-extern size_t fwrite(const void *, size_t, size_t, FILE *);
-
-extern void rewind(FILE *);
-extern int feof(FILE *);
 extern int fileno(FILE *);
 
-extern int fflush(FILE *);
-extern int ferror(FILE *);
-extern void clearerr(FILE *);
-
-extern void setvbuf(FILE *, void *, int, size_t);
-extern void setbuf(FILE *, void *);
-
-/* POSIX specific stuff. */
+#define P_tmpdir "/tmp"
 
 /* Identifying the Terminal */
@@ -119,14 +55,5 @@
 extern char *ctermid(char *s);
 
-/* Error Recovery */
-extern void clearerr(FILE *stream);
-
 /* Input/Output */
-#undef putc
-#define putc fputc
-extern int fputs(const char *__restrict__ s, FILE *__restrict__ stream);
-#undef getc
-#define getc fgetc
-extern int ungetc(int c, FILE *stream);
 extern ssize_t getdelim(char **__restrict__ lineptr, size_t *__restrict__ n,
     int delimiter, FILE *__restrict__ stream);
@@ -134,25 +61,6 @@
     FILE *__restrict__ stream);
 
-/* Opening Streams */
-extern FILE *freopen(const char *__restrict__ filename,
-    const char *__restrict__ mode, FILE *__restrict__ stream);
-
-/* Error Messages */
-extern void perror(const char *s);
-
-/* File Positioning */
-typedef struct {
-	off64_t offset;
-} fpos_t;
-
-extern int fsetpos(FILE *stream, const fpos_t *pos);
-extern int fgetpos(FILE *__restrict__ stream, fpos_t *__restrict__ pos);
-extern int fseek(FILE *stream, long offset, int whence);
 extern int fseeko(FILE *stream, off_t offset, int whence);
-extern long ftell(FILE *stream);
 extern off_t ftello(FILE *stream);
-
-/* Flushing Buffers */
-extern int fflush(FILE *stream);
 
 /* Formatted Output */
@@ -160,19 +68,4 @@
     _HELENOS_PRINTF_ATTRIBUTE(2, 3);
 extern int vdprintf(int fildes, const char *__restrict__ format, va_list ap);
-extern int sprintf(char *__restrict__ s, const char *__restrict__ format, ...)
-    _HELENOS_PRINTF_ATTRIBUTE(2, 3);
-extern int vsprintf(char *__restrict__ s, const char *__restrict__ format, va_list ap);
-
-/* Formatted Input */
-extern int fscanf(
-    FILE *__restrict__ stream, const char *__restrict__ format, ...);
-extern int vfscanf(
-    FILE *__restrict__ stream, const char *__restrict__ format, va_list arg);
-extern int scanf(const char *__restrict__ format, ...);
-extern int vscanf(const char *__restrict__ format, va_list arg);
-extern int sscanf(
-    const char *__restrict__ s, const char *__restrict__ format, ...);
-extern int vsscanf(
-    const char *__restrict__ s, const char *__restrict__ format, va_list arg);
 
 /* File Locking */
@@ -185,17 +78,6 @@
 extern int putchar_unlocked(int c);
 
-/* Deleting Files */
-extern int remove(const char *path);
-
-/* Renaming Files */
-extern int rename(const char *oldname, const char *newname);
-
 /* Temporary Files */
-#undef L_tmpnam
-#define L_tmpnam PATH_MAX
-extern char *tmpnam(char *s);
 extern char *tempnam(const char *dir, const char *pfx);
-extern FILE *tmpfile(void);
-
 
 #endif /* POSIX_STDIO_H_ */
Index: uspace/lib/posix/include/posix/stdlib.h
===================================================================
--- uspace/lib/posix/include/posix/stdlib.h	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/lib/posix/include/posix/stdlib.h	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -37,53 +37,11 @@
 #define POSIX_STDLIB_H_
 
+#include "libc/stdlib.h"
 #include "sys/types.h"
 
 #include <_bits/NULL.h>
 
-#define RAND_MAX  714025
-
-/* Process Termination */
-#undef EXIT_FAILURE
-#define EXIT_FAILURE 1
-#undef EXIT_SUCCESS
-#define EXIT_SUCCESS 0
-#define _Exit exit
-extern int atexit(void (*func)(void));
-extern void exit(int status) __attribute__((noreturn));
-extern void abort(void) __attribute__((noreturn));
-
-/* Absolute Value */
-extern int abs(int i);
-extern long labs(long i);
-extern long long llabs(long long i);
-
-/* Integer Division */
-
-typedef struct {
-	int quot, rem;
-} div_t;
-
-typedef struct {
-	long quot, rem;
-} ldiv_t;
-
-typedef struct {
-	long long quot, rem;
-} lldiv_t;
-
-extern div_t div(int numer, int denom);
-extern ldiv_t ldiv(long numer, long denom);
-extern lldiv_t lldiv(long long numer, long long denom);
-
-/* Array Functions */
-extern void qsort(void *array, size_t count, size_t size,
-    int (*compare)(const void *, const void *));
-extern void *bsearch(const void *key, const void *base,
-    size_t nmemb, size_t size, int (*compar)(const void *, const void *));
-
 /* Environment Access */
-extern char *getenv(const char *name);
 extern int putenv(char *string);
-extern int system(const char *string);
 
 /* Symbolic Links */
@@ -96,35 +54,9 @@
 extern long double strtold(const char *__restrict__ nptr, char **__restrict__ endptr);
 
-/* Integer Conversion */
-extern int atoi(const char *nptr);
-extern long int atol(const char *nptr);
-extern long long int atoll(const char *nptr);
-extern long int strtol(const char *__restrict__ nptr,
-    char **__restrict__ endptr, int base);
-extern long long int strtoll(const char *__restrict__ nptr,
-    char **__restrict__ endptr, int base);
-extern unsigned long int strtoul(const char *__restrict__ nptr,
-    char **__restrict__ endptr, int base);
-extern unsigned long long int strtoull(
-    const char *__restrict__ nptr, char **__restrict__ endptr, int base);
-
-/* Memory Allocation */
-extern void *malloc(size_t size)
-    __attribute__((malloc));
-extern void *calloc(size_t nelem, size_t elsize)
-    __attribute__((malloc));
-extern void *realloc(void *ptr, size_t size)
-    __attribute__((warn_unused_result));
-extern void free(void *ptr);
-
 /* Temporary Files */
 extern int mkstemp(char *tmpl);
 
-/* Pseudo-random number generator */
-extern int rand(void);
-extern void srand(unsigned int seed);
-
 /* Legacy Declarations */
-extern char *mktemp(char *tmpl);
+extern char *mktemp(char *tmpl) __attribute__((deprecated));
 extern int bsd_getloadavg(double loadavg[], int nelem);
 
Index: uspace/lib/posix/include/posix/string.h
===================================================================
--- uspace/lib/posix/include/posix/string.h	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/lib/posix/include/posix/string.h	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -36,7 +36,4 @@
 #ifndef POSIX_STRING_H_
 #define POSIX_STRING_H_
-
-#include "sys/types.h"
-
 /*
  * TODO: not implemented due to missing locale support
@@ -48,24 +45,7 @@
 
 #include <_bits/NULL.h>
+#include <_bits/size_t.h>
 
-/*
- * These are the same as in HelenOS libc.
- * It would be possible to directly include <str.h> and <mem.h> but
- * it is better not to pollute POSIX namespace with other functions
- * defined in that header.
- *
- * Because libposix is always linked with libc, providing only these
- * forward declarations ought to be enough.
- */
-
-/* From mem.h */
-// #define bzero(ptr, len)  memset((ptr), 0, (len))
-extern void *memset(void *, int, size_t)
-    __attribute__((nonnull(1)));
-extern void *memcpy(void *, const void *, size_t)
-    __attribute__((nonnull(1, 2)));
-extern void *memmove(void *, const void *, size_t)
-    __attribute__((nonnull(1, 2)));
-
+#include "libc/mem.h"
 
 /* Copying and Concatenation */
@@ -80,11 +60,9 @@
 extern char *strndup(const char *s, size_t n);
 
-/* String/Array Comparison */
-extern int memcmp(const void *mem1, const void *mem2, size_t n);
+/* String Comparison */
 extern int strcmp(const char *s1, const char *s2);
 extern int strncmp(const char *s1, const char *s2, size_t n);
 
 /* Search Functions */
-extern void *memchr(const void *mem, int c, size_t n);
 extern char *strchr(const char *s, int c);
 extern char *strrchr(const char *s, int c);
@@ -122,5 +100,4 @@
 #endif
 
-
 #endif  // POSIX_STRING_H_
 
Index: uspace/lib/posix/src/internal/common.h
===================================================================
--- uspace/lib/posix/src/internal/common.h	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/lib/posix/src/internal/common.h	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -66,5 +66,5 @@
 }
 
-extern aoff64_t posix_pos[MAX_OPEN_FILES];
+extern aoff64_t posix_pos[VFS_MAX_OPEN_FILES];
 
 #endif /* LIBPOSIX_COMMON_H_ */
Index: uspace/lib/posix/src/stdio.c
===================================================================
--- uspace/lib/posix/src/stdio.c	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/lib/posix/src/stdio.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -2,4 +2,5 @@
  * Copyright (c) 2011 Jiri Zarevucky
  * Copyright (c) 2011 Petr Koupy
+ * Copyright (c) 2018 Jiri Svoboda
  * All rights reserved.
  *
@@ -38,9 +39,12 @@
 
 #include <assert.h>
-
 #include <errno.h>
-
+#include <stdbool.h>
+#include <tmpfile.h>
+
+#include "posix/fcntl.h"
 #include "posix/stdlib.h"
 #include "posix/string.h"
+#include "posix/sys/stat.h"
 #include "posix/sys/types.h"
 #include "posix/unistd.h"
@@ -169,46 +173,4 @@
 
 /**
- * Write error messages to standard error.
- *
- * @param s Error message.
- */
-void perror(const char *s)
-{
-	if (s == NULL || s[0] == '\0') {
-		fprintf(stderr, "%s\n", strerror(errno));
-	} else {
-		fprintf(stderr, "%s: %s\n", s, strerror(errno));
-	}
-}
-
-/** Restores stream a to position previously saved with fgetpos().
- *
- * @param stream Stream to restore
- * @param pos Position to restore
- * @return Zero on success, non-zero (with errno set) on failure
- */
-int fsetpos(FILE *stream, const fpos_t *pos)
-{
-	return fseek64(stream, pos->offset, SEEK_SET);
-}
-
-/** Saves the stream's position for later use by fsetpos().
- *
- * @param stream Stream to save
- * @param pos Place to store the position
- * @return Zero on success, non-zero (with errno set) on failure
- */
-int fgetpos(FILE *restrict stream, fpos_t *restrict pos)
-{
-	off64_t ret = ftell64(stream);
-	if (ret != -1) {
-		pos->offset = ret;
-		return 0;
-	} else {
-		return -1;
-	}
-}
-
-/**
  * Reposition a file-position indicator in a stream.
  *
@@ -320,94 +282,4 @@
 
 /**
- * Print formatted output to the string.
- *
- * @param s Output string.
- * @param format Format description.
- * @return Either the number of printed characters (excluding null byte) or
- *     negative value on error.
- */
-int sprintf(char *s, const char *restrict format, ...)
-{
-	va_list list;
-	va_start(list, format);
-	int result = vsprintf(s, format, list);
-	va_end(list);
-	return result;
-}
-
-/**
- * Print formatted output to the string.
- *
- * @param s Output string.
- * @param format Format description.
- * @param ap Print arguments.
- * @return Either the number of printed characters (excluding null byte) or
- *     negative value on error.
- */
-int vsprintf(char *s, const char *restrict format, va_list ap)
-{
-	return vsnprintf(s, INT_MAX, format, ap);
-}
-
-/**
- * Convert formatted input from the stream.
- *
- * @param stream Input stream.
- * @param format Format description.
- * @return The number of converted output items or EOF on failure.
- */
-int fscanf(FILE *restrict stream, const char *restrict format, ...)
-{
-	va_list list;
-	va_start(list, format);
-	int result = vfscanf(stream, format, list);
-	va_end(list);
-	return result;
-}
-
-/**
- * Convert formatted input from the standard input.
- *
- * @param format Format description.
- * @return The number of converted output items or EOF on failure.
- */
-int scanf(const char *restrict format, ...)
-{
-	va_list list;
-	va_start(list, format);
-	int result = vscanf(format, list);
-	va_end(list);
-	return result;
-}
-
-/**
- * Convert formatted input from the standard input.
- *
- * @param format Format description.
- * @param arg Output items.
- * @return The number of converted output items or EOF on failure.
- */
-int vscanf(const char *restrict format, va_list arg)
-{
-	return vfscanf(stdin, format, arg);
-}
-
-/**
- * Convert formatted input from the string.
- *
- * @param s Input string.
- * @param format Format description.
- * @return The number of converted output items or EOF on failure.
- */
-int sscanf(const char *restrict s, const char *restrict format, ...)
-{
-	va_list list;
-	va_start(list, format);
-	int result = vsscanf(s, format, list);
-	va_end(list);
-	return result;
-}
-
-/**
  * Acquire file stream for the thread.
  *
@@ -485,32 +357,38 @@
 }
 
-/**
- * Get a unique temporary file name (obsolete).
- *
- * @param s Buffer for the file name. Must be at least L_tmpnam bytes long.
- * @return The value of s on success, NULL on failure.
- */
-char *tmpnam(char *s)
-{
-	assert(L_tmpnam >= strlen("/tmp/tnXXXXXX"));
-
-	static char buffer[L_tmpnam + 1];
-	if (s == NULL) {
-		s = buffer;
-	}
-
-	strcpy(s, "/tmp/tnXXXXXX");
-	mktemp(s);
-
-	if (*s == '\0') {
-		/* Errno set by mktemp(). */
-		return NULL;
-	}
-
-	return s;
-}
-
-/**
- * Get an unique temporary file name with additional constraints (obsolete).
+/** Determine if directory is an 'appropriate' temporary directory.
+ *
+ * @param dir Directory path
+ * @return @c true iff directory is appropriate.
+ */
+static bool is_appropriate_tmpdir(const char *dir)
+{
+	struct stat sbuf;
+
+	/* Must not be NULL */
+	if (dir == NULL)
+		return false;
+
+	/* Must not be empty */
+	if (dir[0] == '\0')
+		return false;
+
+	if (stat(dir, &sbuf) != 0)
+		return false;
+
+	/* Must be a directory */
+	if ((sbuf.st_mode & S_IFMT) != S_IFDIR)
+		return false;
+
+	/* Must be writable */
+	if (access(dir, W_OK) != 0)
+		return false;
+
+	return true;
+}
+
+/** Construct unique file name.
+ *
+ * Never use this function.
  *
  * @param dir Path to directory, where the file should be created.
@@ -520,77 +398,34 @@
 char *tempnam(const char *dir, const char *pfx)
 {
-	/* Sequence number of the filename. */
-	static int seq = 0;
-
-	size_t dir_len = strlen(dir);
-	if (dir[dir_len - 1] == '/') {
-		dir_len--;
-	}
-
-	size_t pfx_len = strlen(pfx);
-	if (pfx_len > 5) {
-		pfx_len = 5;
-	}
-
-	char *result = malloc(dir_len + /* slash*/ 1 +
-	    pfx_len + /* three-digit seq */ 3 + /* .tmp */ 4 + /* nul */ 1);
-
-	if (result == NULL) {
-		errno = ENOMEM;
+	const char *dpref;
+	char *d;
+	char *buf;
+	int rc;
+
+	d = getenv("TMPDIR");
+	if (is_appropriate_tmpdir(d))
+		dpref = d;
+	else if (is_appropriate_tmpdir(dir))
+		dpref = dir;
+	else if (is_appropriate_tmpdir(P_tmpdir))
+		dpref = P_tmpdir;
+	else
+		dpref = "/";
+
+	if (dpref[strlen(dpref) - 1] != '/')
+		rc = asprintf(&buf, "%s/%sXXXXXX", dpref, pfx);
+	else
+		rc = asprintf(&buf, "%s%sXXXXXX", dpref, pfx);
+
+	if (rc < 0)
+		return NULL;
+
+	rc = __tmpfile_templ(buf, false);
+	if (rc != 0) {
+		free(buf);
 		return NULL;
 	}
 
-	char *res_ptr = result;
-	strncpy(res_ptr, dir, dir_len);
-	res_ptr += dir_len;
-	strncpy(res_ptr, pfx, pfx_len);
-	res_ptr += pfx_len;
-
-	for (; seq < 1000; ++seq) {
-		snprintf(res_ptr, 8, "%03d.tmp", seq);
-
-		int orig_errno = errno;
-		errno = EOK;
-		/* Check if the file exists. */
-		if (access(result, F_OK) == -1) {
-			if (errno == ENOENT) {
-				errno = orig_errno;
-				break;
-			} else {
-				/* errno set by access() */
-				return NULL;
-			}
-		}
-	}
-
-	if (seq == 1000) {
-		free(result);
-		errno = EINVAL;
-		return NULL;
-	}
-
-	return result;
-}
-
-/**
- * Create and open an unique temporary file.
- * The file is automatically removed when the stream is closed.
- *
- * @param dir Path to directory, where the file should be created.
- * @param pfx Optional prefix up to 5 characters long.
- * @return Newly allocated unique path for temporary file. NULL on failure.
- */
-FILE *tmpfile(void)
-{
-	char filename[] = "/tmp/tfXXXXXX";
-	int fd = mkstemp(filename);
-	if (fd == -1) {
-		/* errno set by mkstemp(). */
-		return NULL;
-	}
-
-	/* Unlink the created file, so that it's removed on close(). */
-	unlink(filename);
-	return fdopen(fd, "w+");
+	return buf;
 }
 
Index: uspace/lib/posix/src/stdio/scanf.c
===================================================================
--- uspace/lib/posix/src/stdio/scanf.c	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ 	(revision )
@@ -1,1307 +1,0 @@
-/*
- * Copyright (c) 2011 Petr Koupy
- * 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 libposix
- * @{
- */
-/** @file Implementation of the scanf backend.
- */
-
-#include <assert.h>
-
-#include <errno.h>
-
-#include "posix/stdio.h"
-#include "posix/stdlib.h"
-#include "posix/stddef.h"
-#include "posix/string.h"
-#include "posix/ctype.h"
-#include "posix/sys/types.h"
-
-#include "../internal/common.h"
-#include "libc/malloc.h"
-#include "libc/stdbool.h"
-
-/** Unified data type for possible data sources for scanf. */
-typedef union __data_source {
-	FILE *stream; /**< Input file stream. */
-	const char *string; /**< Input string. */
-} _data_source;
-
-/** Internal state of the input provider. */
-enum {
-	/** Partly constructed but not yet functional. */
-	_PROV_CONSTRUCTED,
-	/** Ready to serve any request. */
-	_PROV_READY,
-	/** Cursor is temporarily lent to the external entity. No action is
-	 * possible until the cursor is returned.
-	 */
-	_PROV_CURSOR_LENT,
-};
-
-/** Universal abstraction over data input for scanf. */
-typedef struct __input_provider {
-	/** Source of data elements. */
-	_data_source source;
-	/** How many elements was already processed. */
-	int consumed;
-	/** How many elements was already fetched from the source. */
-	int fetched;
-	/** Elements are fetched from the source in batches (e.g. by getline())
-	 * to allow using strtol/strtod family even on streams.
-	 */
-	char *window;
-	/** Size of the current window. */
-	size_t window_size;
-	/** Points to the next element to be processed inside the current window. */
-	const char *cursor;
-	/** Internal state of the provider. */
-	int state;
-
-	/** Take control over data source. Finish initialization of the internal
-	 * structures (e.g. allocation of window).
-	 */
-	void (*capture)(struct __input_provider *);
-	/** Get a single element from the source and update the internal structures
-	 * accordingly (e.g. greedy update of the window). Return -1 if the
-	 * element cannot be obtained.
-	 */
-	int (*pop)(struct __input_provider *);
-	/** Undo the most recent not-undone pop operation. Might be necesarry to
-	 * flush current window and seek data source backwards. Return 0 if the
-	 * pop history is exhausted, non-zero on success.
-	 */
-	int (*undo)(struct __input_provider *);
-	/** Lend the cursor to the caller.  */
-	const char *(*borrow_cursor)(struct __input_provider *);
-	/** Take control over possibly incremented cursor and update the internal
-	 * structures if necessary.
-	 */
-	void (*return_cursor)(struct __input_provider *, const char *);
-	/** Release the control over the source. That is, synchronize any
-	 * fetched but non-consumed elements (e.g. by seeking) and destruct
-	 * internal structures (e.g. window deallocation).
-	 */
-	void (*release)(struct __input_provider *);
-} _input_provider;
-
-/** @see __input_provider */
-static void _capture_stream(_input_provider *self)
-{
-	assert(self->source.stream);
-	assert(self->state == _PROV_CONSTRUCTED);
-	/* Caller could already pre-allocated the window. */
-	assert((self->window == NULL && self->window_size == 0) ||
-	    (self->window && self->window_size > 0));
-
-	/* Initialize internal structures. */
-	self->consumed = 0;
-	ssize_t fetched = getline(
-	    &self->window, &self->window_size, self->source.stream);
-	if (fetched != -1) {
-		self->fetched = fetched;
-		self->cursor = self->window;
-	} else {
-		/* EOF encountered. */
-		self->fetched = 0;
-		self->cursor = NULL;
-	}
-	self->state = _PROV_READY;
-}
-
-/** @see __input_provider */
-static void _capture_string(_input_provider *self)
-{
-	assert(self->source.string);
-	assert(self->state == _PROV_CONSTRUCTED);
-
-	/* Initialize internal structures. */
-	self->consumed = 0;
-	self->fetched = strlen(self->source.string);
-	self->window = (char *) self->source.string;
-	self->window_size = self->fetched + 1;
-	self->cursor = self->window;
-	self->state = _PROV_READY;
-}
-
-/** @see __input_provider */
-static int _pop_stream(_input_provider *self)
-{
-	assert(self->state == _PROV_READY);
-
-	if (self->cursor) {
-		int c = *self->cursor;
-		++self->consumed;
-		++self->cursor;
-		/* Do we need to fetch a new line from the source? */
-		if (*self->cursor == '\0') {
-			ssize_t fetched = getline(&self->window,
-			    &self->window_size, self->source.stream);
-			if (fetched != -1) {
-				self->fetched += fetched;
-				self->cursor = self->window;
-			} else {
-				/* EOF encountered. */
-				self->cursor = NULL;
-			}
-		}
-		return c;
-	} else {
-		/* Already at EOF. */
-		return -1;
-	}
-}
-
-/** @see __input_provider */
-static int _pop_string(_input_provider *self)
-{
-	assert(self->state == _PROV_READY);
-
-	if (*self->cursor != '\0') {
-		int c = *self->cursor;
-		++self->consumed;
-		++self->cursor;
-		return c;
-	} else {
-		/* String depleted. */
-		return -1;
-	}
-}
-
-/** @see __input_provider */
-static int _undo_stream(_input_provider *self)
-{
-	assert(self->state == _PROV_READY);
-
-	if (self->consumed == 0) {
-		/* Undo history exhausted. */
-		return 0;
-	}
-
-	if (!self->cursor || self->window == self->cursor) {
-		/*
-		 * Complex case. Either at EOF (cursor == NULL) or there is no more
-		 * place to retreat to inside the window. Seek the source backwards
-		 * and flush the window. Regarding the scanf, this could happend only
-		 * when matching unbounded string (%s) or unbounded scanset (%[) not
-		 * containing newline, while at the same time newline is the character
-		 * that breaks the matching process.
-		 */
-		int rc = fseek(self->source.stream, -1, SEEK_CUR);
-		if (rc == -1) {
-			/* Seek failed.  */
-			return 0;
-		}
-		ssize_t fetched = getline(&self->window,
-		    &self->window_size, self->source.stream);
-		if (fetched != -1) {
-			assert(fetched == 1);
-			self->fetched = self->consumed + 1;
-			self->cursor = self->window;
-		} else {
-			/* Stream is broken. */
-			return 0;
-		}
-	} else {
-		/* Simple case. Still inside window. */
-		--self->cursor;
-	}
-	--self->consumed;
-	return 1; /* Success. */
-}
-
-/** @see __input_provider */
-static int _undo_string(_input_provider *self)
-{
-	assert(self->state == _PROV_READY);
-
-	if (self->consumed > 0) {
-		--self->consumed;
-		--self->cursor;
-	} else {
-		/* Undo history exhausted. */
-		return 0;
-	}
-	return 1; /* Success. */
-}
-
-/** @see __input_provider */
-static const char *_borrow_cursor_universal(_input_provider *self)
-{
-	assert(self->state == _PROV_READY);
-
-	self->state = _PROV_CURSOR_LENT;
-	return self->cursor;
-}
-
-/** @see __input_provider */
-static void _return_cursor_stream(_input_provider *self, const char *cursor)
-{
-	assert(self->state == _PROV_CURSOR_LENT);
-
-	/* Check how much of the window did external entity consumed. */
-	self->consumed += cursor - self->cursor;
-	self->cursor = cursor;
-	if (*self->cursor == '\0') {
-		/* Window was completely consumed, fetch new data. */
-		ssize_t fetched = getline(&self->window,
-		    &self->window_size, self->source.stream);
-		if (fetched != -1) {
-			self->fetched += fetched;
-			self->cursor = self->window;
-		} else {
-			/* EOF encountered. */
-			self->cursor = NULL;
-		}
-	}
-	self->state = _PROV_READY;
-}
-
-/** @see __input_provider */
-static void _return_cursor_string(_input_provider *self, const char *cursor)
-{
-	assert(self->state == _PROV_CURSOR_LENT);
-
-	/* Check how much of the window did external entity consumed. */
-	self->consumed += cursor - self->cursor;
-	self->cursor = cursor;
-	self->state = _PROV_READY;
-}
-
-/** @see __input_provider */
-static void _release_stream(_input_provider *self)
-{
-	assert(self->state == _PROV_READY);
-	assert(self->consumed >= self->fetched);
-
-	/*
-	 * Try to correct the difference between the stream position and what was
-	 * actually consumed. If it is not possible, continue anyway.
-	 */
-	fseek(self->source.stream, self->consumed - self->fetched, SEEK_CUR);
-
-	/* Destruct internal structures. */
-	self->fetched = 0;
-	self->cursor = NULL;
-	if (self->window) {
-		free(self->window);
-		self->window = NULL;
-	}
-	self->window_size = 0;
-	self->state = _PROV_CONSTRUCTED;
-}
-
-/** @see __input_provider */
-static void _release_string(_input_provider *self)
-{
-	assert(self->state == _PROV_READY);
-
-	/* Destruct internal structures. */
-	self->fetched = 0;
-	self->cursor = NULL;
-	self->window = NULL;
-	self->window_size = 0;
-	self->state = _PROV_CONSTRUCTED;
-}
-
-/** Length modifier values. */
-enum {
-	LMOD_NONE,
-	LMOD_hh,
-	LMOD_h,
-	LMOD_l,
-	LMOD_ll,
-	LMOD_j,
-	LMOD_z,
-	LMOD_t,
-	LMOD_L,
-	LMOD_p, /* Reserved for %p conversion. */
-};
-
-/**
- * Decides whether provided characters specify length modifier. If so, the
- * recognized modifier is stored through provider pointer.
- *
- * @param c Candidate on the length modifier.
- * @param _c Next character (might be NUL).
- * @param modifier Pointer to the modifier value.
- * @return Whether the modifier was recognized or not.
- */
-static inline int is_length_mod(int c, int _c, int *modifier)
-{
-	assert(modifier);
-
-	switch (c) {
-	case 'h':
-		/* Check whether the modifier was not already recognized. */
-		if (*modifier == LMOD_NONE) {
-			*modifier = _c == 'h' ? LMOD_hh : LMOD_h;
-		} else {
-			/* Format string is invalid. Notify the caller. */
-			*modifier = LMOD_NONE;
-		}
-		return 1;
-	case 'l':
-		if (*modifier == LMOD_NONE) {
-			*modifier = _c == 'l' ? LMOD_ll : LMOD_l;
-		} else {
-			*modifier = LMOD_NONE;
-		}
-		return 1;
-	case 'j':
-		*modifier = *modifier == LMOD_NONE ? LMOD_j : LMOD_NONE;
-		return 1;
-	case 'z':
-		*modifier = *modifier == LMOD_NONE ? LMOD_z : LMOD_NONE;
-		return 1;
-	case 't':
-		*modifier = *modifier == LMOD_NONE ? LMOD_t : LMOD_NONE;
-		return 1;
-	case 'L':
-		*modifier = *modifier == LMOD_NONE ? LMOD_L : LMOD_NONE;
-		return 1;
-	default:
-		return 0;
-	}
-}
-
-/**
- * Decides whether provided character specifies integer conversion. If so, the
- * semantics of the conversion is stored through provided pointers..
- *
- * @param c Candidate on the integer conversion.
- * @param is_unsigned Pointer to store whether the conversion is signed or not.
- * @param base Pointer to store the base of the integer conversion.
- * @return Whether the conversion was recognized or not.
- */
-static inline int is_int_conv(int c, bool *is_unsigned, int *base)
-{
-	assert(is_unsigned && base);
-
-	switch (c) {
-	case 'd':
-		*is_unsigned = false;
-		*base = 10;
-		return 1;
-	case 'i':
-		*is_unsigned = false;
-		*base = 0;
-		return 1;
-	case 'o':
-		*is_unsigned = true;
-		*base = 8;
-		return 1;
-	case 'u':
-		*is_unsigned = true;
-		*base = 10;
-		return 1;
-	case 'p':
-		/*
-		 * According to POSIX, %p modifier is implementation defined but
-		 * must correspond to its printf counterpart.
-		 */
-	case 'x':
-	case 'X':
-		*is_unsigned = true;
-		*base = 16;
-		return 1;
-		return 1;
-	default:
-		return 0;
-	}
-}
-
-/**
- * Decides whether provided character specifies conversion of the floating
- * point number.
- *
- * @param c Candidate on the floating point conversion.
- * @return Whether the conversion was recognized or not.
- */
-static inline int is_float_conv(int c)
-{
-	switch (c) {
-	case 'a':
-	case 'A':
-	case 'e':
-	case 'E':
-	case 'f':
-	case 'F':
-	case 'g':
-	case 'G':
-		return 1;
-	default:
-		return 0;
-	}
-}
-
-/**
- * Decides whether provided character specifies conversion of the character
- * sequence.
- *
- * @param c Candidate on the character sequence conversion.
- * @param modifier Pointer to store length modifier for wide chars.
- * @return Whether the conversion was recognized or not.
- */
-static inline int is_seq_conv(int c, int *modifier)
-{
-	assert(modifier);
-
-	switch (c) {
-	case 'S':
-		*modifier = LMOD_l;
-		/* Fallthrough */
-	case 's':
-		return 1;
-	case 'C':
-		*modifier = LMOD_l;
-		/* Fallthrough */
-	case 'c':
-		return 1;
-	case '[':
-		return 1;
-	default:
-		return 0;
-	}
-}
-
-/**
- * Backend for the whole family of scanf functions. Uses input provider
- * to abstract over differences between strings and streams. Should be
- * POSIX compliant (apart from the not supported stuff).
- *
- * NOT SUPPORTED: locale (see strtold), wide chars, numbered output arguments
- *
- * @param in Input provider.
- * @param fmt Format description.
- * @param arg Output arguments.
- * @return The number of converted output items or EOF on failure.
- */
-static inline int _internal_scanf(
-    _input_provider *in, const char *restrict fmt, va_list arg)
-{
-	int c = -1;
-	int converted_cnt = 0;
-	bool converting = false;
-	bool matching_failure = false;
-
-	bool assign_supress = false;
-	bool assign_alloc = false;
-	long width = -1;
-	int length_mod = LMOD_NONE;
-	bool int_conv_unsigned = false;
-	int int_conv_base = 0;
-
-	/*
-	 * Buffers allocated by scanf for optional 'm' specifier must be remembered
-	 * to deallocaate them in case of an error. Because each of those buffers
-	 * corresponds to one of the argument from va_list, there is an upper bound
-	 * on the number of those arguments. In case of C99, this uppper bound is
-	 * 127 arguments.
-	 */
-	char *buffers[127];
-	for (int i = 0; i < 127; ++i) {
-		buffers[i] = NULL;
-	}
-	int next_unused_buffer_idx = 0;
-
-	in->capture(in);
-
-	/*
-	 * Interpret format string. Control shall prematurely jump from the cycle
-	 * on input failure, matching failure or illegal format string. In order
-	 * to keep error reporting simple enough and to keep input consistent,
-	 * error condition shall be always manifested as jump from the cycle,
-	 * not function return. Format string pointer shall be updated specifically
-	 * for each sub-case (i.e. there shall be no loop-wide increment).
-	 */
-	while (*fmt) {
-
-		if (converting) {
-
-			/*
-			 * Processing inside conversion specifier. Either collect optional
-			 * parameters or execute the conversion. When the conversion
-			 * is successfully completed, increment conversion count and switch
-			 * back to normal mode.
-			 */
-			if (*fmt == '*') {
-				/* Assignment-supression (optional). */
-				if (assign_supress) {
-					/* Already set. Illegal format string. */
-					break;
-				}
-				assign_supress = true;
-				++fmt;
-			} else if (*fmt == 'm') {
-				/* Assignment-allocation (optional). */
-				if (assign_alloc) {
-					/* Already set. Illegal format string. */
-					break;
-				}
-				assign_alloc = true;
-				++fmt;
-			} else if (*fmt == '$') {
-				/* Reference to numbered output argument. */
-				// TODO
-				not_implemented();
-			} else if (isdigit(*fmt)) {
-				/* Maximum field length (optional). */
-				if (width != -1) {
-					/* Already set. Illegal format string. */
-					break;
-				}
-				char *fmt_new = NULL;
-				width = strtol(fmt, &fmt_new, 10);
-				if (width != 0) {
-					fmt = fmt_new;
-				} else {
-					/*
-					 * Since POSIX requires width to be non-zero, it is
-					 * sufficient to interpret zero width as error without
-					 * referring to errno.
-					 */
-					break;
-				}
-			} else if (is_length_mod(*fmt, *(fmt + 1), &length_mod)) {
-				/* Length modifier (optional). */
-				if (length_mod == LMOD_NONE) {
-					/*
-					 * Already set. Illegal format string. The actual detection
-					 * is carried out in the is_length_mod().
-					 */
-					break;
-				}
-				if (length_mod == LMOD_hh || length_mod == LMOD_ll) {
-					/* Modifier was two characters long. */
-					++fmt;
-				}
-				++fmt;
-			} else if (is_int_conv(*fmt, &int_conv_unsigned, &int_conv_base)) {
-				/* Integer conversion. */
-
-				/* Check sanity of optional parts of conversion specifier. */
-				if (assign_alloc || length_mod == LMOD_L) {
-					/* Illegal format string. */
-					break;
-				}
-
-				/*
-				 * Conversion of the integer with %p specifier needs special
-				 * handling, because it is not allowed to have arbitrary
-				 * length modifier.
-				 */
-				if (*fmt == 'p') {
-					if (length_mod == LMOD_NONE) {
-						length_mod = LMOD_p;
-					} else {
-						/* Already set. Illegal format string. */
-						break;
-					}
-				}
-
-				/*
-				 * First consume any white spaces, so we can borrow cursor
-				 * from the input provider. This way, the cursor will either
-				 * point to the non-white space while the input will be
-				 * prefetched up to the newline (which is suitable for strtol),
-				 * or the input will be at EOF.
-				 */
-				do {
-					c = in->pop(in);
-				} while (isspace(c));
-
-				/* After skipping the white spaces, can we actually continue? */
-				if (c == -1) {
-					/* Input failure. */
-					break;
-				} else {
-					/*
-					 * Everything is OK, just undo the last pop, so the cursor
-					 * can be borrowed.
-					 */
-					in->undo(in);
-				}
-
-				const char *cur_borrowed = NULL;
-				char *cur_duplicated = NULL;
-				const char *cur_limited = NULL;
-				const char *cur_updated = NULL;
-
-				/*
-				 * Borrow the cursor. Until it is returned to the provider
-				 * we cannot jump from the cycle, because it would leave
-				 * the input inconsistent.
-				 */
-				cur_borrowed = in->borrow_cursor(in);
-
-				/*
-				 * If the width is limited, the cursor horizont must be
-				 * decreased accordingly. Otherwise the strtol could read more
-				 * than allowed by width.
-				 */
-				if (width != -1) {
-					cur_duplicated = strndup(cur_borrowed, width);
-					cur_limited = cur_duplicated;
-				} else {
-					cur_limited = cur_borrowed;
-				}
-				cur_updated = cur_limited;
-
-				long long sres = 0;
-				unsigned long long ures = 0;
-				errno = 0; /* Reset errno to recognize error later. */
-				/* Try to convert the integer. */
-				if (int_conv_unsigned) {
-					ures = strtoull(cur_limited, (char **) &cur_updated, int_conv_base);
-				} else {
-					sres = strtoll(cur_limited, (char **) &cur_updated, int_conv_base);
-				}
-
-				/* Update the cursor so it can be returned to the provider. */
-				cur_borrowed += cur_updated - cur_limited;
-				if (cur_duplicated != NULL) {
-					/* Deallocate duplicated part of the cursor view. */
-					free(cur_duplicated);
-				}
-				cur_limited = NULL;
-				cur_updated = NULL;
-				cur_duplicated = NULL;
-				/*
-				 * Return the cursor to the provider. Input consistency is again
-				 * the job of the provider, so we can report errors from
-				 * now on.
-				 */
-				in->return_cursor(in, cur_borrowed);
-				cur_borrowed = NULL;
-
-				/* Check whether the conversion was successful. */
-				if (errno != EOK) {
-					matching_failure = true;
-					break;
-				}
-
-				/*
-				 * If not supressed, assign the converted integer into
-				 * the next output argument.
-				 */
-				if (!assign_supress) {
-					if (int_conv_unsigned) {
-						unsigned char *phh;
-						unsigned short *ph;
-						unsigned *pdef;
-						unsigned long *pl;
-						unsigned long long *pll;
-						uintmax_t *pj;
-						size_t *pz;
-						// XXX: What is unsigned counterpart of the ptrdiff_t?
-						size_t *pt;
-						void **pp;
-
-						switch (length_mod) {
-						case LMOD_hh:
-							phh = va_arg(arg, unsigned char *);
-							*phh = (unsigned char) ures;
-							break;
-						case LMOD_h:
-							ph = va_arg(arg, unsigned short *);
-							*ph = (unsigned short) ures;
-							break;
-						case LMOD_NONE:
-							pdef = va_arg(arg, unsigned *);
-							*pdef = (unsigned) ures;
-							break;
-						case LMOD_l:
-							pl = va_arg(arg, unsigned long *);
-							*pl = (unsigned long) ures;
-							break;
-						case LMOD_ll:
-							pll = va_arg(arg, unsigned long long *);
-							*pll = (unsigned long long) ures;
-							break;
-						case LMOD_j:
-							pj = va_arg(arg, uintmax_t *);
-							*pj = (uintmax_t) ures;
-							break;
-						case LMOD_z:
-							pz = va_arg(arg, size_t *);
-							*pz = (size_t) ures;
-							break;
-						case LMOD_t:
-							pt = va_arg(arg, size_t *);
-							*pt = (size_t) ures;
-							break;
-						case LMOD_p:
-							pp = va_arg(arg, void **);
-							*pp = (void *) (uintptr_t) ures;
-							break;
-						default:
-							assert(false);
-						}
-					} else {
-						signed char *phh;
-						short *ph;
-						int *pdef;
-						long *pl;
-						long long *pll;
-						intmax_t *pj;
-						ssize_t *pz;
-						ptrdiff_t *pt;
-
-						switch (length_mod) {
-						case LMOD_hh:
-							phh = va_arg(arg, signed char *);
-							*phh = (signed char) sres;
-							break;
-						case LMOD_h:
-							ph = va_arg(arg, short *);
-							*ph = (short) sres;
-							break;
-						case LMOD_NONE:
-							pdef = va_arg(arg, int *);
-							*pdef = (int) sres;
-							break;
-						case LMOD_l:
-							pl = va_arg(arg, long *);
-							*pl = (long) sres;
-							break;
-						case LMOD_ll:
-							pll = va_arg(arg, long long *);
-							*pll = (long long) sres;
-							break;
-						case LMOD_j:
-							pj = va_arg(arg, intmax_t *);
-							*pj = (intmax_t) sres;
-							break;
-						case LMOD_z:
-							pz = va_arg(arg, ssize_t *);
-							*pz = (ssize_t) sres;
-							break;
-						case LMOD_t:
-							pt = va_arg(arg, ptrdiff_t *);
-							*pt = (ptrdiff_t) sres;
-							break;
-						default:
-							assert(false);
-						}
-					}
-					++converted_cnt;
-				}
-
-				converting = false;
-				++fmt;
-			} else if (is_float_conv(*fmt)) {
-				/* Floating point number conversion. */
-
-				/* Check sanity of optional parts of conversion specifier. */
-				if (assign_alloc) {
-					/* Illegal format string. */
-					break;
-				}
-				if (length_mod != LMOD_NONE &&
-				    length_mod != LMOD_l &&
-				    length_mod != LMOD_L) {
-					/* Illegal format string. */
-					break;
-				}
-
-				/*
-				 * First consume any white spaces, so we can borrow cursor
-				 * from the input provider. This way, the cursor will either
-				 * point to the non-white space while the input will be
-				 * prefetched up to the newline (which is suitable for strtof),
-				 * or the input will be at EOF.
-				 */
-				do {
-					c = in->pop(in);
-				} while (isspace(c));
-
-				/* After skipping the white spaces, can we actually continue? */
-				if (c == -1) {
-					/* Input failure. */
-					break;
-				} else {
-					/*
-					 * Everything is OK, just undo the last pop, so the cursor
-					 * can be borrowed.
-					 */
-					in->undo(in);
-				}
-
-				const char *cur_borrowed = NULL;
-				const char *cur_limited = NULL;
-				char *cur_duplicated = NULL;
-				const char *cur_updated = NULL;
-
-				/*
-				 * Borrow the cursor. Until it is returned to the provider
-				 * we cannot jump from the cycle, because it would leave
-				 * the input inconsistent.
-				 */
-				cur_borrowed = in->borrow_cursor(in);
-
-				/*
-				 * If the width is limited, the cursor horizont must be
-				 * decreased accordingly. Otherwise the strtof could read more
-				 * than allowed by width.
-				 */
-				if (width != -1) {
-					cur_duplicated = strndup(cur_borrowed, width);
-					cur_limited = cur_duplicated;
-				} else {
-					cur_limited = cur_borrowed;
-				}
-				cur_updated = cur_limited;
-
-				float fres = 0.0;
-				double dres = 0.0;
-				long double ldres = 0.0;
-				errno = 0; /* Reset errno to recognize error later. */
-				/* Try to convert the floating point nubmer. */
-				switch (length_mod) {
-				case LMOD_NONE:
-					fres = strtof(cur_limited, (char **) &cur_updated);
-					break;
-				case LMOD_l:
-					dres = strtod(cur_limited, (char **) &cur_updated);
-					break;
-				case LMOD_L:
-					ldres = strtold(cur_limited, (char **) &cur_updated);
-					break;
-				default:
-					assert(false);
-				}
-
-				/* Update the cursor so it can be returned to the provider. */
-				cur_borrowed += cur_updated - cur_limited;
-				if (cur_duplicated != NULL) {
-					/* Deallocate duplicated part of the cursor view. */
-					free(cur_duplicated);
-				}
-				cur_limited = NULL;
-				cur_updated = NULL;
-				/*
-				 * Return the cursor to the provider. Input consistency is again
-				 * the job of the provider, so we can report errors from
-				 * now on.
-				 */
-				in->return_cursor(in, cur_borrowed);
-				cur_borrowed = NULL;
-
-				/* Check whether the conversion was successful. */
-				if (errno != EOK) {
-					matching_failure = true;
-					break;
-				}
-
-				/*
-				 * If nto supressed, assign the converted floating point number
-				 * into the next output argument.
-				 */
-				if (!assign_supress) {
-					float *pf;
-					double *pd;
-					long double *pld;
-					switch (length_mod) {
-					case LMOD_NONE:
-						pf = va_arg(arg, float *);
-						*pf = fres;
-						break;
-					case LMOD_l:
-						pd = va_arg(arg, double *);
-						*pd = dres;
-						break;
-					case LMOD_L:
-						pld = va_arg(arg, long double *);
-						*pld = ldres;
-						break;
-					default:
-						assert(false);
-					}
-					++converted_cnt;
-				}
-
-				converting = false;
-				++fmt;
-			} else if (is_seq_conv(*fmt, &length_mod)) {
-				/* Character sequence conversion. */
-
-				/* Check sanity of optional parts of conversion specifier. */
-				if (length_mod != LMOD_NONE &&
-				    length_mod != LMOD_l) {
-					/* Illegal format string. */
-					break;
-				}
-
-				if (length_mod == LMOD_l) {
-					/* Wide chars not supported. */
-					// TODO
-					not_implemented();
-				}
-
-				int term_size = 1; /* Size of the terminator (0 or 1)). */
-				if (*fmt == 'c') {
-					term_size = 0;
-					width = width == -1 ? 1 : width;
-				}
-
-				if (*fmt == 's') {
-					/* Skip white spaces. */
-					do {
-						c = in->pop(in);
-					} while (isspace(c));
-				} else {
-					/* Fetch a single character. */
-					c = in->pop(in);
-				}
-
-				/* Check whether there is still input to read. */
-				if (c == -1) {
-					/* Input failure. */
-					break;
-				}
-
-				/* Prepare scanset. */
-				char terminate_on[256];
-				for (int i = 0; i < 256; ++i) {
-					terminate_on[i] = 0;
-				}
-				if (*fmt == 'c') {
-					++fmt;
-				} else if (*fmt == 's') {
-					terminate_on[' '] = 1;
-					terminate_on['\n'] = 1;
-					terminate_on['\t'] = 1;
-					terminate_on['\f'] = 1;
-					terminate_on['\r'] = 1;
-					terminate_on['\v'] = 1;
-					++fmt;
-				} else {
-					assert(*fmt == '[');
-					bool not = false;
-					bool dash = false;
-					++fmt;
-					/* Check for negation. */
-					if (*fmt == '^') {
-						not = true;
-						++fmt;
-					}
-					/* Check for escape sequences. */
-					if (*fmt == '-' || *fmt == ']') {
-						terminate_on[(int) *fmt] = 1;
-						++fmt;
-					}
-					/* Check for ordinary characters and ranges. */
-					while (*fmt != '\0' && *fmt != ']') {
-						if (dash) {
-							for (char chr = *(fmt - 2); chr <= *fmt; ++chr) {
-								terminate_on[(int) chr] = 1;
-							}
-							dash = false;
-						} else if (*fmt == '-') {
-							dash = true;
-						} else {
-							terminate_on[(int) *fmt] = 1;
-						}
-						++fmt;
-					}
-					/* Check for escape sequence. */
-					if (dash == true) {
-						terminate_on['-'] = 1;
-					}
-					/* Check whether the specifier was correctly terminated.*/
-					if (*fmt == '\0') {
-						/* Illegal format string. */
-						break;
-					} else {
-						++fmt;
-					}
-					/* Inverse the scanset if necessary. */
-					if (not == false) {
-						for (int i = 0; i < 256; ++i) {
-							terminate_on[i] = terminate_on[i] ? 0 : 1;
-						}
-					}
-				}
-
-				char *buf = NULL;
-				size_t buf_size = 0;
-				char *cur = NULL;
-				size_t alloc_step = 80; /* Buffer size gain during reallocation. */
-				int my_buffer_idx = 0;
-
-				/*
-				 * Retrieve the buffer into which popped characters
-				 * will be stored.
-				 */
-				if (!assign_supress) {
-					if (assign_alloc) {
-						/* We must allocate our own buffer. */
-						buf_size =
-						    width == -1 ? alloc_step : (size_t) width + term_size;
-						buf = malloc(buf_size);
-						if (!buf) {
-							/* No memory. */
-							break;
-						}
-						my_buffer_idx = next_unused_buffer_idx;
-						++next_unused_buffer_idx;
-						buffers[my_buffer_idx] = buf;
-						cur = buf;
-					} else {
-						/* Caller provided its buffer. */
-						buf = va_arg(arg, char *);
-						cur = buf;
-						buf_size =
-						    width == -1 ? SIZE_MAX : (size_t) width + term_size;
-					}
-				}
-
-				/* Match the string. The next character is already popped. */
-				while ((width == -1 || width > 0) && c != -1 && !terminate_on[c]) {
-
-					/* Check whether the buffer is still sufficiently large. */
-					if (!assign_supress) {
-						/* Always reserve space for the null terminator. */
-						if (cur == buf + buf_size - term_size) {
-							/* Buffer size must be increased. */
-							buf = realloc(buf, buf_size + alloc_step);
-							if (buf) {
-								buffers[my_buffer_idx] = buf;
-								cur = buf + buf_size - term_size;
-								buf_size += alloc_step;
-							} else {
-								/*
-								 * Break just from this tight loop. Errno will
-								 * be checked after it.
-								 */
-								break;
-							}
-						}
-						/* Store the input character. */
-						*cur = c;
-					}
-
-					width = width == -1 ? -1 : width - 1;
-					++cur;
-					c = in->pop(in);
-				}
-				if (errno == ENOMEM) {
-					/* No memory. */
-					break;
-				}
-				if (c != -1) {
-					/* There is still more input, so undo the last pop. */
-					in->undo(in);
-				}
-
-				/* Check for failures. */
-				if (cur == buf) {
-					/*
-					 * Matching failure. Input failure was already checked
-					 * earlier.
-					 */
-					matching_failure = true;
-					if (!assign_supress && assign_alloc) {
-						/* Roll back. */
-						free(buf);
-						buffers[my_buffer_idx] = NULL;
-						--next_unused_buffer_idx;
-					}
-					break;
-				}
-
-				/* Store the terminator. */
-				if (!assign_supress && term_size > 0) {
-					/* Space for the terminator was reserved. */
-					*cur = '\0';
-				}
-
-				/* Store the result if not already stored. */
-				if (!assign_supress) {
-					if (assign_alloc) {
-						char **pbuf = va_arg(arg, char **);
-						*pbuf = buf;
-					}
-					++converted_cnt;
-				}
-
-				converting = false;
-				/* Format string pointer already incremented. */
-			} else if (*fmt == 'n') {
-				/* Report the number of consumed bytes so far. */
-
-				/* Sanity check. */
-				bool sane =
-				    width == -1 &&
-				    length_mod == LMOD_NONE &&
-				    assign_alloc == false &&
-				    assign_supress == false;
-
-				if (sane) {
-					int *pi = va_arg(arg, int *);
-					*pi = in->consumed;
-				} else {
-					/* Illegal format string. */
-					break;
-				}
-
-				/* This shall not be counted as conversion. */
-				converting = false;
-				++fmt;
-			} else {
-				/* Illegal format string. */
-				break;
-			}
-
-		} else {
-
-			/*
-			 * Processing outside conversion specifier. Either skip white
-			 * spaces or match characters one by one. If conversion specifier
-			 * is detected, switch to coversion mode.
-			 */
-			if (isspace(*fmt)) {
-				/* Skip white spaces in the format string. */
-				while (isspace(*fmt)) {
-					++fmt;
-				}
-				/* Skip white spaces in the input. */
-				do {
-					c = in->pop(in);
-				} while (isspace(c));
-				if (c != -1) {
-					/* Input is not at EOF, so undo the last pop operation. */
-					in->undo(in);
-				}
-			} else if (*fmt == '%' && *(fmt + 1) != '%') {
-				/* Conversion specifier detected. Switch modes. */
-				converting = true;
-				/* Reset the conversion context. */
-				assign_supress = false;
-				assign_alloc = false;
-				width = -1;
-				length_mod = LMOD_NONE;
-				int_conv_unsigned = false;
-				int_conv_base = 0;
-				++fmt;
-			} else {
-				/* One by one matching. */
-				if (*fmt == '%') {
-					/* Escape sequence detected. */
-					++fmt;
-					assert(*fmt == '%');
-				}
-				c = in->pop(in);
-				if (c == -1) {
-					/* Input failure. */
-					break;
-				} else if (c != *fmt) {
-					/* Matching failure. */
-					in->undo(in);
-					matching_failure = true;
-					break;
-				} else {
-					++fmt;
-				}
-			}
-
-		}
-
-	}
-
-	in->release(in);
-
-	/* This somewhat complicated return value decision is required by POSIX. */
-	int rc;
-	if (matching_failure) {
-		rc = converted_cnt;
-	} else {
-		if (errno == EOK) {
-			rc = converted_cnt > 0 ? converted_cnt : EOF;
-		} else {
-			rc = EOF;
-		}
-	}
-	if (rc == EOF) {
-		/*
-		 * Caller will not know how many arguments were successfully converted,
-		 * so the deallocation of buffers is our responsibility.
-		 */
-		for (int i = 0; i < next_unused_buffer_idx; ++i) {
-			free(buffers[i]);
-			buffers[i] = NULL;
-		}
-		next_unused_buffer_idx = 0;
-	}
-	return rc;
-}
-
-/**
- * Convert formatted input from the stream.
- *
- * @param stream Input stream.
- * @param format Format description.
- * @param arg Output items.
- * @return The number of converted output items or EOF on failure.
- */
-int vfscanf(
-    FILE *restrict stream, const char *restrict format, va_list arg)
-{
-	_input_provider provider = {
-		{ 0 }, 0, 0, NULL, 0, NULL, _PROV_CONSTRUCTED,
-		_capture_stream, _pop_stream, _undo_stream,
-		_borrow_cursor_universal, _return_cursor_stream, _release_stream
-	};
-	provider.source.stream = stream;
-	return _internal_scanf(&provider, format, arg);
-}
-
-/**
- * Convert formatted input from the string.
- *
- * @param s Input string.
- * @param format Format description.
- * @param arg Output items.
- * @return The number of converted output items or EOF on failure.
- */
-int vsscanf(
-    const char *restrict s, const char *restrict format, va_list arg)
-{
-	_input_provider provider = {
-		{ 0 }, 0, 0, NULL, 0, NULL, _PROV_CONSTRUCTED,
-		_capture_string, _pop_string, _undo_string,
-		_borrow_cursor_universal, _return_cursor_string, _release_string
-	};
-	provider.source.string = s;
-	return _internal_scanf(&provider, format, arg);
-}
-
-/** @}
- */
Index: uspace/lib/posix/src/stdlib.c
===================================================================
--- uspace/lib/posix/src/stdlib.c	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/lib/posix/src/stdlib.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -38,4 +38,5 @@
 
 #include <errno.h>
+#include <tmpfile.h>
 
 #include "posix/fcntl.h"
@@ -52,138 +53,4 @@
 /**
  *
- * @param array
- * @param count
- * @param size
- * @param compare
- */
-int atexit(void (*func)(void))
-{
-	// TODO: low priority, just a compile-time dependency of binutils
-	not_implemented();
-	return 0;
-}
-
-/**
- * Integer absolute value.
- *
- * @param i Input value.
- * @return Absolute value of the parameter.
- */
-int abs(int i)
-{
-	return i < 0 ? -i : i;
-}
-
-/**
- * Long integer absolute value.
- *
- * @param i Input value.
- * @return Absolute value of the parameter.
- */
-long labs(long i)
-{
-	return i < 0 ? -i : i;
-}
-
-/**
- * Long long integer absolute value.
- *
- * @param i Input value.
- * @return Absolute value of the parameter.
- */
-long long llabs(long long i)
-{
-	return i < 0 ? -i : i;
-}
-
-/**
- * Compute the quotient and remainder of an integer division.
- *
- * @param numer Numerator.
- * @param denom Denominator.
- * @return Quotient and remainder packed into structure.
- */
-div_t div(int numer, int denom)
-{
-	return (div_t) { .quot = numer / denom, .rem = numer % denom };
-}
-
-/**
- * Compute the quotient and remainder of a long integer division.
- *
- * @param numer Numerator.
- * @param denom Denominator.
- * @return Quotient and remainder packed into structure.
- */
-ldiv_t ldiv(long numer, long denom)
-{
-	return (ldiv_t) { .quot = numer / denom, .rem = numer % denom };
-}
-
-/**
- * Compute the quotient and remainder of a long long integer division.
- *
- * @param numer Numerator.
- * @param denom Denominator.
- * @return Quotient and remainder packed into structure.
- */
-lldiv_t lldiv(long long numer, long long denom)
-{
-	return (lldiv_t) { .quot = numer / denom, .rem = numer % denom };
-}
-
-/**
- * Binary search in a sorted array.
- *
- * @param key Object to search for.
- * @param base Pointer to the first element of the array.
- * @param nmemb Number of elements in the array.
- * @param size Size of each array element.
- * @param compar Comparison function.
- * @return Pointer to a matching element, or NULL if none can be found.
- */
-void *bsearch(const void *key, const void *base,
-    size_t nmemb, size_t size, int (*compar)(const void *, const void *))
-{
-	while (nmemb > 0) {
-		const void *middle = base + (nmemb / 2) * size;
-		int cmp = compar(key, middle);
-		if (cmp == 0) {
-			return (void *) middle;
-		}
-		if (middle == base) {
-			/*
-			 * There is just one member left to check and it
-			 * didn't match the key. Avoid infinite loop.
-			 */
-			break;
-		}
-		if (cmp < 0) {
-			nmemb = nmemb / 2;
-		} else if (cmp > 0) {
-			nmemb = nmemb - (nmemb / 2);
-			base = middle;
-		}
-	}
-
-	return NULL;
-}
-
-/**
- * Retrieve a value of the given environment variable.
- *
- * Since HelenOS doesn't support env variables at the moment,
- * this function always returns NULL.
- *
- * @param name Name of the variable.
- * @return Value of the variable or NULL if such variable does not exist.
- */
-char *getenv(const char *name)
-{
-	return NULL;
-}
-
-/**
- *
  * @param name
  * @param resolved
@@ -193,19 +60,4 @@
 {
 	// TODO: low priority, just a compile-time dependency of binutils
-	not_implemented();
-	return 0;
-}
-
-/**
- * Issue a command.
- *
- * @param string String to be passed to a command interpreter or NULL.
- * @return Termination status of the command if the command is not NULL,
- *     otherwise indicate whether there is a command interpreter (non-zero)
- *     or not (zero).
- */
-int system(const char *string)
-{
-	// TODO: does nothing at the moment
 	not_implemented();
 	return 0;
@@ -312,23 +164,26 @@
 int mkstemp(char *tmpl)
 {
-	int fd = -1;
-
-	char *tptr = tmpl + strlen(tmpl) - 6;
-
-	while (fd < 0) {
-		if (*mktemp(tmpl) == '\0') {
-			/* Errno set by mktemp(). */
-			return -1;
-		}
-
-		fd = open(tmpl, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
-
-		if (fd == -1) {
-			/* Restore template to it's original state. */
-			snprintf(tptr, 7, "XXXXXX");
-		}
-	}
-
-	return fd;
+	int tmpl_len;
+	int file;
+
+	tmpl_len = strlen(tmpl);
+	if (tmpl_len < 6) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	char *tptr = tmpl + tmpl_len - 6;
+	if (strcmp(tptr, "XXXXXX") != 0) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	file = __tmpfile_templ(tmpl, true);
+	if (file < 0) {
+		errno = EIO; // XXX could be more specific
+		return -1;
+	}
+
+	return file;
 }
 
@@ -343,5 +198,8 @@
 char *mktemp(char *tmpl)
 {
-	int tmpl_len = strlen(tmpl);
+	int tmpl_len;
+	int rc;
+
+	tmpl_len = strlen(tmpl);
 	if (tmpl_len < 6) {
 		errno = EINVAL;
@@ -357,26 +215,7 @@
 	}
 
-	static int seq = 0;
-
-	for (; seq < 1000000; ++seq) {
-		snprintf(tptr, 7, "%06d", seq);
-
-		int orig_errno = errno;
-		errno = 0;
-		/* Check if the file exists. */
-		if (access(tmpl, F_OK) == -1) {
-			if (errno == ENOENT) {
-				errno = orig_errno;
-				break;
-			} else {
-				/* errno set by access() */
-				*tmpl = '\0';
-				return tmpl;
-			}
-		}
-	}
-
-	if (seq == 10000000) {
-		errno = EEXIST;
+	rc = __tmpfile_templ(tmpl, false);
+	if (rc != 0) {
+		errno = EIO; // XXX could be more specific
 		*tmpl = '\0';
 		return tmpl;
Index: uspace/lib/posix/src/stdlib/strtold.c
===================================================================
--- uspace/lib/posix/src/stdlib/strtold.c	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/lib/posix/src/stdlib/strtold.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -52,8 +52,4 @@
 #ifndef HUGE_VALL
 #define HUGE_VALL (+1.0l / +0.0l)
-#endif
-
-#ifndef abs
-#define abs(x) (((x) < 0) ? -(x) : (x))
 #endif
 
Index: uspace/lib/posix/src/string.c
===================================================================
--- uspace/lib/posix/src/string.c	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/lib/posix/src/string.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -288,27 +288,4 @@
 
 	return 0;
-}
-
-/**
- * Find byte in memory.
- *
- * @param mem Memory area in which to look for the byte.
- * @param c Byte to look for.
- * @param n Maximum number of bytes to be inspected.
- * @return Pointer to the specified byte on success,
- *     NULL pointer otherwise.
- */
-void *memchr(const void *mem, int c, size_t n)
-{
-	assert(mem != NULL);
-
-	const unsigned char *s = mem;
-
-	for (size_t i = 0; i < n; ++i) {
-		if (s[i] == (unsigned char) c) {
-			return (void *) &s[i];
-		}
-	}
-	return NULL;
 }
 
Index: uspace/lib/posix/src/unistd.c
===================================================================
--- uspace/lib/posix/src/unistd.c	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/lib/posix/src/unistd.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -39,5 +39,7 @@
 #include <errno.h>
 
+#include "posix/dirent.h"
 #include "posix/string.h"
+#include "posix/sys/types.h"
 #include "posix/fcntl.h"
 
@@ -51,5 +53,5 @@
 
 // FIXME: replace with a hash table
-aoff64_t posix_pos[MAX_OPEN_FILES];
+aoff64_t posix_pos[VFS_MAX_OPEN_FILES];
 
 /* Array of environment variable strings (NAME=VALUE). */
@@ -368,8 +370,14 @@
 		 */
 		int fd = open(path, O_RDONLY);
-		if (fd < 0)
-			return -1;
-		close(fd);
-		return 0;
+		if (fd >= 0) {
+			close(fd);
+			return 0;
+		}
+		DIR *dir = opendir(path);
+		if (dir != NULL) {
+			closedir(dir);
+			return 0;
+		}
+		return -1;
 	} else {
 		/* Invalid amode argument. */
Index: uspace/lib/posix/test/main.c
===================================================================
--- uspace/lib/posix/test/main.c	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/lib/posix/test/main.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2014 Vojtech Horky
+ * Copyright (c) 2018 Jiri Svoboda
  * All rights reserved.
  *
@@ -31,5 +31,7 @@
 PCUT_INIT;
 
-PCUT_IMPORT(scanf);
+PCUT_IMPORT(stdio);
+PCUT_IMPORT(stdlib);
+PCUT_IMPORT(unistd);
 
 PCUT_MAIN();
Index: uspace/lib/posix/test/scanf.c
===================================================================
--- uspace/lib/posix/test/scanf.c	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ 	(revision )
@@ -1,185 +1,0 @@
-/*
- * Copyright (c) 2014 Vojtech Horky
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * - Redistributions of source code must retain the above copyright
- *   notice, this list of conditions and the following disclaimer.
- * - Redistributions in binary form must reproduce the above copyright
- *   notice, this list of conditions and the following disclaimer in the
- *   documentation and/or other materials provided with the distribution.
- * - The name of the author may not be used to endorse or promote products
- *   derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-
-#include <errno.h>
-
-#include "posix/stdio.h"
-
-#include <pcut/pcut.h>
-
-#define EPSILON 0.000001
-
-PCUT_INIT;
-
-PCUT_TEST_SUITE(scanf);
-
-
-#ifndef UARCH_sparc64
-
-/*
- * We need some floating point functions for scanf() imlementation
- * that are not yet available for SPARC-64.
- */
-
-PCUT_TEST(int_decimal)
-{
-	int number;
-	int rc = sscanf("4242", "%d", &number);
-	PCUT_ASSERT_INT_EQUALS(1, rc);
-	PCUT_ASSERT_INT_EQUALS(4242, number);
-}
-
-PCUT_TEST(int_negative_decimal)
-{
-	int number;
-	int rc = sscanf("-53", "%d", &number);
-	PCUT_ASSERT_INT_EQUALS(1, rc);
-	PCUT_ASSERT_INT_EQUALS(-53, number);
-}
-
-/*
- * The following tests were copied from stdio/scanf.c where they were
- * commented-out. We ought to convert them to more independent tests
- * eventually.
- */
-
-PCUT_TEST(int_misc)
-{
-	unsigned char uhh;
-	signed char shh;
-	unsigned short uh;
-	short sh;
-	unsigned udef;
-	int sdef;
-	unsigned long ul;
-	long sl;
-	unsigned long long ull;
-	long long sll;
-	void *p;
-
-	int rc = sscanf(
-	    "\n j tt % \t -121314 98765 aqw 0765 0x77 0xABCDEF88 -99 884",
-	    " j tt %%%3hhd%1hhu%3hd %3hu%u aqw%n %lo%llx %p %li %lld",
-	    &shh, &uhh, &sh, &uh, &udef, &sdef, &ul, &ull, &p, &sl, &sll);
-
-	PCUT_ASSERT_INT_EQUALS(10, rc);
-
-	PCUT_ASSERT_INT_EQUALS(-12, shh);
-	PCUT_ASSERT_INT_EQUALS(1, uhh);
-	PCUT_ASSERT_INT_EQUALS(314, sh);
-	PCUT_ASSERT_INT_EQUALS(987, uh);
-	PCUT_ASSERT_INT_EQUALS(65, udef);
-	PCUT_ASSERT_INT_EQUALS(28, sdef);
-	PCUT_ASSERT_INT_EQUALS(0765, ul);
-	PCUT_ASSERT_INT_EQUALS(0x77, ull);
-	PCUT_ASSERT_INT_EQUALS(0xABCDEF88, (long long) (uintptr_t) p);
-	PCUT_ASSERT_INT_EQUALS(-99, sl);
-	PCUT_ASSERT_INT_EQUALS(884, sll);
-}
-
-PCUT_TEST(double_misc)
-{
-	float f;
-	double d;
-	long double ld;
-
-	int rc = sscanf(
-	    "\n \t\t1.0 -0x555.AP10 1234.5678e12",
-	    "%f %lf %Lf",
-	    &f, &d, &ld);
-
-	PCUT_ASSERT_INT_EQUALS(3, rc);
-
-	PCUT_ASSERT_DOUBLE_EQUALS(1.0, f, EPSILON);
-	PCUT_ASSERT_DOUBLE_EQUALS(-0x555.AP10, d, EPSILON);
-	PCUT_ASSERT_DOUBLE_EQUALS(1234.5678e12, ld, EPSILON);
-}
-
-PCUT_TEST(str_misc)
-{
-	char str[20];
-	char *pstr;
-
-	int rc = sscanf(
-	    "\n\n\thello world    \n",
-	    "%5s %ms",
-	    str, &pstr);
-
-	PCUT_ASSERT_INT_EQUALS(2, rc);
-
-	PCUT_ASSERT_STR_EQUALS("hello", str);
-	PCUT_ASSERT_STR_EQUALS("world", pstr);
-
-	free(pstr);
-}
-
-PCUT_TEST(str_matchers)
-{
-	char scanset[20];
-	char *pscanset;
-
-	int rc = sscanf(
-	    "\n\n\th-e-l-l-o world-]    \n",
-	    " %9[-eh-o] %m[^]-]",
-	    scanset, &pscanset);
-
-	PCUT_ASSERT_INT_EQUALS(2, rc);
-
-	PCUT_ASSERT_STR_EQUALS("h-e-l-l-o", scanset);
-	PCUT_ASSERT_STR_EQUALS("world", pscanset);
-
-	free(pscanset);
-}
-
-PCUT_TEST(char_misc)
-{
-	char seq[20];
-	char *pseq;
-
-	int rc = sscanf(
-	    "\n\n\thello world    \n",
-	    " %5c %mc",
-	    seq, &pseq);
-
-	PCUT_ASSERT_INT_EQUALS(2, rc);
-
-	/* Manually terminate the strings. */
-	seq[5] = 0;
-	pseq[1] = 0;
-
-	PCUT_ASSERT_STR_EQUALS("hello", seq);
-	PCUT_ASSERT_STR_EQUALS("w", pseq);
-
-	free(pseq);
-}
-
-#endif
-
-PCUT_EXPORT(scanf);
Index: uspace/lib/posix/test/stdio.c
===================================================================
--- uspace/lib/posix/test/stdio.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
+++ uspace/lib/posix/test/stdio.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2018 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <pcut/pcut.h>
+#include <str.h>
+#include "posix/stdio.h"
+
+PCUT_INIT;
+
+PCUT_TEST_SUITE(stdio);
+
+/** tempnam function with directory argument not having trailing slash */
+PCUT_TEST(tempnam_no_slash)
+{
+	char *p;
+	FILE *f;
+
+	p = tempnam("/tmp", "tmp.");
+	PCUT_ASSERT_NOT_NULL(p);
+
+	PCUT_ASSERT_TRUE(str_lcmp(p, "/tmp/tmp.",
+	    str_length("/tmp/tmp.")) == 0);
+
+	f = fopen(p, "w+");
+	PCUT_ASSERT_NOT_NULL(f);
+
+	(void) remove(p);
+	fclose(f);
+}
+
+/** tempnam function with directory argument having trailing slash */
+PCUT_TEST(tempnam_with_slash)
+{
+	char *p;
+	FILE *f;
+
+	p = tempnam("/tmp/", "tmp.");
+	PCUT_ASSERT_NOT_NULL(p);
+
+	PCUT_ASSERT_TRUE(str_lcmp(p, "/tmp/tmp.",
+	    str_length("/tmp/tmp.")) == 0);
+
+	f = fopen(p, "w+");
+	PCUT_ASSERT_NOT_NULL(f);
+
+	(void) remove(p);
+	fclose(f);
+}
+
+/** tempnam function with NULL directory argument */
+PCUT_TEST(tempnam_no_dir)
+{
+	char *p;
+	FILE *f;
+
+	p = tempnam(NULL, "tmp.");
+	PCUT_ASSERT_NOT_NULL(p);
+
+	PCUT_ASSERT_TRUE(str_lcmp(p, P_tmpdir "/tmp.",
+	    str_length(P_tmpdir "/tmp.")) == 0);
+
+	f = fopen(p, "w+");
+	PCUT_ASSERT_NOT_NULL(f);
+
+	(void) remove(p);
+	fclose(f);
+}
+
+PCUT_EXPORT(stdio);
Index: uspace/lib/posix/test/stdlib.c
===================================================================
--- uspace/lib/posix/test/stdlib.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
+++ uspace/lib/posix/test/stdlib.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2018 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <pcut/pcut.h>
+#include <str.h>
+#include "posix/fcntl.h"
+#include "posix/stdio.h"
+#include "posix/stdlib.h"
+#include "posix/unistd.h"
+
+PCUT_INIT;
+
+PCUT_TEST_SUITE(stdlib);
+
+#define MKSTEMP_TEMPL "/tmp/tmp.XXXXXX"
+#define MKTEMP_BAD_TEMPL "/tmp/tmp.XXXXX"
+#define MKTEMP_SHORT_TEMPL "XXXXX"
+
+/** mktemp function */
+PCUT_TEST(mktemp)
+{
+	int file;
+	char buf[sizeof(MKSTEMP_TEMPL)];
+	char *p;
+
+	str_cpy(buf, sizeof(buf), MKSTEMP_TEMPL);
+
+	p = mktemp(buf);
+	PCUT_ASSERT_TRUE(p == buf);
+	PCUT_ASSERT_TRUE(str_lcmp(p, MKSTEMP_TEMPL,
+	    str_length(MKSTEMP_TEMPL) - 6) == 0);
+
+	file = open(p, O_CREAT | O_EXCL | O_RDWR, 0600);
+	PCUT_ASSERT_TRUE(file >= 0);
+	close(file);
+
+	(void) unlink(p);
+}
+
+/** mktemp function called twice should return different names */
+PCUT_TEST(mktemp_twice)
+{
+	char buf1[sizeof(MKSTEMP_TEMPL)];
+	char buf2[sizeof(MKSTEMP_TEMPL)];
+	char *p;
+
+	str_cpy(buf1, sizeof(buf1), MKSTEMP_TEMPL);
+	str_cpy(buf2, sizeof(buf2), MKSTEMP_TEMPL);
+
+	p = mktemp(buf1);
+	PCUT_ASSERT_TRUE(p == buf1);
+	PCUT_ASSERT_TRUE(str_lcmp(p, MKSTEMP_TEMPL,
+	    str_length(MKSTEMP_TEMPL) - 6) == 0);
+
+	p = mktemp(buf2);
+	PCUT_ASSERT_TRUE(p == buf2);
+	PCUT_ASSERT_TRUE(str_lcmp(p, MKSTEMP_TEMPL,
+	    str_length(MKSTEMP_TEMPL) - 6) == 0);
+
+	PCUT_ASSERT_TRUE(str_cmp(buf1, buf2) != 0);
+}
+
+/** mktemp function with malformed template */
+PCUT_TEST(mktemp_bad_templ)
+{
+	char buf[sizeof(MKTEMP_BAD_TEMPL)];
+	char *p;
+
+	str_cpy(buf, sizeof(buf), MKTEMP_BAD_TEMPL);
+
+	p = mktemp(buf);
+	PCUT_ASSERT_TRUE(p == buf);
+	PCUT_ASSERT_TRUE(p[0] == '\0');
+}
+
+/** mktemp function with too short template */
+PCUT_TEST(mktemp_short_templ)
+{
+	char buf[sizeof(MKTEMP_SHORT_TEMPL)];
+	char *p;
+
+	str_cpy(buf, sizeof(buf), MKTEMP_SHORT_TEMPL);
+
+	p = mktemp(buf);
+	PCUT_ASSERT_TRUE(p == buf);
+	PCUT_ASSERT_TRUE(p[0] == '\0');
+}
+
+/** mkstemp function */
+PCUT_TEST(mkstemp)
+{
+	int file;
+	char buf[sizeof(MKSTEMP_TEMPL)];
+	ssize_t rc;
+	char c;
+
+	str_cpy(buf, sizeof(buf), MKSTEMP_TEMPL);
+
+	file = mkstemp(buf);
+	PCUT_ASSERT_TRUE(file >= 0);
+
+	(void) unlink(buf);
+
+	c = 'x';
+	rc = write(file, &c, sizeof(c));
+	PCUT_ASSERT_TRUE(rc == sizeof(c));
+
+	(void) lseek(file, 0, SEEK_SET);
+	c = '\0';
+	rc = read(file, &c, sizeof(c));
+	PCUT_ASSERT_TRUE(rc == sizeof(c));
+	PCUT_ASSERT_INT_EQUALS('x', c);
+
+	close(file);
+}
+
+PCUT_EXPORT(stdlib);
Index: uspace/lib/posix/test/unistd.c
===================================================================
--- uspace/lib/posix/test/unistd.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
+++ uspace/lib/posix/test/unistd.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2018 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <pcut/pcut.h>
+#include "posix/fcntl.h"
+#include "posix/stdlib.h"
+#include "posix/unistd.h"
+
+PCUT_INIT;
+
+PCUT_TEST_SUITE(unistd);
+
+#define MKSTEMP_TEMPL "/tmp/tmp.XXXXXX"
+#define MKTEMP_BAD_TEMPL "/tmp/tmp.XXXXX"
+#define MKTEMP_SHORT_TEMPL "XXXXX"
+
+/** access function with nonexisting entry */
+PCUT_TEST(access_nonexist)
+{
+	char name[L_tmpnam];
+	char *p;
+	int rc;
+
+	p = tmpnam(name);
+	PCUT_ASSERT_NOT_NULL(p);
+
+	rc = access(name, F_OK);
+	PCUT_ASSERT_INT_EQUALS(-1, rc);
+}
+
+/** access function with file */
+PCUT_TEST(access_file)
+{
+	char name[L_tmpnam];
+	char *p;
+	int file;
+	int rc;
+
+	p = tmpnam(name);
+	PCUT_ASSERT_NOT_NULL(p);
+
+	file = open(name, O_CREAT | O_EXCL | O_RDWR, 0600);
+	PCUT_ASSERT_TRUE(file >= 0);
+
+	rc = access(name, F_OK);
+	PCUT_ASSERT_INT_EQUALS(0, rc);
+
+	(void) unlink(name);
+	close(file);
+}
+
+PCUT_EXPORT(unistd);
Index: uspace/lib/untar/Makefile
===================================================================
--- uspace/lib/untar/Makefile	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
+++ uspace/lib/untar/Makefile	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -0,0 +1,36 @@
+#
+# Copyright (c) 2018 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.
+#
+
+USPACE_PREFIX = ../..
+LIBRARY = libuntar
+
+SOURCES = \
+	tar.c \
+	untar.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/lib/untar/private/tar.h
===================================================================
--- uspace/lib/untar/private/tar.h	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
+++ uspace/lib/untar/private/tar.h	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2013 Vojtech Horky
+ * 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 libuntar
+ * @{
+ */
+/** @file
+ */
+
+#ifndef TAR_H_
+#define TAR_H_
+
+#define TAR_BLOCK_SIZE 512
+
+typedef struct tar_header_raw {
+	char filename[100];
+	char permissions[8];
+	char owner[8];
+	char group[8];
+	char size[12];
+	char modification_time[12];
+	char checksum[8];
+	char type;
+	char name[100];
+	char ustar_magic[6];
+	char ustar_version[2];
+	char ustar_owner_name[32];
+	char ustar_group_name[32];
+	char ustar_device_major[8];
+	char ustar_device_minor[8];
+	char ustar_prefix[155];
+	char ignored[12];
+} tar_header_raw_t;
+
+typedef enum tar_type {
+	TAR_TYPE_UNKNOWN,
+	TAR_TYPE_NORMAL,
+	TAR_TYPE_DIRECTORY
+} tar_type_t;
+
+typedef struct tar_header {
+	char filename[100];
+	size_t size;
+	tar_type_t type;
+} tar_header_t;
+
+extern errno_t tar_header_parse(tar_header_t *, const tar_header_raw_t *);
+extern tar_type_t tar_type_parse(const char);
+extern const char *tar_type_str(tar_type_t);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/untar/tar.c
===================================================================
--- uspace/lib/untar/tar.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
+++ uspace/lib/untar/tar.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2013 Vojtech Horky
+ * 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 libuntar
+ * @{
+ */
+/** @file
+ */
+
+#include <str.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <errno.h>
+#include <assert.h>
+#include "private/tar.h"
+
+tar_type_t tar_type_parse(const char type)
+{
+	switch (type) {
+	case '0':
+	case 0:
+		return TAR_TYPE_NORMAL;
+	case '5':
+		return TAR_TYPE_DIRECTORY;
+	default:
+		return TAR_TYPE_UNKNOWN;
+	}
+}
+
+const char *tar_type_str(tar_type_t type)
+{
+	switch (type) {
+	case TAR_TYPE_UNKNOWN:
+		return "unknown";
+	case TAR_TYPE_NORMAL:
+		return "normal";
+	case TAR_TYPE_DIRECTORY:
+		return "directory";
+	default:
+		assert(false && "unexpected tar_type_t enum value");
+		return "?";
+	}
+}
+
+errno_t tar_header_parse(tar_header_t *parsed, const tar_header_raw_t *raw)
+{
+	if (str_length(raw->filename) == 0) {
+		return EEMPTY;
+	}
+
+	size_t size;
+	errno_t rc = str_size_t(raw->size, NULL, 8, true, &size);
+	if (rc != EOK) {
+		return rc;
+	}
+	parsed->size = size;
+
+	str_cpy(parsed->filename, 100, raw->filename);
+
+	parsed->type = tar_type_parse(raw->type);
+
+	return EOK;
+}
+
+/** @}
+ */
Index: uspace/lib/untar/untar.c
===================================================================
--- uspace/lib/untar/untar.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
+++ uspace/lib/untar/untar.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 2013 Vojtech Horky
+ * 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 libuntar
+ * @{
+ */
+/** @file
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <str_error.h>
+#include <vfs/vfs.h>
+#include "private/tar.h"
+#include "untar.h"
+
+static size_t get_block_count(size_t bytes)
+{
+	return (bytes + TAR_BLOCK_SIZE - 1) / TAR_BLOCK_SIZE;
+}
+
+static int tar_open(tar_file_t *tar)
+{
+	return tar->open(tar);
+}
+
+static void tar_close(tar_file_t *tar)
+{
+	tar->close(tar);
+}
+
+static size_t tar_read(tar_file_t *tar, void *data, size_t size)
+{
+	return tar->read(tar, data, size);
+}
+
+static void tar_report(tar_file_t *tar, const char *fmt, ...)
+{
+	va_list args;
+	va_start(args, fmt);
+
+	tar->vreport(tar, fmt, args);
+
+	va_end(args);
+}
+
+static errno_t tar_skip_blocks(tar_file_t *tar, size_t valid_data_size)
+{
+	size_t blocks_to_read = get_block_count(valid_data_size);
+
+	while (blocks_to_read > 0) {
+		uint8_t block[TAR_BLOCK_SIZE];
+		size_t actually_read = tar_read(tar, block, TAR_BLOCK_SIZE);
+		if (actually_read != TAR_BLOCK_SIZE)
+			return errno;
+
+		blocks_to_read--;
+	}
+
+	return EOK;
+}
+
+static errno_t tar_handle_normal_file(tar_file_t *tar,
+    const tar_header_t *header)
+{
+	// FIXME: create the directory first
+
+	FILE *file = fopen(header->filename, "wb");
+	if (file == NULL) {
+		tar_report(tar, "Failed to create %s: %s.\n", header->filename,
+		    str_error(errno));
+		return errno;
+	}
+
+	errno_t rc = EOK;
+	size_t bytes_remaining = header->size;
+	size_t blocks = get_block_count(bytes_remaining);
+
+	while (blocks > 0) {
+		uint8_t block[TAR_BLOCK_SIZE];
+		size_t actually_read = tar_read(tar, block, TAR_BLOCK_SIZE);
+		if (actually_read != TAR_BLOCK_SIZE) {
+			rc = errno;
+			tar_report(tar, "Failed to read block for %s: %s.\n",
+			    header->filename, str_error(rc));
+			break;
+		}
+
+		size_t to_write = TAR_BLOCK_SIZE;
+		if (bytes_remaining < TAR_BLOCK_SIZE)
+			to_write = bytes_remaining;
+
+		size_t actually_written = fwrite(block, 1, to_write, file);
+		if (actually_written != to_write) {
+			rc = errno;
+			tar_report(tar, "Failed to write to %s: %s.\n",
+			    header->filename, str_error(rc));
+			break;
+		}
+
+		blocks--;
+		bytes_remaining -= TAR_BLOCK_SIZE;
+	}
+
+	fclose(file);
+	return rc;
+}
+
+static errno_t tar_handle_directory(tar_file_t *tar, const tar_header_t *header)
+{
+	errno_t rc = vfs_link_path(header->filename, KIND_DIRECTORY, NULL);
+	if (rc != EOK) {
+		if (rc != EEXIST) {
+			tar_report(tar, "Failed to create directory %s: %s.\n",
+			    header->filename, str_error(rc));
+			return rc;
+		}
+	}
+
+	return tar_skip_blocks(tar, header->size);
+}
+
+int untar(tar_file_t *tar)
+{
+	int rc = tar_open(tar);
+	if (rc != EOK) {
+		tar_report(tar, "Failed to open: %s.\n", str_error(rc));
+		return rc;
+	}
+	
+	while (true) {
+		tar_header_raw_t header_raw;
+		size_t header_ok = tar_read(tar, &header_raw, sizeof(header_raw));
+		if (header_ok != sizeof(header_raw))
+			break;
+
+		tar_header_t header;
+		errno_t rc = tar_header_parse(&header, &header_raw);
+		if (rc == EEMPTY)
+			continue;
+
+		if (rc != EOK) {
+			tar_report(tar, "Failed parsing TAR header: %s.\n", str_error(rc));
+			break;
+		}
+
+		switch (header.type) {
+		case TAR_TYPE_DIRECTORY:
+			rc = tar_handle_directory(tar, &header);
+			break;
+		case TAR_TYPE_NORMAL:
+			rc = tar_handle_normal_file(tar, &header);
+			break;
+		default:
+			rc = tar_skip_blocks(tar, header.size);
+			break;
+		}
+
+		if (rc != EOK)
+			break;
+	}
+
+	tar_close(tar);
+	return EOK;
+}
+
+/** @}
+ */
Index: uspace/lib/untar/untar.h
===================================================================
--- uspace/lib/untar/untar.h	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
+++ uspace/lib/untar/untar.h	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2018 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 libuntar
+ * @{
+ */
+/** @file
+ */
+
+#ifndef UNTAR_H_
+#define UNTAR_H_
+
+#include <stdlib.h>
+#include <stdarg.h>
+
+typedef struct tar_file {
+	void *data;
+
+	int (*open)(struct tar_file *);
+	void (*close)(struct tar_file *);
+
+	size_t (*read)(struct tar_file *, void *, size_t);
+	void (*vreport)(struct tar_file *, const char *, va_list);
+} tar_file_t;
+
+extern int untar(tar_file_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/usbhost/src/ddf_helpers.c
===================================================================
--- uspace/lib/usbhost/src/ddf_helpers.c	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/lib/usbhost/src/ddf_helpers.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -373,10 +373,10 @@
 		/* First, with release number. */
 		ADD_MATCHID_OR_RETURN(l, 100,
-		    "usb&vendor=%#04x&product=%#04x&release=%x.%x",
+		    "usb&vendor=%#06x&product=%#06x&release=%x.%x",
 		    d->vendor_id, d->product_id, (d->device_version >> 8),
 		    (d->device_version & 0xff));
 
 		/* Next, without release number. */
-		ADD_MATCHID_OR_RETURN(l, 90, "usb&vendor=%#04x&product=%#04x",
+		ADD_MATCHID_OR_RETURN(l, 90, "usb&vendor=%#06x&product=%#06x",
 		    d->vendor_id, d->product_id);
 	}
Index: uspace/srv/bd/vbd/disk.c
===================================================================
--- uspace/srv/bd/vbd/disk.c	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/srv/bd/vbd/disk.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -181,5 +181,5 @@
 
 	list_foreach(vbds_disks, ldisks, vbds_disk_t, disk)
-	    disk->present = false;
+		disk->present = false;
 
 	for (i = 0; i < count; i++) {
Index: uspace/srv/fs/fat/fat_fat.c
===================================================================
--- uspace/srv/fs/fat/fat_fat.c	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/srv/fs/fat/fat_fat.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -967,5 +967,5 @@
 		 */
 		if (!FAT_IS_FAT12(bs) &&
-		    ((e0 >> 8) != (FAT_MASK(bs) >> 8) || e1 != FAT_MASK(bs)))
+		    ((e0 >> 8) != ((fat_cluster_t) FAT_MASK(bs) >> 8) || e1 != FAT_MASK(bs)))
 			return ENOTSUP;
 	}
Index: uspace/srv/fs/tmpfs/Makefile
===================================================================
--- uspace/srv/fs/tmpfs/Makefile	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/srv/fs/tmpfs/Makefile	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -35,6 +35,5 @@
 SOURCES = \
 	tmpfs.c \
-	tmpfs_ops.c \
-	tmpfs_dump.c
+	tmpfs_ops.c
 
 include $(USPACE_PREFIX)/Makefile.common
Index: uspace/srv/fs/tmpfs/tmpfs.h
===================================================================
--- uspace/srv/fs/tmpfs/tmpfs.h	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/srv/fs/tmpfs/tmpfs.h	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -74,5 +74,4 @@
 
 extern bool tmpfs_init(void);
-extern bool tmpfs_restore(service_id_t);
 
 #endif
Index: uspace/srv/fs/tmpfs/tmpfs_dump.c
===================================================================
--- uspace/srv/fs/tmpfs/tmpfs_dump.c	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ 	(revision )
@@ -1,205 +1,0 @@
-/*
- * Copyright (c) 2008 Jakub Jermar
- * Copyright (c) 2008 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 fs
- * @{
- */
-
-/**
- * @file	tmpfs_dump.c
- * @brief	Support for loading TMPFS file system dump.
- */
-
-#include "tmpfs.h"
-#include "../../vfs/vfs.h"
-#include <errno.h>
-#include <stdlib.h>
-#include <str.h>
-#include <stddef.h>
-#include <stdint.h>
-#include <as.h>
-#include <block.h>
-#include <byteorder.h>
-
-#define TMPFS_COMM_SIZE		1024
-
-static uint8_t tmpfs_buf[TMPFS_COMM_SIZE];
-
-struct rdentry {
-	uint8_t type;
-	uint32_t len;
-} __attribute__((packed));
-
-static bool
-tmpfs_restore_recursion(service_id_t dsid, size_t *bufpos, size_t *buflen,
-    aoff64_t *pos, fs_node_t *pfn)
-{
-	struct rdentry entry;
-	libfs_ops_t *ops = &tmpfs_libfs_ops;
-	errno_t rc;
-
-	do {
-		char *fname;
-		fs_node_t *fn;
-		tmpfs_node_t *nodep;
-		uint32_t size;
-
-		if (block_seqread(dsid, tmpfs_buf, bufpos, buflen, pos, &entry,
-		    sizeof(entry)) != EOK)
-			return false;
-
-		entry.len = uint32_t_le2host(entry.len);
-
-		switch (entry.type) {
-		case TMPFS_NONE:
-			break;
-		case TMPFS_FILE:
-			fname = malloc(entry.len + 1);
-			if (fname == NULL)
-				return false;
-
-			rc = ops->create(&fn, dsid, L_FILE);
-			if (rc != EOK || fn == NULL) {
-				free(fname);
-				return false;
-			}
-
-			if (block_seqread(dsid, tmpfs_buf, bufpos, buflen, pos, fname,
-			    entry.len) != EOK) {
-				(void) ops->destroy(fn);
-				free(fname);
-				return false;
-			}
-			fname[entry.len] = 0;
-
-			rc = ops->link(pfn, fn, fname);
-			if (rc != EOK) {
-				(void) ops->destroy(fn);
-				free(fname);
-				return false;
-			}
-			free(fname);
-
-			if (block_seqread(dsid, tmpfs_buf, bufpos, buflen, pos, &size,
-			    sizeof(size)) != EOK)
-				return false;
-
-			size = uint32_t_le2host(size);
-
-			nodep = TMPFS_NODE(fn);
-			nodep->data = malloc(size);
-			if (nodep->data == NULL)
-				return false;
-
-			nodep->size = size;
-			if (block_seqread(dsid, tmpfs_buf, bufpos, buflen, pos, nodep->data,
-			    size) != EOK)
-				return false;
-
-			break;
-		case TMPFS_DIRECTORY:
-			fname = malloc(entry.len + 1);
-			if (fname == NULL)
-				return false;
-
-			rc = ops->create(&fn, dsid, L_DIRECTORY);
-			if (rc != EOK || fn == NULL) {
-				free(fname);
-				return false;
-			}
-
-			if (block_seqread(dsid, tmpfs_buf, bufpos, buflen, pos, fname,
-			    entry.len) != EOK) {
-				(void) ops->destroy(fn);
-				free(fname);
-				return false;
-			}
-			fname[entry.len] = 0;
-
-			rc = ops->link(pfn, fn, fname);
-			if (rc != EOK) {
-				(void) ops->destroy(fn);
-				free(fname);
-				return false;
-			}
-			free(fname);
-
-			if (!tmpfs_restore_recursion(dsid, bufpos, buflen, pos,
-			    fn))
-				return false;
-
-			break;
-		default:
-			return false;
-		}
-	} while (entry.type != TMPFS_NONE);
-
-	return true;
-}
-
-bool tmpfs_restore(service_id_t dsid)
-{
-	libfs_ops_t *ops = &tmpfs_libfs_ops;
-	fs_node_t *fn;
-	errno_t rc;
-
-	rc = block_init(dsid, TMPFS_COMM_SIZE);
-	if (rc != EOK)
-		return false;
-
-	size_t bufpos = 0;
-	size_t buflen = 0;
-	aoff64_t pos = 0;
-
-	char tag[6];
-	if (block_seqread(dsid, tmpfs_buf, &bufpos, &buflen, &pos, tag, 5) != EOK)
-		goto error;
-
-	tag[5] = 0;
-	if (str_cmp(tag, "TMPFS") != 0)
-		goto error;
-
-	rc = ops->root_get(&fn, dsid);
-	if (rc != EOK)
-		goto error;
-
-	if (!tmpfs_restore_recursion(dsid, &bufpos, &buflen, &pos, fn))
-		goto error;
-
-	block_fini(dsid);
-	return true;
-
-error:
-	block_fini(dsid);
-	return false;
-}
-
-/**
- * @}
- */
Index: uspace/srv/fs/tmpfs/tmpfs_ops.c
===================================================================
--- uspace/srv/fs/tmpfs/tmpfs_ops.c	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/srv/fs/tmpfs/tmpfs_ops.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -447,8 +447,4 @@
 	assert(rc == EOK);
 	tmpfs_node_t *rootp = TMPFS_NODE(rootfn);
-	if (str_cmp(opts, "restore") == 0) {
-		if (!tmpfs_restore(service_id))
-			return ELIMIT;
-	}
 
 	*index = rootp->index;
@@ -601,5 +597,4 @@
 
 	ht_link_t *hlp = hash_table_find(&nodes, &key);
-
 	if (!hlp)
 		return ENOENT;
@@ -613,5 +608,5 @@
 
 	void *newdata = realloc(nodep->data, size);
-	if (!newdata)
+	if (!newdata && size)
 		return ENOMEM;
 
Index: uspace/srv/hid/console/console.c
===================================================================
--- uspace/srv/hid/console/console.c	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/srv/hid/console/console.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -311,5 +311,5 @@
 		break;
 	default:
-		updated = chargrid_putchar(cons->frontbuf, ch, true);
+		updated = chargrid_putwchar(cons->frontbuf, ch, true);
 	}
 
Index: uspace/srv/hid/input/input.c
===================================================================
--- uspace/srv/hid/input/input.c	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/srv/hid/input/input.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -308,5 +308,5 @@
 	/* Mutual exclusion of active clients */
 	list_foreach(clients, link, client_t, client)
-	    client->active = ((active) && (client == active_client));
+		client->active = ((active) && (client == active_client));
 
 	/* Notify clients about the arbitration */
Index: uspace/srv/hid/output/ctl/serial.c
===================================================================
--- uspace/srv/hid/output/ctl/serial.c	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/srv/hid/output/ctl/serial.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -57,5 +57,5 @@
 	vt100_goto(state, col, row);
 	vt100_set_attr(state, field->attrs);
-	vt100_putchar(state, field->ch);
+	vt100_putwchar(state, field->ch);
 }
 
@@ -122,9 +122,9 @@
 };
 
-errno_t serial_init(vt100_putchar_t putchar_fn,
+errno_t serial_init(vt100_putwchar_t putwchar_fn,
     vt100_control_puts_t control_puts_fn, vt100_flush_t flush_fn)
 {
 	vt100_state_t *state =
-	    vt100_state_create(SERIAL_COLS, SERIAL_ROWS, putchar_fn,
+	    vt100_state_create(SERIAL_COLS, SERIAL_ROWS, putwchar_fn,
 	    control_puts_fn, flush_fn);
 	if (state == NULL)
Index: uspace/srv/hid/output/ctl/serial.h
===================================================================
--- uspace/srv/hid/output/ctl/serial.h	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/srv/hid/output/ctl/serial.h	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -36,5 +36,5 @@
 #include "../proto/vt100.h"
 
-extern errno_t serial_init(vt100_putchar_t, vt100_control_puts_t, vt100_flush_t);
+extern errno_t serial_init(vt100_putwchar_t, vt100_control_puts_t, vt100_flush_t);
 
 #endif
Index: uspace/srv/hid/output/port/chardev.c
===================================================================
--- uspace/srv/hid/output/port/chardev.c	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/srv/hid/output/port/chardev.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -77,5 +77,5 @@
 }
 
-static void chardev_putchar(wchar_t ch)
+static void chardev_putwchar(wchar_t ch)
 {
 	if (chardev_bused == chardev_buf_size)
@@ -92,5 +92,5 @@
 	p = str;
 	while (*p != '\0')
-		chardev_putchar(*p++);
+		chardev_putwchar(*p++);
 }
 
@@ -198,5 +198,5 @@
 	}
 
-	serial_init(chardev_putchar, chardev_control_puts, chardev_flush);
+	serial_init(chardev_putwchar, chardev_control_puts, chardev_flush);
 
 	discovery_finished = true;
Index: uspace/srv/hid/output/proto/vt100.c
===================================================================
--- uspace/srv/hid/output/proto/vt100.c	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/srv/hid/output/proto/vt100.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -139,5 +139,5 @@
 
 vt100_state_t *vt100_state_create(sysarg_t cols, sysarg_t rows,
-    vt100_putchar_t putchar_fn, vt100_control_puts_t control_puts_fn,
+    vt100_putwchar_t putwchar_fn, vt100_control_puts_t control_puts_fn,
     vt100_flush_t flush_fn)
 {
@@ -146,5 +146,5 @@
 		return NULL;
 
-	state->putchar = putchar_fn;
+	state->putwchar = putwchar_fn;
 	state->control_puts = control_puts_fn;
 	state->flush = flush_fn;
@@ -219,7 +219,7 @@
 }
 
-void vt100_putchar(vt100_state_t *state, wchar_t ch)
-{
-	state->putchar(ch == 0 ? ' ' : ch);
+void vt100_putwchar(vt100_state_t *state, wchar_t ch)
+{
+	state->putwchar(ch == 0 ? ' ' : ch);
 	state->cur_col++;
 
Index: uspace/srv/hid/output/proto/vt100.h
===================================================================
--- uspace/srv/hid/output/proto/vt100.h	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/srv/hid/output/proto/vt100.h	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -35,5 +35,5 @@
 #include <io/charfield.h>
 
-typedef void (*vt100_putchar_t)(wchar_t ch);
+typedef void (*vt100_putwchar_t)(wchar_t ch);
 typedef void (*vt100_control_puts_t)(const char *str);
 typedef void (*vt100_flush_t)(void);
@@ -47,10 +47,10 @@
 	char_attrs_t cur_attrs;
 
-	vt100_putchar_t putchar;
+	vt100_putwchar_t putwchar;
 	vt100_control_puts_t control_puts;
 	vt100_flush_t flush;
 } vt100_state_t;
 
-extern vt100_state_t *vt100_state_create(sysarg_t, sysarg_t, vt100_putchar_t,
+extern vt100_state_t *vt100_state_create(sysarg_t, sysarg_t, vt100_putwchar_t,
     vt100_control_puts_t, vt100_flush_t);
 extern void vt100_state_destroy(vt100_state_t *);
@@ -63,5 +63,5 @@
 extern void vt100_set_attr(vt100_state_t *, char_attrs_t);
 extern void vt100_cursor_visibility(vt100_state_t *, bool);
-extern void vt100_putchar(vt100_state_t *, wchar_t);
+extern void vt100_putwchar(vt100_state_t *, wchar_t);
 extern void vt100_flush(vt100_state_t *);
 
Index: uspace/srv/vfs/vfs_file.c
===================================================================
--- uspace/srv/vfs/vfs_file.c	(revision e3107e20c715f456c231e946671632911d072f7e)
+++ uspace/srv/vfs/vfs_file.c	(revision fbfe59dafcc4f7e06cbbf09e2d88ed3e434b4a33)
@@ -71,10 +71,10 @@
 	fibril_mutex_lock(&vfs_data->lock);
 	if (!vfs_data->files) {
-		vfs_data->files = malloc(MAX_OPEN_FILES * sizeof(vfs_file_t *));
+		vfs_data->files = malloc(VFS_MAX_OPEN_FILES * sizeof(vfs_file_t *));
 		if (!vfs_data->files) {
 			fibril_mutex_unlock(&vfs_data->lock);
 			return false;
 		}
-		memset(vfs_data->files, 0, MAX_OPEN_FILES * sizeof(vfs_file_t *));
+		memset(vfs_data->files, 0, VFS_MAX_OPEN_FILES * sizeof(vfs_file_t *));
 	}
 	fibril_mutex_unlock(&vfs_data->lock);
@@ -90,5 +90,5 @@
 		return;
 
-	for (i = 0; i < MAX_OPEN_FILES; i++) {
+	for (i = 0; i < VFS_MAX_OPEN_FILES; i++) {
 		if (vfs_data->files[i])
 			(void) _vfs_fd_free(vfs_data, i);
@@ -199,5 +199,5 @@
 	unsigned int i;
 	if (desc)
-		i = MAX_OPEN_FILES - 1;
+		i = VFS_MAX_OPEN_FILES - 1;
 	else
 		i = 0;
@@ -233,5 +233,5 @@
 			i--;
 		} else {
-			if (i == MAX_OPEN_FILES - 1)
+			if (i == VFS_MAX_OPEN_FILES - 1)
 				break;
 
@@ -261,5 +261,5 @@
 static errno_t _vfs_fd_free_locked(vfs_client_data_t *vfs_data, int fd)
 {
-	if ((fd < 0) || (fd >= MAX_OPEN_FILES) || !vfs_data->files[fd]) {
+	if ((fd < 0) || (fd >= VFS_MAX_OPEN_FILES) || !vfs_data->files[fd]) {
 		return EBADF;
 	}
@@ -311,5 +311,5 @@
 
 	fibril_mutex_lock(&VFS_DATA->lock);
-	if ((fd < 0) || (fd >= MAX_OPEN_FILES)) {
+	if ((fd < 0) || (fd >= VFS_MAX_OPEN_FILES)) {
 		fibril_mutex_unlock(&VFS_DATA->lock);
 		return EBADF;
@@ -342,5 +342,5 @@
 
 	fibril_mutex_lock(&vfs_data->lock);
-	if ((fd >= 0) && (fd < MAX_OPEN_FILES)) {
+	if ((fd >= 0) && (fd < VFS_MAX_OPEN_FILES)) {
 		vfs_file_t *file = vfs_data->files[fd];
 		if (file != NULL) {
