Diff options
authorBryce Kwon <bryce@brycekwon.com>
committerBryce Kwon <bryce@brycekwon.com>
commit
parent
tree
download
Keep a readme whole past a NUL byte
cgit takes filter output through a C string sink that stops at the first NUL, so a readme holding one lost everything after it. Each NUL now shows as U+FFFD, as a browser would show it.
-rw-r--r--custom/extensions/about-render.lua22
-rw-r--r--tests/extensions/test-about-render.lua11
2 files changed, 25 insertions, 8 deletions
diff --git a/custom/extensions/about-render.lua b/custom/extensions/about-render.lua
index 1e59716..d7753a3 100644
--- a/custom/extensions/about-render.lua
+++ b/custom/extensions/about-render.lua
@@ -820,9 +820,27 @@ function filter_write(str)
end
-- cgit takes filter output through a C string sink that stops at the first
--- NUL byte, so a write holding one loses everything from the NUL on.
+-- NUL byte, so each one becomes U+FFFD, as a browser would show it, and the
+-- text past it survives. A plain find works on Lua 5.1, where a NUL cannot
+-- sit in a pattern.
+local function replace_nul(text)
+ if not text:find("\0", 1, true) then
+ return text
+ end
+ local parts, start = {}, 1
+ while true do
+ local at = text:find("\0", start, true)
+ if not at then
+ parts[#parts + 1] = text:sub(start)
+ return table.concat(parts, "\239\191\189")
+ end
+ parts[#parts + 1] = text:sub(start, at - 1)
+ start = at + 1
+ end
+end
+
function filter_close()
- local text = table.concat(chunks)
+ local text = replace_nul(table.concat(chunks))
chunks = {}
local ext = (filename:match("%.([^.]+)$") or ""):lower()
local render = handlers[ext] or render_plaintext
diff --git a/tests/extensions/test-about-render.lua b/tests/extensions/test-about-render.lua
index e0fde6f..a493720 100644
--- a/tests/extensions/test-about-render.lua
+++ b/tests/extensions/test-about-render.lua
@@ -48,12 +48,11 @@ out = render(nil, "no name at all")
h.contains("a missing filename renders as plain text", out,
"<pre class='plaintext'>no name at all</pre>")
--- cgit's C sink stops at the first NUL byte, which the script documents, so
--- the text past one is lost while the wrapper written separately survives.
-out = render("README", "before\0after")
-h.contains("text before a nul byte survives", out, "before")
-h.excludes("text after a nul byte is dropped", out, "after")
-h.contains("the wrapper written after the text survives", out, "</pre>")
+-- cgit's C sink stops at the first NUL byte, so the script replaces each one
+-- before anything is written, and the text past it survives.
+out = render("README", "before\0after\0")
+h.contains("a nul byte shows as a replacement character", out,
+ "<pre class='plaintext'>before\239\191\189after\239\191\189</pre>")
out = h.run({ "README" }, { "two ", "writes" })
h.contains("writes are joined before rendering", out, "two writes")