Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • 0x4A6F-master
  • 0x4A6F-rpi4
  • autinerd/experimental-openwrt-24.10
  • experimental
  • feature/addMikrotikwAP
  • master
  • nrb/airmax-test
  • nrb/ar9344-reset-sequence
  • nrb/ex400-remove-wps
  • nrb/gluon-master-cpe510
  • nrb/test-radv-filter
  • nrbffs/fastd-remove-delay
  • nrbffs/netgear-ex6120
  • v2018.2.2-ffs
  • v2018.2.3-ffs
  • v2019.1-ffs
  • v2019.1.1-ffs
  • v2019.1.2-ffs
  • v2020.1-ffs
  • v2020.1.1-ffs
  • v2020.1.3-ffs
  • v2020.2-ffs
  • v2020.2.1-ffs
  • v2020.2.2-ffs
  • v2020.2.3-ffs
  • v2021.1-ffs
  • v2021.1.1-ffs
  • v2021.1.2-ffs
  • v2022.1.1-ffs
  • v2022.1.3-ffs
  • v2022.1.4-ffs
  • v2023.1-ffs
  • v2023.2-ffs
  • v2023.2.2-ffs
  • v2023.2.3-ffs
  • v2023.2.4-ffs
  • v2023.2.5-ffs
  • experimental-2022-09-24
  • experimental-2022-09-24-base
  • experimental-2023-03-11
  • experimental-2023-03-11-base
  • experimental-2023-03-12
  • experimental-2023-03-12-base
  • experimental-2023-03-16
  • experimental-2023-03-16-base
  • experimental-2023-03-20
  • experimental-2023-03-20-base
  • experimental-2023-03-23
  • experimental-2023-03-23-base
  • experimental-2023-03-25
  • experimental-2023-03-25-base
  • experimental-2023-03-26
  • experimental-2023-03-26-base
  • experimental-2023-03-30
  • experimental-2023-03-30-base
  • experimental-2023-03-31
  • experimental-2023-03-31-base
  • experimental-2023-04-01
  • experimental-2023-04-01-base
  • experimental-2023-04-08
  • experimental-2023-04-08-base
  • experimental-2023-04-10
  • experimental-2023-04-10-base
  • experimental-2023-04-13
  • experimental-2023-04-13-base
  • experimental-2023-04-15
  • experimental-2023-04-15-base
  • experimental-2023-04-16
  • experimental-2023-04-16-base
  • experimental-2023-04-18
  • experimental-2023-04-18-base
  • experimental-2023-04-20
  • experimental-2023-04-20-base
  • experimental-2023-04-26
  • experimental-2023-04-26-base
  • experimental-2023-04-28
  • experimental-2023-04-28-base
  • experimental-2023-04-30
  • experimental-2023-04-30-base
  • experimental-2023-05-02
  • experimental-2023-05-02-base
  • experimental-2023-05-03
  • experimental-2023-05-03-base
  • experimental-2023-05-12
  • experimental-2023-05-12-base
  • experimental-2023-05-21
  • experimental-2023-05-21-base
  • experimental-2023-05-25
  • experimental-2023-05-25-base
  • experimental-2023-07-02
  • experimental-2023-07-02-base
  • experimental-2023-07-04
  • experimental-2023-07-04-base
  • experimental-2023-07-12
  • experimental-2023-07-12-base
  • experimental-2023-07-16
  • experimental-2023-07-16-base
  • experimental-2023-08-04
  • experimental-2023-08-04-base
  • experimental-2023-08-10
  • experimental-2023-08-10-base
  • experimental-2023-09-08
  • experimental-2023-09-08-base
  • experimental-2023-09-09
  • experimental-2023-09-09-base
  • experimental-2023-09-10
  • experimental-2023-09-10-base
  • experimental-2023-09-11
  • experimental-2023-09-11-base
  • experimental-2023-09-12
  • experimental-2023-09-12-base
  • experimental-2023-09-13
  • experimental-2023-09-13-base
  • experimental-2023-09-15
  • experimental-2023-09-15-base
  • experimental-2023-09-16
  • experimental-2023-09-16-base
  • experimental-2023-09-18
  • experimental-2023-09-18-base
  • experimental-2023-09-20
  • experimental-2023-09-20-base
  • experimental-2023-09-27
  • experimental-2023-09-27-base
  • experimental-2023-09-28
  • experimental-2023-09-28-base
  • experimental-2023-09-29
  • experimental-2023-09-29-base
  • experimental-2023-10-02
  • experimental-2023-10-02-base
  • experimental-2023-10-13
  • experimental-2023-10-13-base
  • experimental-2023-10-14
  • experimental-2023-10-14-base
  • experimental-2023-10-16
  • experimental-2023-10-16-base
  • experimental-2023-10-23
  • experimental-2023-10-23-base
