123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509 |
- -- vendored from https://raw.githubusercontent.com/bungle/lua-resty-template/1f9a5c24fc7572dbf5be0b9f8168cc3984b03d24/lib/resty/template.lua
- -- only modification: remove / from HTML_ENTITIES to not escape it, and fix the appropriate regex.
- --[[
- Copyright (c) 2014 - 2017 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 loadchunk
- local tostring = tostring
- local setfenv = setfenv
- local require = require
- local capture
- local concat = table.concat
- local assert = assert
- local prefix
- 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 ngx = ngx
- local jit = jit
- local var
- local _VERSION = _VERSION
- local _ENV = _ENV
- local _G = _G
- local HTML_ENTITIES = {
- ["&"] = "&",
- ["<"] = "<",
- [">"] = ">",
- ['"'] = """,
- ["'"] = "'",
- }
- local CODE_ENTITIES = {
- ["{"] = "{",
- ["}"] = "}",
- ["&"] = "&",
- ["<"] = "<",
- [">"] = ">",
- ['"'] = """,
- ["'"] = "'",
- ["/"] = "/"
- }
- local VAR_PHASES
- local ok, newtab = pcall(require, "table.new")
- if not ok then newtab = function() return {} end end
- local caching = true
- local template = newtab(0, 12)
- template._VERSION = "1.9"
- template.cache = {}
- 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+", ""), "%s+$", "")
- end
- local function rpos(view, s)
- while s > 0 do
- local c = sub(view, s, s)
- if c == " " or c == "\t" or c == "\0" or c == "\x0B" then
- s = s - 1
- else
- break
- end
- end
- return s
- end
- local function escaped(view, s)
- if s > 1 and sub(view, s - 1, s - 1) == "\\" then
- if s > 2 and sub(view, s - 2, s - 2) == "\\" then
- return false, 1
- else
- return true, 1
- end
- end
- return false, 0
- end
- local function readfile(path)
- local file = open(path, "rb")
- if not file then return nil end
- local content = file:read "*a"
- file:close()
- return content
- end
- local function loadlua(path)
- return readfile(path) or path
- end
- local function loadngx(path)
- local vars = VAR_PHASES[phase()]
- local file, location = path, vars and var.template_location
- if sub(file, 1) == "/" then file = sub(file, 2) end
- if location and location ~= "" then
- if sub(location, -1) == "/" then location = sub(location, 1, -2) end
- local res = capture(concat{ location, '/', file})
- if res.status == 200 then return res.body end
- end
- local root = vars and (var.template_root or var.document_root) or prefix
- if sub(root, -1) == "/" then root = sub(root, 1, -2) end
- return readfile(concat{ root, "/", file }) or path
- end
- do
- if ngx then
- VAR_PHASES = {
- set = true,
- rewrite = true,
- access = true,
- content = true,
- header_filter = true,
- body_filter = true,
- log = true
- }
- template.print = ngx.print or write
- template.load = loadngx
- prefix, var, capture, null, phase = ngx.config.prefix(), ngx.var, ngx.location.capture, ngx.null, ngx.get_phase
- if VAR_PHASES[phase()] then
- caching = enabled(var.template_cache)
- end
- else
- template.print = write
- template.load = loadlua
- end
- if _VERSION == "Lua 5.1" then
- local context = { __index = function(t, k)
- return t.context[k] or t.template[k] or _G[k]
- end }
- if jit then
- loadchunk = function(view)
- return assert(load(view, nil, nil, setmetatable({ template = template }, context)))
- end
- else
- loadchunk = function(view)
- local func = assert(loadstring(view))
- setfenv(func, setmetatable({ template = template }, context))
- return func
- end
- end
- else
- local context = { __index = function(t, k)
- return t.context[k] or t.template[k] or _ENV[k]
- end }
- loadchunk = function(view)
- return assert(load(view, nil, nil, setmetatable({ template = template }, context)))
- end
- end
- 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 "" 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)
- assert(view, "view was not provided for template.new(view, layout).")
- local render, compile = template.render, template.compile
- if layout then
- if type(layout) == "table" then
- return setmetatable({ render = function(self, context)
- local context = context or self
- context.blocks = context.blocks or {}
- context.view = compile(view)(context)
- layout.blocks = context.blocks or {}
- layout.view = context.view or ""
- return layout:render()
- end }, { __tostring = function(self)
- local context = self
- context.blocks = context.blocks or {}
- context.view = compile(view)(context)
- layout.blocks = context.blocks or {}
- layout.view = context.view
- return tostring(layout)
- end })
- else
- return setmetatable({ render = function(self, context)
- local context = context or self
- context.blocks = context.blocks or {}
- context.view = compile(view)(context)
- return render(layout, context)
- end }, { __tostring = function(self)
- local context = self
- context.blocks = context.blocks or {}
- context.view = compile(view)(context)
- return compile(layout)(context)
- end })
- end
- end
- return setmetatable({ render = function(self, context)
- return render(view, context or self)
- end }, { __tostring = function(self)
- return compile(view)(self)
- end })
- end
- function template.precompile(view, path, strip)
- local chunk = dump(template.compile(view), strip ~= false)
- if path then
- local file = open(path, "wb")
- file:write(chunk)
- file:close()
- end
- return chunk
- end
- function template.compile(view, key, plain)
- assert(view, "view was not provided for template.compile(view, key, plain).")
- if key == "no-cache" then
- return loadchunk(template.parse(view, plain)), false
- end
- key = key or view
- local cache = template.cache
- if cache[key] then return cache[key], true end
- local func = loadchunk(template.parse(view, plain))
- if caching then cache[key] = func end
- return func, false
- end
- function template.parse(view, plain)
- assert(view, "view was not provided for template.parse(view, plain).")
- if not plain then
- view = template.load(view)
- if byte(view, 1, 1) == 27 then return view end
- end
- local j = 2
- local c = {[[
- context=... or {}
- local function include(v, c) return template.compile(v)(c or context) end
- local ___,blocks,layout={},blocks or {}
- ]] }
- local i, s = 1, find(view, "{", 1, true)
- while s do
- local t, p = sub(view, s + 1, s + 1), s + 2
- if t == "{" 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] = 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] = trim(sub(view, p, e - 1))
- c[j+2] = ")\n"
- j=j+3
- s, i = e + 1, e + 2
- end
- end
- elseif t == "*" 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] = 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] = trim(sub(view, p, e - 1))
- c[j+2] = ")\n"
- j=j+3
- s, i = e + 1, e + 2
- end
- end
- elseif t == "%" 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] = sub(view, i, s - 1 - w)
- c[j+2] = "]=]\n"
- j=j+3
- end
- i = s
- else
- local n = e + 2
- if sub(view, n, n) == "\n" then
- n = n + 1
- end
- local r = rpos(view, s - 1)
- if i <= r then
- c[j] = "___[#___+1]=[=[\n"
- c[j+1] = sub(view, i, r)
- c[j+2] = "]=]\n"
- j=j+3
- end
- c[j] = trim(sub(view, p, e - 1))
- c[j+1] = "\n"
- j=j+2
- s, i = n - 1, n
- end
- end
- elseif t == "(" 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] = sub(view, i, s - 1 - w)
- c[j+2] = "]=]\n"
- j=j+3
- end
- if z then
- i = s
- else
- local f = 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 == "[" 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] = 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] = trim(sub(view, p, e - 1))
- c[j+2] = ")\n"
- j=j+3
- s, i = e + 1, e + 2
- end
- end
- elseif t == "-" 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] = sub(view, i, s - 1 - w)
- c[j+2] = "]=]\n"
- j=j+3
- end
- i = s
- else
- y = y + 1
- x = x - 1
- if sub(view, y, y) == "\n" 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] = sub(view, i, s - 1 - w)
- c[j+2] = "]=]\n"
- j=j+3
- end
- c[j] = "___[#___+1]=[=["
- c[j+1] = sub(view, e + 2, x)
- c[j+2] = "]=]\n"
- j=j+3
- else
- if sub(view, x, x) == "\n" then
- x = x - 1
- end
- local r = rpos(view, s - 1)
- if i <= r then
- c[j] = "___[#___+1]=[=[\n"
- c[j+1] = 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] = sub(view, e + 2, x)
- c[j+4] = "]=]\n"
- j=j+5
- end
- s, i = y - 1, y
- end
- end
- end
- elseif t == "#" 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] = sub(view, i, s - 1 - w)
- c[j+2] = "]=]\n"
- j=j+3
- end
- if z then
- i = s
- else
- e = e + 2
- if sub(view, e, e) == "\n" 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 ~= "" then
- c[j] = "___[#___+1]=[=[\n"
- c[j+1] = 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(___)"
- return concat(c)
- end
- function template.render(view, context, key, plain)
- assert(view, "view was not provided for template.render(view, context, key, plain).")
- return template.print(template.compile(view, key, plain)(context))
- end
- return template
|