diff --git a/ltm/README.md b/ltm/README.md
new file mode 100644
index 0000000..6f0a86c
--- /dev/null
+++ b/ltm/README.md
@@ -0,0 +1,32 @@
+# LTM - Lander Template Module
+
+This module provides an interface for generating `char` streams from a given
+template.
+
+```html
+
+ {{ title }}
+
+
+```
+
+The above snippet shows everything the templating language currently offers;
+either singular variables (here: `title`) or nested templates (here: `item`).
+Note the absence of a loop construct. This is because each variable and nested
+template is implicitely already a loop; a variable or a nested template can be
+added as many times as needed, and they will appear in the order they were
+added.
+
+Template strings are compiled into templates. From a template, an instance can
+be created to which the required data can be added to fill in the template.
+Instantiating a template does not modify the original template, meaning
+templates can be safely used concurrently.
+
+Each inserted variable can get its data from a variety of sources, namely
+`char` buffers, files or a custom reader function. The resulting `char` stream
+is never fully loaded into memory; an instance can stream its content to a
+provided buffer to prevent unnecessary memory usage.
diff --git a/ltm/src/_include/ltm/template_internal.h b/ltm/src/_include/ltm/template_internal.h
index 4c651ae..f095183 100644
--- a/ltm/src/_include/ltm/template_internal.h
+++ b/ltm/src/_include/ltm/template_internal.h
@@ -96,6 +96,12 @@ typedef struct ltm_instance_block {
ltm_err ltm_instance_block_init(ltm_instance_block **out);
+/**
+ * Add a new block to the instance with the given type.
+ */
+ltm_err ltm_instance_block_add(ltm_instance_block **out, ltm_instance *instance,
+ const char *name, ltm_instance_block_type type);
+
struct ltm_instance {
const ltm_template *template;
struct {
diff --git a/ltm/src/ltm_instance.c b/ltm/src/ltm_instance.c
index f0c27fa..1c23bd7 100644
--- a/ltm/src/ltm_instance.c
+++ b/ltm/src/ltm_instance.c
@@ -106,7 +106,9 @@ ltm_err ltm_template_instantiate(ltm_instance **out,
} break;
case ltm_template_block_type_var:
case ltm_template_block_type_nested:
- *vars = &instance->blocks.tail->next;
+ // Account for the possibility a template starts with a placeholder
+ *vars = instance->blocks.tail != NULL ? &instance->blocks.tail->next
+ : &instance->blocks.head;
vars++;
break;
}
@@ -117,20 +119,21 @@ ltm_err ltm_template_instantiate(ltm_instance **out,
return ltm_err_ok;
}
-ltm_err ltm_instance_block_add_var(ltm_instance *instance, const char *name,
- ltm_instance_block_type type, void *data,
- size_t len) {
+ltm_err ltm_instance_block_add(ltm_instance_block **out, ltm_instance *instance,
+ const char *name, ltm_instance_block_type type) {
const ltm_template *template = instance->template;
ltm_template_block_name *block_name = NULL;
size_t i = 0;
- for (i = 0; i < template->names.len; i++) {
+ while (i < template->names.len) {
block_name = &template->names.arr[i];
if (strncmp(name, block_name->name.s, block_name->name.len) == 0) {
break;
}
+
+ i++;
}
if (i == template->names.len) {
@@ -138,8 +141,22 @@ ltm_err ltm_instance_block_add_var(ltm_instance *instance, const char *name,
}
ltm_template_block *template_block = &template->blocks.arr[block_name->index];
+ bool correct_type = false;
- if (template_block->type != ltm_template_block_type_var) {
+ switch (type) {
+ case ltm_instance_block_type_nested:
+ correct_type = template_block->type == ltm_template_block_type_nested;
+ break;
+ case ltm_instance_block_type_buf:
+ case ltm_instance_block_type_buf_owned:
+ case ltm_instance_block_type_file:
+ case ltm_instance_block_type_file_owned:
+ case ltm_instance_block_type_fn:
+ correct_type = template_block->type == ltm_template_block_type_var;
+ break;
+ }
+
+ if (!correct_type) {
return ltm_err_wrong_block_type;
}
@@ -147,57 +164,39 @@ ltm_err ltm_instance_block_add_var(ltm_instance *instance, const char *name,
LTM_RES(ltm_instance_block_init(&block));
block->type = type;
- block->data.ptr = data;
- block->data.len = len;
- // We insert the block in the linked list and replace its next pointer as the
- // new attachment point for this variable
+ // Insert the block in the linked list
block->next = *instance->vars[i];
*instance->vars[i] = block;
instance->vars[i] = &block->next;
+ *out = block;
+
+ return ltm_err_ok;
+}
+
+ltm_err ltm_instance_block_add_var(ltm_instance *instance, const char *name,
+ ltm_instance_block_type type, void *data,
+ size_t len) {
+ ltm_instance_block *block;
+ LTM_RES(ltm_instance_block_add(&block, instance, name, type));
+
+ block->data.ptr = data;
+ block->data.len = len;
+
return ltm_err_ok;
}
ltm_err ltm_instance_block_add_var_fn(ltm_instance *instance, const char *name,
ltm_data_fn fn, void *data, size_t len) {
- const ltm_template *template = instance->template;
-
- ltm_template_block_name *block_name = NULL;
- size_t i = 0;
-
- for (i = 0; i < template->names.len; i++) {
- block_name = &template->names.arr[i];
-
- if (strncmp(name, block_name->name.s, block_name->name.len) == 0) {
- break;
- }
- }
-
- if (i == template->names.len) {
- return ltm_err_not_found;
- }
-
- ltm_template_block *template_block = &template->blocks.arr[block_name->index];
-
- if (template_block->type != ltm_template_block_type_var) {
- return ltm_err_wrong_block_type;
- }
-
ltm_instance_block *block;
- LTM_RES(ltm_instance_block_init(&block));
+ LTM_RES(ltm_instance_block_add(&block, instance, name,
+ ltm_instance_block_type_fn));
- block->type = ltm_instance_block_type_fn;
block->fn = fn;
block->data.ptr = data;
block->data.len = len;
- // We insert the block in the linked list and replace its next pointer as the
- // new attachment point for this variable
- block->next = *instance->vars[i];
- *instance->vars[i] = block;
- instance->vars[i] = &block->next;
-
return ltm_err_ok;
}
@@ -205,19 +204,25 @@ ltm_err ltm_instance_block_add_nested(ltm_instance **out,
ltm_instance *instance,
const char *name) {
const ltm_template *template = instance->template;
- bool matched = false;
+ ltm_template_block_name *block_name = NULL;
size_t i = 0;
- for (i = 0; i < template->names.len && !matched; i++) {
- ltm_template_block_name *block_name = &template->names.arr[i];
- matched = strncmp(name, block_name->name.s, block_name->name.len) == 0;
+
+ while (i < template->names.len) {
+ block_name = &template->names.arr[i];
+
+ if (strncmp(name, block_name->name.s, block_name->name.len) == 0) {
+ break;
+ }
+
+ i++;
}
- if (!matched) {
+ if (i == template->names.len) {
return ltm_err_not_found;
}
- ltm_template_block *template_block = &template->blocks.arr[i];
+ ltm_template_block *template_block = &template->blocks.arr[block_name->index];
if (template_block->type != ltm_template_block_type_nested) {
return ltm_err_wrong_block_type;