ltm/src/ltm_template_compile.c

199 lines
5.2 KiB
C

#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include "ltm/common.h"
#include "ltm/template.h"
#include "ltm/template_internal.h"
static bool ltm_template_next_placeholder(ltm_placeholder *ph, const char *s,
size_t len) {
if (len == 0) {
return false;
}
ph->start = memchr(s, '{', len - 1);
if ((ph->start == NULL) || (ph->start[1] != '{')) {
return false;
}
size_t new_len = len - (ph->start - s);
// A template can never be valid without at least 5 characters
if (new_len < 5) {
ph->type = ltm_placeholder_type_invalid;
return true;
}
ph->end = memchr(ph->start + 2, '}', new_len - 3);
// Non-terminated placeholders aren't valid
if ((ph->end == NULL) || (ph->end[1] != '}')) {
ph->type = ltm_placeholder_type_invalid;
return true;
}
// End should point to final character
ph->end++;
// Parse the words
ph->name.s = ph->start + 2;
while ((*ph->name.s == ' ') && (ph->name.s != ph->end - 1)) {
ph->name.s++;
}
// Placeholder is empty
if (ph->name.s == ph->end - 1) {
ph->type = ltm_placeholder_type_invalid;
return true;
}
const char *ident = ph->name.s;
while ((*ident != ' ') && (ident != ph->end - 1)) {
ident++;
}
ph->name.len = ident - ph->name.s;
// Skip whitespace over to next word
while ((*ident == ' ') && (ident != ph->end - 1)) {
ident++;
}
if (ident == ph->end - 1) {
ph->type = ltm_placeholder_type_var;
} else {
// Further parse the identifier
const char *temp = ident;
while ((*temp != ' ') && (temp != ph->end - 1)) {
temp++;
}
size_t ident_len = temp - ident;
if (strncmp("start", ident, ident_len) == 0) {
ph->type = ltm_placeholder_type_nested_start;
} else if (strncmp("end", ident, ident_len) == 0) {
ph->type = ltm_placeholder_type_nested_end;
} else {
ph->type = ltm_placeholder_type_invalid;
}
}
return true;
}
ltm_err ltm_template_compile_n(ltm_template **out, const char *s, size_t len) {
ltm_template *template;
LTM_RES(ltm_template_init(&template));
ltm_placeholder ph;
bool in_nested = false;
const char *nested_start = NULL, *nested_name = NULL;
size_t nested_depth = 0, nested_name_len = 0;
size_t cur_nested_depth = 0;
while (ltm_template_next_placeholder(&ph, s, len)) {
// Add part before placeholder as literal
if (!in_nested && (ph.start != s)) {
LTM_RES2(ltm_template_block_append(template,
ltm_template_block_type_literal,
(void *)s, ph.start - s),
ltm_template_free(template));
}
switch (ph.type) {
// Invalid placeholders can be detected as early as possible
case ltm_placeholder_type_invalid:
ltm_template_free(template);
return ltm_err_invalid_template;
case ltm_placeholder_type_var:
if (!in_nested) {
LTM_RES2(ltm_template_block_append(
template, ltm_template_block_type_var, NULL, 0),
ltm_template_free(template));
LTM_RES2(ltm_template_name_append(template, ph.name.s, ph.name.len,
template->blocks.len - 1),
ltm_template_free(template));
}
break;
case ltm_placeholder_type_nested_start:
if (!in_nested) {
nested_start = ph.end + 1;
nested_depth = cur_nested_depth;
nested_name = ph.name.s;
nested_name_len = ph.name.len;
in_nested = true;
}
cur_nested_depth++;
break;
case ltm_placeholder_type_nested_end:
// This means there's more nested end placeholders than nested starts
if (cur_nested_depth == 0) {
ltm_template_free(template);
return ltm_err_invalid_template;
}
cur_nested_depth--;
if (in_nested && (cur_nested_depth == nested_depth)) {
size_t nested_len = ph.start - nested_start;
ltm_template *nested_template;
LTM_RES2(
ltm_template_compile_n(&nested_template, nested_start, nested_len),
ltm_template_free(template));
LTM_RES(ltm_template_block_append(
template, ltm_template_block_type_nested, nested_template, 0));
LTM_RES2(ltm_template_name_append(template, nested_name,
nested_name_len,
template->blocks.len - 1),
ltm_template_free(template));
in_nested = false;
}
// We encountered a nested end without a start
else if (!in_nested) {
ltm_template_free(template);
return ltm_err_invalid_template;
}
break;
}
len -= ph.end + 1 - s;
s = ph.end + 1;
}
// Unfinished nested
if (in_nested) {
ltm_template_free(template);
return ltm_err_invalid_template;
}
// Add remaining trailing literal
if (len > 0) {
LTM_RES2(ltm_template_block_append(
template, ltm_template_block_type_literal, (void *)s, len),
ltm_template_free(template));
}
*out = template;
return ltm_err_ok;
}
ltm_err ltm_template_compile(ltm_template **out, const char *template) {
return ltm_template_compile_n(out, template, strlen(template));
}