Index: uspace/app/bithenge/Makefile.linux
===================================================================
--- uspace/app/bithenge/Makefile.linux	(revision 1b6b76dd363de2ca79808ebf48d6d78a2a764d2b)
+++ uspace/app/bithenge/Makefile.linux	(revision 2988aec7d345a3d82047f938825fde9cb05c261c)
@@ -50,5 +50,5 @@
 
 $(BINARY): $(OBJECTS)
-	$(CC) -o $@ $^
+	$(CC) $(CFLAGS) -o $@ $^
 
 clean:
Index: uspace/app/bithenge/blob.c
===================================================================
--- uspace/app/bithenge/blob.c	(revision 1b6b76dd363de2ca79808ebf48d6d78a2a764d2b)
+++ uspace/app/bithenge/blob.c	(revision 2988aec7d345a3d82047f938825fde9cb05c261c)
@@ -459,4 +459,132 @@
 }
 
+typedef struct {
+	bithenge_blob_t base;
+	bithenge_blob_t *a, *b;
+	aoff64_t a_size;
+} concat_blob_t;
+
+static inline concat_blob_t *blob_as_concat(bithenge_blob_t *base)
+{
+	return (concat_blob_t *)base;
+}
+
+static inline bithenge_blob_t *concat_as_blob(concat_blob_t *blob)
+{
+	return &blob->base;
+}
+
+static int concat_blob_size(bithenge_blob_t *base, aoff64_t *size)
+{
+	concat_blob_t *self = blob_as_concat(base);
+	int rc = bithenge_blob_size(self->b, size);
+	*size += self->a_size;
+	return rc;
+}
+
+static int concat_blob_read(bithenge_blob_t *base, aoff64_t offset,
+    char *buffer, aoff64_t *size)
+{
+	int rc = EOK;
+	concat_blob_t *self = blob_as_concat(base);
+
+	aoff64_t a_size = 0, b_size = 0;
+	if (offset < self->a_size) {
+		a_size = *size;
+		rc = bithenge_blob_read(self->a, offset, buffer, &a_size);
+		if (rc != EOK)
+			return rc;
+	}
+	if (offset + *size > self->a_size) {
+		b_size = *size - a_size;
+		rc = bithenge_blob_read(self->b,
+		    offset + a_size - self->a_size, buffer + a_size, &b_size);
+		if (rc != EOK)
+			return rc;
+	}
+	assert(a_size + b_size <= *size);
+	*size = a_size + b_size;
+	return rc;
+}
+
+static int concat_blob_read_bits(bithenge_blob_t *base, aoff64_t offset,
+    char *buffer, aoff64_t *size, bool little_endian)
+{
+	int rc = EOK;
+	concat_blob_t *self = blob_as_concat(base);
+
+	aoff64_t a_size = 0, b_size = 0;
+	if (offset < self->a_size) {
+		a_size = *size;
+		rc = bithenge_blob_read_bits(self->a, offset, buffer, &a_size,
+		    little_endian);
+		if (rc != EOK)
+			return rc;
+	}
+	if (offset + *size > self->a_size) {
+		b_size = offset + *size - self->a_size;
+		rc = bithenge_blob_read_bits(self->b,
+		    offset + a_size - self->a_size, buffer + a_size, &b_size,
+		    little_endian);
+		if (rc != EOK)
+			return rc;
+	}
+	*size = a_size + b_size;
+	return rc;
+}
+
+static void concat_blob_destroy(bithenge_blob_t *base)
+{
+	concat_blob_t *self = blob_as_concat(base);
+	bithenge_blob_dec_ref(self->a);
+	bithenge_blob_dec_ref(self->b);
+	free(self);
+}
+
+static const bithenge_random_access_blob_ops_t concat_blob_ops = {
+	.size = concat_blob_size,
+	.read = concat_blob_read,
+	.read_bits = concat_blob_read_bits,
+	.destroy = concat_blob_destroy,
+};
+
+/** Create a concatenated blob. Takes references to @a a and @a b.
+ * @param[out] out Holds the new blob.
+ * @param a The first blob.
+ * @param b The second blob.
+ * @return EOK on success or an error code from errno.h. */
+int bithenge_concat_blob(bithenge_node_t **out, bithenge_blob_t *a,
+    bithenge_blob_t *b)
+{
+	assert(out);
+	assert(a);
+	assert(b);
+	int rc;
+	concat_blob_t *self = malloc(sizeof(*self));
+	if (!self) {
+		rc = ENOMEM;
+		goto error;
+	}
+
+	rc = bithenge_blob_size(a, &self->a_size);
+	if (rc != EOK)
+		goto error;
+
+	rc = bithenge_init_random_access_blob(concat_as_blob(self),
+	    &concat_blob_ops);
+	if (rc != EOK)
+		goto error;
+	self->a = a;
+	self->b = b;
+	*out = bithenge_blob_as_node(concat_as_blob(self));
+	return EOK;
+
+error:
+	bithenge_blob_dec_ref(a);
+	bithenge_blob_dec_ref(b);
+	free(self);
+	return rc;
+}
+
 /** Check whether the contents of two blobs are equal.
  * @memberof bithenge_blob_t
Index: uspace/app/bithenge/blob.h
===================================================================
--- uspace/app/bithenge/blob.h	(revision 1b6b76dd363de2ca79808ebf48d6d78a2a764d2b)
+++ uspace/app/bithenge/blob.h	(revision 2988aec7d345a3d82047f938825fde9cb05c261c)
@@ -243,4 +243,6 @@
 int bithenge_new_subblob(bithenge_node_t **, bithenge_blob_t *, aoff64_t,
     aoff64_t);
+int bithenge_concat_blob(bithenge_node_t **, bithenge_blob_t *,
+    bithenge_blob_t *);
 bool bithenge_blob_equal(bithenge_blob_t *, bithenge_blob_t *);
 
Index: uspace/app/bithenge/expression.c
===================================================================
--- uspace/app/bithenge/expression.c	(revision 1b6b76dd363de2ca79808ebf48d6d78a2a764d2b)
+++ uspace/app/bithenge/expression.c	(revision 2988aec7d345a3d82047f938825fde9cb05c261c)
@@ -101,4 +101,5 @@
 	/* Check types and get values. */
 	bithenge_int_t a_int = 0, b_int = 0;
+	bool a_bool = false, b_bool = false;
 	switch (self->op) {
 	case BITHENGE_EXPRESSION_ADD: /* fallthrough */
@@ -119,4 +120,20 @@
 		b_int = bithenge_integer_node_value(b);
 		break;
+	case BITHENGE_EXPRESSION_AND: /* fallthrough */
+	case BITHENGE_EXPRESSION_OR:
+		rc = EINVAL;
+		if (bithenge_node_type(a) != BITHENGE_NODE_BOOLEAN)
+			goto error;
+		if (bithenge_node_type(b) != BITHENGE_NODE_BOOLEAN)
+			goto error;
+		a_bool = bithenge_boolean_node_value(a);
+		b_bool = bithenge_boolean_node_value(b);
+		break;
+	case BITHENGE_EXPRESSION_CONCAT:
+		if (bithenge_node_type(a) != BITHENGE_NODE_BLOB)
+			goto error;
+		if (bithenge_node_type(b) != BITHENGE_NODE_BLOB)
+			goto error;
+		break;
 	default:
 		break;
@@ -135,5 +152,5 @@
 	case BITHENGE_EXPRESSION_INTEGER_DIVIDE:
 		/* Integer division can behave in three major ways when the
-		 * operands are signed: truncated, floored, or Euclidean. When
+		  operands are signed: truncated, floored, or Euclidean. When
 		 * b > 0, we give the same result as floored and Euclidean;
 		 * otherwise, we currently raise an error. See
@@ -173,5 +190,21 @@
 	case BITHENGE_EXPRESSION_NOT_EQUALS:
 		rc = bithenge_new_boolean_node(out,
-		    ~bithenge_node_equal(a, b));
+		    !bithenge_node_equal(a, b));
+		break;
+	case BITHENGE_EXPRESSION_AND:
+		rc = bithenge_new_boolean_node(out, a_bool && b_bool);
+		break;
+	case BITHENGE_EXPRESSION_OR:
+		rc = bithenge_new_boolean_node(out, a_bool || b_bool);
+		break;
+	case BITHENGE_EXPRESSION_MEMBER:
+		rc = bithenge_node_get(a, b, out);
+		b = NULL;
+		break;
+	case BITHENGE_EXPRESSION_CONCAT:
+		rc = bithenge_concat_blob(out, bithenge_node_as_blob(a),
+		    bithenge_node_as_blob(b));
+		a = NULL;
+		b = NULL;
 		break;
 	case BITHENGE_EXPRESSION_INVALID_BINARY_OP:
@@ -430,86 +463,4 @@
 	free(self);
 	bithenge_node_dec_ref(node);
-	return rc;
-}
-
-
-
-/***************** member_expression                         *****************/
-
-typedef struct {
-	bithenge_expression_t base;
-	bithenge_expression_t *expr;
-	bithenge_node_t *key;
-} member_expression_t;
-
-static member_expression_t *expression_as_member(bithenge_expression_t *base)
-{
-	return (member_expression_t *)base;
-}
-
-static bithenge_expression_t *member_as_expression(member_expression_t *expr)
-{
-	return &expr->base;
-}
-
-static int member_expression_evaluate(bithenge_expression_t *base, 
-    bithenge_scope_t *scope, bithenge_node_t **out)
-{
-	member_expression_t *self = expression_as_member(base);
-	bithenge_node_t *node;
-	int rc = bithenge_expression_evaluate(self->expr, scope, &node);
-	if (rc != EOK)
-		return rc;
-	bithenge_node_inc_ref(self->key);
-	rc = bithenge_node_get(node, self->key, out);
-	bithenge_node_dec_ref(node);
-	if (rc == ENOENT)
-		return bithenge_scope_error(scope, "No member %t", self->key);
-	return rc;
-}
-
-static void member_expression_destroy(bithenge_expression_t *base)
-{
-	member_expression_t *self = expression_as_member(base);
-	bithenge_expression_dec_ref(self->expr);
-	bithenge_node_dec_ref(self->key);
-	free(self);
-}
-
-static const bithenge_expression_ops_t member_expression_ops = {
-	.evaluate = member_expression_evaluate,
-	.destroy = member_expression_destroy,
-};
-
-/** Create an expression that gets a member from a node. Takes references to
- * @a expr and @a key.
- * @param[out] out Holds the new expression.
- * @param expr Calculates the node to get the member of.
- * @param key The member to get.
- * @return EOK on success or an error code from errno.h. */
-int bithenge_member_expression(bithenge_expression_t **out,
-    bithenge_expression_t *expr, bithenge_node_t *key)
-{
-	int rc;
-	member_expression_t *self = malloc(sizeof(*self));
-	if (!self) {
-		rc = ENOMEM;
-		goto error;
-	}
-
-	rc = bithenge_init_expression(member_as_expression(self),
-	    &member_expression_ops);
-	if (rc != EOK)
-		goto error;
-
-	self->expr = expr;
-	self->key = key;
-	*out = member_as_expression(self);
-	return EOK;
-
-error:
-	bithenge_expression_dec_ref(expr);
-	bithenge_node_dec_ref(key);
-	free(self);
 	return rc;
 }
Index: uspace/app/bithenge/expression.h
===================================================================
--- uspace/app/bithenge/expression.h	(revision 1b6b76dd363de2ca79808ebf48d6d78a2a764d2b)
+++ uspace/app/bithenge/expression.h	(revision 2988aec7d345a3d82047f938825fde9cb05c261c)
@@ -93,4 +93,5 @@
 typedef enum {
 	BITHENGE_EXPRESSION_INVALID_BINARY_OP,
+
 	BITHENGE_EXPRESSION_ADD,
 	BITHENGE_EXPRESSION_SUBTRACT,
@@ -98,4 +99,5 @@
 	BITHENGE_EXPRESSION_INTEGER_DIVIDE,
 	BITHENGE_EXPRESSION_MODULO,
+
 	BITHENGE_EXPRESSION_LESS_THAN,
 	BITHENGE_EXPRESSION_GREATER_THAN,
@@ -104,4 +106,10 @@
 	BITHENGE_EXPRESSION_EQUALS,
 	BITHENGE_EXPRESSION_NOT_EQUALS,
+
+	BITHENGE_EXPRESSION_AND,
+	BITHENGE_EXPRESSION_OR,
+
+	BITHENGE_EXPRESSION_MEMBER,
+	BITHENGE_EXPRESSION_CONCAT,
 } bithenge_binary_op_t;
 
@@ -114,6 +122,4 @@
 int bithenge_param_expression(bithenge_expression_t **, int);
 int bithenge_const_expression(bithenge_expression_t **, bithenge_node_t *);
-int bithenge_member_expression(bithenge_expression_t **,
-    bithenge_expression_t *, bithenge_node_t *);
 int bithenge_scope_member_expression(bithenge_expression_t **,
     bithenge_node_t *);
Index: uspace/app/bithenge/print.c
===================================================================
--- uspace/app/bithenge/print.c	(revision 1b6b76dd363de2ca79808ebf48d6d78a2a764d2b)
+++ uspace/app/bithenge/print.c	(revision 2988aec7d345a3d82047f938825fde9cb05c261c)
@@ -181,5 +181,5 @@
 	bithenge_blob_t *blob = bithenge_node_as_blob(node);
 	aoff64_t pos = 0;
-	char buffer[1024];
+	uint8_t buffer[1024];
 	aoff64_t size = sizeof(buffer);
 	int rc;
@@ -187,10 +187,10 @@
 	    state->type == BITHENGE_PRINT_PYTHON ? "b\"" : "\"");
 	do {
-		rc = bithenge_blob_read(blob, pos, buffer, &size);
+		rc = bithenge_blob_read(blob, pos, (char *)buffer, &size);
 		if (rc != EOK)
 			return rc;
 		for (aoff64_t i = 0; i < size; i++)
 			state_printf(state, "\\x%02x",
-			    (unsigned int)(uint8_t)buffer[i]);
+			    (unsigned int)buffer[i]);
 		pos += size;
 	} while (size == sizeof(buffer));
