Index: uspace/app/bithenge/Makefile
===================================================================
--- uspace/app/bithenge/Makefile	(revision d5070eff49234bdcf4f61f6456094186dfa0e673)
+++ uspace/app/bithenge/Makefile	(revision da0fef6126d5ca1836cfcd2bb32043a899aff458)
@@ -29,12 +29,13 @@
 USPACE_PREFIX = ../..
 LIBS = $(LIBBLOCK_PREFIX)/libblock.a
-EXTRA_CFLAGS = -I$(LIBBLOCK_PREFIX)
+EXTRA_CFLAGS = -I$(LIBBLOCK_PREFIX) -D__HELENOS__ -Ihelenos
 BINARY = bithenge
 
 SOURCES = \
+	helenos/block.c \
 	blob.c \
-	block.c \
 	file.c \
 	print.c \
+	source.c \
 	test.c \
 	transform.c \
Index: uspace/app/bithenge/Makefile.linux
===================================================================
--- uspace/app/bithenge/Makefile.linux	(revision da0fef6126d5ca1836cfcd2bb32043a899aff458)
+++ uspace/app/bithenge/Makefile.linux	(revision da0fef6126d5ca1836cfcd2bb32043a899aff458)
@@ -0,0 +1,49 @@
+#
+# Copyright (c) 2012 Sean Bartell
+# 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.
+#
+
+CFLAGS += -fexec-charset=UTF-8 -finput-charset=UTF-8 -Wall -Wextra -Werror -Wno-clobbered -Wno-unused-parameter -Wmissing-prototypes -std=gnu99 -Werror-implicit-function-declaration -Wwrite-strings -pipe
+CFLAGS += -Ilinux
+
+BINARY = bithenge
+
+SOURCES = \
+	blob.c \
+	file.c \
+	print.c \
+	source.c \
+	test.c \
+	transform.c \
+	tree.c
+
+OBJECTS := $(addsuffix .o,$(basename $(SOURCES)))
+
+$(BINARY): $(OBJECTS)
+	$(CC) -o $@ $^
+
+clean:
+	find . -name '*.o' -follow -exec rm \{\} \;
Index: uspace/app/bithenge/blob.c
===================================================================
--- uspace/app/bithenge/blob.c	(revision d5070eff49234bdcf4f61f6456094186dfa0e673)
+++ uspace/app/bithenge/blob.c	(revision da0fef6126d5ca1836cfcd2bb32043a899aff458)
@@ -36,10 +36,8 @@
 
 #include <assert.h>
-#include <bool.h>
 #include <errno.h>
-#include <macros.h>
-#include <mem.h>
 #include <stdlib.h>
 #include "blob.h"
+#include "os.h"
 #include "tree.h"
 
@@ -215,5 +213,5 @@
 	memory_blob_t *blob = memory_from_blob(base);
 	if (blob->needs_free)
-		free(blob->buffer);
+		free((void *)blob->buffer);
 	free(blob);
 	return EOK;
@@ -272,6 +270,6 @@
  * destroyed.
  * @param len The length of the data.
