Index: uspace/app/bithenge/Makefile
===================================================================
--- uspace/app/bithenge/Makefile	(revision 50985c34afbbfea83fb8a03ed868a052d884e50c)
+++ uspace/app/bithenge/Makefile	(revision 11b9ad7655dbecb81e5f452a36742fdc3849b9c7)
@@ -36,5 +36,7 @@
 	block.c \
 	file.c \
-	test.c
+	print.c \
+	test.c \
+	tree.c
 
 include $(USPACE_PREFIX)/Makefile.common
Index: uspace/app/bithenge/print.c
===================================================================
--- uspace/app/bithenge/print.c	(revision 11b9ad7655dbecb81e5f452a36742fdc3849b9c7)
+++ uspace/app/bithenge/print.c	(revision 11b9ad7655dbecb81e5f452a36742fdc3849b9c7)
@@ -0,0 +1,137 @@
+/*
+ * 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
+ * Write a tree as JSON or other text formats.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include "print.h"
+#include "tree.h"
+
+typedef struct {
+	bithenge_print_type_t type;
+	bool first;
+} print_internal_data_t;
+
+static int print_internal_func(bithenge_node_t *key, bithenge_node_t *value, void *data_)
+{
+	print_internal_data_t *data = (print_internal_data_t *)data_;
+	int rc;
+	if (!data->first)
+		printf(", ");
+	data->first = false;
+	bool add_quotes = data->type == BITHENGE_PRINT_JSON
+	    && bithenge_node_type(key) != BITHENGE_NODE_STRING;
+	if (add_quotes)
+		printf("\"");
+	rc = bithenge_print_node(data->type, key);
+	if (rc != EOK)
+		return rc;
+	if (add_quotes)
+		printf("\"");
+	printf(": ");
+	rc = bithenge_print_node(data->type, value);
+	if (rc != EOK)
+		return rc;
+	return EOK;
+}
+
+static int print_internal(bithenge_print_type_t type, bithenge_internal_node_t *node)
+{
+	int rc;
+	print_internal_data_t data = { type, true };
+	printf("{");
+	rc = bithenge_node_for_each(node, print_internal_func, &data);
+	if (rc != EOK)
+		return rc;
+	printf("}");
+	return EOK;
+}
+
+static int print_boolean(bithenge_print_type_t type, bithenge_boolean_node_t *node)
+{
+	bool value = bithenge_boolean_node_value(node);
+	switch (type) {
+	case BITHENGE_PRINT_PYTHON:
+		printf(value ? "True" : "False");
+		break;
+	case BITHENGE_PRINT_JSON:
+		printf(value ? "true" : "false");
+		break;
+	}
+	return EOK;
+}
+
+static int print_integer(bithenge_print_type_t type, bithenge_integer_node_t *node)
+{
+	bithenge_int_t value = bithenge_integer_node_value(node);
+	printf("%" BITHENGE_PRId, value);
+	return EOK;
+}
+
+static int print_string(bithenge_print_type_t type, bithenge_string_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) {
+		if (ch == '"' || ch == '\\') {
+			printf("\\%lc", (wint_t) ch);
+		} else if (ch <= 0x1f) {
+			printf("\\u%04x", (unsigned int) ch);
+		} else {
+			printf("%lc", (wint_t) ch);
+		}
+	}
+	printf("\"");
+	return EOK;
+}
+
+int bithenge_print_node(bithenge_print_type_t type, bithenge_node_t *tree)
+{
+	switch (bithenge_node_type(tree)) {
+	case BITHENGE_NODE_NONE:
+		return EINVAL;
+	case BITHENGE_NODE_INTERNAL:
+		return print_internal(type, bithenge_as_internal_node(tree));
+	case BITHENGE_NODE_BOOLEAN:
+		return print_boolean(type, bithenge_as_boolean_node(tree));
+	case BITHENGE_NODE_INTEGER:
+		return print_integer(type, bithenge_as_integer_node(tree));
+	case BITHENGE_NODE_STRING:
+		return print_string(type, bithenge_as_string_node(tree));
+	}
+	return ENOTSUP;
+}
Index: uspace/app/bithenge/print.h
===================================================================
--- uspace/app/bithenge/print.h	(revision 11b9ad7655dbecb81e5f452a36742fdc3849b9c7)
+++ uspace/app/bithenge/print.h	(revision 11b9ad7655dbecb81e5f452a36742fdc3849b9c7)
@@ -0,0 +1,52 @@
+/*
+ * 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
+ * Write a tree as JSON or other text formats.
+ */
+
+#ifndef BITHENGE_PRINT_H_
+#define BITHENGE_PRINT_H_
+
+#include "tree.h"
+
+typedef enum {
+	BITHENGE_PRINT_PYTHON,
+	BITHENGE_PRINT_JSON,
+} bithenge_print_type_t;
+
+int bithenge_print_node(bithenge_print_type_t, bithenge_node_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/app/bithenge/test.c
===================================================================
--- uspace/app/bithenge/test.c	(revision 50985c34afbbfea83fb8a03ed868a052d884e50c)
+++ uspace/app/bithenge/test.c	(revision 11b9ad7655dbecb81e5f452a36742fdc3849b9c7)
@@ -41,4 +41,6 @@
 #include "block.h"
 #include "file.h"
+#include "print.h"
+#include "tree.h"
 
 static void
@@ -94,4 +96,22 @@
 	bithenge_blob_destroy(blob);
 
+	// {True: {}, -1351: "zero", "true": False, 0: 17}
+	bithenge_node_t *node;
+	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], "zero", 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_integer_node(&nodes[7], 17);
+	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);
+
 	return 0;
 }
