local function resolve(base, relative)
if relative:sub(1, 1) == "/" then
return relative
end
local combined = base .. "/" .. relative
local segments = {}
for segment in combined:gmatch("[^/]+") do
if segment == ".." then
if #segments > 0 and segments[#segments] ~= ".." then
table.remove(segments)
else
table.insert(segments, "..")
end
elseif segment ~= "." and segment ~= "" then
table.insert(segments, segment)
end
end
local prefix = (base:sub(1, 1) == "/" or relative:sub(1, 1) == "/") and "/" or ""
return prefix .. table.concat(segments, "/")
end
-- The branch from the query string (?h=...), or nil. cgit doesn't pass it
-- to filters, but Lua filters run in cgit's process, which has the CGI
-- environment.
local function query_head()
local query = os.getenv("QUERY_STRING") or ""
for key, value in query:gmatch("([^&=]+)=([^&]*)") do
if key == "h" then
value = value:gsub("%+", " "):gsub("%%(%x%x)", function(hex)
return string.char(tonumber(hex, 16))
end)
if value ~= "" then
return value
end
end
end
return nil
end
local function url_escape(s)
return (s:gsub("[^%w._~/-]", function(c)
return string.format("%%%02X", c:byte())
end))
end
-- cgit provides html() to Lua filters. It writes in order with cgit's own
-- output, which io.write's buffered stdout doesn't. io.write is for running
-- the filter outside of cgit.
local emit = html or io.write
-- Run md2html and return its output.
local function md2html(markdown)
local input = os.tmpname()
local file = assert(io.open(input, "w"))
file:write(markdown)
file:close()
local handle = io.popen("@md2html@ --github < " .. input, "r")
local output = handle and handle:read("*a")
if handle then
handle:close()
end
os.remove(input)
return output
end
local table_style = [[
]]
-- Column widths, shared by all tables with the same header. A column's
-- weight is the larger of its longest word and its average cell length, in
-- characters; code counts more, as monospace is wider. Narrow columns get a
-- fixed width, the others a share of what's left.
local code_weight = 1.25
local narrow = 12
local function length(s)
s = s:gsub("?%w+;", "x")
return select(2, s:gsub("[^\128-\191]", ""))
end
local function cell_weights(cell)
local longest, total = 0, 0
local function add(text, factor)
for word in text:gmatch("%S+") do
local n = length(word) * factor
longest = math.max(longest, n)
total = total + n + 1
end
end
for code in cell:gmatch("(.-)") do
add((code:gsub("<[^>]+>", "")), code_weight)
end
add((cell:gsub(".-", " "):gsub("<[^>]+>", " ")), 1)
return longest, math.max(total - 1, 0)
end
local function cells(row)
local result = {}
for cell in row:gmatch("
(.-)", function(code)
return "" .. code:gsub("_", "_") .. " "
end)
return "