- * @param needs_free Whether the buffer should be freed with free() when the
- * blob is destroyed.
+ * @param needs_free If true, the buffer will be freed with free() if this
+ * function fails or the blob is destroyed.
  * @return EOK on success or an error code from errno.h. */
 int bithenge_new_blob_from_buffer(bithenge_node_t **out, const void *buffer,
Index: pace/app/bithenge/block.c
===================================================================
--- uspace/app/bithenge/block.c	(revision d5070eff49234bdcf4f61f6456094186dfa0e673)
+++ 	(revision )
@@ -1,146 +1,0 @@
-/*
- * Copyright (c) 2012 Sean Bartell
- * 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 bithenge
- * @{
- */
-/**
- * @file
- * Access block devices as blobs.
- * @todo Provide more information about the block device (block size).
- */
-
-#include <assert.h>
-#include <errno.h>
-#include <libblock.h>
-#include <loc.h>
-#include <macros.h>
-#include <stdlib.h>
-#include "blob.h"
-#include "block.h"
-
-typedef struct {
-	bithenge_blob_t base;
-	service_id_t service_id;
-	aoff64_t size;
-} block_blob_t;
-
-static inline block_blob_t *block_from_blob(bithenge_blob_t *base)
-{
-	return (block_blob_t *)base;
-}
-
-static inline bithenge_blob_t *blob_from_block(block_blob_t *blob)
-{
-	return &blob->base;
-}
-
-static int block_size(bithenge_blob_t *base, aoff64_t *size)
-{
-	block_blob_t *blob = block_from_blob(base);
-	*size = blob->size;
-	return EOK;
-}
-
-static int block_read(bithenge_blob_t *base, aoff64_t offset, char *buffer,
-    aoff64_t *size)
-{
-	block_blob_t *blob = block_from_blob(base);
-	if (offset > blob->size)
-		return ELIMIT;
-	*size = min(*size, blob->size - offset);
-	return block_read_bytes_direct(blob->service_id, offset, *size, buffer);
-}
-
-static int block_destroy(bithenge_blob_t *base)
-{
-	block_blob_t *blob = block_from_blob(base);
-	block_fini(blob->service_id);
-	free(blob);
-	return EOK;
-}
-
-static const bithenge_random_access_blob_ops_t block_ops = {
-	.size = block_size,
-	.read = block_read,
-	.destroy = block_destroy,
-};
-
-/** Create a blob for a block device. The blob must be freed with
- * @a bithenge_node_t::bithenge_node_destroy after it is used.
- * @param[out] out Stores the created blob.
- * @param service_id The service ID of the block device.
- * @return EOK on success or an error code from errno.h. */
-int bithenge_new_block_blob(bithenge_node_t **out, service_id_t service_id)
-{
-	assert(out);
-
-	// Initialize libblock
-	int rc;
-	rc = block_init(EXCHANGE_SERIALIZE, service_id, 2048);
-	if (rc != EOK)
-		return rc;
-
-	// Calculate total device size
-	size_t bsize;
-	aoff64_t nblocks;
-	aoff64_t size;
-	rc = block_get_bsize(service_id, &bsize);
-	if (rc != EOK) {
-		block_fini(service_id);
-		return rc;
-	}
-	rc = block_get_nblocks(service_id, &nblocks);
-	if (rc != EOK) {
-		block_fini(service_id);
-		return rc;
-	}
-	size = bsize * nblocks;
-
-	// Create blob
-	block_blob_t *blob = malloc(sizeof(*blob));
-	if (!blob) {
-		block_fini(service_id);
-		return ENOMEM;
-	}
-	rc = bithenge_new_random_access_blob(blob_from_block(blob),
-	    &block_ops);
-	if (rc != EOK) {
-		free(blob);
-		block_fini(service_id);
-		return rc;
-	}
-	blob->service_id = service_id;
-	blob->size = size;
-	*out = bithenge_blob_as_node(blob_from_block(blob));
-
-	return EOK;
-}
-
-/** @}
- */
Index: pace/app/bithenge/block.h
===================================================================
--- uspace/app/bithenge/block.h	(revision d5070eff49234bdcf4f61f6456094186dfa0e673)
+++ 	(revision )
@@ -1,48 +1,0 @@
-/*
- * Copyright (c) 2012 Sean Bartell
- * 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 bithenge
- * @{
- */
-/**
- * @file
- * Access block devices as blobs.
- */
-
-#ifndef BITHENGE_BLOCK_H_
-#define BITHENGE_BLOCK_H_
-
-#include <loc.h>
-#include "blob.h"
-
-int bithenge_new_block_blob(bithenge_node_t **, service_id_t);
-
-#endif
-
-/** @}
- */
Index: uspace/app/bithenge/file.c
===================================================================
--- uspace/app/bithenge/file.c	(revision d5070eff49234bdcf4f61f6456094186dfa0e673)
+++ uspace/app/bithenge/file.c	(revision da0fef6126d5ca1836cfcd2bb32043a899aff458)
@@ -39,10 +39,12 @@
 #include <errno.h>
 #include <fcntl.h>
-#include <macros.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
 #include "blob.h"
 #include "file.h"
+#include "os.h"
 
 typedef struct {
@@ -76,9 +78,19 @@
 	if (offset > blob->size)
 		return ELIMIT;
-	*size = min(*size, blob->size - offset);
-	int rc = lseek(blob->fd, offset, SEEK_SET);
-	if (rc != EOK)
-		return rc;
-	return read(blob->fd, buffer, *size);
+	if (lseek(blob->fd, offset, SEEK_SET) < 0)
+		return errno;
+
+	ssize_t amount_read;
+	aoff64_t remaining_size = *size;
+	*size = 0;
+	do {
+		amount_read = read(blob->fd, buffer, remaining_size);
+		if (amount_read < 0)
+			return errno;
+		buffer += amount_read;
+		*size += amount_read;
+		remaining_size -= amount_read;
+	} while (remaining_size && amount_read);
+	return EOK;
 }
 
@@ -125,5 +137,9 @@
 	}
 	blob->fd = fd;
+#ifdef __HELENOS__
 	blob->size = stat.size;
+#else
+	blob->size = stat.st_size;
+#endif
 	blob->needs_close = needs_close;
 	*out = bithenge_blob_as_node(blob_from_file(blob));
Index: uspace/app/bithenge/helenos/block.c
===================================================================
--- uspace/app/bithenge/helenos/block.c	(revision da0fef6126d5ca1836cfcd2bb32043a899aff458)
+++ uspace/app/bithenge/helenos/block.c	(revision da0fef6126d5ca1836cfcd2bb32043a899aff458)
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2012 Sean Bartell
+ * 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 bithenge
+ * @{
+ */
+/**
+ * @file
+ * Access block devices as blobs.
+ * @todo Provide more information about the block device (block size).
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <libblock.h>
+#include <loc.h>
+#include <macros.h>
+#include <stdlib.h>
+#include "../blob.h"
+#include "block.h"
+
+typedef struct {
+	bithenge_blob_t base;
+	service_id_t service_id;
+	aoff64_t size;
+} block_blob_t;
+
+static inline block_blob_t *block_from_blob(bithenge_blob_t *base)
+{
+	return (block_blob_t *)base;
+}
+
+static inline bithenge_blob_t *blob_from_block(block_blob_t *blob)
+{
+	return &blob->base;
+}
+
+static int block_size(bithenge_blob_t *base, aoff64_t *size)
+{
+	block_blob_t *blob = block_from_blob(base);
+	*size = blob->size;
+	return EOK;
+}
+
+static int block_read(bithenge_blob_t *base, aoff64_t offset, char *buffer,
+    aoff64_t *size)
+{
+	block_blob_t *blob = block_from_blob(base);
+	if (offset > blob->size)
+		return ELIMIT;
+	*size = min(*size, blob->size - offset);
+	return block_read_bytes_direct(blob->service_id, offset, *size, buffer);
+}
+
+static int block_destroy(bithenge_blob_t *base)
+{
+	block_blob_t *blob = block_from_blob(base);
+	block_fini(blob->service_id);
+	free(blob);
+	return EOK;
+}
+
+static const bithenge_random_access_blob_ops_t block_ops = {
+	.size = block_size,
+	.read = block_read,
+	.destroy = block_destroy,
+};
+
+/** Create a blob for a block device. The blob must be freed with
+ * @a bithenge_node_t::bithenge_node_destroy after it is used.
+ * @param[out] out Stores the created blob.
+ * @param service_id The service ID of the block device.
+ * @return EOK on success or an error code from errno.h. */
+int bithenge_new_block_blob(bithenge_node_t **out, service_id_t service_id)
+{
+	assert(out);
+
+	// Initialize libblock
+	int rc;
+	rc = block_init(EXCHANGE_SERIALIZE, service_id, 2048);
+	if (rc != EOK)
+		return rc;
+
+	// Calculate total device size
+	size_t bsize;
+	aoff64_t nblocks;
+	aoff64_t size;
+	rc = block_get_bsize(service_id, &bsize);
+	if (rc != EOK) {
+		block_fini(service_id);
+		return rc;
+	}
+	rc = block_get_nblocks(service_id, &nblocks);
+	if (rc != EOK) {
+		block_fini(service_id);
+		return rc;
+	}
+	size = bsize * nblocks;
+
+	// Create blob
+	block_blob_t *blob = malloc(sizeof(*blob));
+	if (!blob) {
+		block_fini(service_id);
+		return ENOMEM;
+	}
+	rc = bithenge_new_random_access_blob(blob_from_block(blob),
+	    &block_ops);
+	if (rc != EOK) {
+		free(blob);
+		block_fini(service_id);
+		return rc;
+	}
+	blob->service_id = service_id;
+	blob->size = size;
+	*out = bithenge_blob_as_node(blob_from_block(blob));
+
+	return EOK;
+}
+
+/** @}
+ */
Index: uspace/app/bithenge/helenos/block.h
===================================================================
--- uspace/app/bithenge/helenos/block.h	(revision da0fef6126d5ca1836cfcd2bb32043a899aff458)
+++ uspace/app/bithenge/helenos/block.h	(revision da0fef6126d5ca1836cfcd2bb32043a899aff458)
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2012 Sean Bartell
+ * 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 bithenge
+ * @{
+ */
+/**
+ * @file
+ * Access block devices as blobs.
+ */
+
+#ifndef BITHENGE_BLOCK_H_
+#define BITHENGE_BLOCK_H_
+
+#include <loc.h>
+#include "../blob.h"
+
+int bithenge_new_block_blob(bithenge_node_t **, service_id_t);
+
+#endif
+
+/** @}
+ */
Index: uspace/app/bithenge/helenos/os.h
===================================================================
--- uspace/app/bithenge/helenos/os.h	(revision da0fef6126d5ca1836cfcd2bb32043a899aff458)
+++ uspace/app/bithenge/helenos/os.h	(revision da0fef6126d5ca1836cfcd2bb32043a899aff458)
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2012 Sean Bartell
+ * 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 BITHENGE_OS_H_
+#define BITHENGE_OS_H_
+
+#include <bool.h>
+#include <byteorder.h>
+#include <macros.h>
+#include <mem.h>
+#include <str.h>
+#include <str_error.h>
+
+typedef struct {
+	const char *string;
+	size_t offset;
+	wchar_t ch;
+} string_iterator_t;
+
+static inline string_iterator_t string_iterator(const char *string)
+{
+	string_iterator_t i;
+	i.string = string;
+	i.offset = 0;
+	i.ch = str_decode(i.string, &i.offset, STR_NO_LIMIT);
+	return i;
+}
+
+static inline bool string_iterator_done(const string_iterator_t *i)
+{
+	return i->ch == L'\0';
+}
+
+static inline int string_iterator_next(string_iterator_t *i, wchar_t *out)
+{
+	*out = i->ch;
+	if (*out == U_SPECIAL)
+		return EINVAL;
+	i->ch = str_decode(i->string, &i->offset, STR_NO_LIMIT);
+	return EOK;
+}
+
+#endif
Index: uspace/app/bithenge/linux/os.h
===================================================================
--- uspace/app/bithenge/linux/os.h	(revision da0fef6126d5ca1836cfcd2bb32043a899aff458)
+++ uspace/app/bithenge/linux/os.h	(revision da0fef6126d5ca1836cfcd2bb32043a899aff458)
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2012 Sean Bartell
+ * 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 BITHENGE_OS_H_
+#define BITHENGE_OS_H_
+
+#include <endian.h>
+#include <errno.h>
+#include <memory.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include <wchar.h>
+
+#define max(aleph, bet) ((aleph) > (bet) ? (aleph) : (bet))
+#define min(aleph, bet) ((aleph) < (bet) ? (aleph) : (bet))
+
+#define EOK 0
+#define ELIMIT EINVAL
+
+typedef uint64_t aoff64_t;
+
+typedef const char *string_iterator_t;
+
+static inline string_iterator_t string_iterator(const char *string)
+{
+	return string;
+}
+
+static inline int string_iterator_next(string_iterator_t *i, wchar_t *out)
+{
+	wint_t rc = btowc(*(*i)++); // TODO
+	*out = (wchar_t) rc;
+	return rc == WEOF ? EILSEQ : EOK;
+}
+
+static inline bool string_iterator_done(const string_iterator_t *i)
+{
+	return !**i;
+}
+
+static inline size_t str_length(const char *string)
+{
+	return strlen(string);
+}
+
+static inline const char *str_chr(const char *string, wchar_t ch)
+{
+	return strchr(string, wctob(ch)); // TODO
+}
+
+static inline int str_cmp(const char *s1, const char *s2)
+{
+	return strcmp(s1, s2);
+}
+
+static inline int str_lcmp(const char *s1, const char *s2, size_t max_len)
+{
+	return strncmp(s1, s2, max_len);
+}
+
+static inline const char *str_error(int e)
+{
+	return strerror(e);
+}
+
+static inline uint32_t uint32_t_le2host(uint32_t val)
+{
+	return le32toh(val);
+}
+
+#endif
Index: uspace/app/bithenge/print.c
===================================================================
--- uspace/app/bithenge/print.c	(revision d5070eff49234bdcf4f61f6456094186dfa0e673)
+++ uspace/app/bithenge/print.c	(revision da0fef6126d5ca1836cfcd2bb32043a899aff458)
@@ -104,9 +104,11 @@
 static int print_string(bithenge_print_type_t type, bithenge_node_t *node)
 {
-	size_t off = 0;
 	const char *value = bithenge_string_node_value(node);
-	wchar_t ch;
 	printf("\"");
-	while ((ch = str_decode(value, &off, STR_NO_LIMIT)) != 0) {
+	for (string_iterator_t i = string_iterator(value); !string_iterator_done(&i); ) {
+		wchar_t ch;
+		int rc = string_iterator_next(&i, &ch);
+		if (rc != EOK)
+			return rc;
 		if (ch == '"' || ch == '\\') {
 			printf("\\%lc", (wint_t) ch);
@@ -134,5 +136,5 @@
 			return rc;
 		for (aoff64_t i = 0; i < size; i++)
-			printf("\\x%02x", buffer[i]);
+			printf("\\x%02x", (unsigned int)(uint8_t)buffer[i]);
 		pos += size;
 	} while (size == sizeof(buffer));
Index: uspace/app/bithenge/source.c
===================================================================
--- uspace/app/bithenge/source.c	(revision da0fef6126d5ca1836cfcd2bb32043a899aff458)
+++ uspace/app/bithenge/source.c	(revision da0fef6126d5ca1836cfcd2bb32043a899aff458)
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2012 Sean Bartell
+ * 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 bithenge
+ * @{
+ */
+/**
+ * @file
+ * Provide various external sources of data.
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include "blob.h"
+#include "file.h"
+#include "source.h"
+
+#ifdef __HELENOS__
+#include <loc.h>
+#include "helenos/block.h"
+#endif
+
+static inline int hex_digit(char digit)
+{
+	if ('0' <= digit && digit <= '9')
+		return digit - '0' + 0x0;
+	if ('a' <= digit && digit <= 'f')
+		return digit - 'a' + 0xa;
+	if ('A' <= digit && digit <= 'F')
+		return digit - 'A' + 0xA;
+	return -1;
+}
+
+static int blob_from_hex(bithenge_node_t **out, const char *hex)
+{
+	size_t size = str_length(hex);
+	if (size % 2)
+		return EINVAL;
+	size /= 2;
+	char *buffer = malloc(size);
+	if (!buffer)
+		return ENOMEM;
+	for (size_t i = 0; i < size; i++) {
+		int upper = hex_digit(hex[2 * i + 0]);
+		int lower = hex_digit(hex[2 * i + 1]);
+		if (upper == -1 || lower == -1) {
+			free(buffer);
+			return EINVAL;
+		}
+		buffer[i] = upper << 4 | lower;
+	}
+	return bithenge_new_blob_from_buffer(out, buffer, size, true);
+}
+
+int bithenge_node_from_source(bithenge_node_t **out, const char *source)
+{
+	if (str_chr(source, ':')) {
+		if (!str_lcmp(source, "file:", 5)) {
+			// Example: file:/textdemo
+			return bithenge_new_file_blob(out, source + 5);
+#ifdef __HELENOS__
+		} else if (!str_lcmp(source, "block:", 6)) {
+			// Example: block:bd/initrd
+			service_id_t service_id;
+			int rc = loc_service_get_id(source + 6, &service_id, 0);
+			if (rc != EOK)
+				return rc;
+			return bithenge_new_block_blob(out, service_id);
+#endif
+		} else if (!str_lcmp(source, "hex:", 4)) {
+			// Example: hex:04000000
+			return blob_from_hex(out, source + 4);
+		} else {
+			return EINVAL;
+		}
+	}
+	return bithenge_new_file_blob(out, source);
+}
+
+/** @}
+ */
Index: uspace/app/bithenge/source.h
===================================================================
--- uspace/app/bithenge/source.h	(revision da0fef6126d5ca1836cfcd2bb32043a899aff458)
+++ uspace/app/bithenge/source.h	(revision da0fef6126d5ca1836cfcd2bb32043a899aff458)
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2012 Sean Bartell
+ * 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 bithenge
+ * @{
+ */
+/**
+ * @file
+ * Provide various external sources of data.
+ */
+
+#ifndef BITHENGE_SOURCE_H_
+#define BITHENGE_SOURCE_H_
+
+#include "tree.h"
+
+int bithenge_node_from_source(bithenge_node_t **out, const char *source);
+
+#endif
+
+/** @}
+ */
Index: uspace/app/bithenge/test.c
===================================================================
--- uspace/app/bithenge/test.c	(revision d5070eff49234bdcf4f61f6456094186dfa0e673)
+++ uspace/app/bithenge/test.c	(revision da0fef6126d5ca1836cfcd2bb32043a899aff458)
@@ -35,82 +35,49 @@
  */
 
-#include <loc.h>
+#include <errno.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <sys/types.h>
 #include "blob.h"
-#include "block.h"
-#include "file.h"
+#include "source.h"
 #include "print.h"
 #include "tree.h"
 
-static void
-print_data(const char *data, size_t len)
-{
-	while (len--)
-		printf("%02x ", (uint8_t)(*data++));
-	printf("\n");
-}
-
-static void
-print_blob(bithenge_node_t *node)
-{
-	bithenge_blob_t *blob = bithenge_node_as_blob(node);
-	aoff64_t size;
-	bithenge_blob_size(blob, &size);
-	printf("Size: %d; ", (int)size);
-	char buffer[64];
-	size = sizeof(buffer);
-	bithenge_blob_read(blob, 0, buffer, &size);
-	print_data(buffer, size);
-}
-
 int main(int argc, char *argv[])
 {
-	bithenge_node_t *node;
-
-	service_id_t service_id;
-	loc_service_get_id("bd/initrd", &service_id, 0);
-	bithenge_new_block_blob(&node, service_id);
-	printf("Data from block:bd/initrd: ");
-	print_blob(node);
-	bithenge_node_destroy(node);
-
-	const char data[] = "'Twas brillig, and the slithy toves";
-	bithenge_new_blob_from_data(&node, data, sizeof(data));
-	printf("Data from memory (from_data): ");
-	print_blob(node);
-	bithenge_node_destroy(node);
-
-	bithenge_new_blob_from_buffer(&node, data, sizeof(data), false);
-	printf("Data from memory (from_buffer): ");
-	print_blob(node);
-	bithenge_node_destroy(node);
-
-	bithenge_new_file_blob(&node, "/textdemo");
-	printf("Data from file:/textdemo: ");
-	print_blob(node);
-	bithenge_node_destroy(node);
-
-	bithenge_new_file_blob_from_fd(&node, 0);
-	printf("Data from fd:0: ");
-	print_blob(node);
-	bithenge_node_destroy(node);
-
-	// {True: {}, -1351: "\"false\"", "true": False, 0: b"..."}
-	bithenge_node_t *nodes[8];
-	bithenge_new_boolean_node(&nodes[0], true);
-	bithenge_new_simple_internal_node(&nodes[1], NULL, 0, false);
-	bithenge_new_integer_node(&nodes[2], -1351);
-	bithenge_new_string_node(&nodes[3], "\"false\"", false);
-	bithenge_new_string_node(&nodes[4], "true", false);
-	bithenge_new_boolean_node(&nodes[5], false);
-	bithenge_new_integer_node(&nodes[6], 0);
-	bithenge_new_blob_from_data(&nodes[7], data, sizeof(data));
-	bithenge_new_simple_internal_node(&node, nodes, 4, false);
-	bithenge_print_node(BITHENGE_PRINT_PYTHON, node);
-	printf("\n");
-	bithenge_print_node(BITHENGE_PRINT_JSON, node);
-	printf("\n");
-	bithenge_node_destroy(node);
+	if (argc < 2) {
+		// {True: {}, -1351: "\"false\"", "true": False, 0: b"..."}
+		const char data[] = "'Twas brillig, and the slithy toves";
+		bithenge_node_t *node;
+		bithenge_node_t *subnodes[8];
+		bithenge_new_boolean_node(&subnodes[0], true);
+		bithenge_new_simple_internal_node(&subnodes[1], NULL, 0, false);
+		bithenge_new_integer_node(&subnodes[2], -1351);
+		bithenge_new_string_node(&subnodes[3], "\"false\"", false);
+		bithenge_new_string_node(&subnodes[4], "true", false);
+		bithenge_new_boolean_node(&subnodes[5], false);
+		bithenge_new_integer_node(&subnodes[6], 0);
+		bithenge_new_blob_from_data(&subnodes[7], data, sizeof(data));
+		bithenge_new_simple_internal_node(&node, subnodes, 4, false);
+		bithenge_print_node(BITHENGE_PRINT_PYTHON, node);
+		printf("\n");
+		bithenge_print_node(BITHENGE_PRINT_JSON, node);
+		printf("\n");
+		bithenge_node_destroy(node);
+	} else {
+		bithenge_node_t *node;
+		int rc = bithenge_node_from_source(&node, argv[1]);
+		if (rc != EOK) {
+			printf("Error creating node from source: %s\n", str_error(rc));
+			return 1;
+		}
+		rc = bithenge_print_node(BITHENGE_PRINT_PYTHON, node);
+		if (rc != EOK) {
+			printf("Error printing node: %s\n", str_error(rc));
+			return 1;
+		}
+		printf("\n");
+		bithenge_node_destroy(node);
+	}
 
 	return 0;
Index: uspace/app/bithenge/transform.c
===================================================================
--- uspace/app/bithenge/transform.c	(revision d5070eff49234bdcf4f61f6456094186dfa0e673)
+++ uspace/app/bithenge/transform.c	(revision da0fef6126d5ca1836cfcd2bb32043a899aff458)
@@ -35,5 +35,4 @@
  */
 
-#include <byteorder.h>
 #include <errno.h>
 #include "blob.h"
Index: uspace/app/bithenge/transform.h
===================================================================
--- uspace/app/bithenge/transform.h	(revision d5070eff49234bdcf4f61f6456094186dfa0e673)
+++ uspace/app/bithenge/transform.h	(revision da0fef6126d5ca1836cfcd2bb32043a899aff458)
@@ -75,5 +75,5 @@
 /** Find the length of the prefix of a blob this transform can use as input. In
  * other words, figure out how many bytes this transform will use up.  This
- * method is optional and can return an error, but it is required for struct
+ * method is optional and can return an error, but it must succeed for struct
  * subtransforms.
  * @memberof bithenge_transform_t
Index: uspace/app/bithenge/tree.c
===================================================================
--- uspace/app/bithenge/tree.c	(revision d5070eff49234bdcf4f61f6456094186dfa0e673)
+++ uspace/app/bithenge/tree.c	(revision da0fef6126d5ca1836cfcd2bb32043a899aff458)
@@ -37,6 +37,6 @@
 #include <errno.h>
 #include <stdlib.h>
-#include <str.h>
 #include "blob.h"
+#include "os.h"
 #include "tree.h"
 
@@ -59,5 +59,5 @@
 	case BITHENGE_NODE_STRING:
 		if (node->string_value.needs_free)
-			free(node->string_value.ptr);
+			free((void *)node->string_value.ptr);
 		break;
 	case BITHENGE_NODE_INTERNAL:
Index: uspace/app/bithenge/tree.h
===================================================================
--- uspace/app/bithenge/tree.h	(revision d5070eff49234bdcf4f61f6456094186dfa0e673)
+++ uspace/app/bithenge/tree.h	(revision da0fef6126d5ca1836cfcd2bb32043a899aff458)
@@ -39,7 +39,7 @@
 
 #include <assert.h>
-#include <bool.h>
 #include <inttypes.h>
 #include <sys/types.h>
+#include "os.h"
 
 #ifdef INTMAX_MAX