137 results

Target

Select target project
  • firmware/gluon
  • 0x4A6F/gluon
  • patrick/gluon
3 results
Select Git revision
  • 0x4A6F-master
  • 0x4A6F-rpi4
  • 2014.3.x
  • 2014.4.x
  • babel
  • hoodselector
  • master
  • radv-filterd
  • v2015.1.x
  • v2016.1.x
  • v2016.2.4-batmanbug
  • v2016.2.x
  • v2018.2.2-ffs
  • v2018.2.x
  • v2014.1
  • v2014.2
  • v2014.3
  • v2014.3.1
  • v2014.4
  • v2015.1
  • v2015.1.1
  • v2015.1.2
  • v2016.1
  • v2016.1.1
  • v2016.1.2
  • v2016.1.3
  • v2016.1.4
  • v2016.1.5
  • v2016.1.6
  • v2016.2
  • v2016.2.1
  • v2016.2.2
  • v2016.2.3
  • v2016.2.4
  • v2016.2.5
  • v2016.2.6
  • v2016.2.7
  • v2017.1
  • v2017.1.1
  • v2017.1.2
  • v2017.1.3
  • v2017.1.4
  • v2017.1.5
  • v2017.1.6
  • v2017.1.7
  • v2017.1.8
  • v2018.1
  • v2018.1.1
  • v2018.1.2
  • v2018.1.3
  • v2018.1.4
  • v2018.2
  • v2018.2-ffs0.1
  • v2018.2.1
  • v2018.2.1-ffs0.1
  • v2018.2.2-ffs0.1
56 results
Show changes
-- Copyright 2018 Matthias Schiffer <mschiffer@universe-factory.net>
-- Licensed to the public under the Apache License 2.0.
local tparser = require 'gluon.web.template.parser'
local unistd = require 'posix.unistd'
return function(config)
local i18ndir = config.base_path .. "/i18n"
local function i18n_file(lang, pkg)
return string.format('%s/%s.%s.lmo', i18ndir, pkg, lang)
end
local function no_translation()
return nil
end
local function load_catalog(lang, pkg)
if pkg then
local file = i18n_file(lang, pkg)
local cat = unistd.access(file) and tparser.load_catalog(file)
if cat then return cat end
end
return no_translation
end
local i18n = {}
function i18n.supported(lang)
return lang == 'en' or unistd.access(i18n_file(lang, 'gluon-web'))
end
function i18n.load(lang, pkg)
local _translate = load_catalog(lang, pkg)
local function translate(key)
return _translate(key) or key
end
local function translatef(key, ...)
return translate(key):format(...)
end
return {
_translate = _translate,
translate = translate,
translatef = translatef,
}
end
return i18n
end
-- Copyright 2008 Steven Barth <steven@midlink.org>
-- Copyright 2017-2018 Matthias Schiffer <mschiffer@universe-factory.net>
-- Licensed to the public under the Apache License 2.0.
local tparser = require 'gluon.web.template.parser'
local tostring, ipairs, setmetatable, setfenv = tostring, ipairs, setmetatable, setfenv
local pcall, assert = pcall, assert
return function(config, env)
local i18n = require('gluon.web.i18n')(config)
local viewdir = config.base_path .. '/view/'
local ctx = {}
local language = 'en'
local catalogs = {}
function ctx.set_language(langs)
for _, lang in ipairs(langs) do
if i18n.supported(lang) then
language = lang
catalogs = {}
return
end
end
end
function ctx.i18n(pkg)
local cat = catalogs[pkg] or i18n.load(language, pkg)
if pkg then catalogs[pkg] = cat end
return cat
end
local function render_template(name, template, scope, pkg)
scope = scope or {}
local t = ctx.i18n(pkg)
local locals = {
renderer = ctx,
i18n = ctx.i18n,
translate = t.translate,
translatef = t.translatef,
_translate = t._translate,
include = function(include_name)
ctx.render(include_name, scope, pkg)
end,
}
setfenv(template, setmetatable({}, {
__index = function(_, key)
return scope[key] or locals[key] or env[key]
end
}))
-- Now finally render the thing
local stat, err = pcall(template)
assert(stat, "Failed to execute template '" .. name .. "'.\n" ..
"A runtime error occurred: " .. tostring(err or "(nil)"))
end
--- Render a certain template.
-- @param name Template name
-- @param scope Scope to assign to template (optional)
-- @param pkg i18n namespace package (optional)
function ctx.render(name, scope, pkg)
local sourcefile = viewdir .. name .. ".html"
local template, _, err = tparser.parse(sourcefile)
assert(template, "Failed to load template '" .. name .. "'.\n" ..
"Error while parsing template '" .. sourcefile .. "':\n" ..
(err or "Unknown syntax error"))
render_template(name, template, scope, pkg)
end
--- Render a template from a string.
-- @param template Template string
-- @param scope Scope to assign to template (optional)
-- @param pkg i18n namespace package (optional)
function ctx.render_string(str, scope, pkg)
local template, _, err = tparser.parse_string(str)
assert(template, "Error while parsing template:\n" ..
(err or "Unknown syntax error"))
render_template('(local)', template, scope, pkg)
end
--- Render a template, wrapped in the configured layout.
-- @param name Template name
-- @param scope Scope to assign to template (optional)
-- @param pkg i18n namespace package (optional)
-- @param layout_scope Additional variables to pass to the layout template
function ctx.render_layout(name, scope, pkg, layout_scope)
ctx.render(config.layout_template, setmetatable({
content = name,
scope = scope,
pkg = pkg,
}, {
__index = layout_scope
}), config.layout_package)
end
return ctx
end
-- Copyright 2008 Steven Barth <steven@midlink.org>
-- Copyright 2017 Matthias Schiffer <mschiffer@universe-factory.net>
-- Licensed to the public under the Apache License 2.0.
local tparser = require "gluon.web.template.parser"
local M = {}
--
-- Class helper routines
--
-- Instantiates a class
local function _instantiate(class, ...)
local inst = setmetatable({}, {__index = class})
if inst.__init__ then
inst:__init__(...)
end
return inst
end
-- The class object can be instantiated by calling itself.
-- Any class functions or shared parameters can be attached to this object.
-- Attaching a table to the class object makes this table shared between
-- all instances of this class. For object parameters use the __init__ function.
-- Classes can inherit member functions and values from a base class.
-- Class can be instantiated by calling them. All parameters will be passed
-- to the __init__ function of this class - if such a function exists.
-- The __init__ function must be used to set any object parameters that are not shared
-- with other objects of this class. Any return values will be ignored.
function M.class(base)
return setmetatable({}, {
__call = _instantiate,
__index = base
})
end
function M.instanceof(object, class)
while object do
if object == class then
return true
end
local mt = getmetatable(object)
object = mt and mt.__index
end
return false
end
--
-- String and data manipulation routines
--
function M.pcdata(value)
return value and tparser.pcdata(tostring(value))
end
return M
all: compile
%.o: %.c
$(CC) $(CPPFLAGS) $(CFLAGS) -D_GNU_SOURCE -std=c99 -Wall -Wextra -fPIC -fvisibility=hidden -c -o $@ $<
clean:
rm -f parser.so *.o
parser.so: template_parser.o template_utils.o template_lmo.o template_lualib.o
$(CC) $(LDFLAGS) -shared -o $@ $^
gluon-po2lmo: gluon-po2lmo.o template_lmo.o
compile: parser.so
install: compile
mkdir -p $(DESTDIR)/usr/lib/lua/gluon/web/template
cp parser.so $(DESTDIR)/usr/lib/lua/gluon/web/template/parser.so
/*
* lmo - Lua Machine Objects - PO to LMO conversion tool
*
* Copyright (C) 2009-2012 Jo-Philipp Wich <jow@openwrt.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_lmo.h"
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
__attribute__((noreturn))
static void die(const char *msg)
{
fprintf(stderr, "Error: %s\n", msg);
exit(1);
}
__attribute__((noreturn))
static void usage(const char *name)
{
fprintf(stderr, "Usage: %s input.po output.lmo\n", name);
exit(1);
}
static void print(const void *ptr, size_t size, size_t nmemb, FILE *stream)
{
if( fwrite(ptr, size, nmemb, stream) == 0 )
die("Failed to write stdout");
}
static ssize_t extract_string(const char *src, char *dest, size_t len)
{
size_t pos = 0;
int esc = 0;
int off = -1;
for( pos = 0; (pos < strlen(src)) && (pos < len); pos++ )
{
if( (off == -1) && (src[pos] == '"') )
{
off = pos + 1;
}
else if( off >= 0 )
{
if( esc == 1 )
{
switch (src[pos])
{
case '"':
case '\\':
off++;
break;
}
dest[pos-off] = src[pos];
esc = 0;
}
else if( src[pos] == '\\' )
{
dest[pos-off] = src[pos];
esc = 1;
}
else if( src[pos] != '"' )
{
dest[pos-off] = src[pos];
}
else
{
dest[pos-off] = '\0';
break;
}
}
}
return (off > -1) ? (ssize_t) strlen(dest) : -1;
}
static int cmp_index(const void *a, const void *b)
{
uint32_t x = ((const lmo_entry_t *)a)->key_id;
uint32_t y = ((const lmo_entry_t *)b)->key_id;
if (x < y)
return -1;
else if (x > y)
return 1;
return 0;
}
static void print_uint32(uint32_t x, FILE *out)
{
uint32_t y = htonl(x);
print(&y, sizeof(uint32_t), 1, out);
}
static void print_index(void *array, int n, FILE *out)
{
lmo_entry_t *e;
qsort(array, n, sizeof(*e), cmp_index);
for (e = array; n > 0; n--, e++)
{
print_uint32(e->key_id, out);
print_uint32(e->val_id, out);
print_uint32(e->offset, out);
print_uint32(e->length, out);
}
}
int main(int argc, char *argv[])
{
char line[4096];
char key[4096];
char val[4096];
char tmp[4096];
int state = 0;
int offset = 0;
int length = 0;
int n_entries = 0;
void *array = NULL;
lmo_entry_t *entry = NULL;
uint32_t key_id, val_id;
FILE *in;
FILE *out;
if( (argc != 3) || ((in = fopen(argv[1], "r")) == NULL) || ((out = fopen(argv[2], "w")) == NULL) )
usage(argv[0]);
memset(line, 0, sizeof(key));
memset(key, 0, sizeof(val));
memset(val, 0, sizeof(val));
while( (NULL != fgets(line, sizeof(line), in)) || (state >= 2 && feof(in)) )
{
if( state == 0 && strstr(line, "msgid \"") == line )
{
switch(extract_string(line, key, sizeof(key)))
{
case -1:
die("Syntax error in msgid");
case 0:
state = 1;
break;
default:
state = 2;
}
}
else if( state == 1 || state == 2 )
{
if( strstr(line, "msgstr \"") == line || state == 2 )
{
switch(extract_string(line, val, sizeof(val)))
{
case -1:
state = 4;
break;
default:
state = 3;
}
}
else
{
switch(extract_string(line, tmp, sizeof(tmp)))
{
case -1:
state = 2;
break;
default:
strcat(key, tmp);
}
}
}
else if( state == 3 )
{
switch(extract_string(line, tmp, sizeof(tmp)))
{
case -1:
state = 4;
break;
default:
strcat(val, tmp);
}
}
if( state == 4 )
{
if( strlen(key) > 0 && strlen(val) > 0 )
{
key_id = sfh_hash(key, strlen(key));
val_id = sfh_hash(val, strlen(val));
if( key_id != val_id )
{
n_entries++;
array = realloc(array, n_entries * sizeof(lmo_entry_t));
entry = (lmo_entry_t *)array + n_entries - 1;
if (!array)
die("Out of memory");
entry->key_id = key_id;
entry->val_id = val_id;
entry->offset = offset;
entry->length = strlen(val);
length = strlen(val) + ((4 - (strlen(val) % 4)) % 4);
print(val, length, 1, out);
offset += length;
}
}
state = 0;
memset(key, 0, sizeof(key));
memset(val, 0, sizeof(val));
}
memset(line, 0, sizeof(line));
}
print_index(array, n_entries, out);
if( offset > 0 )
{
print_uint32(offset, out);
fsync(fileno(out));
fclose(out);
}
else
{
fclose(out);
unlink(argv[2]);
}
fclose(in);
return(0);
}
/*
* lmo - Lua Machine Objects - Base functions
*
* Copyright (C) 2009-2010 Jo-Philipp Wich <jow@openwrt.org>
* Copyright (C) 2018 Matthias Schiffer <mschiffer@universe-factory.net>
*
* 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_lmo.h"
#include <sys/stat.h>
#include <sys/mman.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
static inline uint16_t get_le16(const void *data) {
const uint8_t *d = data;
return (((uint16_t)d[1]) << 8) | d[0];
}
static inline uint32_t get_be32(const void *data) {
const uint8_t *d = data;
return (((uint32_t)d[0]) << 24)
| (((uint32_t)d[1]) << 16)
| (((uint32_t)d[2]) << 8)
| d[3];
}
/*
* Hash function from http://www.azillionmonkeys.com/qed/hash.html
* Copyright (C) 2004-2008 by Paul Hsieh
*/
uint32_t sfh_hash(const void *input, size_t len)
{
const uint8_t *data = input;
uint32_t hash = len, tmp;
/* Main loop */
for (; len > 3; len -= 4) {
hash += get_le16(data);
tmp = (get_le16(data+2) << 11) ^ hash;
hash = (hash << 16) ^ tmp;
data += 4;
hash += hash >> 11;
}
/* Handle end cases */
switch (len) {
case 3: hash += get_le16(data);
hash ^= hash << 16;
hash ^= data[2] << 18;
hash += hash >> 11;
break;
case 2: hash += get_le16(data);
hash ^= hash << 11;
hash += hash >> 17;
break;
case 1: hash += *data;
hash ^= hash << 10;
hash += hash >> 1;
}
/* Force "avalanching" of final 127 bits */
hash ^= hash << 3;
hash += hash >> 5;
hash ^= hash << 4;
hash += hash >> 17;
hash ^= hash << 25;
hash += hash >> 6;
return hash;
}
bool lmo_load(lmo_catalog_t *cat, const char *file)
{
int fd = -1;
struct stat s;
cat->data = MAP_FAILED;
fd = open(file, O_RDONLY|O_CLOEXEC);
if (fd < 0)
goto err;
if (fstat(fd, &s))
goto err;
cat->data = mmap(NULL, s.st_size, PROT_READ, MAP_SHARED, fd, 0);
close(fd);
fd = -1;
if (cat->data == MAP_FAILED)
goto err;
cat->end = cat->data + s.st_size;
uint32_t idx_offset = get_be32(cat->end - sizeof(uint32_t));
cat->index = (const lmo_entry_t *)(cat->data + idx_offset);
if ((const char *)cat->index > (cat->end - sizeof(uint32_t)))
goto err;
cat->length = (cat->end - sizeof(uint32_t) - (const char *)cat->index) / sizeof(lmo_entry_t);
return true;
err:
if (fd >= 0)
close(fd);
if (cat->data != MAP_FAILED)
munmap(cat->data, cat->end - cat->data);
return false;
}
void lmo_unload(lmo_catalog_t *cat)
{
if (cat->data != MAP_FAILED)
munmap(cat->data, cat->end - cat->data);
}
static int lmo_compare_entry(const void *a, const void *b)
{
const lmo_entry_t *ea = a, *eb = b;
uint32_t ka = ntohl(ea->key_id), kb = ntohl(eb->key_id);
if (ka < kb)
return -1;
else if (ka > kb)
return 1;
else
return 0;
}
static const lmo_entry_t * lmo_find_entry(const lmo_catalog_t *cat, uint32_t hash)
{
lmo_entry_t key;
key.key_id = htonl(hash);
return bsearch(&key, cat->index, cat->length, sizeof(lmo_entry_t), lmo_compare_entry);
}
bool lmo_translate(const lmo_catalog_t *cat, const char *key, size_t keylen, const char **out, size_t *outlen)
{
uint32_t hash = sfh_hash(key, keylen);
const lmo_entry_t *e = lmo_find_entry(cat, hash);
if (!e)
return false;
*out = cat->data + ntohl(e->offset);
*outlen = ntohl(e->length);
if (*out + *outlen > cat->end)
return false;
return true;
}
/*
* lmo - Lua Machine Objects - General header
*
* Copyright (C) 2009-2012 Jo-Philipp Wich <jow@openwrt.org>
* Copyright (C) 2018 Matthias Schiffer <mschiffer@universe-factory.net>
*
* 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.
*/
#ifndef _TEMPLATE_LMO_H_
#define _TEMPLATE_LMO_H_
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
struct lmo_entry {
uint32_t key_id;
uint32_t val_id;
uint32_t offset;
uint32_t length;
} __attribute__((packed));
typedef struct lmo_entry lmo_entry_t;
struct lmo_catalog {
size_t length;
const lmo_entry_t *index;
char *data;
const char *end;
};
typedef struct lmo_catalog lmo_catalog_t;
uint32_t sfh_hash(const void *input, size_t len);
bool lmo_load(lmo_catalog_t *cat, const char *file);
void lmo_unload(lmo_catalog_t *cat);
bool lmo_translate(const lmo_catalog_t *cat, const char *key, size_t keylen, const char **out, size_t *outlen);
#endif
/*
* gluon-web Template - Lua binding
*
* Copyright (C) 2009 Jo-Philipp Wich <jow@openwrt.org>
* Copyright (C) 2018 Matthias Schiffer <mschiffer@universe-factory.net>
*
* 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"
#include <lualib.h>
#include <lauxlib.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#define TEMPLATE_CATALOG "gluon.web.template.parser.catalog"
static int template_L_do_parse(lua_State *L, struct template_parser *parser, const char *chunkname)
{
int lua_status, rv;
if (!parser)
{
lua_pushnil(L);
lua_pushinteger(L, errno);
lua_pushstring(L, strerror(errno));
return 3;
}
lua_status = lua_load(L, template_reader, parser, chunkname);
if (lua_status == 0)
rv = 1;
else
rv = template_error(L, parser);
template_close(parser);
return rv;
}
static int template_L_parse(lua_State *L)
{
const char *file = luaL_checkstring(L, 1);
struct template_parser *parser = template_open(file);
return template_L_do_parse(L, parser, file);
}
static int template_L_parse_string(lua_State *L)
{
size_t len;
const char *str = luaL_checklstring(L, 1, &len);
struct template_parser *parser = template_string(str, len);
return template_L_do_parse(L, parser, "[string]");
}
static int template_L_pcdata(lua_State *L)
{
size_t inlen, outlen;
char *out;
const char *in = luaL_checklstring(L, 1, &inlen);
if (!pcdata(in, inlen, &out, &outlen))
return 0;
lua_pushlstring(L, out, outlen);
free(out);
return 1;
}
static int template_L_load_catalog(lua_State *L)
{
const char *file = luaL_checkstring(L, 1);
lmo_catalog_t *cat = lua_newuserdata(L, sizeof(*cat));
if (!lmo_load(cat, file)) {
lua_pop(L, 1);
return 0;
}
luaL_getmetatable(L, TEMPLATE_CATALOG);
lua_setmetatable(L, -2);
return 1;
}
static int template_catalog_call(lua_State *L)
{
size_t inlen, outlen;
lmo_catalog_t *cat = luaL_checkudata(L, 1, TEMPLATE_CATALOG);
const char *in = luaL_checklstring(L, 2, &inlen), *out;
if (!lmo_translate(cat, in, inlen, &out, &outlen))
return 0;
lua_pushlstring(L, out, outlen);
return 1;
}
static int template_catalog_gc(lua_State *L)
{
lmo_catalog_t *cat = luaL_checkudata(L, 1, TEMPLATE_CATALOG);
lmo_unload(cat);
return 0;
}
static const luaL_reg R[] = {
{ "parse", template_L_parse },
{ "parse_string", template_L_parse_string },
{ "pcdata", template_L_pcdata },
{ "load_catalog", template_L_load_catalog },
{}
};
static const luaL_reg template_catalog_methods[] = {
{ "__call", template_catalog_call },
{ "__gc", template_catalog_gc },
{}
};
__attribute__ ((visibility("default")))
LUALIB_API int luaopen_gluon_web_template_parser(lua_State *L) {
luaL_register(L, "gluon.web.template.parser", R);
luaL_newmetatable(L, TEMPLATE_CATALOG);
luaL_register(L, NULL, template_catalog_methods);
lua_pop(L, 1);
return 1;
}
/*
* gluon-web Template - Parser implementation
*
* Copyright (C) 2009-2012 Jo-Philipp Wich <jow@openwrt.org>
* Copyright (C) 2018 Matthias Schiffer <mschiffer@universe-factory.net>
*
* 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"
#include <lualib.h>
#include <lauxlib.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <ctype.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
typedef enum {
T_TYPE_INIT,
T_TYPE_TEXT,
T_TYPE_COMMENT,
T_TYPE_EXPR,
T_TYPE_EXPR_RAW,
T_TYPE_INCLUDE,
T_TYPE_I18N,
T_TYPE_I18N_RAW,
T_TYPE_CODE,
T_TYPE_EOF,
} t_type_t;
struct template_chunk {
const char *s;
const char *e;
t_type_t type;
int line;
};
/* parser state */
struct template_parser {
size_t size;
char *data;
char *off;
char *lua_chunk;
int line;
int in_expr;
bool strip_before;
bool strip_after;
struct template_chunk prv_chunk;
struct template_chunk cur_chunk;
const char *file;
};
/* leading and trailing code for different types */
static const char *const gen_code[][2] = {
[T_TYPE_INIT] = {NULL, NULL},
[T_TYPE_TEXT] = {"write('", "')"},
[T_TYPE_COMMENT] = {NULL, NULL},
[T_TYPE_EXPR] = {"write(pcdata(tostring(", " or '')))"},
[T_TYPE_EXPR_RAW] = {"write(tostring(", " or ''))"},
[T_TYPE_INCLUDE] = {"include('", "')"},
[T_TYPE_I18N] = {"write(pcdata(translate('", "')))"},
[T_TYPE_I18N_RAW] = {"write(translate('", "'))"},
[T_TYPE_CODE] = {NULL, " "},
[T_TYPE_EOF] = {NULL, NULL},
};
static struct template_parser * template_init(struct template_parser *parser)
{
parser->off = parser->data;
parser->cur_chunk.type = T_TYPE_INIT;
parser->cur_chunk.s = parser->data;
parser->cur_chunk.e = parser->data;
return parser;
}
struct template_parser * template_open(const char *file)
{
int fd = -1;
struct stat s;
struct template_parser *parser;
if (!(parser = calloc(1, sizeof(*parser))))
goto err;
parser->file = file;
fd = open(file, O_RDONLY|O_CLOEXEC);
if (fd < 0)
goto err;
if (fstat(fd, &s))
goto err;
parser->size = s.st_size;
parser->data = mmap(NULL, parser->size, PROT_READ, MAP_PRIVATE,
fd, 0);
close(fd);
fd = -1;
if (parser->data == MAP_FAILED)
goto err;
return template_init(parser);
err:
if (fd >= 0)
close(fd);
template_close(parser);
return NULL;
}
struct template_parser * template_string(const char *str, size_t len)
{
struct template_parser *parser;
if (!(parser = calloc(1, sizeof(*parser))))
goto err;
parser->size = len;
parser->data = (char *)str;
return template_init(parser);
err:
template_close(parser);
return NULL;
}
void template_close(struct template_parser *parser)
{
if (!parser)
return;
free(parser->lua_chunk);
/* if file is not set, we were parsing a string */
if (parser->file) {
if ((parser->data != NULL) && (parser->data != MAP_FAILED))
munmap(parser->data, parser->size);
}
free(parser);
}
static void template_text(struct template_parser *parser, const char *e)
{
const char *s = parser->off;
if (s < (parser->data + parser->size)) {
if (parser->strip_after) {
while ((s < e) && isspace(s[0]))
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;
}
static void template_code(struct template_parser *parser, const char *e)
{
const char *s = parser->off;
parser->strip_before = false;
parser->strip_after = false;
if (s < e && s[0] == '-') {
parser->strip_before = true;
s++;
}
if (s < e && e[-1] == '-') {
parser->strip_after = true;
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;
/* expr raw */
case '=':
s++;
parser->cur_chunk.type = T_TYPE_EXPR_RAW;
break;
/* code */
default:
parser->cur_chunk.type = T_TYPE_CODE;
}
parser->cur_chunk.line = parser->line;
parser->cur_chunk.s = s;
parser->cur_chunk.e = e;
}
static void luastr_escape(struct template_buffer *out, const char *s, const char *e)
{
for (const char *ptr = s; ptr < e; ptr++) {
switch (*ptr) {
case '\\':
buf_append(out, "\\\\", 2);
break;
case '\'':
buf_append(out, "\\\'", 2);
break;
case '\n':
buf_append(out, "\\n", 2);
break;
default:
buf_putchar(out, *ptr);
}
}
}
static struct template_buffer * template_format_chunk(struct template_parser *parser)
{
const char *p;
const char *head, *tail;
struct template_chunk *c = &parser->prv_chunk;
if (parser->strip_before && c->type == T_TYPE_TEXT) {
while ((c->e > c->s) && isspace(c->e[-1]))
c->e--;
}
/* empty chunk */
if (c->type == T_TYPE_EOF)
return NULL;
struct template_buffer *buf = buf_init(c->e - c->s);
if (!buf)
return NULL;
if (c->e > c->s) {
if ((head = gen_code[c->type][0]) != NULL)
buf_append(buf, head, strlen(head));
switch (c->type) {
case T_TYPE_TEXT:
case T_TYPE_INCLUDE:
case T_TYPE_I18N:
case T_TYPE_I18N_RAW:
luastr_escape(buf, c->s, c->e);
break;
case T_TYPE_EXPR:
case T_TYPE_EXPR_RAW:
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_CODE:
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_INIT:
case T_TYPE_COMMENT:
case T_TYPE_EOF:
break;
}
if ((tail = gen_code[c->type][1]) != NULL)
buf_append(buf, tail, strlen(tail));
}
return buf;
}
const char * template_reader(lua_State *L __attribute__((unused)), void *ud, size_t *sz)
{
struct template_parser *parser = ud;
/* free previous chunk */
free(parser->lua_chunk);
parser->lua_chunk = NULL;
while (true) {
int rem = parser->size - (parser->off - parser->data);
char *tag;
parser->prv_chunk = parser->cur_chunk;
/* before tag */
if (!parser->in_expr) {
if ((tag = memmem(parser->off, rem, "<%", 2)) != NULL) {
template_text(parser, tag);
parser->off = tag + 2;
parser->in_expr = 1;
} else {
template_text(parser, parser->data + parser->size);
parser->off = parser->data + parser->size;
}
}
/* inside tag */
else {
if ((tag = memmem(parser->off, rem, "%>", 2)) != NULL) {
template_code(parser, tag);
parser->off = tag + 2;
parser->in_expr = 0;
} else {
/* unexpected EOF */
template_code(parser, parser->data + parser->size);
*sz = 1;
return "\033";
}
}
struct template_buffer *buf = template_format_chunk(parser);
if (!buf)
return NULL;
*sz = buf_length(buf);
if (*sz) {
parser->lua_chunk = buf_destroy(buf);
return parser->lua_chunk;
}
}
}
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 = memmem(err, strlen(err), "]:", 2)) != NULL) {
chunkline = atoi(ptr + 2) - parser->prv_chunk.line;
while (*ptr) {
if (*ptr++ == ' ') {
err = ptr;
break;
}
}
}
if (memmem(err, strlen(err), "'char(27)'", 10) != NULL) {
off = parser->data + parser->size;
err = "'%>' expected before end of file";
chunkline = 0;
}
for (ptr = parser->data; ptr < off; ptr++) {
if (*ptr == '\n')
line++;
}
snprintf(msg, sizeof(msg), "Syntax error in %s:%d: %s",
parser->file ?: "[string]", line + chunkline, err ?: "(unknown error)");
lua_pushnil(L);
lua_pushinteger(L, line + chunkline);
lua_pushstring(L, msg);
return 3;
}
/*
* gluon-web Template - Parser header
*
* Copyright (C) 2009 Jo-Philipp Wich <jow@openwrt.org>
* Copyright (C) 2018 Matthias Schiffer <mschiffer@universe-factory.net>
*
* 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.
*/
#ifndef _TEMPLATE_PARSER_H_
#define _TEMPLATE_PARSER_H_
#include <lua.h>
struct template_parser;
struct template_parser * template_open(const char *file);
struct template_parser * template_string(const char *str, size_t len);
void template_close(struct template_parser *parser);
const char *template_reader(lua_State *L, void *ud, size_t *sz);
int template_error(lua_State *L, struct template_parser *parser);
#endif