diff options
Diffstat (limited to 'modules/base/src/template_parser.c')
-rw-r--r-- | modules/base/src/template_parser.c | 386 |
1 files changed, 386 insertions, 0 deletions
diff --git a/modules/base/src/template_parser.c b/modules/base/src/template_parser.c new file mode 100644 index 000000000..605445131 --- /dev/null +++ b/modules/base/src/template_parser.c @@ -0,0 +1,386 @@ +/* + * LuCI Template - Parser implementation + * + * Copyright (C) 2009-2012 Jo-Philipp Wich <xm@subsignal.org> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "template_parser.h" +#include "template_utils.h" +#include "template_lmo.h" + + +/* leading and trailing code for different types */ +const char *gen_code[9][2] = { + { NULL, NULL }, + { "write(\"", "\")" }, + { NULL, NULL }, + { "write(tostring(", " or \"\"))" }, + { "include(\"", "\")" }, + { "write(\"", "\")" }, + { "write(\"", "\")" }, + { NULL, " " }, + { NULL, NULL }, +}; + +/* Simple strstr() like function that takes len arguments for both haystack and needle. */ +static char *strfind(char *haystack, int hslen, const char *needle, int ndlen) +{ + int match = 0; + int i, j; + + for( i = 0; i < hslen; i++ ) + { + if( haystack[i] == needle[0] ) + { + match = ((ndlen == 1) || ((i + ndlen) <= hslen)); + + for( j = 1; (j < ndlen) && ((i + j) < hslen); j++ ) + { + if( haystack[i+j] != needle[j] ) + { + match = 0; + break; + } + } + + if( match ) + return &haystack[i]; + } + } + + return NULL; +} + +struct template_parser * template_open(const char *file) +{ + struct stat s; + static struct template_parser *parser; + + if (!(parser = malloc(sizeof(*parser)))) + goto err; + + memset(parser, 0, sizeof(*parser)); + parser->fd = -1; + parser->file = file; + + if (stat(file, &s)) + goto err; + + if ((parser->fd = open(file, O_RDONLY)) < 0) + goto err; + + parser->size = s.st_size; + parser->mmap = mmap(NULL, parser->size, PROT_READ, MAP_PRIVATE, + parser->fd, 0); + + if (parser->mmap != MAP_FAILED) + { + parser->off = parser->mmap; + parser->cur_chunk.type = T_TYPE_INIT; + parser->cur_chunk.s = parser->mmap; + parser->cur_chunk.e = parser->mmap; + + return parser; + } + +err: + template_close(parser); + return NULL; +} + +void template_close(struct template_parser *parser) +{ + if (!parser) + return; + + if (parser->gc != NULL) + free(parser->gc); + + if ((parser->mmap != NULL) && (parser->mmap != MAP_FAILED)) + munmap(parser->mmap, parser->size); + + if (parser->fd >= 0) + close(parser->fd); + + free(parser); +} + +void template_text(struct template_parser *parser, const char *e) +{ + const char *s = parser->off; + + if (s < (parser->mmap + parser->size)) + { + if (parser->strip_after) + { + while ((s <= e) && isspace(*s)) + s++; + } + + parser->cur_chunk.type = T_TYPE_TEXT; + } + else + { + parser->cur_chunk.type = T_TYPE_EOF; + } + + parser->cur_chunk.line = parser->line; + parser->cur_chunk.s = s; + parser->cur_chunk.e = e; +} + +void template_code(struct template_parser *parser, const char *e) +{ + const char *s = parser->off; + + parser->strip_before = 0; + parser->strip_after = 0; + + if (*s == '-') + { + parser->strip_before = 1; + for (s++; (s <= e) && (*s == ' ' || *s == '\t'); s++); + } + + if (*(e-1) == '-') + { + parser->strip_after = 1; + for (e--; (e >= s) && (*e == ' ' || *e == '\t'); e--); + } + + switch (*s) + { + /* comment */ + case '#': + s++; + parser->cur_chunk.type = T_TYPE_COMMENT; + break; + + /* include */ + case '+': + s++; + parser->cur_chunk.type = T_TYPE_INCLUDE; + break; + + /* translate */ + case ':': + s++; + parser->cur_chunk.type = T_TYPE_I18N; + break; + + /* translate raw */ + case '_': + s++; + parser->cur_chunk.type = T_TYPE_I18N_RAW; + break; + + /* expr */ + case '=': + s++; + parser->cur_chunk.type = T_TYPE_EXPR; + break; + + /* code */ + default: + parser->cur_chunk.type = T_TYPE_CODE; + break; + } + + parser->cur_chunk.line = parser->line; + parser->cur_chunk.s = s; + parser->cur_chunk.e = e; +} + +static const char * +template_format_chunk(struct template_parser *parser, size_t *sz) +{ + const char *s, *p; + const char *head, *tail; + struct template_chunk *c = &parser->prv_chunk; + struct template_buffer *buf; + + *sz = 0; + s = parser->gc = NULL; + + if (parser->strip_before && c->type == T_TYPE_TEXT) + { + while ((c->e > c->s) && isspace(*(c->e - 1))) + c->e--; + } + + /* empty chunk */ + if (c->s == c->e) + { + if (c->type == T_TYPE_EOF) + { + *sz = 0; + s = NULL; + } + else + { + *sz = 1; + s = " "; + } + } + + /* format chunk */ + else if ((buf = buf_init(c->e - c->s)) != NULL) + { + if ((head = gen_code[c->type][0]) != NULL) + buf_append(buf, head, strlen(head)); + + switch (c->type) + { + case T_TYPE_TEXT: + luastr_escape(buf, c->s, c->e - c->s, 0); + break; + + case T_TYPE_EXPR: + buf_append(buf, c->s, c->e - c->s); + for (p = c->s; p < c->e; p++) + parser->line += (*p == '\n'); + break; + + case T_TYPE_INCLUDE: + luastr_escape(buf, c->s, c->e - c->s, 0); + break; + + case T_TYPE_I18N: + luastr_translate(buf, c->s, c->e - c->s, 1); + break; + + case T_TYPE_I18N_RAW: + luastr_translate(buf, c->s, c->e - c->s, 0); + break; + + case T_TYPE_CODE: + buf_append(buf, c->s, c->e - c->s); + for (p = c->s; p < c->e; p++) + parser->line += (*p == '\n'); + break; + } + + if ((tail = gen_code[c->type][1]) != NULL) + buf_append(buf, tail, strlen(tail)); + + *sz = buf_length(buf); + s = parser->gc = buf_destroy(buf); + + if (!*sz) + { + *sz = 1; + s = " "; + } + } + + return s; +} + +const char *template_reader(lua_State *L, void *ud, size_t *sz) +{ + struct template_parser *parser = ud; + int rem = parser->size - (parser->off - parser->mmap); + char *tag; + + parser->prv_chunk = parser->cur_chunk; + + /* free previous string */ + if (parser->gc) + { + free(parser->gc); + parser->gc = NULL; + } + + /* before tag */ + if (!parser->in_expr) + { + if ((tag = strfind(parser->off, rem, "<%", 2)) != NULL) + { + template_text(parser, tag); + parser->off = tag + 2; + parser->in_expr = 1; + } + else + { + template_text(parser, parser->mmap + parser->size); + parser->off = parser->mmap + parser->size; + } + } + + /* inside tag */ + else + { + if ((tag = strfind(parser->off, rem, "%>", 2)) != NULL) + { + template_code(parser, tag); + parser->off = tag + 2; + parser->in_expr = 0; + } + else + { + /* unexpected EOF */ + template_code(parser, parser->mmap + parser->size); + + *sz = 1; + return "\033"; + } + } + + return template_format_chunk(parser, sz); +} + +int template_error(lua_State *L, struct template_parser *parser) +{ + const char *err = luaL_checkstring(L, -1); + const char *off = parser->prv_chunk.s; + const char *ptr; + char msg[1024]; + int line = 0; + int chunkline = 0; + + if ((ptr = strfind((char *)err, strlen(err), "]:", 2)) != NULL) + { + chunkline = atoi(ptr + 2) - parser->prv_chunk.line; + + while (*ptr) + { + if (*ptr++ == ' ') + { + err = ptr; + break; + } + } + } + + if (strfind((char *)err, strlen(err), "'char(27)'", 10) != NULL) + { + off = parser->mmap + parser->size; + err = "'%>' expected before end of file"; + chunkline = 0; + } + + for (ptr = parser->mmap; ptr < off; ptr++) + if (*ptr == '\n') + line++; + + snprintf(msg, sizeof(msg), "Syntax error in %s:%d: %s", + parser->file, line + chunkline, err ? err : "(unknown error)"); + + lua_pushnil(L); + lua_pushinteger(L, line + chunkline); + lua_pushstring(L, msg); + + return 3; +} |