Skip to content

Instantly share code, notes, and snippets.

@max1220
Last active July 8, 2016 16:42
Show Gist options
  • Save max1220/493f21e2e68061f799dfd1298e0bee8c to your computer and use it in GitHub Desktop.
Save max1220/493f21e2e68061f799dfd1298e0bee8c to your computer and use it in GitHub Desktop.
helper function common for all CGI scripts
-- helper function common for all CGI
local t_insert = table.insert
local t_concat = table.concat
local t_remove = table.remove
local s_gfind = string.gfind or string.gmatch
local s_gsub = string.gsub
local s_char = string.char
local s_format = string.format
local i_write = io.write
local o_getenv = os.getenv
local _G = _G or _ENV
local tostring = tostring
-- Use LFS if it's possible
local ok, lfs = pcall(require, "lfs")
if not ok then
lfs = nil
end
function kvtostring(t, kToStr, strToNum)
-- Converts table elements and optional indexes to strings
--[[
kvtostring({hello=123, foo=true}) --> {hello="123", foo="true"}
]]
local ret = {}
for k,v in pairs(t) do
if kToStr then
ret[tostring(k)] = tostring(v)
else
ret[k] = tostring(v)
end
end
return ret
end
function tconcat(t, sep)
return t_concat(kvtostring(t,false, true), sep)
end
function tcontains(t, v)
for k,t_v in pairs(t) do
if t_v == v then
return k, v
end
end
return false
end
function filter(t, f)
-- Filters a Table based on a function
--[[
filter({42, 1337, 123, 1234}, function(v) if v%2 == 0 then return true end end) --> {42, 1234}
]]
local newt = {}
for k,v in ipairs(t) do
if f(v, k) then
table.insert(newt, v)
end
end
end
function color(str, color)
local colors = { black=0, red=1, green=2, yellow=3, blue=4, magenta=5, cyan=6, white=7 }
local color = color or colors.red
if type(color) == "string" then
if colors[color] then
color = 90 + colors[color]
elseif color:sub(1, #"bright") == "bright" and colors[color:sub(#"bright" + 1)] then
color = "1;" .. 90 + colors[color:sub(#"bright" + 1)]
end
end
return string.char(27) .. "[" .. color .. "m" .. tostring(str) .. string.char(27) .. "[m"
end
function logger(logpath, logdate, dateformat, stderr, logcolor)
-- Creates a function that logs to a file.
--[[
example.lua:
log = loggerToFile("log.txt", false)
log("Hello World!")
log.txt:
Hello World!
]]
local dateformat = dateformat or "%c"
local logfile = io.open(logpath, "a")
if not logfile then
logfile = assert(io.open(logpath, "a"), "Can't open logfile!")
end
local function log(...)
local logstr = ""
if logdate then
logstr = os.date(dateformat) .. "\t"
end
logstr = logstr .. tconcat({...}, "\t")
if stderr then
io.stderr:write(color(logstr, logcolor), "\n")
end
logfile:write(logstr, "\n")
logfile:flush()
end
local function logf(str, ...)
return log(str:format(...))
end
return log, logf
end
function pack(...)
-- Takes all arguments and packs them in a table. (Reverse unpack)
--[[
pack("foo", "bar", "buzz") --> {"foo, "bar", "buzz"}
]]
return {...}
end
function htmlescape(str)
-- Removes HTML controll characters from a string
--[[
name = "<script>doBadStuff()</script>"
htmlescape("Hello " .. name .. "!") --> "Hello &lt;script&gt;doBadStuff()&lt;/script&gt;!"
]]
str = s_gsub(str, "&", "&amp;")
for k,v in pairs({["<"]="&lt;", [">"]="&gt;", ['"']="&quot;", ["'"]="&#39;"}) do
str = s_gsub(str, k, v)
end
return str
end
function templateFromFile(filepath, removeUnused, escape)
-- Creates a template, which is a function that renders parameters into a template.
--[[
example.template:
<html>
<head>
<title>{{title}}</title>
</head>
<body>
{{body}}
</body>
</html>
example.lua:
mainTemplate = templateFromFile("example.template")
function get(name)
return mainTemplate{
title = "Hello World!",
body = "<h1>Hello World!</h1>"
}
end
]]--
local file = assert(io.open(filepath, "r"), "Can't open template file!")
local template = file:read("*a")
return function(inserts)
local ret = template
for k,v in pairs(inserts) do
if escape then
ret = s_gsub(ret, "{{" .. htmlescape(tostring(k)) .. "}}", htmlescape(tostring(v)))
else
ret = s_gsub(ret, "{{" .. tostring(k) .. "}}", tostring(v))
end
end
if removeUnused then
return (s_gsub(ret, "({{.-}})", ""))
else
return ret
end
end
end
function unescape (str)
-- Removes URL escape characters(Converts "+" to " " and "%XX" to it's correct character value)
--[[
unescape("Hello+World%21") --> "Hello World!"
]]
str = s_gsub(str, "+", " ")
str = s_gsub(str, "%%(..)", function (hex)
return s_char(tonumber(hex, 16))
end)
return str
end
function urldecode(str)
-- Seperates URL encoded key=value pairs to a lua table
--[[
urldecode("foo=bar&list=a&list=b&list=c") --> { foo="bar", list={ "a", "b", "c" } }
]]
local parms = {}
for name, value in s_gfind(str, "([^&=]+)=([^&=]+)") do
local name = unescape(name)
local value = unescape(value)
if type(parms[name]) == "table" then
t_insert(parms[name], value)
elseif parms[name] then
local orig = parms[name]
parms[name] = {orig, value}
else
parms[name] = value
end
end
return parms
end
function urlencode(str)
-- Makes a string URL-useable by replacing illegal digits with their correct notation
--[[
urlencode("Hello World!") --> "Hello+World%21"
]]
if (str) then
-- str = string.gsub (str, "\n", "\r\n")
str = s_gsub (str, "([^%w %-%_%.%~])", function (c)
return string.format ("%%%02X", string.byte(c))
end)
str = s_gsub (str, " ", "+")
end
return str
end
function CSVtoTable(str, indexes)
-- Reads a CSV string into a table.
--[[
example.lua:
csv = readFile("example.csv")
for _,fruit in pairs(CSVtoTable(csv, {"name", "color"})) do
printf("The fruit %q has the color %q", fruit.name, fruit.color)
end
example.csv:
#format: [name],[color]
apple,red
banana,yellow
tomato,red
]]
local indexes = indexes or {}
local function lineToTable(line)
local parts = {}
while true do
if line:find(",") then
t_insert(parts, line:sub(1, line:find(",") - 1))
line = line:sub(line:find(",") + 1, -1)
else
t_insert(parts, line)
break
end
end
local ret = {}
for k,v in pairs(parts) do
if indexes[k] then
ret[indexes[k]] = v
else
ret[k] = v
end
end
return ret
end
local ret = {}
for line in s_gfind(str, "[^\r\n]+") do
if trim(line):sub(1,1) ~= "#" then
t_insert(ret, lineToTable(line))
end
end
return ret
end
function trim(str)
-- Trims(removes leading/trailing whitespace) a string
--[[
trim(" Hello World! ") --> "Hello World!"
]]
return string.match(str, "^%s*(.-)%s*$")
end
function readFile(path)
-- Reads a file, returns it's content
--[[
example.lua:
readFile("example.txt") --> "Hello World!"
example.txt:
Hello World!
]]
local file = assert(io.open(path, "r"))
local content = file:read("*a")
file:close()
return content
end
function writeFile(path, content)
-- Writes a file
--[[
example.lua:
writeFile("example.txt", "Hello World!")
example.txt:
Hello World!
]]
local file = assert(io.open(path, "w"), "Can't open file for writing!")
file:write((assert(content, "Missing Content!")))
file:close()
return true
end
if lfs then
function scanDir(pwd)
local ret = {files = {}, dirs = {}, pwd = pwd}
for file in lfs.dir(pwd) do
if file ~= "." and file ~= ".." then
local attrs = lfs.attributes(pwd .. "/" .. file)
if attrs and attrs.mode == "directory" then
ret.dirs[file] = scanDir(pwd .. "/" .. file)
elseif attrs and attrs.mode == "file" then
table.insert(ret.files, file)
else
print("Not path or directory: ", file)
end
end
end
return ret
end
end
function concater(...)
local t = {...}
return function(append, ...)
if append and type(append) == "string" then
table.insert(t, append .. table.concat({...}))
end
return table.concat(t)
end
end
function printf(str, ...)
-- prints a formated string. (For format, see lua's string.format)
--[[
example.lua:
printf("Hello %s!", "World") --> prints "Hello World!"
]]
print(s_format(str, ...))
end
function dispatchHandlers(errhandler, prefix)
-- Reads the CGI parameters, and calls a global handler function(e.g. HTTP_GET). If an error occurs, errhandler is called.
--[[
cgi-bin/example.lua:
function HTTP_GET(parms)
io.write("Content-type: text/plain\n\nParameter listing:")
for k,v in pairs(parms) do
io.write(" ", tostring(k), "\t", tostring(v))
end
end
dispatchHandlers()
]]
local getenv
if type(fakeenv) == "table" then
getenv = function(key)
return fakeenv[key]
end
else
getenv = o_getenv
end
local errhandler = errhandler or function(err)
i_write("Content-type: text/plain\n\n An error occured!\n " .. ("="):rep(78) .. "\nThe occured error was:\n\n" .. tostring(err))
os.exit(1)
end
local prefix = prefix or "HTTP_"
local req_method = assert(os.getenv("REQUEST_METHOD"), "Missing REQUEST_METHOD!"):upper()
local request_handler = assert(_G[prefix .. req_method], "Unknown REQUEST_METHOD!")
local query_parms = urldecode(assert(os.getenv("QUERY_STRING"), "Missing QUERY_STRING!"))
local ret = pack(pcall(request_handler, query_parms))
if t_remove(ret, 1) then
i_write(unpack(kvtostring(ret)))
else
i_write(errhandler(unpack(kvtostring(ret))))
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment