Index: uspace/app/bithenge/script.c
===================================================================
--- uspace/app/bithenge/script.c	(revision e3f2765d1f9f4db04f79955850663d979327ea95)
+++ uspace/app/bithenge/script.c	(revision c3437d984e5b4d1c7556b49b8b5e73b8b9f22382)
@@ -59,4 +59,5 @@
 
 	/* Keywords */
+	TOKEN_DO,
 	TOKEN_ELSE,
 	TOKEN_FALSE,
@@ -67,4 +68,5 @@
 	TOKEN_TRANSFORM,
 	TOKEN_TRUE,
+	TOKEN_WHILE,
 } token_type_t;
 
@@ -211,4 +213,7 @@
 		if (!value) {
 			error_errno(state, ENOMEM);
+		} else if (!str_cmp(value, "do")) {
+			state->token = TOKEN_DO;
+			free(value);
 		} else if (!str_cmp(value, "else")) {
 			state->token = TOKEN_ELSE;
@@ -234,4 +239,7 @@
 		} else if (!str_cmp(value, "true")) {
 			state->token = TOKEN_TRUE;
+			free(value);
+		} else if (!str_cmp(value, "while")) {
+			state->token = TOKEN_WHILE;
 			free(value);
 		} else {
@@ -686,4 +694,30 @@
 }
 
+static bithenge_transform_t *parse_do_while(state_t *state)
+{
+	expect(state, TOKEN_DO);
+	expect(state, '{');
+	bithenge_transform_t *xform = parse_transform(state);
+	expect(state, '}');
+	expect(state, TOKEN_WHILE);
+	expect(state, '(');
+	bithenge_expression_t *expr = parse_expression(state);
+	expect(state, ')');
+
+	if (state->error != EOK) {
+		bithenge_expression_dec_ref(expr);
+		bithenge_transform_dec_ref(xform);
+		return NULL;
+	}
+
+	bithenge_transform_t *do_while_xform;
+	int rc = bithenge_do_while_transform(&do_while_xform, xform, expr);
+	if (rc != EOK) {
+		error_errno(state, rc);
+		return NULL;
+	}
+	return do_while_xform;
+}
+
 /* The TOKEN_STRUCT and '{' must already have been skipped. */
 static bithenge_transform_t *parse_struct(state_t *state)
@@ -740,5 +774,7 @@
 static bithenge_transform_t *parse_transform_no_compose(state_t *state)
 {
-	if (state->token == TOKEN_IDENTIFIER) {
+	if (state->token == TOKEN_DO) {
+		return parse_do_while(state);
+	} else if (state->token == TOKEN_IDENTIFIER) {
 		return parse_invocation(state);
 	} else if (state->token == TOKEN_IF) {
Index: uspace/app/bithenge/sequence.c
===================================================================
--- uspace/app/bithenge/sequence.c	(revision e3f2765d1f9f4db04f79955850663d979327ea95)
+++ uspace/app/bithenge/sequence.c	(revision c3437d984e5b4d1c7556b49b8b5e73b8b9f22382)
@@ -44,4 +44,6 @@
 
 
+/***************** seq_node                                  *****************/
+
 typedef struct {
 	bithenge_node_t base;
@@ -236,4 +238,10 @@
 	bithenge_blob_dec_ref(self->blob);
 	free(self->ends);
+}
+
+static void seq_node_set_num_xforms(seq_node_t *self,
+    bithenge_int_t num_xforms)
+{
+	self->num_xforms = num_xforms;
 }
 
@@ -270,4 +278,6 @@
 
 
+/***************** bithenge_new_struct                       *****************/
+
 typedef struct {
 	bithenge_transform_t base;
@@ -581,4 +591,6 @@
 
 
+/***************** bithenge_repeat_transform                 *****************/
+
 typedef struct {
 	bithenge_transform_t base;
@@ -636,5 +648,8 @@
 		rc = seq_node_subtransform(repeat_as_seq(self),
 		    &subxform_result, i);
-		if (rc != EOK && self->count == -1) {
+		if ((rc == EINVAL || rc == ENOENT) && self->count == -1) {
+			self->count = i;
+			seq_node_set_num_xforms(repeat_as_seq(self),
+			    self->count);
 			rc = EOK;
 			break;
@@ -845,4 +860,272 @@
 }
 
+
+
+/***************** bithenge_do_while_transform               *****************/
+
+typedef struct {
+	bithenge_transform_t base;
+	bithenge_expression_t *expr;
+	bithenge_transform_t *xform;
+} do_while_transform_t;
+
+static inline bithenge_transform_t *do_while_as_transform(
+    do_while_transform_t *self)
+{
+	return &self->base;
+}
+
+static inline do_while_transform_t *transform_as_do_while(
+    bithenge_transform_t *base)
+{
+	return (do_while_transform_t *)base;
+}
+
+typedef struct {
+	seq_node_t base;
+	bool prefix;
+	bithenge_expression_t *expr;
+	bithenge_transform_t *xform;
+	bithenge_int_t count;
+} do_while_node_t;
+
+static seq_node_t *do_while_as_seq(do_while_node_t *self)
+{
+	return &self->base;
+}
+
+static do_while_node_t *seq_as_do_while(seq_node_t *base)
+{
+	return (do_while_node_t *)base;
+}
+
+static bithenge_node_t *do_while_as_node(do_while_node_t *self)
+{
+	return seq_as_node(do_while_as_seq(self));
+}
+
+static do_while_node_t *node_as_do_while(bithenge_node_t *base)
+{
+	return seq_as_do_while(node_as_seq(base));
+}
+
+static int do_while_node_for_each(bithenge_node_t *base,
+    bithenge_for_each_func_t func, void *data)
+{
+	int rc = EOK;
+	do_while_node_t *self = node_as_do_while(base);
+
+	for (bithenge_int_t i = 0; ; i++) {
+		bithenge_node_t *subxform_result;
+		rc = seq_node_subtransform(do_while_as_seq(self),
+		    &subxform_result, i);
+		if (rc != EOK)
+			return rc;
+
+		bithenge_node_t *key_node;
+		rc = bithenge_new_integer_node(&key_node, i);
+		if (rc != EOK) {
+			bithenge_node_dec_ref(subxform_result);
+			return rc;
+		}
+		bithenge_node_inc_ref(subxform_result);
+		rc = func(key_node, subxform_result, data);
+		if (rc != EOK) {
+			bithenge_node_dec_ref(subxform_result);
+			return rc;
+		}
+
+		bithenge_scope_t scope;
+		bithenge_scope_init(&scope);
+		rc = bithenge_scope_copy(&scope,
+		    seq_node_scope(do_while_as_seq(self)));
+		bithenge_scope_set_current_node(&scope, subxform_result);
+		if (rc != EOK) {
+			bithenge_scope_destroy(&scope);
+			return rc;
+		}
+		bithenge_node_t *expr_result;
+		rc = bithenge_expression_evaluate(self->expr, &scope,
+		    &expr_result);
+		bithenge_scope_destroy(&scope);
+		if (rc != EOK)
+			return rc;
+		if (bithenge_node_type(expr_result) != BITHENGE_NODE_BOOLEAN) {
+			bithenge_node_dec_ref(expr_result);
+			return EINVAL;
+		}
+		bool cond = bithenge_boolean_node_value(expr_result);
+		bithenge_node_dec_ref(expr_result);
+		if (!cond) {
+			self->count = i + 1;
+			seq_node_set_num_xforms(do_while_as_seq(self),
+			    self->count);
+			break;
+		}
+	}
+
+	if (!self->prefix) {
+		bool complete;
+		rc = seq_node_complete(do_while_as_seq(self), &complete);
+		if (rc != EOK)
+			return rc;
+		if (!complete)
+			return EINVAL;
+	}
+
+	return rc;
+}
+
+static void do_while_node_destroy(bithenge_node_t *base)
+{
+	do_while_node_t *self = node_as_do_while(base);
+	seq_node_destroy(do_while_as_seq(self));
+	bithenge_expression_dec_ref(self->expr);
+	bithenge_transform_dec_ref(self->xform);
+	free(self);
+}
+
+static const bithenge_internal_node_ops_t do_while_node_ops = {
+	.for_each = do_while_node_for_each,
+	.destroy = do_while_node_destroy,
+};
+
+static int do_while_node_get_transform(seq_node_t *base,
+    bithenge_transform_t **out, bithenge_int_t index)
+{
+	do_while_node_t *self = seq_as_do_while(base);
+	*out = self->xform;
+	bithenge_transform_inc_ref(*out);
+	return EOK;
+}
+
+static const seq_node_ops_t do_while_node_seq_ops = {
+	.get_transform = do_while_node_get_transform,
+};
+
+static int do_while_transform_make_node(do_while_transform_t *self,
+    bithenge_node_t **out, bithenge_scope_t *scope, bithenge_blob_t *blob,
+    bool prefix)
+{
+	do_while_node_t *node = malloc(sizeof(*node));
+	if (!node)
+		return ENOMEM;
+
+	int rc = bithenge_init_internal_node(do_while_as_node(node),
+	    &do_while_node_ops);
+	if (rc != EOK) {
+		free(node);
+		return rc;
+	}
+
+	rc = seq_node_init(do_while_as_seq(node), &do_while_node_seq_ops,
+	    scope, blob, -1, false);
+	if (rc != EOK) {
+		free(node);
+		return rc;
+	}
+
+	bithenge_transform_inc_ref(self->xform);
+	node->xform = self->xform;
+	bithenge_expression_inc_ref(self->expr);
+	node->expr = self->expr;
+	node->prefix = prefix;
+	node->count = -1;
+	*out = do_while_as_node(node);
+	return EOK;
+}
+
+static int do_while_transform_apply(bithenge_transform_t *base,
+    bithenge_scope_t *scope, bithenge_node_t *in, bithenge_node_t **out)
+{
+	do_while_transform_t *self = transform_as_do_while(base);
+	if (bithenge_node_type(in) != BITHENGE_NODE_BLOB)
+		return EINVAL;
+	return do_while_transform_make_node(self, out, scope,
+	    bithenge_node_as_blob(in), false);
+}
+
+static int for_each_noop(bithenge_node_t *key, bithenge_node_t *value,
+    void *data)
+{
+	bithenge_node_dec_ref(key);
+	bithenge_node_dec_ref(value);
+	return EOK;
+}
+
+static int do_while_transform_prefix_apply(bithenge_transform_t *base,
+    bithenge_scope_t *scope, bithenge_blob_t *blob, bithenge_node_t **out_node,
+    aoff64_t *out_size)
+{
+	do_while_transform_t *self = transform_as_do_while(base);
+	int rc = do_while_transform_make_node(self, out_node, scope, blob,
+	    true);
+	if (rc != EOK)
+		return rc;
+
+	rc = bithenge_node_for_each(*out_node, for_each_noop, NULL);
+	if (rc != EOK) {
+		bithenge_node_dec_ref(*out_node);
+		return rc;
+	}
+
+	rc = seq_node_field_offset(node_as_seq(*out_node), out_size,
+	    node_as_do_while(*out_node)->count);
+	if (rc != EOK) {
+		bithenge_node_dec_ref(*out_node);
+		return rc;
+	}
+
+	return EOK;
+}
+
+static void do_while_transform_destroy(bithenge_transform_t *base)
+{
+	do_while_transform_t *self = transform_as_do_while(base);
+	bithenge_transform_dec_ref(self->xform);
+	bithenge_expression_dec_ref(self->expr);
+	free(self);
+}
+
+static const bithenge_transform_ops_t do_while_transform_ops = {
+	.apply = do_while_transform_apply,
+	.prefix_apply = do_while_transform_prefix_apply,
+	.destroy = do_while_transform_destroy,
+};
+
+/** Create a transform that applies its subtransform while an expression on the
+ * result returns true. Takes a reference to @a xform and @a expr.
+ * @param[out] out Holds the new transform.
+ * @param xform The subtransform to apply repeatedly.
+ * @param expr Applied in the result of each application of @a xform to
+ * determine whether there will be more.
+ * @return EOK on success or an error code from errno.h. */
+int bithenge_do_while_transform(bithenge_transform_t **out,
+    bithenge_transform_t *xform, bithenge_expression_t *expr)
+{
+	int rc;
+	do_while_transform_t *self = malloc(sizeof(*self));
+	if (!self) {
+		rc = ENOMEM;
+		goto error;
+	}
+
+	rc = bithenge_init_transform(do_while_as_transform(self),
+	    &do_while_transform_ops, 0);
+	if (rc != EOK)
+		goto error;
+
+	self->expr = expr;
+	self->xform = xform;
+	*out = do_while_as_transform(self);
+	return EOK;
+
+error:
+	free(self);
+	bithenge_expression_dec_ref(expr);
+	bithenge_transform_dec_ref(xform);
+	return rc;
+}
+
 /** @}
  */
Index: uspace/app/bithenge/sequence.h
===================================================================
--- uspace/app/bithenge/sequence.h	(revision e3f2765d1f9f4db04f79955850663d979327ea95)
+++ uspace/app/bithenge/sequence.h	(revision c3437d984e5b4d1c7556b49b8b5e73b8b9f22382)
@@ -44,4 +44,6 @@
 int bithenge_repeat_transform(bithenge_transform_t **, bithenge_transform_t *,
     bithenge_expression_t *);
+int bithenge_do_while_transform(bithenge_transform_t **,
+    bithenge_transform_t *, bithenge_expression_t *);
 
 #endif
Index: uspace/app/bithenge/transform.c
===================================================================
--- uspace/app/bithenge/transform.c	(revision e3f2765d1f9f4db04f79955850663d979327ea95)
+++ uspace/app/bithenge/transform.c	(revision c3437d984e5b4d1c7556b49b8b5e73b8b9f22382)
@@ -201,4 +201,5 @@
 	for (int i = 0; i < out->num_params; i++)
 		bithenge_node_inc_ref(out->params[i]);
+	bithenge_node_dec_ref(out->current_node);
 	out->current_node = scope->current_node;
 	if (out->current_node)
@@ -474,4 +475,23 @@
 bithenge_transform_t bithenge_known_length_transform = {
 	&known_length_ops, 1, 1
+};
+
+static int nonzero_boolean_apply(bithenge_transform_t *self,
+    bithenge_scope_t *scope, bithenge_node_t *in, bithenge_node_t **out)
+{
+	if (bithenge_node_type(in) != BITHENGE_NODE_INTEGER)
+		return EINVAL;
+	bool value = bithenge_integer_node_value(in) != 0;
+	return bithenge_new_boolean_node(out, value);
+}
+
+static const bithenge_transform_ops_t nonzero_boolean_ops = {
+	.apply = nonzero_boolean_apply,
+	.destroy = transform_indestructible,
+};
+
+/** A transform that converts integers to booleans, true if nonzero. */
+bithenge_transform_t bithenge_nonzero_boolean_transform = {
+	&nonzero_boolean_ops, 1, 0
 };
 
@@ -602,4 +622,5 @@
 	{"ascii", &bithenge_ascii_transform},
 	{"known_length", &bithenge_known_length_transform},
+	{"nonzero_boolean", &bithenge_nonzero_boolean_transform},
 	{"uint8", &bithenge_uint8_transform},
 	{"uint16le", &bithenge_uint16le_transform},
Index: uspace/app/bithenge/transform.h
===================================================================
--- uspace/app/bithenge/transform.h	(revision e3f2765d1f9f4db04f79955850663d979327ea95)
+++ uspace/app/bithenge/transform.h	(revision c3437d984e5b4d1c7556b49b8b5e73b8b9f22382)
@@ -112,6 +112,7 @@
 
 extern bithenge_transform_t bithenge_ascii_transform;
+extern bithenge_transform_t bithenge_invalid_transform;
 extern bithenge_transform_t bithenge_known_length_transform;
-extern bithenge_transform_t bithenge_invalid_transform;
+extern bithenge_transform_t bithenge_nonzero_boolean_transform;
 extern bithenge_transform_t bithenge_uint8_transform;
 extern bithenge_transform_t bithenge_uint16le_transform;
Index: uspace/dist/src/bithenge/test-repeat.bh
===================================================================
--- uspace/dist/src/bithenge/test-repeat.bh	(revision e3f2765d1f9f4db04f79955850663d979327ea95)
+++ uspace/dist/src/bithenge/test-repeat.bh	(revision c3437d984e5b4d1c7556b49b8b5e73b8b9f22382)
@@ -7,10 +7,18 @@
 
 transform without_count = struct {
-	.error <- repeat { uint8 <- zero_terminated };
-	.end <- repeat { uint8 };
+	.until_error <- repeat { uint8 <- zero_terminated };
+	.until_end <- repeat { uint8 };
 };
+
+transform do_while = do {
+	struct {
+		.valid <- nonzero_boolean <- uint8;
+		.val <- uint8;
+	}
+} while (.valid);
 
 transform main = struct {
 	.with_count <- with_count;
-	.without_count <- without_count;
+	.without_count <- without_count <- known_length(9);
+	.do_while <- do_while;
 };
