Diff options
| author | Bryce Kwon <bryce@brycekwon.com> | |
|---|---|---|
| committer | Bryce 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.lua | 22 | |
| -rw-r--r-- | tests/extensions/test-about-render.lua | 11 |
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") |
