mirror of
https://git.FreeBSD.org/src.git
synced 2024-11-21 07:15:49 +00:00
6ef644f588
Remove /^--\s*\$FreeBSD\$.*$\n/
652 lines
22 KiB
Lua
652 lines
22 KiB
Lua
-- From lua-resty-template (modified to remove external dependencies)
|
|
--[[
|
|
Copyright (c) 2014 - 2020 Aapo Talvensaari
|
|
All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without modification,
|
|
are permitted provided that the following conditions are met:
|
|
|
|
* Redistributions of source code must retain the above copyright notice, this
|
|
list of conditions and the following disclaimer.
|
|
|
|
* Redistributions in binary form must reproduce the above copyright notice, this
|
|
list of conditions and the following disclaimer in the documentation and/or
|
|
other materials provided with the distribution.
|
|
|
|
* Neither the name of the {organization} nor the names of its
|
|
contributors may be used to endorse or promote products derived from
|
|
this software without specific prior written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
|
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
]]--
|
|
|
|
local setmetatable = setmetatable
|
|
local loadstring = loadstring
|
|
local tostring = tostring
|
|
local setfenv = setfenv
|
|
local require = require
|
|
local concat = table.concat
|
|
local assert = assert
|
|
local write = io.write
|
|
local pcall = pcall
|
|
local phase
|
|
local open = io.open
|
|
local load = load
|
|
local type = type
|
|
local dump = string.dump
|
|
local find = string.find
|
|
local gsub = string.gsub
|
|
local byte = string.byte
|
|
local null
|
|
local sub = string.sub
|
|
local var
|
|
|
|
local _VERSION = _VERSION
|
|
local _ENV = _ENV -- luacheck: globals _ENV
|
|
local _G = _G
|
|
|
|
local HTML_ENTITIES = {
|
|
["&"] = "&",
|
|
["<"] = "<",
|
|
[">"] = ">",
|
|
['"'] = """,
|
|
["'"] = "'",
|
|
["/"] = "/"
|
|
}
|
|
|
|
local CODE_ENTITIES = {
|
|
["{"] = "{",
|
|
["}"] = "}",
|
|
["&"] = "&",
|
|
["<"] = "<",
|
|
[">"] = ">",
|
|
['"'] = """,
|
|
["'"] = "'",
|
|
["/"] = "/"
|
|
}
|
|
|
|
local VAR_PHASES
|
|
|
|
local ESC = byte("\27")
|
|
local NUL = byte("\0")
|
|
local HT = byte("\t")
|
|
local VT = byte("\v")
|
|
local LF = byte("\n")
|
|
local SOL = byte("/")
|
|
local BSOL = byte("\\")
|
|
local SP = byte(" ")
|
|
local AST = byte("*")
|
|
local NUM = byte("#")
|
|
local LPAR = byte("(")
|
|
local LSQB = byte("[")
|
|
local LCUB = byte("{")
|
|
local MINUS = byte("-")
|
|
local PERCNT = byte("%")
|
|
|
|
local EMPTY = ""
|
|
|
|
local VIEW_ENV
|
|
if _VERSION == "Lua 5.1" then
|
|
VIEW_ENV = { __index = function(t, k)
|
|
return t.context[k] or t.template[k] or _G[k]
|
|
end }
|
|
else
|
|
VIEW_ENV = { __index = function(t, k)
|
|
return t.context[k] or t.template[k] or _ENV[k]
|
|
end }
|
|
end
|
|
|
|
local newtab
|
|
do
|
|
local ok
|
|
ok, newtab = pcall(require, "table.new")
|
|
if not ok then newtab = function() return {} end end
|
|
end
|
|
|
|
local function enabled(val)
|
|
if val == nil then return true end
|
|
return val == true or (val == "1" or val == "true" or val == "on")
|
|
end
|
|
|
|
local function trim(s)
|
|
return gsub(gsub(s, "^%s+", EMPTY), "%s+$", EMPTY)
|
|
end
|
|
|
|
local function rpos(view, s)
|
|
while s > 0 do
|
|
local c = byte(view, s, s)
|
|
if c == SP or c == HT or c == VT or c == NUL then
|
|
s = s - 1
|
|
else
|
|
break
|
|
end
|
|
end
|
|
return s
|
|
end
|
|
|
|
local function escaped(view, s)
|
|
if s > 1 and byte(view, s - 1, s - 1) == BSOL then
|
|
if s > 2 and byte(view, s - 2, s - 2) == BSOL then
|
|
return false, 1
|
|
else
|
|
return true, 1
|
|
end
|
|
end
|
|
return false, 0
|
|
end
|
|
|
|
local function read_file(path)
|
|
local file, err = open(path, "rb")
|
|
if not file then return nil, err end
|
|
local content
|
|
content, err = file:read "*a"
|
|
file:close()
|
|
return content, err
|
|
end
|
|
|
|
local function load_view(template)
|
|
return function(view, plain)
|
|
if plain == true then return view end
|
|
local path, root = view, template.root
|
|
if root and root ~= EMPTY then
|
|
if byte(root, -1) == SOL then root = sub(root, 1, -2) end
|
|
if byte(view, 1) == SOL then path = sub(view, 2) end
|
|
path = root .. "/" .. path
|
|
end
|
|
return plain == false and assert(read_file(path)) or read_file(path) or view
|
|
end
|
|
end
|
|
|
|
local function load_file(func)
|
|
return function(view) return func(view, false) end
|
|
end
|
|
|
|
local function load_string(func)
|
|
return function(view) return func(view, true) end
|
|
end
|
|
|
|
local function loader(template)
|
|
return function(view)
|
|
return assert(load(view, nil, nil, setmetatable({ template = template }, VIEW_ENV)))
|
|
end
|
|
end
|
|
|
|
local function visit(visitors, content, tag, name)
|
|
if not visitors then
|
|
return content
|
|
end
|
|
|
|
for i = 1, visitors.n do
|
|
content = visitors[i](content, tag, name)
|
|
end
|
|
|
|
return content
|
|
end
|
|
|
|
local function new(template, safe)
|
|
template = template or newtab(0, 26)
|
|
|
|
template._VERSION = "2.0"
|
|
template.cache = {}
|
|
template.load = load_view(template)
|
|
template.load_file = load_file(template.load)
|
|
template.load_string = load_string(template.load)
|
|
template.print = write
|
|
|
|
local load_chunk = loader(template)
|
|
|
|
local caching
|
|
if VAR_PHASES and VAR_PHASES[phase()] then
|
|
caching = enabled(var.template_cache)
|
|
else
|
|
caching = true
|
|
end
|
|
|
|
local visitors
|
|
function template.visit(func)
|
|
if not visitors then
|
|
visitors = { func, n = 1 }
|
|
return
|
|
end
|
|
visitors.n = visitors.n + 1
|
|
visitors[visitors.n] = func
|
|
end
|
|
|
|
function template.caching(enable)
|
|
if enable ~= nil then caching = enable == true end
|
|
return caching
|
|
end
|
|
|
|
function template.output(s)
|
|
if s == nil or s == null then return EMPTY end
|
|
if type(s) == "function" then return template.output(s()) end
|
|
return tostring(s)
|
|
end
|
|
|
|
function template.escape(s, c)
|
|
if type(s) == "string" then
|
|
if c then return gsub(s, "[}{\">/<'&]", CODE_ENTITIES) end
|
|
return gsub(s, "[\">/<'&]", HTML_ENTITIES)
|
|
end
|
|
return template.output(s)
|
|
end
|
|
|
|
function template.new(view, layout)
|
|
local vt = type(view)
|
|
|
|
if vt == "boolean" then return new(nil, view) end
|
|
if vt == "table" then return new(view, safe) end
|
|
if vt == "nil" then return new(nil, safe) end
|
|
|
|
local render
|
|
local process
|
|
if layout then
|
|
if type(layout) == "table" then
|
|
render = function(self, context)
|
|
context = context or self
|
|
context.blocks = context.blocks or {}
|
|
context.view = template.process(view, context)
|
|
layout.blocks = context.blocks or {}
|
|
layout.view = context.view or EMPTY
|
|
layout:render()
|
|
end
|
|
process = function(self, context)
|
|
context = context or self
|
|
context.blocks = context.blocks or {}
|
|
context.view = template.process(view, context)
|
|
layout.blocks = context.blocks or {}
|
|
layout.view = context.view
|
|
return tostring(layout)
|
|
end
|
|
else
|
|
render = function(self, context)
|
|
context = context or self
|
|
context.blocks = context.blocks or {}
|
|
context.view = template.process(view, context)
|
|
template.render(layout, context)
|
|
end
|
|
process = function(self, context)
|
|
context = context or self
|
|
context.blocks = context.blocks or {}
|
|
context.view = template.process(view, context)
|
|
return template.process(layout, context)
|
|
end
|
|
end
|
|
else
|
|
render = function(self, context)
|
|
return template.render(view, context or self)
|
|
end
|
|
process = function(self, context)
|
|
return template.process(view, context or self)
|
|
end
|
|
end
|
|
|
|
if safe then
|
|
return setmetatable({
|
|
render = function(...)
|
|
local ok, err = pcall(render, ...)
|
|
if not ok then
|
|
return nil, err
|
|
end
|
|
end,
|
|
process = function(...)
|
|
local ok, output = pcall(process, ...)
|
|
if not ok then
|
|
return nil, output
|
|
end
|
|
return output
|
|
end,
|
|
}, {
|
|
__tostring = function(...)
|
|
local ok, output = pcall(process, ...)
|
|
if not ok then
|
|
return ""
|
|
end
|
|
return output
|
|
end })
|
|
end
|
|
|
|
return setmetatable({
|
|
render = render,
|
|
process = process
|
|
}, {
|
|
__tostring = process
|
|
})
|
|
end
|
|
|
|
function template.precompile(view, path, strip, plain)
|
|
local chunk = dump(template.compile(view, nil, plain), strip ~= false)
|
|
if path then
|
|
local file = open(path, "wb")
|
|
file:write(chunk)
|
|
file:close()
|
|
end
|
|
return chunk
|
|
end
|
|
|
|
function template.precompile_string(view, path, strip)
|
|
return template.precompile(view, path, strip, true)
|
|
end
|
|
|
|
function template.precompile_file(view, path, strip)
|
|
return template.precompile(view, path, strip, false)
|
|
end
|
|
|
|
function template.compile(view, cache_key, plain)
|
|
assert(view, "view was not provided for template.compile(view, cache_key, plain)")
|
|
if cache_key == "no-cache" then
|
|
return load_chunk(template.parse(view, plain)), false
|
|
end
|
|
cache_key = cache_key or view
|
|
local cache = template.cache
|
|
if cache[cache_key] then return cache[cache_key], true end
|
|
local func = load_chunk(template.parse(view, plain))
|
|
if caching then cache[cache_key] = func end
|
|
return func, false
|
|
end
|
|
|
|
function template.compile_file(view, cache_key)
|
|
return template.compile(view, cache_key, false)
|
|
end
|
|
|
|
function template.compile_string(view, cache_key)
|
|
return template.compile(view, cache_key, true)
|
|
end
|
|
|
|
function template.parse(view, plain)
|
|
assert(view, "view was not provided for template.parse(view, plain)")
|
|
if plain ~= true then
|
|
view = template.load(view, plain)
|
|
if byte(view, 1, 1) == ESC then return view end
|
|
end
|
|
local j = 2
|
|
local c = {[[
|
|
context=... or {}
|
|
local ___,blocks,layout={},blocks or {}
|
|
local function include(v, c) return template.process(v, c or context) end
|
|
local function echo(...) for i=1,select("#", ...) do ___[#___+1] = tostring(select(i, ...)) end end
|
|
]] }
|
|
local i, s = 1, find(view, "{", 1, true)
|
|
while s do
|
|
local t, p = byte(view, s + 1, s + 1), s + 2
|
|
if t == LCUB then
|
|
local e = find(view, "}}", p, true)
|
|
if e then
|
|
local z, w = escaped(view, s)
|
|
if i < s - w then
|
|
c[j] = "___[#___+1]=[=[\n"
|
|
c[j+1] = visit(visitors, sub(view, i, s - 1 - w))
|
|
c[j+2] = "]=]\n"
|
|
j=j+3
|
|
end
|
|
if z then
|
|
i = s
|
|
else
|
|
c[j] = "___[#___+1]=template.escape("
|
|
c[j+1] = visit(visitors, trim(sub(view, p, e - 1)), "{")
|
|
c[j+2] = ")\n"
|
|
j=j+3
|
|
s, i = e + 1, e + 2
|
|
end
|
|
end
|
|
elseif t == AST then
|
|
local e = find(view, "*}", p, true)
|
|
if e then
|
|
local z, w = escaped(view, s)
|
|
if i < s - w then
|
|
c[j] = "___[#___+1]=[=[\n"
|
|
c[j+1] = visit(visitors, sub(view, i, s - 1 - w))
|
|
c[j+2] = "]=]\n"
|
|
j=j+3
|
|
end
|
|
if z then
|
|
i = s
|
|
else
|
|
c[j] = "___[#___+1]=template.output("
|
|
c[j+1] = visit(visitors, trim(sub(view, p, e - 1)), "*")
|
|
c[j+2] = ")\n"
|
|
j=j+3
|
|
s, i = e + 1, e + 2
|
|
end
|
|
end
|
|
elseif t == PERCNT then
|
|
local e = find(view, "%}", p, true)
|
|
if e then
|
|
local z, w = escaped(view, s)
|
|
if z then
|
|
if i < s - w then
|
|
c[j] = "___[#___+1]=[=[\n"
|
|
c[j+1] = visit(visitors, sub(view, i, s - 1 - w))
|
|
c[j+2] = "]=]\n"
|
|
j=j+3
|
|
end
|
|
i = s
|
|
else
|
|
local n = e + 2
|
|
if byte(view, n, n) == LF then
|
|
n = n + 1
|
|
end
|
|
local r = rpos(view, s - 1)
|
|
if i <= r then
|
|
c[j] = "___[#___+1]=[=[\n"
|
|
c[j+1] = visit(visitors, sub(view, i, r))
|
|
c[j+2] = "]=]\n"
|
|
j=j+3
|
|
end
|
|
c[j] = visit(visitors, trim(sub(view, p, e - 1)), "%")
|
|
c[j+1] = "\n"
|
|
j=j+2
|
|
s, i = n - 1, n
|
|
end
|
|
end
|
|
elseif t == LPAR then
|
|
local e = find(view, ")}", p, true)
|
|
if e then
|
|
local z, w = escaped(view, s)
|
|
if i < s - w then
|
|
c[j] = "___[#___+1]=[=[\n"
|
|
c[j+1] = visit(visitors, sub(view, i, s - 1 - w))
|
|
c[j+2] = "]=]\n"
|
|
j=j+3
|
|
end
|
|
if z then
|
|
i = s
|
|
else
|
|
local f = visit(visitors, sub(view, p, e - 1), "(")
|
|
local x = find(f, ",", 2, true)
|
|
if x then
|
|
c[j] = "___[#___+1]=include([=["
|
|
c[j+1] = trim(sub(f, 1, x - 1))
|
|
c[j+2] = "]=],"
|
|
c[j+3] = trim(sub(f, x + 1))
|
|
c[j+4] = ")\n"
|
|
j=j+5
|
|
else
|
|
c[j] = "___[#___+1]=include([=["
|
|
c[j+1] = trim(f)
|
|
c[j+2] = "]=])\n"
|
|
j=j+3
|
|
end
|
|
s, i = e + 1, e + 2
|
|
end
|
|
end
|
|
elseif t == LSQB then
|
|
local e = find(view, "]}", p, true)
|
|
if e then
|
|
local z, w = escaped(view, s)
|
|
if i < s - w then
|
|
c[j] = "___[#___+1]=[=[\n"
|
|
c[j+1] = visit(visitors, sub(view, i, s - 1 - w))
|
|
c[j+2] = "]=]\n"
|
|
j=j+3
|
|
end
|
|
if z then
|
|
i = s
|
|
else
|
|
c[j] = "___[#___+1]=include("
|
|
c[j+1] = visit(visitors, trim(sub(view, p, e - 1)), "[")
|
|
c[j+2] = ")\n"
|
|
j=j+3
|
|
s, i = e + 1, e + 2
|
|
end
|
|
end
|
|
elseif t == MINUS then
|
|
local e = find(view, "-}", p, true)
|
|
if e then
|
|
local x, y = find(view, sub(view, s, e + 1), e + 2, true)
|
|
if x then
|
|
local z, w = escaped(view, s)
|
|
if z then
|
|
if i < s - w then
|
|
c[j] = "___[#___+1]=[=[\n"
|
|
c[j+1] = visit(visitors, sub(view, i, s - 1 - w))
|
|
c[j+2] = "]=]\n"
|
|
j=j+3
|
|
end
|
|
i = s
|
|
else
|
|
y = y + 1
|
|
x = x - 1
|
|
if byte(view, y, y) == LF then
|
|
y = y + 1
|
|
end
|
|
local b = trim(sub(view, p, e - 1))
|
|
if b == "verbatim" or b == "raw" then
|
|
if i < s - w then
|
|
c[j] = "___[#___+1]=[=[\n"
|
|
c[j+1] = visit(visitors, sub(view, i, s - 1 - w))
|
|
c[j+2] = "]=]\n"
|
|
j=j+3
|
|
end
|
|
c[j] = "___[#___+1]=[=["
|
|
c[j+1] = visit(visitors, sub(view, e + 2, x))
|
|
c[j+2] = "]=]\n"
|
|
j=j+3
|
|
else
|
|
if byte(view, x, x) == LF then
|
|
x = x - 1
|
|
end
|
|
local r = rpos(view, s - 1)
|
|
if i <= r then
|
|
c[j] = "___[#___+1]=[=[\n"
|
|
c[j+1] = visit(visitors, sub(view, i, r))
|
|
c[j+2] = "]=]\n"
|
|
j=j+3
|
|
end
|
|
c[j] = 'blocks["'
|
|
c[j+1] = b
|
|
c[j+2] = '"]=include[=['
|
|
c[j+3] = visit(visitors, sub(view, e + 2, x), "-", b)
|
|
c[j+4] = "]=]\n"
|
|
j=j+5
|
|
end
|
|
s, i = y - 1, y
|
|
end
|
|
end
|
|
end
|
|
elseif t == NUM then
|
|
local e = find(view, "#}", p, true)
|
|
if e then
|
|
local z, w = escaped(view, s)
|
|
if i < s - w then
|
|
c[j] = "___[#___+1]=[=[\n"
|
|
c[j+1] = visit(visitors, sub(view, i, s - 1 - w))
|
|
c[j+2] = "]=]\n"
|
|
j=j+3
|
|
end
|
|
if z then
|
|
i = s
|
|
else
|
|
e = e + 2
|
|
if byte(view, e, e) == LF then
|
|
e = e + 1
|
|
end
|
|
s, i = e - 1, e
|
|
end
|
|
end
|
|
end
|
|
s = find(view, "{", s + 1, true)
|
|
end
|
|
s = sub(view, i)
|
|
if s and s ~= EMPTY then
|
|
c[j] = "___[#___+1]=[=[\n"
|
|
c[j+1] = visit(visitors, s)
|
|
c[j+2] = "]=]\n"
|
|
j=j+3
|
|
end
|
|
c[j] = "return layout and include(layout,setmetatable({view=table.concat(___),blocks=blocks},{__index=context})) or table.concat(___)" -- luacheck: ignore
|
|
return concat(c)
|
|
end
|
|
|
|
function template.parse_file(view)
|
|
return template.parse(view, false)
|
|
end
|
|
|
|
function template.parse_string(view)
|
|
return template.parse(view, true)
|
|
end
|
|
|
|
function template.process(view, context, cache_key, plain)
|
|
assert(view, "view was not provided for template.process(view, context, cache_key, plain)")
|
|
return template.compile(view, cache_key, plain)(context)
|
|
end
|
|
|
|
function template.process_file(view, context, cache_key)
|
|
assert(view, "view was not provided for template.process_file(view, context, cache_key)")
|
|
return template.compile(view, cache_key, false)(context)
|
|
end
|
|
|
|
function template.process_string(view, context, cache_key)
|
|
assert(view, "view was not provided for template.process_string(view, context, cache_key)")
|
|
return template.compile(view, cache_key, true)(context)
|
|
end
|
|
|
|
function template.render(view, context, cache_key, plain)
|
|
assert(view, "view was not provided for template.render(view, context, cache_key, plain)")
|
|
template.print(template.process(view, context, cache_key, plain))
|
|
end
|
|
|
|
function template.render_file(view, context, cache_key)
|
|
assert(view, "view was not provided for template.render_file(view, context, cache_key)")
|
|
template.render(view, context, cache_key, false)
|
|
end
|
|
|
|
function template.render_string(view, context, cache_key)
|
|
assert(view, "view was not provided for template.render_string(view, context, cache_key)")
|
|
template.render(view, context, cache_key, true)
|
|
end
|
|
|
|
if safe then
|
|
return setmetatable({}, {
|
|
__index = function(_, k)
|
|
if type(template[k]) == "function" then
|
|
return function(...)
|
|
local ok, a, b = pcall(template[k], ...)
|
|
if not ok then
|
|
return nil, a
|
|
end
|
|
return a, b
|
|
end
|
|
end
|
|
return template[k]
|
|
end,
|
|
__new_index = function(_, k, v)
|
|
template[k] = v
|
|
end,
|
|
})
|
|
end
|
|
|
|
return template
|
|
end
|
|
|
|
return new()
|