diff options
context:
space:
mode:
authorBryce Kwon <bryce@brycekwon.com>
committerBryce Kwon <bryce@brycekwon.com>
commit
parent
tree
download
Trim the comments and dead code across the tree
Diffstat (limited to 'custom/extensions')
-rw-r--r--custom/extensions/about-render.lua91
-rw-r--r--custom/extensions/auth-file.lua73
-rw-r--r--custom/extensions/auth-inline.lua66
-rw-r--r--custom/extensions/email-gravatar.lua22
-rw-r--r--custom/extensions/email-libravatar.lua19
-rw-r--r--custom/extensions/link-commits.lua53
-rw-r--r--custom/extensions/syntax-highlight.lua67
7 files changed, 172 insertions, 219 deletions
diff --git a/custom/extensions/about-render.lua b/custom/extensions/about-render.lua
index ea13746..f4e7685 100644
--- a/custom/extensions/about-render.lua
+++ b/custom/extensions/about-render.lua
@@ -1,20 +1,15 @@
-- Server-side rendering of a repository's about page, named by the
-- about-filter setting in cgitrc and run inside cgit's embedded Lua
--- interpreter so a readme costs no extra process per request.
--- Markdown, man pages and plain text are the three formats, chosen from the
--- readme's file extension, and anything that fails to parse falls back to
--- escaped plain text. Adding a format takes a render function and a row in
--- the handler table at the foot of this file, and nothing else. The wrappers
--- it emits carry the classes that assets/cgit.css styles. It runs on Lua 5.1
--- through 5.4 and LuaJIT.
+-- interpreter so a readme costs no extra process per request. Markdown, man
+-- pages and plain text are the three formats, chosen from the readme's file
+-- extension, and anything that fails to parse falls back to escaped plain
+-- text. The wrappers it emits carry the classes that assets/cgit.css styles.
+-- It runs on Lua 5.1 through 5.4 and LuaJIT.
--
--- about-filter=lua:/usr/lib/cgit/filters/about-render.lua
+-- about-filter=lua:/usr/local/lib/cgit/filters/about-render.lua
--- Markdown and man pages are parsed with lpeg grammars, so where the parsing
--- module is missing both fall back to escaped plain text and only plain text
--- still renders. It has to be built for the Lua cgit is linked against.
--- cgit's syntax highlighter needs it too, so a cgit that colours source
--- already has it.
+-- Markdown and man pages need lpeg, built for the Lua cgit is linked
+-- against. Without it both fall back to escaped plain text.
--
-- # Debian and Ubuntu
-- sudo apt install lua-lpeg
@@ -42,9 +37,8 @@ local function trim(text)
return (text:gsub("^%s+", ""):gsub("%s+$", ""))
end
--- Split on newlines after normalising CRLF and CR, returning the lines
--- without their terminators. A trailing newline yields a final empty line,
--- which every caller treats as blank.
+-- Normalise CRLF and CR to newlines, then split. A trailing newline yields
+-- a final empty line, which every caller treats as blank.
local function split_lines(text)
text = text:gsub("\r\n?", "\n")
local lines, start = {}, 1
@@ -59,8 +53,6 @@ local function split_lines(text)
end
end
--- Split a table row into trimmed cells, dropping one optional leading and one
--- optional trailing pipe.
local function split_cells(row)
row = trim(row):gsub("^|", ""):gsub("|$", "")
local cells = {}
@@ -70,11 +62,10 @@ local function split_cells(row)
return cells
end
--- Return the URL if its scheme is safe, else nil. Control and whitespace
--- bytes are stripped anywhere first because a browser ignores them when
--- resolving the scheme, so "java\nscript:" must still be caught as
--- javascript. The class is %c%s rather than a literal 0x00 range so it is
--- safe on Lua 5.1, where an embedded zero byte ends a pattern.
+-- Control and whitespace bytes are stripped before the scheme check because
+-- a browser ignores them when resolving it, so "java\nscript:" must still be
+-- caught as javascript. The class is %c%s rather than a literal 0x00 range
+-- so it is safe on Lua 5.1, where an embedded zero byte ends a pattern.
local function safe_url(url)
url = url:gsub("[%c%s]", "")
-- A leading "//" or "/\" is scheme-relative, which a browser
@@ -94,14 +85,10 @@ local function safe_url(url)
end
--- The about page renders untrusted repository content, so the rule the two
--- functions below keep is that every run of text reaches the page through
--- cgit's own html_txt, every attribute value through html_attr, and every
--- link or image target through safe_url before html_attr. Those three come
--- from cgit's Lua filter host rather than from here, and the only thing
--- passed to html is literal tag scaffolding. Because all output is routed
--- through those sinks by construction, a bug in the parser can only
--- mis-render, never inject markup or a URL with a javascript scheme.
+-- The page renders untrusted repository content. Every run of text goes out
+-- through cgit's html_txt, every attribute value through html_attr, and
+-- every link or image target through safe_url before html_attr. html() only
+-- ever gets literal tag scaffolding.
local emit_inline
@@ -198,16 +185,12 @@ local function render_plaintext(text)
end
--- Markdown here is a deliberate subset rather than CommonMark, covering
--- headings, thematic breaks, fenced and inline code, blockquotes, pipe
--- tables, single-level lists, links, images and emphasis, and leaving out
--- reference links, raw HTML passthrough, nested lists and setext headings.
--- Emphasis does not span a hard line break inside a paragraph. Man rendering
--- covers the common macros, that is section headings, filled and no-fill
--- paragraphs, bold and italic and the font escapes, and drops the rest. Both
--- are parsed with lpeg into the node trees emit_blocks and emit_inline above
--- consume, and parsing only builds a tree, so a parse that fails falls back
--- to plain text without leaving half a page behind.
+-- Markdown is a deliberate subset rather than CommonMark, leaving out
+-- reference links, raw HTML passthrough, nested lists and setext headings,
+-- and emphasis does not span a hard line break. Man rendering covers the
+-- common macros and drops the rest. Both parse into a node tree before
+-- anything is emitted, so a failed parse falls back to plain text without
+-- leaving half a page behind.
local render_markdown, render_man
@@ -223,8 +206,6 @@ if has_lpeg then
-- Bound the link and image inner scans. Without a cap a readme of
-- unclosed brackets ("[[[[...") makes every position scan to the
-- end of the line for a "]" that never comes, which is quadratic.
- -- Real link text and urls sit far under this, and anything longer
- -- simply renders as plain text.
local max_scan = 512
local parse_inline
@@ -275,8 +256,6 @@ if has_lpeg then
return nodes
end
- -- Line matchers. Each is anchored at the start of a single line and
- -- returns its captures, or nil when the line is not of that kind.
local lang_char = R("az", "AZ", "09") + S("_.+#-")
local heading_line = C(P("#") * P("#") ^ -5) * space ^ 1 * C(P(1) ^ 0)
local function thematic(mark)
@@ -434,20 +413,24 @@ if has_lpeg then
html("</div>")
end
- -- Macro lines are matched with lpeg, and the roff inline font and
- -- character escapes are a grammar producing a flat token list that
- -- a fold turns into the same inline nodes markdown emits. Bold and
- -- italic are the current font, which carries across a run rather
- -- than being opened and closed, so the inline pass tokenises and
- -- folds instead of nesting the way markdown does.
+ -- Roff fonts are a current state carried across a run rather than
+ -- opened and closed, so the inline pass tokenises to a flat list
+ -- and folds it into the nodes markdown emits, instead of nesting.
local macro_line = S(".'") * space ^ 0 * C((1 - space) ^ 1)
* space ^ 0 * C(P(1) ^ 0)
local one_font = { B = "B", I = "I" }
local two_font = { CB = "B", BI = "B", CI = "I" }
local man_chars = {
- aq = "'", cq = "'", oq = "'", dq = '"', lq = '"', rq = '"',
- hy = "-", en = "-", em = "-",
+ aq = "'",
+ cq = "'",
+ oq = "'",
+ dq = '"',
+ lq = '"',
+ rq = '"',
+ hy = "-",
+ en = "-",
+ em = "-",
}
local backslash = P("\\")
local function font_token(name) return { kind = "font", font = name } end
@@ -610,7 +593,7 @@ function filter_write(str)
end
-- cgit takes filter output through a C string sink that stops at the first
--- NUL byte, so a readme holding one is truncated there.
+-- NUL byte, so a write holding one loses everything from the NUL on.
function filter_close()
local text = table.concat(chunks)
chunks = {}
diff --git a/custom/extensions/auth-file.lua b/custom/extensions/auth-file.lua
index 74a04b6..957bd49 100644
--- a/custom/extensions/auth-file.lua
+++ b/custom/extensions/auth-file.lua
@@ -11,11 +11,9 @@
-- be the same Lua that cgit was built against. Lua 5.5 will not do, because
-- luaossl has no 5.5 build.
--
--- Serve cgit over HTTPS and terminate TLS in the web server in front of it.
--- The session cookie is marked Secure by default, so a browser only sends it
--- back over HTTPS, and on an instance served over plain HTTP with no TLS
--- anywhere the cookie never comes back and login appears to loop until
--- cookie_insecure below is set.
+-- The session cookie is marked Secure by default, so a browser only sends
+-- it back over HTTPS. On an instance served over plain HTTP login loops
+-- until cookie_insecure below is set.
--
-- Two libraries are needed. luaossl provides openssl.rand and openssl.hmac and
-- lives at <https://github.com/wahern/luaossl>, and luaposix provides
@@ -42,16 +40,10 @@
-- luarocks install luaossl OPENSSL_DIR="$(brew --prefix openssl)"
-- luarocks install luaposix
--
--- Some distributions package both as well, for example lua-luaossl and
--- lua-posix on Debian, and such a package has to be built for the same Lua
--- version as cgit.
---
--- The cookie carries only a user name and there is no server-side session
--- store, so deleting an account does not revoke a cookie already issued until
--- it expires, and instances that share a secret file accept each other's
--- cookies. The login form carries no CSRF token. Both are acceptable for
--- gating read access to a git browser, so weigh them before guarding anything
--- more sensitive.
+-- There is no server-side session store and no CSRF token, so a deleted
+-- account's cookie stays valid until it expires and instances sharing a
+-- secret file accept each other's cookies. That is acceptable for gating
+-- read access to a git browser, so weigh it before guarding anything more.
local sysstat = require("posix.sys.stat")
local unistd = require("posix.unistd")
@@ -59,7 +51,6 @@ local rand = require("openssl.rand")
local hmac = require("openssl.hmac")
-- The values that follow are the configuration and are meant to be edited.
--- Nothing below them needs changing for ordinary use.
-- Accounts live one per line as username:hash. Generate a hash with
-- mkpasswd -m sha-512 -R 300000
@@ -73,7 +64,7 @@ local groups_filename = "/etc/cgit-auth/groups"
-- Per-repository access lives one per line as reponame:group1,group2 and so
-- on. A repository named here is protected and one that is not named is
-- public. The repository name has to match exactly, while group and user names
--- match whatever their case.
+-- match regardless of case.
local repos_filename = "/etc/cgit-auth/repos"
-- Where the cookie-signing secret is stored, created on first use. It must be
@@ -91,8 +82,7 @@ local cookie_name = "cgitauth"
-- more tightly.
local cookie_path = "/"
--- Leave this false so the cookie is marked Secure and only travels over HTTPS.
--- Set it true only if cgit is served over plain HTTP with no TLS anywhere.
+-- Set this true only if cgit is served over plain HTTP with no TLS anywhere.
local cookie_insecure = false
-- A throwaway hash of the documented shape, used only to spend the same work
@@ -104,9 +94,8 @@ local dummy_hash = "$6$rounds=300000$0000000000000000$"
-- open decodes is kept here for the calls that follow.
local action, http, cgit, post
--- The two lookups below, account_hash and repo_userset, are the only part of
--- this script that differs from auth-inline.lua. Replacing them is all it
--- takes to keep accounts somewhere else.
+-- The two lookups below, account_hash and repo_userset, are the only part
+-- of this script that differs from auth-inline.lua.
local function trim(s)
return (string.gsub(s, "^%s*(.-)%s*$", "%1"))
@@ -118,14 +107,12 @@ local function add_names(list, set)
end
end
--- A missing or unreadable users file is not fatal, and neither is a line that
--- does not parse, so a broken file turns every login down rather than failing
--- the request outright.
+-- A missing, unreadable or unparsable users file turns every login down
+-- rather than failing the request outright.
--
--- The hash is trimmed as well as the name because reading by line strips the
--- newline but not a carriage return, so a users file saved with CRLF endings
--- would otherwise hand crypt a hash with a trailing \r and fail every login
--- with nothing in the log to say why.
+-- The hash is trimmed as well as the name because reading by line strips
+-- the newline but not a carriage return, so a CRLF users file would hand
+-- crypt a hash with a trailing \r and fail every login.
function account_hash(user)
if user == nil then
return nil
@@ -224,14 +211,9 @@ local function pattern_escape(s)
return (string.gsub(s, "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1"))
end
--- The stored token was already URL encoded by secure_value, so it comes back
--- verbatim and the write path in set_cookie stays symmetric with this read
--- path. Decoding it here would break the signature check for any value
--- carrying a percent escape.
---
--- The name is escaped because it lands in a pattern. A cookie_name holding a
--- magic character, say "cgit-auth", would otherwise read as a pattern and stop
--- matching its own cookie while matching names nobody configured.
+-- The token comes back still URL encoded by secure_value. Decoding it here
+-- would break the signature check for any value carrying a percent escape.
+-- The name is escaped because it lands in a Lua pattern.
function get_cookie(cookies, name)
cookies = string.gsub(";" .. (cookies or "") .. ";", "%s*;%s*", ";")
return string.match(cookies, ";" .. pattern_escape(name) .. "=(.-);")
@@ -516,18 +498,21 @@ function body()
html_attr(secure_value("redirect", target, 0))
html("'>")
html("<table>")
- html("<tr><td><label for='username'>Username:</label></td><td><input id='username' name='username' autocomplete='username' autofocus></td></tr>")
- html("<tr><td><label for='password'>Password:</label></td><td><input id='password' name='password' type='password' autocomplete='current-password'></td></tr>")
- html("<tr><td colspan='2'><input value='Login' type='submit'></td></tr>")
+ html("<tr><td><label for='username'>Username:</label></td><td>")
+ html("<input id='username' name='username'")
+ html(" autocomplete='username' autofocus></td></tr>")
+ html("<tr><td><label for='password'>Password:</label></td><td>")
+ html("<input id='password' name='password' type='password'")
+ html(" autocomplete='current-password'></td></tr>")
+ html("<tr><td colspan='2'>")
+ html("<input value='Login' type='submit'></td></tr>")
html("</table></form>")
return 0
end
--- cgit calls filter_open with the action name followed by the request fields
--- in a fixed order, so they are unpacked here into the tables the functions
--- above read. Only a post reaches filter_write, carrying the form body, and
--- filter_close is where the action finally runs and answers cgit.
+-- filter_open unpacks the action and request fields cgit passes in fixed
+-- order, filter_write collects a post body, and filter_close runs the action.
local actions = {}
actions["authenticate-post"] = authenticate_post
diff --git a/custom/extensions/auth-inline.lua b/custom/extensions/auth-inline.lua
index aa4b9f1..5489189 100644
--- a/custom/extensions/auth-inline.lua
+++ b/custom/extensions/auth-inline.lua
@@ -11,11 +11,9 @@
-- be the same Lua that cgit was built against. Lua 5.5 will not do, because
-- luaossl has no 5.5 build.
--
--- Serve cgit over HTTPS and terminate TLS in the web server in front of it.
--- The session cookie is marked Secure by default, so a browser only sends it
--- back over HTTPS, and on an instance served over plain HTTP with no TLS
--- anywhere the cookie never comes back and login appears to loop until
--- cookie_insecure below is set.
+-- The session cookie is marked Secure by default, so a browser only sends
+-- it back over HTTPS. On an instance served over plain HTTP login loops
+-- until cookie_insecure below is set.
--
-- Two libraries are needed. luaossl provides openssl.rand and openssl.hmac and
-- lives at <https://github.com/wahern/luaossl>, and luaposix provides
@@ -42,16 +40,10 @@
-- luarocks install luaossl OPENSSL_DIR="$(brew --prefix openssl)"
-- luarocks install luaposix
--
--- Some distributions package both as well, for example lua-luaossl and
--- lua-posix on Debian, and such a package has to be built for the same Lua
--- version as cgit.
---
--- The cookie carries only a user name and there is no server-side session
--- store, so deleting an account does not revoke a cookie already issued until
--- it expires, and instances that share a secret file accept each other's
--- cookies. The login form carries no CSRF token. Both are acceptable for
--- gating read access to a git browser, so weigh them before guarding anything
--- more sensitive.
+-- There is no server-side session store and no CSRF token, so a deleted
+-- account's cookie stays valid until it expires and instances sharing a
+-- secret file accept each other's cookies. That is acceptable for gating
+-- read access to a git browser, so weigh it before guarding anything more.
local sysstat = require("posix.sys.stat")
local unistd = require("posix.unistd")
@@ -59,13 +51,11 @@ local rand = require("openssl.rand")
local hmac = require("openssl.hmac")
-- The values that follow are the configuration and are meant to be edited.
--- Nothing below them needs changing for ordinary use.
-- Protected repositories and the users allowed into each. A repository named
-- here is protected and one that is not named is public. The repository key
--- has to match exactly, while user names match whatever their case. Replace
--- the examples below with your own. They are commented out, so an unedited
--- copy protects nothing and grants no accounts.
+-- has to match exactly, while user names match regardless of case. The
+-- examples are commented out, so an unedited copy protects nothing.
local protected_repos = {
-- ["secret-repo"] = { alice = true, bob = true },
-- ["another"] = { alice = true },
@@ -73,8 +63,6 @@ local protected_repos = {
-- Accounts as name and hash. Generate a hash with
-- mkpasswd -m sha-512 -R 300000
--- Replace the examples below. They are not real credentials and must not be
--- deployed as they stand.
local users = {
-- alice = "$6$rounds=300000$REPLACE_THIS_SALT$REPLACE_THIS_HASH",
-- bob = "$6$rounds=300000$REPLACE_THIS_SALT$REPLACE_THIS_HASH",
@@ -95,8 +83,7 @@ local cookie_name = "cgitauth"
-- more tightly.
local cookie_path = "/"
--- Leave this false so the cookie is marked Secure and only travels over HTTPS.
--- Set it true only if cgit is served over plain HTTP with no TLS anywhere.
+-- Set this true only if cgit is served over plain HTTP with no TLS anywhere.
local cookie_insecure = false
-- A throwaway hash of the documented shape, used only to spend the same work
@@ -108,9 +95,8 @@ local dummy_hash = "$6$rounds=300000$0000000000000000$"
-- open decodes is kept here for the calls that follow.
local action, http, cgit, post
--- The two lookups below, account_hash and repo_userset, are the only part of
--- this script that differs from auth-file.lua. Replacing them is all it takes
--- to keep accounts somewhere else.
+-- The two lookups below, account_hash and repo_userset, are the only part
+-- of this script that differs from auth-file.lua.
-- The configured tables are folded to lowercased user names once, so that a
-- lookup matches whatever case the login form was filled in with, the way
@@ -186,14 +172,9 @@ local function pattern_escape(s)
return (string.gsub(s, "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1"))
end
--- The stored token was already URL encoded by secure_value, so it comes back
--- verbatim and the write path in set_cookie stays symmetric with this read
--- path. Decoding it here would break the signature check for any value
--- carrying a percent escape.
---
--- The name is escaped because it lands in a pattern. A cookie_name holding a
--- magic character, say "cgit-auth", would otherwise read as a pattern and stop
--- matching its own cookie while matching names nobody configured.
+-- The token comes back still URL encoded by secure_value. Decoding it here
+-- would break the signature check for any value carrying a percent escape.
+-- The name is escaped because it lands in a Lua pattern.
function get_cookie(cookies, name)
cookies = string.gsub(";" .. (cookies or "") .. ";", "%s*;%s*", ";")
return string.match(cookies, ";" .. pattern_escape(name) .. "=(.-);")
@@ -478,18 +459,21 @@ function body()
html_attr(secure_value("redirect", target, 0))
html("'>")
html("<table>")
- html("<tr><td><label for='username'>Username:</label></td><td><input id='username' name='username' autocomplete='username' autofocus></td></tr>")
- html("<tr><td><label for='password'>Password:</label></td><td><input id='password' name='password' type='password' autocomplete='current-password'></td></tr>")
- html("<tr><td colspan='2'><input value='Login' type='submit'></td></tr>")
+ html("<tr><td><label for='username'>Username:</label></td><td>")
+ html("<input id='username' name='username'")
+ html(" autocomplete='username' autofocus></td></tr>")
+ html("<tr><td><label for='password'>Password:</label></td><td>")
+ html("<input id='password' name='password' type='password'")
+ html(" autocomplete='current-password'></td></tr>")
+ html("<tr><td colspan='2'>")
+ html("<input value='Login' type='submit'></td></tr>")
html("</table></form>")
return 0
end
--- cgit calls filter_open with the action name followed by the request fields
--- in a fixed order, so they are unpacked here into the tables the functions
--- above read. Only a post reaches filter_write, carrying the form body, and
--- filter_close is where the action finally runs and answers cgit.
+-- filter_open unpacks the action and request fields cgit passes in fixed
+-- order, filter_write collects a post body, and filter_close runs the action.
local actions = {}
actions["authenticate-post"] = authenticate_post
diff --git a/custom/extensions/email-gravatar.lua b/custom/extensions/email-gravatar.lua
index 85947c4..6eb5376 100644
--- a/custom/extensions/email-gravatar.lua
+++ b/custom/extensions/email-gravatar.lua
@@ -27,29 +27,25 @@
-- luarocks install luaossl OPENSSL_DIR="$(brew --prefix openssl)"
--
-- Every page view sends the visitor's IP address and a hash of each
--- committer's email to a third-party service, so leave this filter off if that
--- is not acceptable for your instance. Addresses are hashed with MD5, which
--- Gravatar still accepts. Gravatar also supports SHA-256 now, so change the
--- digest in hash_hex if you prefer it.
+-- committer's email to a third-party service, so leave this filter off if
+-- that is not acceptable for your instance. Addresses are hashed with MD5,
+-- which Gravatar still accepts.
local digest = require("openssl.digest")
--- These are the values to change. The size is in pixels and serves both as the
--- image asked of the service and as the width and height attributes. The
--- default image is the style Gravatar draws for an address it has never seen,
--- and its documented choices include retro, identicon, monsterid and mp. The
--- endpoint is https so the icon is not blocked as mixed content on an https
--- page.
+-- The size is in pixels and serves both as the image asked of the service
+-- and as the width and height attributes. The default image is what
+-- Gravatar draws for an address it has never seen. The endpoint is https so
+-- the icon is not blocked as mixed content on an https page.
local avatar_size = 13
local default_image = "retro"
local base_url = "https://www.gravatar.com/avatar/"
local alt_text = "Gravatar"
--- cgit calls filter_open once, then filter_write for each piece of the name,
--- then filter_close, so what one call works out has to be left here for the
--- next one.
+-- One name reaches this filter as an open, writes and a close, so what the
+-- open works out is kept here for the close.
local buffer = ""
local avatar_hash = nil
diff --git a/custom/extensions/email-libravatar.lua b/custom/extensions/email-libravatar.lua
index e958346..9b3d595 100644
--- a/custom/extensions/email-libravatar.lua
+++ b/custom/extensions/email-libravatar.lua
@@ -26,27 +26,24 @@
-- luarocks install luaossl OPENSSL_DIR="$(brew --prefix openssl)"
--
-- Every page view sends the visitor's IP address and a hash of each
--- committer's email to a third-party service, so leave this filter off if that
--- is not acceptable for your instance. Addresses are hashed with MD5.
+-- committer's email to a third-party service, so leave this filter off if
+-- that is not acceptable for your instance. Addresses are hashed with MD5.
local digest = require("openssl.digest")
--- These are the values to change. The size is in pixels and serves both as the
--- image asked of the service and as the width and height attributes. The
--- default image is the style Libravatar draws for an address it has never
--- seen, and its documented choices include retro, identicon, monsterid and mm.
--- The endpoint is the secure CDN so the icon loads over https and is not
--- blocked as mixed content on an https page.
+-- The size is in pixels and serves both as the image asked of the service
+-- and as the width and height attributes. The default image is what
+-- Libravatar draws for an address it has never seen. The endpoint is the
+-- secure CDN so the icon is not blocked as mixed content on an https page.
local avatar_size = 13
local default_image = "retro"
local base_url = "https://seccdn.libravatar.org/avatar/"
local alt_text = "Libravatar"
--- cgit calls filter_open once, then filter_write for each piece of the name,
--- then filter_close, so what one call works out has to be left here for the
--- next one.
+-- One name reaches this filter as an open, writes and a close, so what the
+-- open works out is kept here for the close.
local buffer = ""
local avatar_hash = nil
diff --git a/custom/extensions/link-commits.lua b/custom/extensions/link-commits.lua
index 3f2429d..42252f3 100644
--- a/custom/extensions/link-commits.lua
+++ b/custom/extensions/link-commits.lua
@@ -10,12 +10,10 @@
-- Object names are handled apart from the rules below because the length
--- bound on them cannot be written as a plain Lua pattern. Recognition is by
--- shape, since a commit-filter cannot ask the repository whether a hash is
--- real, so any hex run within the bounds is linked whatever mix of digits and
--- letters it has and abbreviated and all-digit names are both caught. The
--- cost is that a long hex-looking number now and then links to an object that
--- does not exist, which cgit renders as a harmless "bad object name" page.
+-- bound on them cannot be written as a plain Lua pattern. A commit-filter
+-- cannot ask the repository whether a hash is real, so matching is by shape
+-- and a long hex number may link to an object that does not exist, which
+-- cgit renders as a harmless "Bad object id" page.
local objects = {
-- Set false to stop linking bare hashes.
enabled = true,
@@ -32,24 +30,23 @@ local objects = {
-- Text-reference rules, each one a Lua pattern with a single capture and a
-- URL where %s is replaced by that capture, percent-encoded. The whole match
-- is what gets shown and the capture is only what goes into the URL. Rules are
--- tried in order and the leftmost match on the line wins, so put the more
--- specific patterns first, and an empty list leaves only object names linked.
+-- tried in order and the leftmost match wins, so put the more specific
+-- patterns first, and an empty list leaves only object names linked.
--
--- Lua patterns are not regular expressions. There is no alternation and no
--- {n,m} repetition, %d is a digit, %a a letter, %w a letter or digit, %x a
--- hex digit, and a literal magic character is escaped with %, so a literal
--- dash is '%-'. The whole set is in the reference manual at
+-- Lua patterns are not regular expressions. The reference is
-- https://www.lua.org/manual/5.1/manual.html#5.4.1
--
--- Patterns run against the escaped message, so '&', '<' and '>' reach them as
--- '&amp;', '&lt;' and '&gt;'. Match those entity spellings rather than the
--- bare character, and keep a pattern from ending part way through one, since
--- the matched run is what gets wrapped in the anchor.
+-- Patterns run against the escaped message, so match the entity spellings
+-- '&amp;', '&lt;' and '&gt;' rather than the bare characters, and keep a
+-- pattern from ending part way through one.
local rules = {
{ pattern = "#(%d+)", url = "https://bugs.example.com/?bug=%s" },
- -- { pattern = "CVE%-(%d%d%d%d%-%d+)", url = "https://www.cve.org/CVERecord?id=CVE-%s" },
- -- { pattern = "!(%d+)", url = "https://gitlab.example.com/group/repo/-/merge_requests/%s" },
- -- { pattern = "RFC%s?(%d+)", url = "https://www.rfc-editor.org/rfc/rfc%s" },
+ -- { pattern = "CVE%-(%d%d%d%d%-%d+)",
+ -- url = "https://www.cve.org/CVERecord?id=CVE-%s" },
+ -- { pattern = "!(%d+)",
+ -- url = "https://gitlab.example.com/my/repo/-/merge_requests/%s" },
+ -- { pattern = "RFC%s?(%d+)",
+ -- url = "https://www.rfc-editor.org/rfc/rfc%s" },
}
@@ -68,7 +65,9 @@ end
-- for a gsub reference.
local function make_link(url_template, capture, display)
local encoded = url_encode(capture)
- local href = string.gsub(url_template, "%%s", function() return encoded end)
+ local href = string.gsub(url_template, "%%s", function()
+ return encoded
+ end)
return "<a href='" .. href .. "'>" .. display .. "</a>"
end
@@ -89,7 +88,9 @@ local function collect(text)
capture = string.sub(text, start, stop)
end
candidates[#candidates + 1] = {
- start = start, stop = stop, priority = priority,
+ start = start,
+ stop = stop,
+ priority = priority,
link = make_link(rule.url, capture,
string.sub(text, start, stop)),
}
@@ -106,9 +107,12 @@ local function collect(text)
local start, stop, run =
string.find(text, "%f[%w](%x+)%f[%W]", init)
if not start then break end
- if #run >= objects.min_length and #run <= objects.max_length then
+ if #run >= objects.min_length
+ and #run <= objects.max_length then
candidates[#candidates + 1] = {
- start = start, stop = stop, priority = priority,
+ start = start,
+ stop = stop,
+ priority = priority,
link = make_link(objects.url, run, run),
}
end
@@ -141,7 +145,8 @@ function filter_close()
-- A candidate reaching back into one already emitted is
-- dropped, so no run of text is ever wrapped twice.
if candidate.start >= pos then
- out[#out + 1] = string.sub(text, pos, candidate.start - 1)
+ out[#out + 1] =
+ string.sub(text, pos, candidate.start - 1)
out[#out + 1] = candidate.link
pos = candidate.stop + 1
end
diff --git a/custom/extensions/syntax-highlight.lua b/custom/extensions/syntax-highlight.lua
index 862965a..e2032aa 100644
--- a/custom/extensions/syntax-highlight.lua
+++ b/custom/extensions/syntax-highlight.lua
@@ -1,16 +1,13 @@
--- Server-side syntax highlighting for cgit's tree and blob views, run inside
--- cgit's embedded Lua interpreter so a coloured blob costs no extra process
--- per request. Colouring is deliberately left out of cgit itself, which
--- serves plain escaped text on its own, so this filter is named by the
--- source-filter setting and any other program could take its place. Tokens
--- come from the Scintillua lexers and reach the page wrapped in span elements
--- carrying the hl- classes that assets/cgit.css styles. Whenever a piece is
--- missing or will not load, from lpeg down to a single lexer, the file falls
--- back to plain escaped text instead of failing, so uncoloured code means a
--- missing dependency rather than an error. It runs on Lua 5.1 through 5.5 and
+-- Server-side syntax highlighting for cgit's tree and blob views, named by
+-- the source-filter setting and run inside cgit's embedded Lua interpreter
+-- so a coloured blob costs no extra process per request. Tokens come from the
+-- Scintillua lexers and reach the page wrapped in span elements carrying the
+-- hl- classes that assets/cgit.css styles. Whenever a piece is missing or
+-- will not load, from lpeg down to a single lexer, the filter falls back to
+-- plain escaped text instead of failing. It runs on Lua 5.1 through 5.5 and
-- LuaJIT.
--
--- source-filter=lua:/usr/lib/cgit/extensions/syntax-highlight.lua
+-- source-filter=lua:/usr/local/lib/cgit/filters/syntax-highlight.lua
-- Files larger than this many bytes are served escaped but unhighlighted, so
@@ -28,12 +25,10 @@ local scintillua_env = "CGIT_SCINTILLUA_PATH"
-- Directories probed for the lexers when that variable is not set, tried
-- after the directory of $CGIT_CONFIG, so placing or symlinking a scintillua
--- directory next to cgitrc is enough to be found. Scintillua is the lexer
--- collection from the Textadept editor, around 160 languages as plain .lua
--- files with nothing to compile, from
--- https://orbitalquark.github.io/scintillua/. It does not bundle lpeg, which
--- it needs and which has to be built for the Lua cgit is linked against, and
--- forgetting that is the usual reason nothing is coloured.
+-- directory next to cgitrc is enough for it to be found. The lexers come
+-- from https://orbitalquark.github.io/scintillua/ and need lpeg, built for
+-- the Lua cgit is linked against. Forgetting that is the usual reason
+-- nothing is coloured.
--
-- # Debian and Ubuntu
-- sudo apt install lua-lpeg
@@ -76,15 +71,23 @@ local css = {
["function"] = "hl-func",
}
--- Extension to lexer name fixes for the fallback path, reached only when this
--- Scintillua has no detect(). Most extensions already equal their lexer name
--- and these are the frequent exceptions. A wrong guess only falls back to
--- plain text, so a best-effort entry costs nothing.
+-- Extension to lexer name fixes for the fallback path, reached only when
+-- this Scintillua has no detect(). Most extensions already equal their lexer
+-- name and these are the frequent exceptions.
local ext_lexer = {
- py = "python", js = "javascript", ts = "typescript",
- rb = "ruby", pl = "perl", pm = "perl", sh = "bash",
- md = "markdown", htm = "html", yml = "yaml",
- rs = "rust", c = "ansi_c", h = "ansi_c",
+ py = "python",
+ js = "javascript",
+ ts = "typescript",
+ rb = "ruby",
+ pl = "perl",
+ pm = "perl",
+ sh = "bash",
+ md = "markdown",
+ htm = "html",
+ yml = "yaml",
+ rs = "rust",
+ c = "ansi_c",
+ h = "ansi_c",
}
@@ -108,7 +111,8 @@ local function scintillua_path()
if config then
local dir = string.match(config, "^(.*)/[^/]+$")
if dir then
- candidates[#candidates + 1] = dir .. "/scintillua/lexers"
+ candidates[#candidates + 1] =
+ dir .. "/scintillua/lexers"
end
end
for _, dir in ipairs(scintillua_dirs) do
@@ -159,9 +163,6 @@ local function load_lexer_name(name)
return nil
end
--- Resolve a lexer for the file, preferring Scintillua's own filename
--- detection where this version provides it, then the extension map above,
--- then the raw extension.
local function lexer_for(name)
if type(scintillua.detect) == "function" then
local ok, lang = pcall(scintillua.detect, name)
@@ -200,7 +201,8 @@ local function highlight(text)
local part = escape(string.sub(text, pos, stop - 1))
local class = css[string.match(tag, "^[%w_]+")]
if class and part ~= "" then
- part = "<span class='" .. class .. "'>" .. part .. "</span>"
+ part = "<span class='" .. class .. "'>"
+ .. part .. "</span>"
end
out[#out + 1] = part
pos = stop
@@ -223,8 +225,9 @@ function filter_write(str)
end
-- cgit takes filter output through a C string sink that stops at the first
--- NUL byte, so a blob holding one is truncated there. That reaches binary
--- files which slip past cgit's text detection, not ordinary source.
+-- NUL byte, so a write holding one loses everything from the NUL on. That
+-- reaches binary files which slip past cgit's text detection, not ordinary
+-- source.
function filter_close()
local text = table.concat(chunks)
chunks = {}