static int compile_append_text(lwan_tpl_t *tpl, strbuf_t *buf) { int length = strbuf_get_length(buf); if (!length) return 0; lwan_tpl_chunk_t *chunk = malloc(sizeof(*chunk)); if (!chunk) return -ENOMEM; if (length == 1) { chunk->action = TPL_ACTION_APPEND_CHAR; chunk->data = (void *)((uintptr_t)strbuf_get_buffer(buf)[0]); } else { chunk->action = TPL_ACTION_APPEND; chunk->data = strdup(strbuf_get_buffer(buf)); } chunk->next = tpl->chunks; tpl->chunks = chunk; tpl->minimum_size += strbuf_get_length(buf); strbuf_reset(buf); return 0; }
static int compile_append_var(lwan_tpl_t *tpl, strbuf_t *buf) { lwan_tpl_chunk_t *chunk = malloc(sizeof(*chunk)); if (!chunk) return -ENOMEM; char *variable = strbuf_get_buffer(buf); int length = strbuf_get_length(buf) - 1; switch (*variable) { case '>': { char template_file[PATH_MAX]; snprintf(template_file, sizeof(template_file), "%s.tpl", variable + 1); lwan_tpl_t *included = lwan_tpl_compile(template_file); if (!included) return -ENOENT; chunk->action = TPL_ACTION_APPLY_TPL; chunk->data = included; break; } case '#': chunk->action = TPL_ACTION_LIST_START_ITER; chunk->data = strdup(variable + 1); break; case '/': if (variable[length] == '?') { chunk->action = TPL_ACTION_END_IF_VARIABLE_NOT_EMPTY; variable[length] = '\0'; } else { chunk->action = TPL_ACTION_LIST_END_ITER; } chunk->data = strdup(variable + 1); break; default: if (variable[length] == '?') { chunk->action = TPL_ACTION_IF_VARIABLE_NOT_EMPTY; variable[length] = '\0'; } else { chunk->action = TPL_ACTION_VARIABLE; } chunk->data = strdup(variable); } chunk->next = tpl->chunks; tpl->chunks = chunk; tpl->minimum_size += strbuf_get_length(buf); strbuf_reset(buf); return 0; }
int main(int argc, char *argv[]) { if (argc < 2) { printf("Usage: %s file.tpl\n", argv[0]); return 1; } printf("*** Compiling template...\n"); lwan_tpl_t *tpl = lwan_tpl_compile(argv[1]); if (!tpl) return 1; printf("*** Applying template...\n"); strbuf_t *applied = lwan_tpl_apply(tpl, var_getter, NULL); puts(strbuf_get_buffer(applied)); strbuf_free(applied); lwan_tpl_free(tpl); return 0; }
size_t header_len = lwan_prepare_response_header(request, status, headers, sizeof(headers)); if (UNLIKELY(!header_len)) return lwan_default_response(request, HTTP_INTERNAL_ERROR); if (request->method == HTTP_HEAD) { if (UNLIKELY(write(request->fd, headers, header_len) < 0)) { perror("write"); return false; } return true; } struct iovec response_vec[] = { { .iov_base = headers, .iov_len = header_len }, { .iov_base = strbuf_get_buffer(request->response.buffer), .iov_len = strbuf_get_length(request->response.buffer) } }; if (UNLIKELY(writev(request->fd, response_vec, N_ELEMENTS(response_vec)) < 0)) { perror("writev"); return false; } return true; } bool lwan_default_response(lwan_request_t *request, lwan_http_status_t status) { static const char *default_response = "<html><head><style>" \ "body{" \
lwan_tpl_chunk_t * lwan_tpl_apply_until(lwan_tpl_chunk_t *chunks, strbuf_t *buf, char *(*var_get)(const char *name, void *data), void *var_get_data, bool (*until)(lwan_tpl_chunk_t *chunk, void *data), void *until_data) { lwan_tpl_chunk_t *chunk = chunks; for (; chunk; chunk = chunk->next) { if (until(chunk, until_data)) break; switch (chunk->action) { case TPL_ACTION_APPEND: strbuf_append_str(buf, chunk->data, 0); break; case TPL_ACTION_APPEND_CHAR: strbuf_append_char(buf, (char)(uintptr_t)chunk->data); break; case TPL_ACTION_VARIABLE: { char *tmp = var_get((const char*)chunk->data, var_get_data); strbuf_append_str(buf, tmp, 0); free(tmp); } break; case TPL_ACTION_LIST_START_ITER: strbuf_append_str(buf, "[begin_iter:", 0); strbuf_append_str(buf, chunk->data, 0); strbuf_append_str(buf, "]", 0); break; case TPL_ACTION_LIST_END_ITER: strbuf_append_str(buf, "[end_iter:", 0); strbuf_append_str(buf, chunk->data, 0); strbuf_append_str(buf, "]", 0); break; case TPL_ACTION_IF_VARIABLE_NOT_EMPTY: { const char *var_name = (const char*)chunk->data; char *tmp = var_get(var_name, var_get_data); if (tmp && *tmp) { chunk = lwan_tpl_apply_until(chunk->next, buf, var_get, var_get_data, until_not_empty, chunk->data); } else { for (chunk = chunk->next; chunk; chunk = chunk->next) { if (chunk->action == TPL_ACTION_END_IF_VARIABLE_NOT_EMPTY && !strcmp(chunk->data, var_name)) break; } } free(tmp); } break; case TPL_ACTION_END_IF_VARIABLE_NOT_EMPTY: /* Shouldn't happen */ break; case TPL_ACTION_APPLY_TPL: { strbuf_t *tmp = lwan_tpl_apply(chunk->data, var_get, var_get_data); strbuf_append_str(buf, strbuf_get_buffer(tmp), strbuf_get_length(tmp)); strbuf_free(tmp); } } } return chunk; }
lwan_tpl_t * lwan_tpl_compile(const char *filename) { lwan_tpl_t *tpl; strbuf_t *buf; FILE *file; int state = STATE_DEFAULT; char error_msg[512]; tpl = calloc(1, sizeof(*tpl)); if (!tpl) return NULL; buf = strbuf_new(); if (!buf) { free(tpl); return NULL; } file = fopen(filename, "r"); if (!file) { strbuf_free(buf); free(tpl); return NULL; } int line = 1; int column = 1; char ch; while ((ch = fgetc(file)) != EOF) { if (ch == '\n') { if (state == STATE_DEFAULT) strbuf_append_char(buf, '\n'); line++; column = 1; continue; } ++column; switch (state) { case STATE_DEFAULT: if (ch == '{') { state = STATE_FIRST_BRACE; continue; } strbuf_append_char(buf, ch); break; case STATE_FIRST_BRACE: if (ch == '{') { switch (compile_append_text(tpl, buf)) { case -ENOMEM: PARSE_ERROR("Out of memory while appending text."); } state = STATE_SECOND_BRACE; continue; } strbuf_append_char(buf, '{'); strbuf_append_char(buf, ch); state = STATE_DEFAULT; break; case STATE_SECOND_BRACE: if (ch == '{') PARSE_ERROR("Unexpected open brace."); if (ch == '}') { state = STATE_FIRST_CLOSING_BRACE; continue; } strbuf_append_char(buf, ch); break; case STATE_FIRST_CLOSING_BRACE: if (ch == '}') { state = STATE_SECOND_CLOSING_BRACE; continue; } PARSE_ERROR("Closing brace expected."); case STATE_SECOND_CLOSING_BRACE: if (ch == '}') PARSE_ERROR("Unexpected close brace."); if (strbuf_get_length(buf) == 0) PARSE_ERROR("Expecting variable name."); switch (compile_append_var(tpl, buf)) { case -ENOMEM: PARSE_ERROR("Out of memory while appending variable."); case -ENOENT: PARSE_ERROR("Cannot find included template: ``%s''.", strbuf_get_buffer(buf) + 1); } if (ch == '{') { state = STATE_FIRST_BRACE; continue; } strbuf_append_char(buf, ch); state = STATE_DEFAULT; } } switch (state) { case STATE_DEFAULT: switch (compile_append_text(tpl, buf)) { case -ENOMEM: PARSE_ERROR("Out of memory while appending text."); } break; case STATE_FIRST_BRACE: case STATE_SECOND_BRACE: PARSE_ERROR("Expecting close brace."); case STATE_FIRST_CLOSING_BRACE: PARSE_ERROR("Expecting second close brace."); case STATE_SECOND_CLOSING_BRACE: if (strbuf_get_length(buf) == 0) PARSE_ERROR("Expecting variable name."); switch (compile_append_var(tpl, buf)) { case -ENOMEM: PARSE_ERROR("Out of memory while appending variable."); case -ENOENT: PARSE_ERROR("Cannot find included template: ``%s''.", strbuf_get_buffer(buf)); } } lwan_tpl_chunk_t *last = malloc(sizeof(*last)); if (!last) goto error; last->action = TPL_ACTION_LAST; last->data = NULL; last->next = tpl->chunks; tpl->chunks = last; lwan_tpl_chunk_t *prev = NULL; while (tpl->chunks) { lwan_tpl_chunk_t *next = tpl->chunks->next; tpl->chunks->next = prev; prev = tpl->chunks; tpl->chunks = next; } tpl->chunks = prev; strbuf_free(buf); return tpl; error: lwan_tpl_free(tpl); strbuf_free(buf); fclose(file); printf("Line %d, column %d: %s\n", line, column, error_msg); return NULL; }
static void *parse_key_value(struct parser *parser) { struct config_line line = { .type = CONFIG_LINE_TYPE_LINE }; struct lexeme *lexeme; size_t key_size; while (lexeme_buffer_consume(&parser->buffer, &lexeme)) { strbuf_append_str(&parser->strbuf, lexeme->value.value, lexeme->value.len); if (parser->buffer.population >= 1) strbuf_append_char(&parser->strbuf, '_'); } key_size = strbuf_get_length(&parser->strbuf); strbuf_append_char(&parser->strbuf, '\0'); while (lex_next(&parser->lexer, &lexeme)) { switch (lexeme->type) { case LEXEME_VARIABLE: { const char *value; value = secure_getenv_len(lexeme->value.value, lexeme->value.len); if (!value) { lwan_status_error("Variable '$%.*s' not defined in environment", (int)lexeme->value.len, lexeme->value.value); return NULL; } strbuf_append_str(&parser->strbuf, value, 0); break; } case LEXEME_EQUAL: strbuf_append_char(&parser->strbuf, '='); break; case LEXEME_STRING: strbuf_append_str(&parser->strbuf, lexeme->value.value, lexeme->value.len); break; case LEXEME_CLOSE_BRACKET: backup(&parser->lexer); /* fallthrough */ case LEXEME_LINEFEED: line.key = strbuf_get_buffer(&parser->strbuf); line.value = line.key + key_size + 1; if (!config_buffer_emit(&parser->items, &line)) return NULL; return parse_config; default: lwan_status_error("Unexpected token while parsing key-value: %s", lexeme_type_str[lexeme->type]); return NULL; } } lwan_status_error("EOF while parsing key-value"); return NULL; }