summaryrefslogtreecommitdiffstats
path: root/pkgs/simple/cgit-about-filter/cgit-about-filter.lua
blob: 64e16fa824d1f2907c26e3fa1284679ffe5c4b8f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
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

local buffer = ""
local should_render = true
local current_dir = "" -- Stores the directory prefix of the file being processed
local current_file = ""

function filter_open(...)
    local args = {...}
    current_file = args[1] or ""

    if current_file == "" then
        current_file = "README.md"
    end

    -- Strict route enforcement: Allow README.md OR any file starting with docs/ and ending in .md
    if current_file == "README.md" or (current_file:sub(1, 5) == "docs/" and current_file:sub(-3) == ".md") then
        should_render = true

        -- Extract directory context (e.g., "docs/ops.md" -> "docs/")
        -- If it's the root README.md, current_dir stays an empty string ""
        if current_file ~= "README.md" then
            current_dir = current_file:match("(.*/)") or ""
        else
            current_dir = ""
        end
    else
        should_render = false
    end

    buffer = ""
end

function filter_write(str)
    if should_render then
        buffer = buffer .. str
    end
end

function filter_close()
    if not should_render then
        io.write("<p><em>Error: Access Denied. Only README.md and documentation markdown files are viewable here.</em></p>")
        return
    end

    local repo = os.getenv("CGIT_REPO_URL") or ""

    local processed = buffer:gsub("%[([^%]]+)%]%(([^:)]+)%)", function(text, path)
        -- Ignore protocols (http, https, etc.), absolute paths (/), and anchors (#)
        if path:sub(1, 1) == "/" or path:sub(1, 1) == "#" or path:match("^%w+:") then
            return string.format("[%s](%s)", text, path)
        end

        local fragment
        path, fragment = path:match("^([^#]*)(.*)$")

        path = resolve(current_dir, path)

        if path:sub(-3) == ".md" then
          return string.format("[%s](/%s/about/%s%s)", text, repo, path, fragment)
        else
          return string.format("[%s](/%s/tree/%s%s)", text, repo, path, fragment)
        end
    end)

    -- Pipe everything into md2html
    local handle = io.popen("@md2html@ --github", "w")
    if handle then
        handle:write(processed)
        handle:close()
    else
        io.write("<p>Error: Could not execute md2html.</p>")
    end
end