Index: uspace/app/bithenge/tree.c
===================================================================
--- uspace/app/bithenge/tree.c	(revision 11b9ad7655dbecb81e5f452a36742fdc3849b9c7)
+++ uspace/app/bithenge/tree.c	(revision 11b9ad7655dbecb81e5f452a36742fdc3849b9c7)
@@ -0,0 +1,145 @@
+/*
+ * 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
+ * Trees and nodes.
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include "tree.h"
+
+int bithenge_node_destroy(bithenge_node_t *node)
+{
+	bithenge_string_node_t *string_node;
+	switch (bithenge_node_type(node)) {
+	case BITHENGE_NODE_STRING:
+		string_node = bithenge_as_string_node(node);
+		if (string_node->needs_free)
+			free(string_node->value);
+		break;
+	case BITHENGE_NODE_INTERNAL:
+		/* TODO */
+		break;
+	case BITHENGE_NODE_BOOLEAN:
+		return EOK;
+	case BITHENGE_NODE_INTEGER: /* pass-through */
+	case BITHENGE_NODE_NONE:
+		break;
+	}
+	free(node);
+	return EOK;
+}
+
+typedef struct
+{
+	bithenge_internal_node_t base;
+	bithenge_node_t **nodes;
+	bithenge_int_t len;
+	bool needs_free;
+} simple_internal_node_t;
+
+static simple_internal_node_t *internal_as_simple(bithenge_internal_node_t *node)
+{
+	return (simple_internal_node_t *)node;
+}
+
+static int simple_internal_node_for_each(bithenge_internal_node_t *base, bithenge_for_each_func_t func, void *data)
+{
+	int rc;
+	simple_internal_node_t *node = internal_as_simple(base);
+	for (bithenge_int_t i = 0; i < node->len; i++) {
+		rc = func(node->nodes[2*i+0], node->nodes[2*i+1], data);
+		if (rc != EOK)
+			return rc;
+	}
+	return EOK;
+}
+
+static bithenge_internal_node_ops_t simple_internal_node_ops = {
+	.for_each = simple_internal_node_for_each
+};
+
+static bithenge_node_t *simple_internal_as_node(simple_internal_node_t *node)
+{
+	return bithenge_internal_as_node(&node->base);
+}
+
+int bithenge_new_simple_internal_node(bithenge_node_t **out, bithenge_node_t **nodes, bithenge_int_t len, bool needs_free)
+{
+	assert(out);
+	simple_internal_node_t *node = malloc(sizeof(*node));
+	if (!node)
+		return ENOMEM;
+	node->base.base.type = BITHENGE_NODE_INTERNAL;
+	node->base.ops = &simple_internal_node_ops;
+	node->nodes = nodes;
+	node->len = len;
+	node->needs_free = needs_free;
+	*out = simple_internal_as_node(node);
+	return EOK;
+}
+
+static bithenge_boolean_node_t false_node = { {BITHENGE_NODE_BOOLEAN}, false };
+static bithenge_boolean_node_t true_node = { {BITHENGE_NODE_BOOLEAN}, true };
+
+int bithenge_new_boolean_node(bithenge_node_t **out, bool value)
+{
+	assert(out);
+	*out = bithenge_boolean_as_node(value ? &true_node : &false_node);
+	return EOK;
+}
+
+int bithenge_new_integer_node(bithenge_node_t **out, bithenge_int_t value)
+{
+	assert(out);
+	bithenge_integer_node_t *node = malloc(sizeof(*node));
+	if (!node)
+		return ENOMEM;
+	node->base.type = BITHENGE_NODE_INTEGER;
+	node->value = value;
+	*out = bithenge_integer_as_node(node);
+	return EOK;
+}
+
+int bithenge_new_string_node(bithenge_node_t **out, const char *value, bool needs_free)
+{
+	assert(out);
+	bithenge_string_node_t *node = malloc(sizeof(*node));
+	if (!node)
+		return ENOMEM;
+	node->base.type = BITHENGE_NODE_STRING;
+	node->value = value;
+	node->needs_free = needs_free;
+	*out = bithenge_string_as_node(node);
+	return EOK;
+}
Index: uspace/app/bithenge/tree.h
===================================================================
--- uspace/app/bithenge/tree.h	(revision 11b9ad7655dbecb81e5f452a36742fdc3849b9c7)
+++ uspace/app/bithenge/tree.h	(revision 11b9ad7655dbecb81e5f452a36742fdc3849b9c7)
@@ -0,0 +1,171 @@
+/*
+ * 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
+ * Trees and nodes.
+ */
+
+#ifndef BITHENGE_TREE_H_
+#define BITHENGE_TREE_H_
+
+#include <assert.h>
+#include <bool.h>
+#include <inttypes.h>
+#include <sys/types.h>
+
+#ifdef INTMAX_MAX
+typedef intmax_t bithenge_int_t;
+#define BITHENGE_PRId PRIdMAX
+#else
+typedef int64_t bithenge_int_t;
+#define BITHENGE_PRId PRId64
+#endif
+
+typedef enum {
+	BITHENGE_NODE_NONE,
+	BITHENGE_NODE_INTERNAL,
+	BITHENGE_NODE_BOOLEAN,
+	BITHENGE_NODE_INTEGER,
+	BITHENGE_NODE_STRING,
+	// TODO: BITHENGE_NODE_BLOB,
+} bithenge_node_type_t;
+
+typedef struct bithenge_node_t {
+	bithenge_node_type_t type;
+} bithenge_node_t;
+
+static inline bithenge_node_type_t bithenge_node_type(const bithenge_node_t *node)
+{
+	return node->type;
+}
+
+typedef int (*bithenge_for_each_func_t)(bithenge_node_t *key, bithenge_node_t *value, void *data);
+
+typedef struct {
+	bithenge_node_t base;
+	const struct bithenge_internal_node_ops_t *ops;
+} bithenge_internal_node_t;
+
+typedef struct bithenge_internal_node_ops_t {
+	int (*for_each)(bithenge_internal_node_t *node, bithenge_for_each_func_t func, void *data);
+} bithenge_internal_node_ops_t;
+
+typedef struct {
+	bithenge_node_t base;
+	bool value;
+} bithenge_boolean_node_t;
+
+typedef struct {
+	bithenge_node_t base;
+	bithenge_int_t value;
+} bithenge_integer_node_t;
+
+typedef struct {
+	bithenge_node_t base;
+	const char *value;
+	bool needs_free;
+} bithenge_string_node_t;
+
+static inline bithenge_node_t *bithenge_internal_as_node(bithenge_internal_node_t *node)
+{
+	return &node->base;
+}
+
+static inline bithenge_internal_node_t *bithenge_as_internal_node(bithenge_node_t *node)
+{
+	assert(node->type == BITHENGE_NODE_INTERNAL);
+	return (bithenge_internal_node_t *)node;
+}
+
+static inline int bithenge_node_for_each(bithenge_internal_node_t *node, bithenge_for_each_func_t func, void *data)
+{
+	return node->ops->for_each(node, func, data);
+}
+
+static inline bithenge_node_t *bithenge_boolean_as_node(bithenge_boolean_node_t *node)
+{
+	return &node->base;
+}
+
+static inline bithenge_boolean_node_t *bithenge_as_boolean_node(bithenge_node_t *node)
+{
+	assert(node->type == BITHENGE_NODE_BOOLEAN);
+	return (bithenge_boolean_node_t *)node;
+}
+
+static inline bool bithenge_boolean_node_value(bithenge_boolean_node_t *node)
+{
+	return node->value;
+}
+
+static inline bithenge_node_t *bithenge_integer_as_node(bithenge_integer_node_t *node)
+{
+	return &node->base;
+}
+
+static inline bithenge_integer_node_t *bithenge_as_integer_node(bithenge_node_t *node)
+{
+	assert(node->type == BITHENGE_NODE_INTEGER);
+	return (bithenge_integer_node_t *)node;
+}
+
+static inline bithenge_int_t bithenge_integer_node_value(bithenge_integer_node_t *node)
+{
+	return node->value;
+}
+
+static inline bithenge_node_t *bithenge_string_as_node(bithenge_string_node_t *node)
+{
+	return &node->base;
+}
+
+static inline bithenge_string_node_t *bithenge_as_string_node(bithenge_node_t *node)
+{
+	assert(node->type == BITHENGE_NODE_STRING);
+	return (bithenge_string_node_t *)node;
+}
+
+static inline const char *bithenge_string_node_value(bithenge_string_node_t *node)
+{
+	return node->value;
+}
+
+int bithenge_new_simple_internal_node(bithenge_node_t **, bithenge_node_t **, bithenge_int_t, bool needs_free);
+int bithenge_new_boolean_node(bithenge_node_t **, bool);
+int bithenge_new_integer_node(bithenge_node_t **, bithenge_int_t);
+int bithenge_new_string_node(bithenge_node_t **, const char *, bool);
+int bithenge_node_destroy(bithenge_node_t *);
+
+#endif
+
+/** @}
+ */