Index: uspace/app/bithenge/script.c
===================================================================
--- uspace/app/bithenge/script.c	(revision 1b6b76dd363de2ca79808ebf48d6d78a2a764d2b)
+++ uspace/app/bithenge/script.c	(revision 2988aec7d345a3d82047f938825fde9cb05c261c)
@@ -53,6 +53,9 @@
  * other token uses one of these values: */
 typedef enum {
-	TOKEN_EQUALS = -128,
-	TOKEN_ERROR,
+	TOKEN_ERROR = -128,
+
+	TOKEN_AND,
+	TOKEN_CONCAT,
+	TOKEN_EQUALS,
 	TOKEN_EOF,
 	TOKEN_GREATER_THAN_OR_EQUAL,
@@ -63,4 +66,5 @@
 	TOKEN_LESS_THAN_OR_EQUAL,
 	TOKEN_NOT_EQUAL,
+	TOKEN_OR,
 
 	/* Keywords */
@@ -308,4 +312,25 @@
 			state->buffer_pos++;
 		}
+	} else if (ch == '&') {
+		state->token = ch;
+		state->buffer_pos++;
+		if (state->buffer[state->buffer_pos] == '&') {
+			state->token = TOKEN_AND;
+			state->buffer_pos++;
+		}
+	} else if (ch == '|') {
+		state->token = ch;
+		state->buffer_pos++;
+		if (state->buffer[state->buffer_pos] == '|') {
+			state->token = TOKEN_OR;
+			state->buffer_pos++;
+		}
+	} else if (ch == '+') {
+		state->token = ch;
+		state->buffer_pos++;
+		if (state->buffer[state->buffer_pos] == '+') {
+			state->token = TOKEN_CONCAT;
+			state->buffer_pos++;
+		}
 	} else {
 		state->token = ch;
@@ -415,4 +440,5 @@
 typedef enum {
 	PRECEDENCE_NONE,
+	PRECEDENCE_AND,
 	PRECEDENCE_EQUALS,
 	PRECEDENCE_COMPARE,
@@ -446,4 +472,10 @@
 	case TOKEN_NOT_EQUAL:
 		return BITHENGE_EXPRESSION_NOT_EQUALS;
+	case TOKEN_AND:
+		return BITHENGE_EXPRESSION_AND;
+	case TOKEN_OR:
+		return BITHENGE_EXPRESSION_OR;
+	case TOKEN_CONCAT:
+		return BITHENGE_EXPRESSION_CONCAT;
 	default:
 		return BITHENGE_EXPRESSION_INVALID_BINARY_OP;
@@ -455,5 +487,6 @@
 	switch (op) {
 	case BITHENGE_EXPRESSION_ADD: /* fallthrough */
-	case BITHENGE_EXPRESSION_SUBTRACT:
+	case BITHENGE_EXPRESSION_SUBTRACT: /* fallthrough */
+	case BITHENGE_EXPRESSION_CONCAT:
 		return PRECEDENCE_ADD;
 	case BITHENGE_EXPRESSION_MULTIPLY: /* fallthrough */
@@ -469,4 +502,7 @@
 	case BITHENGE_EXPRESSION_NOT_EQUALS:
 		return PRECEDENCE_EQUALS;
+	case BITHENGE_EXPRESSION_AND: /* fallthrough */
+	case BITHENGE_EXPRESSION_OR:
+		return PRECEDENCE_AND;
 	default:
 		assert(false);
@@ -609,5 +645,14 @@
 			}
 
-			rc = bithenge_member_expression(&expr, expr, key);
+			bithenge_expression_t *key_expr;
+			rc = bithenge_const_expression(&key_expr, key);
+			if (rc != EOK) {
+				error_errno(state, rc);
+				bithenge_expression_dec_ref(expr);
+				return NULL;
+			}
+
+			rc = bithenge_binary_expression(&expr,
+			    BITHENGE_EXPRESSION_MEMBER, expr, key_expr);
 			if (rc != EOK) {
 				error_errno(state, rc);
@@ -618,28 +663,42 @@
 			bithenge_expression_t *start = parse_expression(state);
 			bool absolute_limit = false;
-			if (state->token == ',') {
-				absolute_limit = false;
+			if (state->token == ',' || state->token == ':') {
+				absolute_limit = state->token == ':';
 				next_token(state);
-			} else if (state->token == ':') {
-				absolute_limit = true;
+				bithenge_expression_t *limit = NULL;
+				if (!(state->token == ']' && absolute_limit))
+					limit = parse_expression(state);
+				expect(state, ']');
+
+				if (state->error != EOK) {
+					bithenge_expression_dec_ref(expr);
+					bithenge_expression_dec_ref(start);
+					bithenge_expression_dec_ref(limit);
+					return NULL;
+				}
+				rc = bithenge_subblob_expression(&expr, expr, start,
+				    limit, absolute_limit);
+				if (rc != EOK) {
+					error_errno(state, rc);
+					return NULL;
+				}
+			} else if (state->token == ']') {
 				next_token(state);
+
+				if (state->error != EOK) {
+					bithenge_expression_dec_ref(expr);
+					bithenge_expression_dec_ref(start);
+					return NULL;
+				}
+				rc = bithenge_binary_expression(&expr,
+				    BITHENGE_EXPRESSION_MEMBER, expr, start);
+				if (rc != EOK) {
+					error_errno(state, rc);
+					return NULL;
+				}
 			} else {
-				syntax_error(state, "expected ',' or ':'");
-			}
-			bithenge_expression_t *limit = NULL;
-			if (!(state->token == ']' && absolute_limit))
-				limit = parse_expression(state);
-			expect(state, ']');
-
-			if (state->error != EOK) {
+				syntax_error(state, "expected ',', ':', or ']'");
 				bithenge_expression_dec_ref(expr);
 				bithenge_expression_dec_ref(start);
-				bithenge_expression_dec_ref(limit);
-				return NULL;
-			}
-			rc = bithenge_subblob_expression(&expr, expr, start,
-			    limit, absolute_limit);
-			if (rc != EOK) {
-				error_errno(state, rc);
 				return NULL;
 			}
@@ -1173,4 +1232,16 @@
 	}
 
+	bithenge_transform_t *barrier = NULL;
+	if (state->error == EOK) {
+		int rc = bithenge_new_barrier_transform(&barrier,
+		    state->num_params);
+		if (rc != EOK) {
+			barrier = NULL;
+			error_errno(state, rc);
+		}
+	}
+
+	add_named_transform(state, barrier, name);
+
 	expect(state, '=');
 	bithenge_transform_t *xform = parse_transform(state);
@@ -1178,13 +1249,13 @@
 
 	if (state->error == EOK) {
-		int rc = bithenge_new_barrier_transform(&xform, xform,
-		    state->num_params);
-		if (rc != EOK) {
-			xform = NULL;
-			error_errno(state, rc);
-		}
-	}
-
-	add_named_transform(state, xform, name);
+		int rc = bithenge_barrier_transform_set_subtransform(barrier,
+		    xform);
+		xform = NULL;
+		if (rc != EOK)
+			error_errno(state, rc);
+	}
+
+	if (state->error != EOK)
+		bithenge_transform_dec_ref(xform);
 
 	for (int i = 0; i < state->num_params; i++)
Index: uspace/app/bithenge/sequence.c
===================================================================
--- uspace/app/bithenge/sequence.c	(revision 1b6b76dd363de2ca79808ebf48d6d78a2a764d2b)
+++ uspace/app/bithenge/sequence.c	(revision 2988aec7d345a3d82047f938825fde9cb05c261c)
@@ -619,4 +619,6 @@
 /***************** bithenge_repeat_transform                 *****************/
 
+/* TODO: ignore errors */
+
 typedef struct {
 	bithenge_transform_t base;
Index: uspace/app/bithenge/transform.c
===================================================================
--- uspace/app/bithenge/transform.c	(revision 1b6b76dd363de2ca79808ebf48d6d78a2a764d2b)
+++ uspace/app/bithenge/transform.c	(revision 2988aec7d345a3d82047f938825fde9cb05c261c)
@@ -477,18 +477,30 @@
 };
 
+/** Set the subtransform of a barrier transform. This must be done before the
+ * barrier transform is used. Takes a reference to @a transform.
+ * @param base The barrier transform.
+ * @param transform The subtransform to use for all operations.
+ * @return EOK on success or an error code from errno.h. */
+int bithenge_barrier_transform_set_subtransform(bithenge_transform_t *base,
+    bithenge_transform_t *transform)
+{
+	assert(transform);
+	assert(bithenge_transform_num_params(transform) == 0);
+
+	barrier_transform_t *self = transform_as_barrier(base);
+	assert(!self->transform);
+	self->transform = transform;
+	return EOK;
+}
+
 /** Create a wrapper transform that creates a new scope. This ensures nothing
  * from the outer scope is passed in, other than parameters. The wrapper may
- * have a different value for num_params. Takes a reference to @a transform,
- * which it will use for all operations.
+ * have a different value for num_params. The subtransform must be set with @a
+ * bithenge_barrier_transform_set_subtransform before the result is used.
  * @param[out] out Holds the created transform.
- * @param transform The transform to wrap.
  * @param num_params The number of parameters to require, which may be 0.
  * @return EOK on success or an error code from errno.h. */
-int bithenge_new_barrier_transform(bithenge_transform_t **out,
-    bithenge_transform_t *transform, int num_params)
-{
-	assert(transform);
-	assert(bithenge_transform_num_params(transform) == 0);
-
+int bithenge_new_barrier_transform(bithenge_transform_t **out, int num_params)
+{
 	int rc;
 	barrier_transform_t *self = malloc(sizeof(*self));
@@ -501,9 +513,8 @@
 	if (rc != EOK)
 		goto error;
-	self->transform = transform;
+	self->transform = NULL;
 	*out = barrier_as_transform(self);
 	return EOK;
 error:
-	bithenge_transform_dec_ref(transform);
 	free(self);
 	return rc;
Index: uspace/app/bithenge/transform.h
===================================================================
--- uspace/app/bithenge/transform.h	(revision 1b6b76dd363de2ca79808ebf48d6d78a2a764d2b)
+++ uspace/app/bithenge/transform.h	(revision 2988aec7d345a3d82047f938825fde9cb05c261c)
@@ -147,6 +147,7 @@
 int bithenge_transform_prefix_apply(bithenge_transform_t *, bithenge_scope_t *,
     bithenge_blob_t *, bithenge_node_t **, aoff64_t *);
-int bithenge_new_barrier_transform(bithenge_transform_t **,
-    bithenge_transform_t *, int);
+int bithenge_new_barrier_transform(bithenge_transform_t **, int);
+int bithenge_barrier_transform_set_subtransform(bithenge_transform_t *,
+    bithenge_transform_t *);
 
 int bithenge_scope_new(bithenge_scope_t **, bithenge_scope_t *);
Index: uspace/dist/src/bithenge/fat.bh
===================================================================
--- uspace/dist/src/bithenge/fat.bh	(revision 1b6b76dd363de2ca79808ebf48d6d78a2a764d2b)
+++ uspace/dist/src/bithenge/fat.bh	(revision 2988aec7d345a3d82047f938825fde9cb05c261c)
@@ -44,5 +44,21 @@
 } <- bits_le <- known_length(1);
 
-transform fat_dir_entry(disk) = struct {
+transform file_data(data, bits, fat, cluster_size, start) = (in.data) <- struct {
+	.cluster <- (data[(start-2)*cluster_size, cluster_size]);
+	.last_cluster_number <- switch (bits) {
+		12: (488);       # 0x00000ff8
+		16: (65528);     # 0x0000fff8
+		32: (268435448); # 0x0ffffff8
+	};
+	.next <- (fat[start]);
+	if (.next == 0 || .next >= .last_cluster_number) {
+		.data <- (.cluster);
+	} else {
+		.rest <- file_data(data, bits, fat, cluster_size, .next);
+		.data <- (.cluster ++ .rest);
+	}
+};
+
+transform fat_dir_entry(data, bits, fat, cluster_size, self_start, parent) = struct {
 	.filename <- known_length(8);
 	.extension <- known_length(3);
@@ -58,4 +74,16 @@
 	.start <- u16;
 	.size <- u32;
+	.size_shown <- if (.size > 32) { (32) } else { (.size) };
+
+	if (.start != 0 && .start != self_start && .start != parent) {
+		.data
+		    <- if (.attrs.subdirectory) {
+		        repeat { fat_dir_entry(data, bits, fat, cluster_size, .start, self_start) }
+		    } else {
+		        (in[0,.size_shown])
+		    }
+		    <- if (.size != 0) { (in[0,.size]) } else { (in) }
+		    <- file_data(data, bits, fat, cluster_size, .start);
+	}
 };
 
@@ -112,4 +140,5 @@
 	};
 
+	.cluster_size <- (.super.sectors_per_cluster * .super.bytes_per_sector);
 	.first_root_sector <- (.super.num_reserved_sectors + .super.num_fats * .super.sectors_per_fat);
 	.first_data_sector <- (.first_root_sector +
@@ -128,5 +157,9 @@
 
 	.root <- partial(.first_root_sector * .super.bytes_per_sector) {
-		repeat(.super.num_root_entries) { fat_dir_entry(disk) } } <- (disk);
+		repeat(.super.num_root_entries) {
+			fat_dir_entry(disk[.first_data_sector * .super.bytes_per_sector:],
+			    .bits, .fats[0], .cluster_size, 0, 0)
+		}
+	} <- (disk);
 };
 
