diff options
69 files changed, 652 insertions, 942 deletions
@@ -883,8 +883,8 @@ use of git notes. For example, the following command may be used to add a signature to a .tar.xz archive: git notes --ref=refs/notes/signatures/tar.xz add -C "$( - gpg --output - --armor --detach-sign cgit-1.1.tar.xz | - git hash-object -w --stdin + gpg --output - --armor --detach-sign cgit-1.1.tar.xz | + git hash-object -w --stdin )" v1.1 If it is instead desirable to attach a signature of the underlying .tar, this @@ -20,7 +20,7 @@ BUILDDIR = build CGIT_ROOT = ../.. # The Git release that get-git fetches when the submodule is not checked out. -# Keep it in step with the submodule pin in .gitmodules. +# Keep it in step with the commit the submodule is pinned to. GIT_VERSION = 2.55.0 GIT_URL = https://www.kernel.org/pub/software/scm/git/git-$(GIT_VERSION).tar.xz @@ -42,7 +42,11 @@ filterdir = $(libdir)/cgit/filters # The files that go into the data path and the filter directory. One list each # drives both install and uninstall, so the two can never drift apart. -ASSETS = cgit.css cgit.js cgit.png favicon.ico robots.txt +ASSETS = cgit.css +ASSETS += cgit.js +ASSETS += cgit.png +ASSETS += favicon.ico +ASSETS += robots.txt FILTER_FILES = $(notdir $(wildcard $(EXTDIR)/*.lua)) INSTALL = install @@ -52,7 +56,8 @@ INSTALL = install -include $(GITDIR)/config.mak.uname -include cgit.conf -export CGIT_VERSION CGIT_SCRIPT_NAME CGIT_SCRIPT_PATH CGIT_DATA_PATH CGIT_CONFIG CACHE_ROOT +export CGIT_VERSION CGIT_SCRIPT_NAME CGIT_SCRIPT_PATH +export CGIT_DATA_PATH CGIT_CONFIG CACHE_ROOT all: cgit @@ -81,11 +86,14 @@ cgit-with-git: install: all $(INSTALL) -m 0755 -d $(DESTDIR)$(CGIT_SCRIPT_PATH) - $(INSTALL) -m 0755 $(BUILDDIR)/cgit $(DESTDIR)$(CGIT_SCRIPT_PATH)/$(CGIT_SCRIPT_NAME) + $(INSTALL) -m 0755 $(BUILDDIR)/cgit \ + $(DESTDIR)$(CGIT_SCRIPT_PATH)/$(CGIT_SCRIPT_NAME) $(INSTALL) -m 0755 -d $(DESTDIR)$(CGIT_DATA_PATH) - $(INSTALL) -m 0644 $(addprefix $(ASSETDIR)/,$(ASSETS)) $(DESTDIR)$(CGIT_DATA_PATH) + $(INSTALL) -m 0644 $(addprefix $(ASSETDIR)/,$(ASSETS)) \ + $(DESTDIR)$(CGIT_DATA_PATH) $(INSTALL) -m 0755 -d $(DESTDIR)$(filterdir) - $(INSTALL) -m 0644 $(EXTDIR)/*.lua $(DESTDIR)$(filterdir) + $(INSTALL) -m 0644 $(addprefix $(EXTDIR)/,$(FILTER_FILES)) \ + $(DESTDIR)$(filterdir) uninstall: $(RM) $(DESTDIR)$(CGIT_SCRIPT_PATH)/$(CGIT_SCRIPT_NAME) @@ -111,7 +119,6 @@ tags: @mkdir -p $(BUILDDIR) find $(SRCDIR) -name '*.[ch]' | xargs ctags -f $(BUILDDIR)/tags -# Grouped to mirror the order of the targets above. .PHONY: all cgit cgit-with-git sparse test .PHONY: install uninstall .PHONY: clean cleanall get-git tags diff --git a/assets/cgit.css b/assets/cgit.css index 66d9bb8..3d754a5 100644 --- a/assets/cgit.css +++ b/assets/cgit.css @@ -1,9 +1,8 @@ /* - * cgit.css: styling for the cgit web interface - * * Colours are driven by CSS custom properties via light-dark(), so the - * whole theme follows the reader's system preference. All rules stay - * scoped under div#cgit so cgit can be embedded in another page. + * whole theme follows the reader's system preference. Rules stay scoped + * under div#cgit, or behind the cgit-standalone class cgit sets on its own + * pages, so cgit can be embedded in another page. */ div#cgit { @@ -31,7 +30,7 @@ div#cgit { --upd: light-dark(#0000cc, #6ea8fe); --hunk: light-dark(#000099, #7aa2f7); --accent: light-dark(#cc0000, #ff6b6b); - /* Faded wash over the fragment targeted source line. */ + /* Faded wash over the fragment-targeted source line. */ --line-hl: light-dark(rgba(212, 167, 44, 0.18), rgba(224, 192, 80, 0.14)); --notes-bg: light-dark(#ffffdd, #33331a); @@ -89,7 +88,7 @@ div#cgit { --radius-xs: 2px; padding: 0; - margin: 0em; + margin: 0; font-family: var(--font-sans); font-size: 13px; line-height: 1.4; @@ -124,7 +123,8 @@ div#cgit a:hover { text-decoration: underline; } -div#cgit img { +div#cgit img, +div#cgit video { border: none; max-width: 100%; } @@ -133,11 +133,8 @@ div#cgit table { border-collapse: collapse; } -/* Form controls */ - div#cgit select, -div#cgit input, -div#cgit button { +div#cgit input { font-family: inherit; font-size: inherit; height: var(--control-h); @@ -165,27 +162,21 @@ div#cgit select { cursor: pointer; } -div#cgit button, div#cgit input[type="submit"] { cursor: pointer; } div#cgit select:hover, -div#cgit button:hover, div#cgit input[type="submit"]:hover { border-color: var(--border-mid); - color: var(--fg); } div#cgit select:focus-visible, -div#cgit input:focus-visible, -div#cgit button:focus-visible { +div#cgit input:focus-visible { outline: 2px solid var(--link); outline-offset: 1px; } -/* Masthead */ - div#cgit #header { display: grid; grid-template-columns: auto 1fr auto; @@ -260,8 +251,6 @@ div#cgit #header .owner { text-align: right; } -/* Tab bar */ - div#cgit .tabs { display: flex; flex-wrap: wrap; @@ -314,12 +303,10 @@ div#cgit input.txt { max-width: 12em; } -/* Page frame */ - div#cgit .path { display: flex; flex-wrap: wrap; - margin: 0px; + margin: 0; padding: 0.45rem var(--gutter); color: var(--fg); background-color: var(--surface); @@ -331,18 +318,15 @@ div#cgit .path .rev { } div#cgit .content { - margin: 0px; + margin: 0; padding: 1.25rem var(--gutter); border-bottom: solid 2px var(--border); overflow-x: auto; } -/* Listing tables */ - div#cgit table.list { width: 100%; border: none; - border-collapse: collapse; } div#cgit table.list tr { @@ -373,8 +357,6 @@ div#cgit table.list tr.nohover:hover { background: var(--bg); } -/* Spacer rows between sections hold one empty cell. Cell height counts the - border box, so the vertical padding is added back on top of the line. */ div#cgit table.list tr.nohover td:empty { height: calc(1lh + 0.6em); } @@ -461,18 +443,11 @@ div#cgit table.list td a:hover { color: var(--link); } -/* Repository index */ - div#cgit div#summary { vertical-align: top; margin-bottom: 1em; } -div#cgit div#summary img, -div#cgit div#summary video { - max-width: 100%; -} - div#cgit div#summary pre { overflow-x: auto; } @@ -588,7 +563,6 @@ div#cgit .markdown pre code { } div#cgit .markdown table { - border-collapse: collapse; margin: 0.7em 0; } @@ -602,10 +576,6 @@ div#cgit .markdown th { background: var(--surface-2); } -div#cgit div#blob { - border: solid 1px var(--border-strong); -} - div#cgit div.error { color: var(--accent); font-weight: bold; @@ -621,8 +591,6 @@ div#cgit table.list td.sublevel-repo { padding-left: 1.5em; } -/* Directory / blob listings */ - div#cgit a.ls-blob, div#cgit a.ls-dir, div#cgit .ls-mod { @@ -657,8 +625,6 @@ div#cgit td.ls-links { white-space: nowrap; } -/* Source blob */ - div#cgit table.blob { margin-top: 0.5em; border-top: solid 1px var(--border-strong); @@ -669,14 +635,12 @@ div#cgit table.blob { } div#cgit table.blob td.lines { - margin: 0; padding: 0 0 0 0.5em; vertical-align: top; color: var(--fg); } div#cgit table.blob td.linenumbers { - margin: 0; padding: 0 0.5em 0 0.5em; vertical-align: top; text-align: right; @@ -754,8 +718,6 @@ div#cgit .hl-func { color: light-dark(#6639ba, #d2a8ff); } -/* Blame */ - div#cgit table.blame { display: block; width: max-content; @@ -801,8 +763,6 @@ div#cgit table.blame .oid { font-size: 100%; } -/* Binary blob */ - div#cgit table.bin-blob { margin-top: 0.5em; border: solid 1px var(--border-strong); @@ -823,13 +783,10 @@ div#cgit table.bin-blob td { font-family: var(--font-mono); white-space: pre; border-left: solid 1px var(--border-mid); - padding: 0em 1em; + padding: 0 1em; } -/* Commit / tag */ - div#cgit table.commit-info { - border-collapse: collapse; margin-top: 1.5em; } @@ -839,7 +796,6 @@ div#cgit div.cgit-panel { } div#cgit div.cgit-panel table { - border-collapse: collapse; border: solid 1px var(--border-mid); background-color: var(--surface); } @@ -852,14 +808,6 @@ div#cgit div.cgit-panel td { padding: 0.25em 0.5em; } -div#cgit div.cgit-panel td.label { - padding-right: 0.5em; -} - -div#cgit div.cgit-panel td.ctrl { - padding-left: 0.5em; -} - div#cgit table.commit-info th { text-align: left; font-weight: normal; @@ -875,8 +823,8 @@ div#cgit table.commit-info td { div#cgit div.commit-subject { font-weight: bold; font-size: 125%; - margin: 1.5em 0em 0.5em 0em; - padding: 0em; + margin: 1.5em 0 0.5em 0; + padding: 0; overflow-wrap: anywhere; } @@ -905,15 +853,12 @@ div#cgit div.notes-footer { clear: left; } -/* Diffstat */ - div#cgit div.diffstat-header { font-weight: bold; padding-top: 1.5em; } div#cgit table.diffstat { - border-collapse: collapse; border: solid 1px var(--border-mid); background-color: var(--surface); max-width: 100%; @@ -960,14 +905,13 @@ div#cgit table.diffstat td.graph { div#cgit table.diffstat td.graph table { border: none; - border-spacing: 0; table-layout: fixed; width: 100%; } div#cgit table.diffstat td.graph td { - padding: 0px; - border: 0px; + padding: 0; + border: 0; height: 7pt; } @@ -984,8 +928,6 @@ div#cgit div.diffstat-summary { padding-top: 0.5em; } -/* Unified diff */ - /* A scroll box beside the options float would shrink to fit the gap. */ div#cgit div.diff-scroll { clear: right; @@ -1044,17 +986,15 @@ div#cgit .right { text-align: right; } -/* Buttons / pager */ - div#cgit a.button { font-size: 90%; - padding: 0em 0.5em; + padding: 0 0.5em; } div#cgit ul.pager { list-style-type: none; text-align: center; - margin: 1em 0em 0em 0em; + margin: 1em 0 0 0; padding: 0; } @@ -1071,8 +1011,6 @@ div#cgit ul.pager .current { font-weight: bold; } -/* Ages / line counts */ - div#cgit time.age-mins, div#cgit time.age-hours, div#cgit time.age-days, @@ -1115,8 +1053,6 @@ div#cgit span.deletions { color: var(--del); } -/* Footer */ - div#cgit .footer { padding: 0.85rem var(--gutter); text-align: center; @@ -1133,8 +1069,6 @@ div#cgit .footer a:hover { text-decoration: underline; } -/* Ref decorations */ - div#cgit a.branch-deco, div#cgit a.tag-deco, div#cgit a.tag-annotated-deco, @@ -1145,8 +1079,8 @@ div#cgit a.deco { div#cgit a.branch-deco { color: var(--deco-fg); - margin: 0px 0.5em; - padding: 0px 0.25em; + margin: 0 0.5em; + padding: 0 0.25em; background-color: var(--branch-bg); border: solid 1px var(--branch-bd); border-radius: var(--radius-xs); @@ -1154,8 +1088,8 @@ div#cgit a.branch-deco { div#cgit a.tag-deco { color: var(--deco-fg); - margin: 0px 0.5em; - padding: 0px 0.25em; + margin: 0 0.5em; + padding: 0 0.25em; background-color: var(--tag-bg); border: solid 1px var(--tag-bd); border-radius: var(--radius-xs); @@ -1163,8 +1097,8 @@ div#cgit a.tag-deco { div#cgit a.tag-annotated-deco { color: var(--deco-fg); - margin: 0px 0.5em; - padding: 0px 0.25em; + margin: 0 0.5em; + padding: 0 0.25em; background-color: var(--tag-ann-bg); border: solid 1px var(--tag-ann-bd); border-radius: var(--radius-xs); @@ -1172,8 +1106,8 @@ div#cgit a.tag-annotated-deco { div#cgit a.remote-deco { color: var(--deco-fg); - margin: 0px 0.5em; - padding: 0px 0.25em; + margin: 0 0.5em; + padding: 0 0.25em; background-color: var(--remote-bg); border: solid 1px var(--remote-bd); border-radius: var(--radius-xs); @@ -1181,8 +1115,8 @@ div#cgit a.remote-deco { div#cgit a.deco { color: var(--deco-fg); - margin: 0px 0.5em; - padding: 0px 0.25em; + margin: 0 0.5em; + padding: 0 0.25em; background-color: var(--head-bg); border: solid 1px var(--head-bd); border-radius: var(--radius-xs); @@ -1197,11 +1131,8 @@ div#cgit div.commit-subject a.deco { font-size: 80%; } -/* Statistics */ - div#cgit table.stats { border: solid 1px var(--border-strong); - border-collapse: collapse; display: block; width: max-content; max-width: 100%; @@ -1235,8 +1166,6 @@ div#cgit table.stats td.left { text-align: left; } -/* Side-by-side diff */ - div#cgit table.ssdiff { width: 100%; } diff --git a/assets/cgit.js b/assets/cgit.js index c5bdf33..29b9745 100644 --- a/assets/cgit.js +++ b/assets/cgit.js @@ -12,8 +12,7 @@ // Written the way SECONDS_PER_* are derived in source/cgit.h, so that the // bucket a browser picks is the bucket cgit_print_age already picked on the -// server. A month is a twelfth of a year rather than thirty days, which is -// the definition the server uses. +// server. A month is a twelfth of a year rather than thirty days. var MINUTE = 60; var HOUR = 60 * MINUTE; var DAY = 24 * HOUR; @@ -21,15 +20,18 @@ var WEEK = 7 * DAY; var YEAR = 365 * DAY; var MONTH = YEAR / 12; -// The five arrays are indexed together by the bucket an age falls into. The -// search in render_age can stop one place past the last class, so the arrays -// it reads there repeat their final entry. Each limit is twice the next unit -// up, matching the thresholds cgit_print_age compares against. -var age_classes = [ "age-mins", "age-hours", "age-days", "age-weeks", "age-months", "age-years" ]; -var age_suffix = [ "min.", "hours", "days", "weeks", "months", "years", "years" ]; -var age_unit = [ MINUTE, HOUR, DAY, WEEK, MONTH, YEAR, YEAR ]; -var age_limit = [ 2 * HOUR, 2 * DAY, 2 * WEEK, 2 * MONTH, 2 * YEAR, 2 * YEAR ]; -var update_delay = [ 10, 5 * MINUTE, 30 * MINUTE, DAY, DAY, DAY, DAY ]; +// The five arrays are indexed together by the bucket an age falls into. +// The search in render_age can stop one place past the last class, so +// age_suffix and age_unit repeat their final entry. Each limit is twice the +// next unit up, matching the thresholds cgit_print_age compares against. +var age_classes = [ "age-mins", "age-hours", "age-days", "age-weeks", + "age-months", "age-years" ]; +var age_suffix = [ "min.", "hours", "days", "weeks", "months", "years", + "years" ]; +var age_unit = [ MINUTE, HOUR, DAY, WEEK, MONTH, YEAR, YEAR ]; +var age_limit = [ 2 * HOUR, 2 * DAY, 2 * WEEK, 2 * MONTH, 2 * YEAR, + 2 * YEAR ]; +var update_delay = [ 10, 5 * MINUTE, 30 * MINUTE, DAY, DAY, DAY ]; function render_age(element, age) { var text, bucket; @@ -40,13 +42,13 @@ function render_age(element, age) { text = Math.round(age / age_unit[bucket]) + " " + age_suffix[bucket]; - // Every age on the page is measured again on each pass, so most of - // them are already reading correctly and writing to them would cost a - // repaint for nothing. + // Every age on the page is measured again on each pass, so most of them + // are already reading correctly and writing to them would cost a repaint + // for nothing. if (element.textContent != text) { element.textContent = text; - // An age past the last limit is still counted in years, but - // there is no class beyond age-years to put on it. + // An age past the last limit is still counted in years, but there + // is no class beyond age-years to put on it. if (bucket == age_classes.length) bucket--; if (element.className != age_classes[bucket]) @@ -56,7 +58,7 @@ function render_age(element, age) { /* * Measures every age on the page against the clock and books the next pass. - * There is no point coming back before the coarsest unit on the page could + * There is no point coming back before the finest unit on the page could * change, so a page already counted in hours is left alone for minutes and * one counted in years for a day. */ @@ -74,9 +76,8 @@ function refresh_ages() { for (i = 0; i < elements.length; i++) { age = now - elements[i].getAttribute("data-ut"); - // A commit dated ahead of the viewer's clock would - // otherwise show a negative age, and ui-shared.c - // clamps it the same way. + // A commit dated ahead of the viewer's clock would otherwise show + // a negative age, and ui-shared.c clamps it the same way. if (age < 0) age = 0; @@ -87,11 +88,7 @@ function refresh_ages() { window.setTimeout(refresh_ages, delay * 1000); } -document.addEventListener("DOMContentLoaded", function () { - // Nothing here depends on layout, so the first pass can run as soon - // as the document is parsed. - refresh_ages(); -}, false); +document.addEventListener("DOMContentLoaded", refresh_ages, false); })(); @@ -136,8 +133,8 @@ function place_bar() { bar.style.height = (bottom - top) + "px"; table.appendChild(bar); - // A browser only scrolls to a fragment that names a real id, which a - // range never does, so bring the start of one into view here. + // A browser only scrolls to a fragment that names a real id, which a range + // never does, so bring the start of one into view here. if (match[2]) first.scrollIntoView({ block: "center" }); } diff --git a/custom/cgitrc b/custom/cgitrc index c522b90..8cb1831 100644 --- a/custom/cgitrc +++ b/custom/cgitrc @@ -10,17 +10,13 @@ # list repositories by hand in the per-repository section at the end of this # file. # -# About pages (markdown, man and plain-text readmes) render through the bundled -# about-render.lua about-filter, see the filter section below. Source syntax +# About pages (markdown, man and plain-text readmes) can render through the +# bundled about-render.lua about-filter in the filter section below. Source +# syntax # highlighting is optional and ships as a source-filter there too. # # One key=value pair per line. Lines starting with # are comments. - -## -## Cache -## - # Maximum number of entries in the cgit output cache. A value of 0 disables # caching. Value is an integer. Default is 0. cache-size=0 @@ -44,7 +40,7 @@ cache-summary-ttl=5 cache-scan-ttl=15 # Minutes to cache repo pages requested with a fixed SHA1. A negative value -# never expires. Value is an integer number of minutes. Default is -1. +# keeps the page forever. Value is an integer number of minutes. Default is -1. cache-static-ttl=-1 # Minutes to cache repo pages requested without a fixed SHA1. Value is an @@ -63,11 +59,6 @@ cache-snapshot-ttl=5 # requested. Values are 0 or 1. Default is 0. enable-cache-list=0 - -## -## Site appearance -## - # Heading text on the repository index page. Value is any text. Default is Git # repository browser. #root-title=Git repository browser @@ -84,8 +75,8 @@ enable-cache-list=0 # is one or more [ref]path entries. Default is none. #readme=:README.md -# CSS document urls added to the head of every page. Value is one or more urls. -# Default is /cgit.css. +# CSS document urls added to the head of every page. An empty value disables +# it. Value is one or more urls. Default is /cgit.css. #css=/cgit.css # JavaScript document urls included on every page. An empty value disables it. @@ -133,11 +124,6 @@ embedded=0 # format string taking path and sha1. Default is none. #module-link=/%s/commit/?id=%s - -## -## Repository discovery -## - # Directory scanned for git repositories at parse time. Value is a filesystem # path that may use macros. Default is none. #scan-path=/var/lib/git @@ -184,11 +170,6 @@ enable-git-config=0 # filesystem path that may use macros. Default is none. #include=/etc/cgitrc.d/extra - -## -## Features -## - # Provide a blame page for files and links to it. Values are 0 or 1. Default is # 0. enable-blame=0 @@ -251,13 +232,9 @@ enable-tree-group-dirs=0 # year. Default is unset. #max-stats=year - -## -## Snapshots and cloning -## - -# Default set of snapshot archive formats to offer. Value is a space list of tar -# tar.gz tar.bz2 tar.lz tar.xz tar.zst zip, or the word all. Default is none. +# Default set of snapshot archive formats to offer. Value is a space-separated +# list of tar tar.gz tar.bz2 tar.lz tar.xz tar.zst zip, or the word all. Default +# is none. #snapshots=tar.gz tar.xz zip # Act as a dumb HTTP endpoint for git clones. Values are 0 or 1. Default is 1. @@ -272,15 +249,10 @@ enable-http-clone=1 # space-separated url templates that may use macros. Default is none. #clone-url=https://example.com/$CGIT_REPO_URL git://example.com/$CGIT_REPO_URL - -## -## Filters -## -# # A filter is an external command cgit runs to transform a piece of content. # Prefix the command with lua: to run it through the built-in Lua interpreter, # which avoids a fork per call, or with exec: to run a normal program. The -# commands below that point at /usr/share/cgit/extensions/ use scripts this +# commands below that point at /usr/local/lib/cgit/filters/ use scripts this # repository ships under custom/extensions/. The ones written as # /path/to/your-command mark where your own command goes. @@ -291,36 +263,29 @@ enable-filter-overrides=0 # Filter command used to format about-page content. The bundled about-render.lua # renders markdown, man pages and plain text. Value is a command optionally # prefixed with exec or lua. Default is none. -#about-filter=lua:/usr/share/cgit/extensions/about-render.lua +#about-filter=lua:/usr/local/lib/cgit/filters/about-render.lua # Filter command used to format commit messages, for example to turn object # names and issue numbers into links. Value is a command optionally prefixed # with exec or lua. Default is none. -#commit-filter=lua:/usr/share/cgit/extensions/link-commits.lua +#commit-filter=lua:/usr/local/lib/cgit/filters/link-commits.lua # Filter command used to format author and committer emails, for example to add # avatar images. Value is a command optionally prefixed with exec or lua. # Default is none. -#email-filter=lua:/usr/share/cgit/extensions/email-gravatar.lua +#email-filter=lua:/usr/local/lib/cgit/filters/email-gravatar.lua -# Filter command used to format plaintext blobs in the tree view. Without it -# cgit serves the text plain. For syntax highlighting, the shipped filter below -# uses the Scintillua lexers and needs the lpeg module installed too, on Debian -# that is "apt install lua-lpeg". Missing either dependency means plain -# uncolored text, not an error. See the comments in that file. Value is a -# command optionally prefixed with exec or lua. Default is none. -#source-filter=lua:/usr/share/cgit/extensions/syntax-highlight.lua +# Filter command used to format plaintext blobs in the tree view. The shipped +# filter uses the Scintillua lexers and needs lpeg, and missing either means +# plain uncolored text, not an error. +# Value is a command optionally prefixed with exec or lua. Default is none. +#source-filter=lua:/usr/local/lib/cgit/filters/syntax-highlight.lua # Filter command invoked to authenticate access, gating repositories behind a # login. See custom/extensions/auth-inline.lua and # custom/extensions/auth-file.lua. Value is a command optionally prefixed with # exec or lua. Default is none. -#auth-filter=lua:/usr/share/cgit/extensions/auth-inline.lua - - -## -## Limits and safety -## +#auth-filter=lua:/usr/local/lib/cgit/filters/auth-inline.lua # Number of items to display in atom feeds. Value is an integer. Default is 10. max-atom-items=10 @@ -337,7 +302,7 @@ max-repodesc-length=80 # value of 0 disables the limit. Value is an integer. Default is 10240. max-blob-size=10240 -# Number of repos listed per page on the index. A value of 0 or negative shows +# Number of repos listed per page on the index. Zero or a negative value shows # all repos. Value is an integer. Default is 50. max-repo-count=50 @@ -369,11 +334,6 @@ max-ref-count=200 # compile-time value. Value is an integer. Default is -1. rename-limit=-1 - -## -## Presentation -## - # Number of log entries shown in the summary view. Value is an integer. Default # is 10. summary-log=10 @@ -408,8 +368,8 @@ branch-sort=name # Sort items in the repo list case-sensitively. Values are 0 or 1. Default is 1. case-sensitive-sort=1 -# Show full author email addresses beside names. Set to 0 to hide them. Values -# are 0 or 1. Default is 1. +# Show full author email addresses beside names. Values are 0 or 1. Default is +# 1. enable-plain-email=1 # File giving the timestamp of the youngest commit. Value is a path relative to @@ -429,16 +389,10 @@ enable-plain-email=1 #mimetype.png=image/png #mimetype.svg=image/svg+xml - -## -## Per-repository overrides -## -# -# Each repository is introduced by a repo.url line, which must be the first -# setting of the block. All following repo.* settings apply to that repo until -# the next repo.url line. When repos are discovered via scan-path the repo. -# prefix is dropped inside each repo cgitrc and repo.url and repo.path are not -# allowed there. +# Each repository is introduced by a repo.url line. All following repo.* +# settings apply to that repo until the next repo.url line. When repos are +# discovered via scan-path the repo. prefix is dropped inside each repo cgitrc +# and repo.url and repo.path are not allowed there. # Relative url for a repo. Must be the first setting of each repo block. Value # is a relative url path. Default is none. @@ -566,7 +520,7 @@ enable-plain-email=1 # Per-repo override of the commit-filter. Only honoured when # enable-filter-overrides is 1. Value is a command. Default is the global # commit-filter value. -#repo.commit-filter=lua:/usr/share/cgit/extensions/link-commits.lua +#repo.commit-filter=lua:/usr/local/lib/cgit/filters/link-commits.lua # Per-repo override of the source-filter. Only honoured when # enable-filter-overrides is 1. Value is a command. Default is the global @@ -576,4 +530,4 @@ enable-plain-email=1 # Per-repo override of the email-filter. Only honoured when # enable-filter-overrides is 1. Value is a command. Default is the global # email-filter value. -#repo.email-filter=lua:/usr/share/cgit/extensions/email-gravatar.lua +#repo.email-filter=lua:/usr/local/lib/cgit/filters/email-gravatar.lua 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 --- '&', '<' and '>'. 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 +-- '&', '<' and '>' 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 = {} diff --git a/custom/hooks/post-receive.cgit-cache b/custom/hooks/post-receive.cgit-cache index 235de27..4825a44 100755 --- a/custom/hooks/post-receive.cgit-cache +++ b/custom/hooks/post-receive.cgit-cache @@ -4,7 +4,7 @@ # show up right away instead of once the cache-*-ttl minutes have passed. # # This only matters when cache-size in your cgitrc is above 0, which turns -# the cache on. With it off the hook does nothing and costs one stat call. +# the cache on. # # cgit names each cache slot after a hash of the request url, so there is no # way to expire one repository's pages on their own and this clears the lot. @@ -16,9 +16,8 @@ # change the value in your cgitrc then you must also change it here. # # The web server owns the cache root, so the pushing user needs write access -# to it. Making the directory group writable and putting both users in that -# group is usually enough. Nothing here fails a push if that access is -# missing, since a stale page is not worth rejecting a push over. +# to it. Nothing here exits nonzero when that access is missing, so the +# pusher is not shown an error over a stale page. # # To install the hook, copy (or link) it to the file "hooks/post-receive" in # each of your repositories. Git runs one post-receive per repository, so diff --git a/source/cache.c b/source/cache.c index dd8ae7f..2d546bb 100644 --- a/source/cache.c +++ b/source/cache.c @@ -96,9 +96,8 @@ static int open_slot(struct cache_slot *slot) } /* - * A key longer than the buffer above can never be read back by open_slot, so - * a slot keyed on one would never match and every such request would - * regenerate its page while still writing a slot nothing can use. + * A key longer than the buffer above can never be read back by open_slot, + * so a slot keyed on one would be written but never match. */ static int key_fits_slot(const char *key) { @@ -238,10 +237,8 @@ static int lock_slot(struct cache_slot *slot) } // The lock landed on whatever inode the path named at open. A holder // finishing in between renames that inode into place as the live - // slot, so truncating it on the strength of the stale descriptor - // would tear down the page other requests are reading. Once the path - // is confirmed to still name this file the rename can no longer - // happen, because doing so takes the lock now held here. + // slot, and once the path is confirmed to still name this file that + // rename can no longer happen, because it takes the lock held here. if (fstat(slot->lock_fd, &held) || stat(slot->lock_path, &named) || held.st_ino != named.st_ino || held.st_dev != named.st_dev) { close(slot->lock_fd); @@ -291,10 +288,9 @@ void cache_abandon_fill(void) slot_being_filled = NULL; slot->abandoned = 1; - // The page is sitting in html.c's buffer and in stdio's, and both are - // emptied while stdout still points at the lock file, so the half - // rendered page goes into the file about to be removed rather than - // reaching the client ahead of whatever is written next. + // The page is sitting in html.c's buffer and in stdio's. Empty both + // while stdout still points at the lock file so the half rendered + // page never reaches the client. html_flush(); fflush(stdout); @@ -323,10 +319,9 @@ static int fill_slot(struct cache_slot *slot) slot->fn(); slot_being_filled = NULL; - // The page is sitting in html.c's buffer and then in stdio's, and all - // of it has to reach the lock file before that file is renamed into - // place. After an abandoned fill stdout is the client again and this - // same flush delivers the tail of the error page instead. + // All of the page has to reach the lock file before it is renamed + // into place. After an abandoned fill stdout is the client again and + // this same flush delivers the tail of the error page instead. html_flush(); if (fflush(stdout)) return errno; @@ -352,9 +347,7 @@ static void refresh_slot(struct cache_slot *slot) return; // If another process replaced the slot between open_slot and - // lock_slot, the copy already open is served rather than the newer - // one, which would mean opening that file and comparing the key in it, - // not worth a second descriptor and read on every expiry. + // lock_slot, the copy already open is served rather than the newer. if (is_modified(slot) || fill_slot(slot)) { unlock_slot(slot, 0); close_lock(slot); @@ -391,8 +384,7 @@ static int process_slot(struct cache_slot *slot) // A slot that opened cleanly but holds another key is a collision, // and two popular pages sharing one slot evict each other on every - // alternating visit. Nothing else makes that visible, because the - // cache keeps working and only quietly stops helping. + // alternating visit. if (!err) log_error("[cgit] Cache slot %s holds a different key, " "consider a larger cache-size\n", slot->path); @@ -449,10 +441,8 @@ static char *format_time(const char *format, time_t when) } /* - * The accumulator is an unsigned long rather than a fixed 32 bit type, so on a - * 64 bit host this is not the published FNV-1 value. All that decides is which - * slot a key lands in, and nothing outside a single build has to agree on the - * answer. + * The accumulator is unsigned long, so on a 64 bit host this is not the + * published FNV-1 value. Only slot selection depends on it. */ unsigned long cache_hash_str(const char *str) { diff --git a/source/cgit.c b/source/cgit.c index 730e2c6..dc56a0e 100644 --- a/source/cgit.c +++ b/source/cgit.c @@ -59,12 +59,10 @@ struct refmatch { const char *cgit_version = CGIT_VERSION; /* - * Isolate git from the calling user's configuration, so a snapshot cannot be - * broken by something like a core.excludesfile pointing at a "~" path that git - * can no longer expand once HOME is unset below. Called from cmd_main rather - * than from a constructor attribute, because git-compat-util.h defines - * __attribute__ away on a compiler that does not support it, which would leave - * this silently never running. + * Isolate git from the calling user's configuration, which could otherwise + * break once HOME is unset below. Called from cmd_main rather than from a + * constructor attribute, because git-compat-util.h defines __attribute__ + * away on a compiler without it, which would leave this silently unrun. */ static void isolate_git_environment(void) { @@ -173,7 +171,8 @@ static void prepare_context(void) static void print_version(void) { - printf("CGit %s | https://github.com/brycekwon/cgit\n\nCompiled in features:\n", CGIT_VERSION); + printf("CGit %s | https://github.com/brycekwon/cgit\n\n" + "Compiled in features:\n", CGIT_VERSION); #ifdef NO_LUA printf("[-] "); #else @@ -319,10 +318,9 @@ static void parse_args(int argc, const char **argv) ctx.qry.ofs = atoi(arg); } else if (skip_prefix(argv[i], "--scan-tree=", &arg) || skip_prefix(argv[i], "--scan-path=", &arg)) { - // A repository's own snapshots setting is masked with - // the global one, which normally comes from cgitrc. - // That has not been read yet here, so an empty mask - // would discard whatever the repository asked for. + // A repository's snapshots setting is masked with the + // global one, and cgitrc has not been read here, so an + // empty mask would discard what the repository set. ctx.cfg.snapshots = ALL_SNAPSHOT_FORMATS; scanned++; scan_tree(arg); @@ -338,9 +336,8 @@ static void parse_args(int argc, const char **argv) /* * The lock is a fcntl lock rather than the mere existence of the lock file, - * because a lock the kernel drops with its process cannot outlive a scan that - * was killed mid-run. A leftover lock file used to count as a scan in - * progress, and one crash would silently freeze the repolist for good. + * because a lock the kernel drops with its process cannot outlive a scan + * killed mid-run. A leftover file would count as a scan forever in progress. */ static int generate_cached_repolist(const char *path, const char *cached_rc) { @@ -379,10 +376,8 @@ static int generate_cached_repolist(const char *path, const char *cached_rc) } // The lock landed on whatever inode the path named at open. A holder // finishing in between renames that inode into place as the live - // list, so truncating it on the strength of the stale descriptor - // would tear down the list other requests are reading. Once the path - // is confirmed to still name this file the rename can no longer - // happen, because doing so takes the lock now held here. + // list, and once the path is confirmed to still name this file that + // rename can no longer happen, because it takes the lock held here. if (fstat(fd, &held) || stat(locked_rc.buf, &named) || held.st_ino != named.st_ino || held.st_dev != named.st_dev) { err = EAGAIN; @@ -469,11 +464,10 @@ static void process_cached_repolist(const char *path) if (fork()) goto out; - // The child inherits the descriptors of the request, and the web server - // reads stdout until every holder of it is gone, so leaving them in - // place would keep the visitor waiting for the whole scan after their - // page was written. Anything the scan prints would land on that - // response as well. + // The child inherits the request's descriptors, and the web server + // reads stdout until every holder is gone, so left in place they + // would keep the visitor waiting on the scan and let its output + // land on the response. devnull = open("/dev/null", O_RDWR); if (devnull >= 0) { dup2(devnull, STDIN_FILENO); @@ -703,9 +697,8 @@ static void apply_config(const char *name, const char *value) /* * Read a whole number a request supplied, clamped into the range the caller - * accepts. strtol rather than atoi, because atoi has no defined behaviour once - * the digits overflow and every value here arrives straight from the query - * string. + * accepts. strtol rather than atoi, because atoi is undefined on overflow + * and every value here arrives straight from the query string. */ static int query_int(const char *value, int min, int max) { @@ -729,10 +722,9 @@ static void apply_query_param(const char *name, const char *value) } else if (!strcmp(name, "p")) { ctx.qry.page = xstrdup(value); } else if (!strcmp(name, "url")) { - // Every leading slash goes, not just one. What is left is - // joined onto the virtual root, so a value like //example.com - // would otherwise survive as /example.com and make that join a - // scheme-relative link to another host. + // Every leading slash goes, not just one, so a value like + // //example.com cannot survive as /example.com and make the + // virtual root join a scheme-relative link to another host. while (*value == '/') value++; ctx.qry.url = xstrdup(value); @@ -754,11 +746,9 @@ static void apply_query_param(const char *name, const char *value) ctx.qry.oid2 = xstrdup(value); ctx.qry.has_oid = 1; } else if (!strcmp(name, "ofs")) { - // Bounded above so a crafted value cannot force a walk over the - // whole history. Negatives stop at -1 rather than at zero, - // because the offset is overloaded, the stats page submits -1 - // for all authors, and the log skip loop floors a negative - // itself. + // Bounded above so a crafted value cannot force a walk over + // the whole history. The floor is -1 rather than 0 because + // the stats page submits -1 for all authors. ctx.qry.ofs = query_int(value, -1, MAX_QUERY_OFFSET); } else if (!strcmp(name, "path")) { ctx.qry.path = cgit_trim_end(value, '/'); @@ -847,10 +837,9 @@ static void authenticate_cookie(void) } /* - * Only a full object id names content that can never change. The id parameter - * accepts anything git can resolve, so a ref name or an abbreviation arrives - * here just as marked as a real id, and a page pinned to one of those must - * not be cached under the never-expiring static ttl. + * Only a full object id names content that can never change. The id + * parameter accepts anything git can resolve, so a page pinned to a ref + * name or abbreviation must not be cached under the static ttl. */ static int is_full_oid(const char *rev) { @@ -895,10 +884,9 @@ static int calc_ttl(void) } /* - * The scheme and the host are folded in because the absolute urls a page - * carries, its clone urls and atom links, are built from them, so a request - * arriving with a spoofed Host must not poison the page served to a visitor - * who came in on the real one. + * The scheme and host are folded in because the page's absolute urls, its + * clone urls and atom links, are built from them, so a spoofed Host must + * not poison the page served on the real one. */ static void build_cache_key(struct strbuf *key) { @@ -911,11 +899,9 @@ static void build_cache_key(struct strbuf *key) }; size_t i; - // Each part is written behind its own length, so nothing a value - // contains can make two different requests spell one key. The path and - // the query come from the environment rather than the query string cgit - // rebuilds, since that rebuild folds the two together and would let the - // PATH_INFO and QUERY_STRING forms of one request share a slot. + // Each part is written behind its own length so no value can make two + // requests spell one key, and the path and query come from the + // environment because cgit's rebuilt query string folds them together. for (i = 0; i < ARRAY_SIZE(parts); i++) strbuf_addf(key, "%zu|%s", strlen(parts[i]), parts[i]); free(hosturl); @@ -1013,7 +999,6 @@ static void choose_readme(struct cgit_repo *repo) for_each_string_list_item(entry, &repo->readme) { parse_readme(entry->string, &filename, &ref, repo); if (!filename) { - free(filename); free(ref); continue; } @@ -1041,8 +1026,6 @@ static void prepare_repo_env(int *nongit) { setenv("GIT_DIR", ctx.repo->path, 1); - // Both read configuration out of the repository, with the user's own - // git configuration already stripped by isolate_git_environment. setup_git_directory_gently(the_repository, nongit); load_display_notes(NULL); } @@ -1230,7 +1213,8 @@ void cgit_repo_config(struct cgit_repo *repo, const char *name, const char *valu else if (!strcmp(name, "about-filter") || !strcmp(name, "commit-filter") || !strcmp(name, "source-filter") || !strcmp(name, "email-filter")) { if (!ctx.cfg.enable_filter_overrides) - fprintf(stderr, "[cgit] Ignoring repo %s: enable-filter-overrides is not set\n", name); + fprintf(stderr, "[cgit] Ignoring repo %s: " + "enable-filter-overrides is not set\n", name); else if (!strcmp(name, "about-filter")) repo->about_filter = cgit_new_filter(value, ABOUT); else if (!strcmp(name, "commit-filter")) @@ -1275,8 +1259,7 @@ int cmd_main(int argc, const char **argv) // string keeps it part of the cache key. path = ctx.env.path_info; if (!ctx.qry.url && path) { - // Stripped like the url parameter and for the same reason, so - // a request for //example.com cannot turn into a link off site. + // Stripped like the url parameter and for the same reason. while (*path == '/') path++; ctx.qry.url = xstrdup(path); diff --git a/source/cgit.h b/source/cgit.h index 8d2d686..308fda2 100644 --- a/source/cgit.h +++ b/source/cgit.h @@ -50,13 +50,12 @@ // converted on the way in. #define PAGE_ENCODING "UTF-8" -// The month is a double because a twelfth of a year is not a whole number of -// seconds. #define SECONDS_PER_MINUTE 60 #define SECONDS_PER_HOUR (SECONDS_PER_MINUTE * 60) #define SECONDS_PER_DAY (SECONDS_PER_HOUR * 24) #define SECONDS_PER_WEEK (SECONDS_PER_DAY * 7) #define SECONDS_PER_YEAR (SECONDS_PER_DAY * 365) +// A double, because a twelfth of a year is not a whole number of seconds. #define SECONDS_PER_MONTH (SECONDS_PER_YEAR / 12.0) typedef enum { diff --git a/source/cgit.mk b/source/cgit.mk index 0683cb7..1106b64 100644 --- a/source/cgit.mk +++ b/source/cgit.mk @@ -9,10 +9,10 @@ # ../../build. include Makefile -# TOOLSDIR and BUILDDIR are named relative to the project root, matching the -# top level Makefile, because the version recipe changes into the root before -# using them. CGIT_SRC and CGIT_BUILD lead to the sources and the output from -# vendor/git, where everything else here runs. +# TOOLSDIR and BUILDDIR are named relative to the project root because the +# version recipe changes into the root before using them. CGIT_SRC and +# CGIT_BUILD lead to the sources and the output from vendor/git, where +# everything else here runs. CGIT_ROOT = ../.. SRCDIR = source TOOLSDIR = tools @@ -24,8 +24,7 @@ CGIT_BUILD = $(CGIT_ROOT)/$(BUILDDIR) # Makefile exports, which leaves out the build options this file reads. -include $(CGIT_ROOT)/cgit.conf -# CGIT_VERSION and the other CGIT_ values used below come from the top level -# Makefile, which exports them, rather than being defined in this file. +# The CGIT_ values used below are exported by the top level Makefile. $(CGIT_BUILD)/VERSION: force-version @mkdir -p $(CGIT_BUILD)/ @cd $(CGIT_ROOT) && '$(SHELL_PATH_SQ)' $(TOOLSDIR)/gen-version.sh "$(CGIT_VERSION)" $(BUILDDIR)/VERSION @@ -42,15 +41,16 @@ $(CGIT_BUILD)/VERSION: force-version # standard again. CGIT_STD ?= gnu17 -# CGIT_CFLAGS is tracked separately so that changing it does not force a -# rebuild of Git itself. +# CGIT_CFLAGS is tracked separately so that changing it does not force a rebuild +# of Git itself. CGIT_CFLAGS += -std=$(CGIT_STD) CGIT_CFLAGS += -DCGIT_CONFIG='"$(CGIT_CONFIG)"' CGIT_CFLAGS += -DCGIT_SCRIPT_NAME='"$(CGIT_SCRIPT_NAME)"' CGIT_CFLAGS += -DCGIT_CACHE_ROOT='"$(CACHE_ROOT)"' -# Reaches only the cgit objects, so a caller can tighten the build, the way CI -# passes -Werror, without holding git's own sources to the same standard. +# Reaches only the cgit objects, so a caller can tighten the build, the way the +# release build passes -Werror=format-security, without holding git's own +# sources to the same standard. CGIT_CFLAGS += $(CGIT_EXTRA_CFLAGS) PKG_CONFIG ?= pkg-config @@ -161,10 +161,12 @@ $(CGIT_BUILD)/CGIT-CFLAGS: FORCE if test x"$$FLAGS" != x"`cat $(CGIT_BUILD)/CGIT-CFLAGS 2>/dev/null`" ; then \ echo 1>&2 " * new CGit build flags"; \ echo "$$FLAGS" >$(CGIT_BUILD)/CGIT-CFLAGS; \ - fi + fi -$(CGIT_OBJS): $(CGIT_BUILD)/%.o: $(CGIT_SRC)/%.c GIT-CFLAGS $(CGIT_BUILD)/CGIT-CFLAGS $(missing_dep_dirs) - $(QUIET_CC)$(CC) -o $@ -c $(dep_args) $(ALL_CFLAGS) $(EXTRA_CPPFLAGS) $(CGIT_CFLAGS) $< +$(CGIT_OBJS): $(CGIT_BUILD)/%.o: $(CGIT_SRC)/%.c GIT-CFLAGS \ + $(CGIT_BUILD)/CGIT-CFLAGS $(missing_dep_dirs) + $(QUIET_CC)$(CC) -o $@ -c $(dep_args) $(ALL_CFLAGS) $(EXTRA_CPPFLAGS) \ + $(CGIT_CFLAGS) $< $(CGIT_BUILD)/cgit: $(CGIT_OBJS) GIT-LDFLAGS $(GITLIBS) @echo 1>&2 " * $(LUA_MESSAGE)" diff --git a/source/config.c b/source/config.c index 6a5d136..0ff8588 100644 --- a/source/config.c +++ b/source/config.c @@ -1,12 +1,9 @@ /* - * The reader for cgit's config files, which are the main cgitrc, any file it - * pulls in with an include line, the cgitrc that sits beside a scanned - * repository, and the cached repolist. A file is a sequence of name=value - * lines, and each pair is handed to a callback that decides what it means, so - * nothing here knows a single key by name. A value runs to the end of its line - * and keeps whatever spacing it has, there is no quoting. A line whose first - * non blank character is a hash or a semicolon is a comment, and blank lines - * are ignored. + * The reader for cgit's config files, which are the main cgitrc, any file + * it pulls in with an include line, the cgitrc beside a scanned repository, + * and the cached repolist. A file is a sequence of name=value lines, each + * handed to a callback that decides what it means, so nothing here knows a + * single key by name. */ #include "cgit.h" @@ -95,9 +92,8 @@ int config_file_parse(const char *filename, config_file_value_fn fn) struct strbuf value = STRBUF_INIT; FILE *f; - // An include line calls back into here, so a file that includes itself, - // directly or round a longer loop, would recurse until the stack gave - // out. + // An include line calls back into here, so an include cycle would + // recurse until the stack gave out. if (nesting > MAX_INCLUDE_NESTING) return -1; if (!(f = fopen(filename, "r"))) diff --git a/source/filter.c b/source/filter.c index 3c0c7e4..f5f971c 100644 --- a/source/filter.c +++ b/source/filter.c @@ -78,14 +78,10 @@ static void fprintf_exec_filter(struct cgit_filter *base, FILE *f, static void cleanup_exec_filter(struct cgit_filter *base) { struct cgit_exec_filter *filter = (struct cgit_exec_filter *)base; - if (filter->argv) { - free(filter->argv); - filter->argv = NULL; - } - if (filter->cmd) { - free(filter->cmd); - filter->cmd = NULL; - } + free(filter->argv); + filter->argv = NULL; + free(filter->cmd); + filter->cmd = NULL; } static struct cgit_filter *new_exec_filter(const char *cmd, int argument_count) @@ -175,11 +171,10 @@ static inline void unhook_write(void) current_write_filter = NULL; } -static void die_lua_error(struct lua_filter *filter) +static NORETURN void die_lua_error(struct lua_filter *filter) { die("Lua error in %s: %s", filter->script_file, lua_tostring(filter->lua_state, -1)); - lua_pop(filter->lua_state, 1); } static ssize_t write_lua_filter(struct cgit_filter *base, const void *buf, @@ -189,18 +184,15 @@ static ssize_t write_lua_filter(struct cgit_filter *base, const void *buf, lua_getglobal(filter->lua_state, "filter_write"); lua_pushlstring(filter->lua_state, buf, count); - if (lua_pcall(filter->lua_state, 1, 0, 0)) { + if (lua_pcall(filter->lua_state, 1, 0, 0)) die_lua_error(filter); - errno = EIO; - return -1; - } return count; } /* - * Output a script asks for belongs on the page and not back in its own filter, - * so the hook comes off around the call. The zero returned is Lua's count of - * values pushed for the script, not a success code. + * Output a script asks for belongs on the page, not back in its own filter, + * so the hook comes off around the call. The zero returned is Lua's count + * of values pushed, not a success code. */ static inline int emit_unfiltered(lua_State *lua_state, void (*emit)(const char *text)) @@ -295,12 +287,8 @@ static int init_lua_filter(struct lua_filter *filter) lua_setglobal(filter->lua_state, script_globals[i].name); } - if (luaL_dofile(filter->lua_state, filter->script_file)) { + if (luaL_dofile(filter->lua_state, filter->script_file)) die_lua_error(filter); - lua_close(filter->lua_state); - filter->lua_state = NULL; - return 1; - } return 0; } @@ -317,26 +305,21 @@ static int open_lua_filter(struct cgit_filter *base, va_list ap) lua_getglobal(filter->lua_state, "filter_open"); for (i = 0; i < filter->base.argument_count; ++i) lua_pushstring(filter->lua_state, va_arg(ap, char *)); - if (lua_pcall(filter->lua_state, filter->base.argument_count, 0, 0)) { + if (lua_pcall(filter->lua_state, filter->base.argument_count, 0, 0)) die_lua_error(filter); - return 1; - } return 0; } static int close_lua_filter(struct cgit_filter *base) { struct lua_filter *filter = (struct lua_filter *)base; - int ret = 0; + int ret; lua_getglobal(filter->lua_state, "filter_close"); - if (lua_pcall(filter->lua_state, 0, 1, 0)) { + if (lua_pcall(filter->lua_state, 0, 1, 0)) die_lua_error(filter); - ret = -1; - } else { - ret = lua_tonumber(filter->lua_state, -1); - lua_pop(filter->lua_state, 1); - } + ret = lua_tonumber(filter->lua_state, -1); + lua_pop(filter->lua_state, 1); unhook_write(); return ret; @@ -358,10 +341,8 @@ static void cleanup_lua_filter(struct cgit_filter *base) lua_close(filter->lua_state); filter->lua_state = NULL; - if (filter->script_file) { - free(filter->script_file); - filter->script_file = NULL; - } + free(filter->script_file); + filter->script_file = NULL; } static struct cgit_filter *new_lua_filter(const char *cmd, int argument_count) diff --git a/source/html.c b/source/html.c index 2e4ec66..2e3a3b7 100644 --- a/source/html.c +++ b/source/html.c @@ -3,10 +3,9 @@ * for page text, attribute values, URL paths and query arguments along with * the small formatting helpers the rest of the code prints through. What is * written here is gathered into one buffer and handed to stdout in whole - * blocks, because a page is made of a great many small fragments and a write - * apiece spent more time in the kernel than rendering the page did. cgit - * shares stdout with the filters it runs and with git itself, so that buffer - * has to be emptied wherever another writer takes over. + * blocks, since a page is made of a great many small fragments. cgit shares + * stdout with the filters it runs and with git itself, so that buffer has + * to be emptied wherever another writer takes over. */ #include "cgit.h" @@ -57,9 +56,8 @@ static struct strbuf *capture; static void write_out(const char *data, size_t size) { - // A blob, a snapshot or a patch reaches this with a size well past what - // one write can move onto a pipe, so a short write is ordinary rather - // than an error and has to be resumed instead of reported. + // A blob or snapshot is well past what one write can move onto a + // pipe, so short writes are resumed rather than reported. if (write_in_full(STDOUT_FILENO, data, size) < 0) die_errno("write error on html output"); } @@ -256,11 +254,11 @@ void html_url_path(const char *txt) const char *p = txt; while (p && *p) { unsigned char c = *p; - // A raw ampersand or plus is legal in a URL path, but the - // paths written here land in attribute values, where a bare - // ampersand can start a character reference and quietly turn - // "a©.txt" into a different filename. Encoding both keeps - // the output byte-safe in every sink. + // A raw ampersand or plus is legal in a URL path, but this + // table is shared with html_url_arg, where a bare plus decodes + // back as a space, and the paths written here land in attribute + // values, where an ampersand can start a character reference + // and quietly turn "a©.txt" into a different filename. const char *esc = url_escape_table[c]; if (esc) { html_raw(txt, p - txt); diff --git a/source/html.h b/source/html.h index ecc7141..3bbb30e 100644 --- a/source/html.h +++ b/source/html.h @@ -14,8 +14,8 @@ // How much of a generated run to build before handing it to html_raw. Growing // past this gains nothing, since html_raw gathers what it is given into a -// buffer of its own, and a run built whole would instead be sized by the file -// it came from, which the blob limits do not bound. +// buffer of its own, and a run built whole would grow with the file it came +// from, to many times the blob's size, which max-blob-size never measures. #define HTML_BATCH (64 * 1024) extern void html_raw(const char *txt, size_t size); diff --git a/source/parsing.c b/source/parsing.c index de2798a..745bbba 100644 --- a/source/parsing.c +++ b/source/parsing.c @@ -77,9 +77,6 @@ static const char *reencode(char **text, const char *from, const char *to) { char *converted; - if (!text) - return NULL; - if (!*text || !from || !to) return *text; @@ -160,7 +157,7 @@ struct commitinfo *cgit_parse_commit(struct commit *commit) while (skip_prefix(p, "parent ", &p)) p += the_hash_algo->hexsz + 1; - if (p && skip_prefix(p, "author ", &p)) { + if (skip_prefix(p, "author ", &p)) { parse_user(p, &info->author, &info->author_email, &info->author_date, &info->author_tz); p = next_header_line(p); @@ -197,10 +194,8 @@ struct commitinfo *cgit_parse_commit(struct commit *commit) eol++; info->msg = xstrdup(eol); } else { - // Reached when an object is truncated mid header, which - // leaves nothing at all after them. Callers render subject - // and msg as text without checking, so they get empty - // strings rather than NULL. + // An object truncated mid header leaves nothing after it, + // and callers render subject and msg without NULL checks. info->subject = xstrdup(""); info->msg = xstrdup(""); } diff --git a/source/parsing.h b/source/parsing.h index 2230eaa..b1dc9f1 100644 --- a/source/parsing.h +++ b/source/parsing.h @@ -10,8 +10,8 @@ #include "cgit.h" -// Split PATH_INFO into the repository and the page it names, storing both in -// ctx.qry. +// Split a request url into the repository, the page and the path it names, +// storing them in ctx.qry. extern void cgit_parse_url(const char *url); extern struct commitinfo *cgit_parse_commit(struct commit *commit); diff --git a/source/scan-tree.c b/source/scan-tree.c index ac74eb3..f72a5be 100644 --- a/source/scan-tree.c +++ b/source/scan-tree.c @@ -15,7 +15,9 @@ // Git writes this line into the description file of every repository it // creates, so a repository still carrying it gets cgit's own default instead. -static const char *default_git_desc = "Unnamed repository; edit this file 'description' to name the repository."; +static const char *default_git_desc = + "Unnamed repository; edit this file 'description' to name the " + "repository."; // The config callbacks are handed only a name and a value, so the repository // being filled in waits here for the length of one add_repo call. @@ -88,7 +90,7 @@ static char *section_slash(struct strbuf *relpath, int depth) if (depth > 0) { slash = relpath->buf - 1; - while (slash && depth && (slash = strchr(slash + 1, '/'))) + while (depth && (slash = strchr(slash + 1, '/'))) depth--; } else { slash = relpath->buf + relpath->len; @@ -150,9 +152,8 @@ static void add_repo(const char *base, struct strbuf *path) else strbuf_addstr(&relpath, path->buf + strlen(base) + 1); - // Drop the trailing slash added above before testing for "/.git", since - // with it still attached the suffix never matches and every ordinary - // working tree is named "repo/.git" instead of "repo". + // Drop the trailing slash added above, or the "/.git" suffix test + // below never matches and a working tree is named "repo/.git". if (relpath.len && relpath.buf[relpath.len - 1] == '/') strbuf_setlen(&relpath, relpath.len - 1); if (relpath.len >= 5 && !strcmp(relpath.buf + relpath.len - 5, "/.git")) diff --git a/source/shared.c b/source/shared.c index 00d5605..fab5c13 100644 --- a/source/shared.c +++ b/source/shared.c @@ -89,9 +89,8 @@ static int load_mmfile(mmfile_t *file, const struct object_id *oid) } /* - * The test is on the oid rather than on the size, because load_mmfile uses a - * literal only for a null oid, and a real blob that happens to be empty does - * own its buffer. + * The test is on the oid rather than the size, because load_mmfile uses a + * literal only for a null oid, while a real but empty blob owns its buffer. */ static void release_mmfile(mmfile_t *file, const struct object_id *oid) { @@ -101,9 +100,7 @@ static void release_mmfile(mmfile_t *file, const struct object_id *oid) /* * Xdiff emits buffers that need not end on a line boundary, so a trailing - * fragment is held back and joined with whatever arrives next. Git's own - * xdiff_outf keeps that fragment in a callback struct, which is not an option - * here because priv already carries the caller's function. + * fragment is held back and joined with whatever arrives next. */ static int emit_line(void *priv, mmbuffer_t *mb, int nbuf) { @@ -138,9 +135,8 @@ static int emit_line(void *priv, mmbuffer_t *mb, int nbuf) return 0; } -// Takes an unsigned char because a byte over 0x7f is negative in a plain -// char wherever char is signed, and a negative one is not a value the ctype -// tests are defined for. +// Takes an unsigned char because a byte over 0x7f is negative where char +// is signed, which the ctype tests are not defined for. static int is_token_char(unsigned char c) { return isalnum(c) || c == '_'; @@ -466,9 +462,8 @@ void cgit_diff_commit(struct commit *commit, filepair_fn fn, const char *prefix) } /* - * Git's parse_date_format dies on anything it does not recognize, which would - * turn a typo in cgitrc into a failed request, so only the formats cgit - * documents reach it. + * Git's parse_date_format dies on anything unknown, which would turn a + * cgitrc typo into a failed request, so only documented formats reach it. */ void cgit_parse_date_format(const char *format, struct date_mode *mode) { @@ -578,9 +573,8 @@ char *cgit_expand_macros(const char *text) out = expand_macro(start, limit - start) - 1; } start = NULL; - // Step back so the character that ended the - // token is written again past the expansion, - // where it may open a token of its own. + // Step back so the byte that ended the token + // is written again and may open a new one. text--; } out++; diff --git a/source/ui-atom.c b/source/ui-atom.c index 09d3501..3eb4332 100644 --- a/source/ui-atom.c +++ b/source/ui-atom.c @@ -28,9 +28,9 @@ static const char *feed_date(timestamp_t when) #define XML_REPLACEMENT "?" /* - * How many bytes the UTF-8 sequence at p holds, or 0 when the bytes there are - * not a valid sequence. The lead-byte ranges fold in the overlong, surrogate - * and out-of-range cases, so a 0 is the only error signal a caller needs. + * How many bytes the UTF-8 sequence at p holds, or 0 when invalid. The + * lead-byte ranges fold in the overlong, surrogate and out-of-range cases, + * so 0 is the only error signal a caller needs. */ static size_t utf8_seq_len(const unsigned char *p, size_t left) { @@ -61,10 +61,9 @@ static size_t utf8_seq_len(const unsigned char *p, size_t left) } /* - * The XML counterpart of html_txt. Commit metadata is arbitrary bytes, and - * where a browser shrugs at a stray control byte or a broken UTF-8 sequence, - * an XML reader must reject the whole feed, so both are replaced instead of - * passed through. + * The XML counterpart of html_txt. A browser shrugs at a stray control byte + * or broken UTF-8, but an XML reader must reject the whole feed, so both + * are replaced instead of passed through. */ static void xml_txt(const char *txt) { @@ -219,8 +218,7 @@ void cgit_print_atom(char *tip, const char *path, int max_count) prepare_revision_walk(&rev); // CGI guarantees a server name, so only a bare test run reaches the - // fallback, and a placeholder there keeps the mandatory feed id and - // the links present rather than dropping them. + // fallback, which keeps the mandatory feed id and links present. host = cgit_hosturl(); if (!host) host = xstrdup("localhost"); @@ -276,9 +274,8 @@ void cgit_print_atom(char *tip, const char *path, int max_count) commit->parents = NULL; } if (need_updated) { - // Atom makes a feed level updated mandatory, and an empty feed - // has no commit to take one from, so the epoch stands in and - // keeps the feed byte for byte stable. + // Atom makes a feed level updated mandatory, and an empty + // feed has no commit to take one from, so the epoch stands in. html("<updated>"); xml_txt(feed_date(0)); html("</updated>\n"); diff --git a/source/ui-blame.c b/source/ui-blame.c index ff3795e..8a7f8f0 100644 --- a/source/ui-blame.c +++ b/source/ui-blame.c @@ -17,9 +17,8 @@ #include "ui-blame.h" #include "ui-shared.h" -// A tab in the rendered source runs on to the next multiple of this. The -// stylesheet leaves tab-size alone, so the measurement has to match what the -// browser does on its own rather than anything cgit picks. +// A tab in the rendered source runs on to the next multiple of this, +// matching what the browser does on its own since tab-size is left alone. #define TAB_WIDTH 8 enum blame_target { @@ -225,9 +224,9 @@ static size_t line_width(struct blame_scoreboard *sb, int line) } /* - * The stylesheet takes the source pre out of flow and positions it over these - * blocks, so nothing else gives the cell a size and each block has to be - * padded to the height and the width of the lines it stands behind. + * The stylesheet takes the source pre out of flow and positions it over + * these blocks, so each block has to be padded to the height and width of + * the lines it stands behind. */ static void emit_entry_background(struct blame_scoreboard *sb, struct blame_entry *ent) diff --git a/source/ui-blob.c b/source/ui-blob.c index efe1596..67af675 100644 --- a/source/ui-blob.c +++ b/source/ui-blob.c @@ -72,9 +72,8 @@ static int find_path_oid(struct object_id *oid, char *path, int file_only) } /* - * Callers ask before reading the object, because the point of the limit is to - * keep a huge blob out of memory rather than to notice it once it is already - * there. + * Callers ask before reading the object, so a huge blob stays out of + * memory rather than being noticed once already there. */ static int over_size_limit(unsigned long size) { diff --git a/source/ui-clone.c b/source/ui-clone.c index 75e5f95..f5c4d59 100644 --- a/source/ui-clone.c +++ b/source/ui-clone.c @@ -5,7 +5,6 @@ * files under objects, and HEAD, each written as raw bytes rather than as a * page. The two listings a clone starts from are built per request, so a * repository serves without anyone having run git update-server-info over it. - * The shape of it all follows git's own http-backend. */ #define USE_THE_REPOSITORY_VARIABLE diff --git a/source/ui-diff.c b/source/ui-diff.c index e08b096..3c7d8d2 100644 --- a/source/ui-diff.c +++ b/source/ui-diff.c @@ -17,12 +17,10 @@ #include "ui-shared.h" #include "ui-ssdiff.h" -// A file's body is collected while the walk still has that file open, because -// the stat table above it has to be printed first and rendering the bodies -// afterwards meant a second walk with its own rename detection and its own -// xdiff of every file. This bounds what one request may hold that way, so it -// is not something to configure, and once it is passed the collected bodies -// are dropped and that second walk happens after all. +// A file's body is collected while the walk still has that file open, +// since the stat table above it has to be printed first. This bounds what +// one request may hold that way. Past it the collected bodies are dropped +// and a second walk renders the page instead. #define BODY_BUDGET (8 * 1024 * 1024) // What a context of zero means once the diff runs, mirroring the fallback in @@ -81,9 +79,8 @@ static void release_bodies(void) /* * One bar segment of the per-file diffstat graph. The bar is a fixed-layout - * table whose row always spans 1000 columns, so a segment takes its share - * of the width through colspan rather than through an inline style, which - * a Content-Security-Policy would have to allow. + * table whose row always spans 1000 columns, so a segment takes its width + * through colspan rather than an inline style a CSP would have to allow. */ static void print_graph_cell(const char *class, int span) { @@ -168,9 +165,8 @@ static void print_fileinfo(struct fileinfo *info) } /* - * Counting is only half of what this does. It also renders each line, until - * max-diff-lines is passed and the rest of the file is dropped, which is why - * the two cannot be separated into a counting pass and a rendering one. + * Counting is only half of what this does. It also renders each line until + * max-diff-lines is passed, so the two cannot be split into separate passes. */ static void count_diff_lines(char *line, int len) { @@ -376,9 +372,9 @@ static void collect_body(struct diff_filepair *pair, struct fileinfo *item, render_suppressed = 0; if (S_ISGITLINK(pair->one->mode) || S_ISGITLINK(pair->two->mode)) { - // The stat has always counted what a diff of the pair produces - // rather than the two lines the body shows, so run that diff - // for the count alone. + // The stat counts what a diff of the pair produces rather + // than the two lines the body shows, so run that diff for + // the count alone. render_line_fn = NULL; cgit_diff_files(&pair->one->oid, &pair->two->oid, old_size, new_size, binary, 0, ctx.qry.ignorews, @@ -398,10 +394,9 @@ static void collect_body(struct diff_filepair *pair, struct fileinfo *item, cgit_ssdiff_footer(); if (render_suppressed) { - // Setting the length back would leave the buffer holding - // everything it grew to while rendering, which the budget below - // cannot see because it only counts what is kept, so rebuild it - // at the size actually kept. + // Setting the length back would keep the grown allocation, + // which the budget below cannot see because it only counts + // what is kept, so rebuild the buffer at the kept size. char *header_text = xmemdupz(body->buf, header_len); strbuf_release(body); diff --git a/source/ui-log.c b/source/ui-log.c index bbfeb67..5b5dd32 100644 --- a/source/ui-log.c +++ b/source/ui-log.c @@ -337,7 +337,7 @@ static char *next_token(char **src) { char *token; - if (!src || !*src) + if (!*src) return NULL; while (isspace((unsigned char)**src)) (*src)++; @@ -398,7 +398,6 @@ void cgit_print_commit_decorations(struct commit *commit) const struct name_decoration *deco; static char buf[1024]; - buf[sizeof(buf) - 1] = 0; deco = get_name_decoration(&commit->object); if (!deco) return; @@ -463,10 +462,9 @@ void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, tip = disambiguate_ref(tip, &must_free_tip); if (tip && tip[0] == '-') { // setup_revisions() reads a leading-dash argument as an - // option, so a tip arriving as the id= value "--output=<path>" - // would be handled by git as a request to write an arbitrary - // file. No valid ref or object name begins with a dash, so - // refuse it. + // option, so a tip like "--output=<path>" would become a + // request to write an arbitrary file. No valid ref or object + // name begins with a dash, so refuse it. cgit_print_error_page(400, "Bad Request", "Invalid revision"); if (must_free_tip) free((char *)tip); @@ -484,10 +482,10 @@ void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *arg; // Each whitespace separated token is taken as a - // revision expression and nothing else, since anything - // starting with a dash would reach setup_revisions as - // a rev-list option. The tip pushed above goes away - // because the range supersedes it. + // revision expression only, since a leading dash + // would reach setup_revisions as a rev-list option. + // The tip pushed above goes away, since the range + // supersedes it. strvec_pop(&rev_argv); while ((arg = next_token(&pattern))) { if (*arg == '-') { diff --git a/source/ui-patch.c b/source/ui-patch.c index ef63d00..bc98f2e 100644 --- a/source/ui-patch.c +++ b/source/ui-patch.c @@ -1,11 +1,9 @@ /* * The patch page, which serves a commit or a range of commits as plain text * in the mail format git format-patch writes, so that a change read in cgit - * can be fed straight to git am. It is one of the repository commands in - * cmd.c, taking the newer revision from id, the older one from id2, and an - * optional path that narrows the diff. A merge carries no single patch and so - * drops out of a range, and max-patch-count bounds how many commits one - * request may emit. + * can be fed straight to git am. A merge carries no single patch and drops + * out of a range, and max-patch-count bounds how many commits one request + * may emit. */ #define USE_THE_REPOSITORY_VARIABLE diff --git a/source/ui-plain.c b/source/ui-plain.c index b63b9c6..d3b4771 100644 --- a/source/ui-plain.c +++ b/source/ui-plain.c @@ -45,10 +45,10 @@ static int is_unsafe_type(const char *mimetype) } /* - * A nonzero return says the response has been written, error pages included, - * so the walk does not go on to report the path as missing. + * Writes the response for the object, error pages included, so the walk + * does not go on to report the path as missing. */ -static int print_object(const struct object_id *oid, const char *path) +static void print_object(const struct object_id *oid, const char *path) { enum object_type type; char *buf, *mimetype; @@ -57,7 +57,7 @@ static int print_object(const struct object_id *oid, const char *path) type = odb_read_object_info(the_repository->objects, oid, &size); if (type == OBJ_BAD) { cgit_print_error_page(404, "Not Found", "Not found"); - return 1; + return; } // The limit counts kilobytes and is checked before the read, so a huge @@ -67,13 +67,13 @@ static int print_object(const struct object_id *oid, const char *path) cgit_print_error_page(413, "Content Too Large", "Object size (%luKB) exceeds limit (%dKB)", size / 1024, ctx.cfg.max_blob_size); - return 1; + return; } buf = odb_read_object(the_repository->objects, oid, &type, &size); if (!buf) { cgit_print_error_page(404, "Not Found", "Not found"); - return 1; + return; } mimetype = cgit_get_mimetype_for_filename(path); @@ -99,7 +99,6 @@ static int print_object(const struct object_id *oid, const char *path) html_raw(buf, size); free(mimetype); free(buf); - return 1; } static char *build_path(const char *base, int baselen, const char *path) @@ -110,8 +109,7 @@ static char *build_path(const char *base, int baselen, const char *path) return cgit_fmtalloc("%.*s/", baselen, base); } -static void print_dir(const struct object_id *oid, const char *base, - int baselen, const char *path) +static void print_dir(const char *base, int baselen, const char *path) { char *fullpath; const char *leading_slash; @@ -120,9 +118,8 @@ static void print_dir(const struct object_id *oid, const char *base, fullpath = build_path(base, baselen, path); leading_slash = (fullpath[0] == '/' ? "" : "/"); cgit_print_http_headers(); - // The listing is a full document of its own, so it carries the same - // doctype and charset as the layout pages or the browser would parse - // it in quirks mode. + // A full document of its own, and without the doctype and charset + // the browser would parse it in quirks mode. html("<!DOCTYPE html>\n<html lang='en'>\n<head>\n"); html("<meta charset='UTF-8'>\n"); htmlf("<title>%s", leading_slash); @@ -186,10 +183,10 @@ static int walk_tree(const struct object_id *oid, struct strbuf *base, if (walk->dir_len >= 0 && base->len == (size_t)walk->dir_len) { if (S_ISREG(mode) || S_ISLNK(mode)) { - if (print_object(oid, pathname)) - walk->response = RESPONSE_BLOB; + print_object(oid, pathname); + walk->response = RESPONSE_BLOB; } else if (S_ISDIR(mode)) { - print_dir(oid, base->buf, base->len, pathname); + print_dir(base->buf, base->len, pathname); walk->response = RESPONSE_LISTING; return READ_TREE_RECURSIVE; } @@ -217,10 +214,9 @@ void cgit_print_plain(void) struct object_id oid; struct commit *commit; int path_len = ctx.qry.path ? strlen(ctx.qry.path) : 0; - // A hand built pathspec leaves nowildcard_len at zero, which tells git - // the match may be a glob. It would then hand this walk every entry a - // pattern like * matches, and each one would be answered with its own - // set of HTTP headers inside the body of the first. + // nowildcard_len matches len so git treats the path as literal. As a + // glob, every entry a pattern like * matches would be answered with + // its own HTTP headers inside the body of the first. struct pathspec_item path_items = { .match = ctx.qry.path, .len = path_len, @@ -251,7 +247,7 @@ void cgit_print_plain(void) // itself, so the listing it would have opened is opened here. path_items.match = ""; walk.dir_len = -1; - print_dir(get_commit_tree_oid(commit), "", 0, ""); + print_dir("", 0, ""); walk.response = RESPONSE_LISTING; } else { walk.dir_len = dir_prefix_len(path_items.match); diff --git a/source/ui-repolist.c b/source/ui-repolist.c index 39978ae..9f60228 100644 --- a/source/ui-repolist.c +++ b/source/ui-repolist.c @@ -267,7 +267,7 @@ static void print_repo_row(const char *currenturl, int sublevel) static void print_pager(int total, int pagelen, char *search, char *sort) { int i, ofs; - char *class = NULL; + char *class; html("<ul class='pager'>\n"); for (i = 0, ofs = 0; ofs < total; i++, ofs = i * pagelen) { @@ -356,8 +356,7 @@ static int cmp_section(const void *a, const void *b) /* * get_repo_modtime caches into the repository it is handed, but qsort moves - * those structs around as it works, so a comparator left to fill the cache - * loses most of what it stored and stats the same repository over and over. + * those structs as it works, so the cache is filled before sorting. */ static void resolve_modtimes(void) { diff --git a/source/ui-shared.c b/source/ui-shared.c index a79c291..2ec9d0e 100644 --- a/source/ui-shared.c +++ b/source/ui-shared.c @@ -611,7 +611,8 @@ static void print_repo_tabs(void) ctx.qry.oid, ctx.qry.vpath); else cgit_tree_link("tree", - tab_title("Browse the tree at %s", "Browse the file tree", vpath), + tab_title("Browse the tree at %s", + "Browse the file tree", vpath), tab_class("tree"), ctx.qry.head, ctx.qry.oid, ctx.qry.vpath); html("</li>\n<li>"); @@ -686,8 +687,6 @@ static void print_site_search(void) free(currenturl); } -// The link is built out of the request in ctx.qry, so a caller that alters a -// field of ctx.qry first gets a link differing in exactly that. static void snapshot_link(const char *name, const char *title, const char *class, const char *head, const char *rev, const char *archivename) @@ -695,6 +694,8 @@ static void snapshot_link(const char *name, const char *title, const char *class reporevlink("snapshot", name, title, class, head, rev, archivename); } +// The link is built out of the request in ctx.qry, so a caller that alters a +// field of ctx.qry first gets a link differing in exactly that. static void self_link(const char *name, const char *title, const char *class) { if (!strcmp(ctx.qry.page, "repolist")) @@ -1225,10 +1226,7 @@ static const struct forge *forge_for_host(const char *host, size_t len) * Derives a submodule row's links from its .gitmodules entry. The url is * matched against this instance's own repositories first, so ssh, file and * relative urls still land on an internal page when their target is served - * here, and the pinned commit gets a page of its own. A plain web url is - * linked as it is, an ssh url to a known host is rewritten to its web form, - * and anything else is left unlinked with the url as a tooltip, since a - * scheme cgit cannot vouch for has no place in an href. + * here, and a scheme cgit cannot vouch for is left unlinked as a tooltip. */ static void gitmodules_link(const char *path, const char *rev, char **module, char **commit, @@ -1477,7 +1475,8 @@ void cgit_print_http_headers(void) void cgit_redirect(const char *url, bool permanent) { - htmlf("Status: %d %s\n", permanent ? 301 : 302, permanent ? "Moved Permanently" : "Found"); + htmlf("Status: %d %s\n", permanent ? 301 : 302, + permanent ? "Moved Permanently" : "Found"); html("Location: "); html_url_path(url); html("\n\n"); @@ -1500,8 +1499,10 @@ void cgit_print_docstart(void) html("<meta charset='UTF-8'>\n"); html("<meta name='viewport' content='width=device-width, initial-scale=1'>\n"); html("<meta name='color-scheme' content='light dark'>\n"); - html("<meta name='theme-color' media='(prefers-color-scheme: light)' content='#ffffff'>\n"); - html("<meta name='theme-color' media='(prefers-color-scheme: dark)' content='#1b1b1b'>\n"); + html("<meta name='theme-color' media='(prefers-color-scheme: light)'" + " content='#ffffff'>\n"); + html("<meta name='theme-color' media='(prefers-color-scheme: dark)'" + " content='#1b1b1b'>\n"); html("<title>"); // An error page reached before a title was chosen still has to name // itself, since an empty title element is not valid. @@ -1585,7 +1586,8 @@ void cgit_print_docend(void) if (ctx.cfg.footer) html_include(ctx.cfg.footer); else { - htmlf("<footer class='footer'>generated by <a href='https://github.com/brycekwon/cgit'>cgit %s</a> " + htmlf("<footer class='footer'>generated by " + "<a href='https://github.com/brycekwon/cgit'>cgit %s</a> " "(<a href='https://git-scm.com/'>git %s</a>)</footer>\n", cgit_version, git_version_string); } @@ -1652,11 +1654,10 @@ static void capture_http_clone_url(const char *url) } /* - * One row of the clone table. The url is shown as written, and every row is - * a link, but a browser cannot follow the ssh, scp or git forms, so those - * rows point at the first http url in the clone list, or at the repository's - * own page when the list has none, which with http clone enabled is itself a - * working clone url. A colspan of zero leaves the attribute out. + * One row of the clone table. Every row is a link, but a browser cannot + * follow the ssh, scp or git forms, so those rows point at the first http + * url in the clone list, or at the repository's own page when the list has + * none. A colspan of zero leaves the attribute out. */ void cgit_print_clone_row(const char *url, int colspan) { @@ -1858,7 +1859,8 @@ void cgit_print_snapshot_links(const struct cgit_repo *repo, const char *ref, snapshot_link("sig", NULL, NULL, NULL, NULL, filename.buf); html(")"); - } else if (starts_with(f->suffix, ".tar") && cgit_snapshot_get_sig(ref, &cgit_snapshot_formats[0])) { + } else if (starts_with(f->suffix, ".tar") && + cgit_snapshot_get_sig(ref, &cgit_snapshot_formats[0])) { // A compressed tarball offers the signature made for // the plain tar it expands to, which is the first // format in the table. diff --git a/source/ui-snapshot.c b/source/ui-snapshot.c index 7da7252..af985f4 100644 --- a/source/ui-snapshot.c +++ b/source/ui-snapshot.c @@ -165,7 +165,7 @@ static const char *ref_from_filename(const struct cgit_repo *repo, if (starts_with(rev.buf, repo_prefix)) { const char *rest = rev.buf + strlen(repo_prefix); - while (rest && (*rest == '-' || *rest == '_')) + while (*rest == '-' || *rest == '_') rest++; strbuf_splice(&rev, 0, rest - rev.buf, "", 0); } diff --git a/source/ui-snapshot.h b/source/ui-snapshot.h index 9f1ec02..9d2910f 100644 --- a/source/ui-snapshot.h +++ b/source/ui-snapshot.h @@ -26,8 +26,9 @@ extern unsigned cgit_snapshot_format_bit(const struct cgit_snapshot_format *f); /* * Turn a cgitrc snapshots value into a mask over cgit_snapshot_formats. A - * plain number is the legacy form meaning every format, as is the word all, - * otherwise the value is a space separated list of suffixes. + * plain nonzero number is the legacy boolean form and enables plain tar + * alone, the word all enables every format, and otherwise the value is a + * space separated list of suffixes. */ extern int cgit_parse_snapshots_mask(const char *str); diff --git a/source/ui-ssdiff.c b/source/ui-ssdiff.c index 8651ce7..759a77b 100644 --- a/source/ui-ssdiff.c +++ b/source/ui-ssdiff.c @@ -33,8 +33,7 @@ static struct deferred_line *deferred_new, *deferred_new_last; /* * The table is reused by every comparison and nothing clears it in between, * because the fill in longest_common_subsequence works back from the far - * corner and writes every cell it goes on to read. Clearing it for each pair - * of lines cost more than the comparison it was preparing for. + * corner and writes every cell it goes on to read. */ static void create_lcs_table(void) { @@ -107,9 +106,8 @@ static char *longest_common_subsequence(const char *old_line, } /* - * The line with its tabs expanded, which the caller owns. Appending as the - * line is walked replaces a loop that rescanned the rest of the input at every - * tab, which made a tab heavy line quadratic in its own length. + * The line with its tabs expanded, which the caller owns. Built in one + * forward pass so a tab heavy line stays linear in its own length. */ static char *expand_tabs(const char *line) { @@ -135,9 +133,8 @@ static void flush_run(struct strbuf *run) } /* - * A stretch that the other side does not share is escaped in one call because - * escaping a character at a time sent every character of every changed line - * through the output path on its own, which dominated this page. + * A stretch the other side does not share is escaped in one call, so a + * changed line does not go through the output path a byte at a time. */ static void print_line_with_lcs(const char *class, const char *line, const char *lcs) @@ -397,8 +394,7 @@ void cgit_ssdiff_line_cb(char *line, int len) } if (line[0] == ' ') { - if (deferred_old || deferred_new) - print_deferred_lines(); + print_deferred_lines(); print_row("ctx", current_old_line, line, current_new_line, line, 0); current_old_line += 1; @@ -438,7 +434,6 @@ void cgit_ssdiff_header_end(void) void cgit_ssdiff_footer(void) { - if (deferred_old || deferred_new) - print_deferred_lines(); + print_deferred_lines(); html("<tr><td class='foot' colspan='4'></td></tr>\n"); } diff --git a/source/ui-stats.c b/source/ui-stats.c index 5b36f54..62aac56 100644 --- a/source/ui-stats.c +++ b/source/ui-stats.c @@ -149,10 +149,10 @@ static char *pretty_year(struct tm *tm) * caps the page by storing an index into this table as its max-stats. */ static const struct cgit_period periods[] = { - {'w', "week", 12, 4, trunc_week, dec_week, inc_week, pretty_week}, - {'m', "month", 12, 4, trunc_month, dec_month, inc_month, pretty_month}, - {'q', "quarter", 12, 4, trunc_quarter, dec_quarter, inc_quarter, pretty_quarter}, - {'y', "year", 12, 4, trunc_year, dec_year, inc_year, pretty_year}, + {'w', "week", 4, trunc_week, dec_week, inc_week, pretty_week}, + {'m', "month", 4, trunc_month, dec_month, inc_month, pretty_month}, + {'q', "quarter", 4, trunc_quarter, dec_quarter, inc_quarter, pretty_quarter}, + {'y', "year", 4, trunc_year, dec_year, inc_year, pretty_year}, }; static void window_start(const struct cgit_period *period, struct tm *tm) diff --git a/source/ui-stats.h b/source/ui-stats.h index ccb0598..1b20e6c 100644 --- a/source/ui-stats.h +++ b/source/ui-stats.h @@ -13,7 +13,6 @@ struct cgit_period { const char code; const char *name; - int max_periods; // How many periods a page shows side by side. int count; diff --git a/source/ui-summary.c b/source/ui-summary.c index dacf0f7..197cd07 100644 --- a/source/ui-summary.c +++ b/source/ui-summary.c @@ -65,10 +65,9 @@ static int path_within(const char *base, const char *path) /* * Returns a path the caller must free, or NULL when the request cannot be - * served. A null ref means the readme is a file on the server's disk rather - * than a path inside a ref, and such a readme is confined to its own - * directory, so one named without a directory has nothing to confine it to - * and is refused. + * served. A null ref means the readme is a file on the server's disk, and + * such a readme is confined to its own directory, so one named without a + * directory is refused. */ static char *resolve_about_path(const char *filename, const char *ref, const char *path) @@ -158,10 +157,8 @@ void cgit_print_repo_readme(const char *path) html("<div id='summary'>"); if (!ctx.repo->about_filter) { - // With no about-filter configured there is nothing to turn the - // readme source into safe HTML, so it is escaped rather than - // served raw, which would let an untrusted repository put - // script on this page. + // With no about-filter there is nothing to turn the readme + // into safe HTML, so it is escaped rather than served raw. html("<pre class='plaintext'>"); if (ref) { cgit_print_file(filename, ref, 1, 1); diff --git a/source/ui-tree.c b/source/ui-tree.c index cbf783f..26a3fd1 100644 --- a/source/ui-tree.c +++ b/source/ui-tree.c @@ -53,10 +53,8 @@ struct only_child { }; /* - * A formatted write per line meant a syscall and a temporary buffer for every - * line of the file, so the anchors are handed over in batches. Building the - * column whole was rejected because it would come to several times the size of - * the blob. + * The anchors are handed over in batches rather than a write per line, and + * never built whole, which would come to several times the blob's size. */ static void print_linenumbers(const char *buf, unsigned long size) { @@ -109,8 +107,6 @@ static void print_text_buffer(const char *filename, char *buf, return; } - // Syntax highlighting ships as one of the filters under - // custom/extensions, which keeps knowledge of languages out of cgit. html("<td class='lines'><pre><code>"); html_txt(buf); html("</code></pre></td></tr></table>\n"); @@ -134,8 +130,7 @@ static void print_binary_buffer(char *buf, unsigned long size) html("<table class='bin-blob'>\n"); html("<tr><th>ofs</th><th>hex dump</th><th>ascii</th></tr>\n"); - // At the default blob size limit a write per byte spent almost all of - // its time in the kernel, so a row goes out in one write. + // A row goes out in one write rather than a write per byte. for (offset = 0; offset < size; offset += HEXDUMP_ROW_BYTES, buf += HEXDUMP_ROW_BYTES) { strbuf_reset(&row); @@ -424,7 +419,7 @@ static void ls_tail(void) cgit_print_layout_end(); } -static void ls_tree(const struct object_id *oid, const char *path, +static void ls_tree(const struct object_id *oid, struct walk_tree_context *walk) { struct tree *tree; @@ -518,7 +513,7 @@ void cgit_print_tree(const char *rev, char *path) walk.rev = xstrdup(rev); if (path == NULL) { - ls_tree(get_commit_tree_oid(commit), NULL, &walk); + ls_tree(get_commit_tree_oid(commit), &walk); goto cleanup; } diff --git a/tests/extensions/harness.lua b/tests/extensions/harness.lua index 9085010..d0299e3 100644 --- a/tests/extensions/harness.lua +++ b/tests/extensions/harness.lua @@ -118,9 +118,7 @@ end -- Deterministic bytes standing in for a digest, built from djb2 style lanes -- in plain arithmetic so they compute the same on every Lua version. Not --- remotely cryptographic, and enough for what the checks assert, that equal --- input hashes equal, different input hashes different and a tampered --- payload no longer verifies. +-- cryptographic, just stable and collision-shy enough for the checks. local function fake_digest_bytes(text) local lanes = { 5381, 52711, 1313, 7919 } for i = 1, #text do @@ -140,8 +138,6 @@ local function fake_digest_bytes(text) return table.concat(bytes) end -harness.fake_digest_bytes = fake_digest_bytes - -- The slice of the luaossl digest interface the avatar filters use. function harness.stub_digest() harness.preload("openssl.digest", { @@ -168,8 +164,6 @@ local function fake_crypt(password, setting) return prefix .. password end -harness.fake_crypt = fake_crypt - -- The slices of luaossl and luaposix the auth filters use. The link and -- unlink stubs serve the secret creation path, which the auth checks bypass -- by replacing get_secret, so they only have to exist. diff --git a/tests/extensions/lib.sh b/tests/extensions/lib.sh index 1b31a4b..f472faf 100644 --- a/tests/extensions/lib.sh +++ b/tests/extensions/lib.sh @@ -15,9 +15,8 @@ ext_lua_version() { } # Prints the interpreters found on the path whose version falls between 5.1 -# and the given 5.x ceiling, one per line, since each extension states the -# versions it runs on and a test must not fail a script on a version it never -# claimed. +# and the given 5.x ceiling, one per line, so a script is never failed on a +# version it never claimed to run on. ext_lua_interpreters() { ext_lua_ceiling=$1 for ext_lua_bin in luajit lua5.1 lua5.2 lua5.3 lua5.4 lua5.5 lua diff --git a/tests/filters/dump.lua b/tests/filters/dump.lua index aa16dfd..d654621 100644 --- a/tests/filters/dump.lua +++ b/tests/filters/dump.lua @@ -1,7 +1,6 @@ -- Test fixture for the cgit Lua filter API, exercised by t0201-filters.sh. --- It echoes the filter_open arguments and upper-cases the body, which lets the --- test confirm that arguments and content flow through the lua: filter path. It --- is not a production filter. Runs on Lua 5.1 through 5.4 and LuaJIT. +-- It echoes the filter_open arguments and upper-cases the body, which lets +-- the test confirm arguments and content flow through the lua: filter path. function filter_open(...) buffer = "" diff --git a/tests/setup.sh b/tests/setup.sh index 757ae04..de4fcc6 100755 --- a/tests/setup.sh +++ b/tests/setup.sh @@ -6,16 +6,13 @@ # CGIT_TEST_NO_CREATE_REPOS to get the helpers without paying for the fixtures. # The Git test library would run every Git command under Valgrind if it saw -# --valgrind, and only cgit itself is worth watching, so the option is taken out -# here and acted on further down. The arguments are carried across as a newline -# separated list, which keeps any other whitespace in them intact but assumes -# that none holds a newline of its own. +# --valgrind, and only cgit is worth watching, so the option is taken out +# here and acted on further down. LF=' ' -# Trash directories are collected under one trash/ rather than left beside -# the scripts, which keeps the ignore and clean rules to a single plain -# name. The option is seeded ahead of the real arguments so one given on -# the command line still wins. +# Trash directories are collected under one trash/, which keeps the ignore +# and clean rules to a single name. Seeded ahead of the real arguments so +# one given on the command line still wins. test_argv="${LF}--root=trash" while test $# != 0 @@ -45,17 +42,15 @@ TEST_NO_CREATE_REPO=YesPlease . "$TEST_DIRECTORY"/test-lib.sh # The library spells its results directory test-results and derives these -# variables before it can be told otherwise, so they are re-pointed here and -# every count lands under the plainer results/ instead. The rarely used -# --tee and --stress options write their raw logs before this line and keep -# the library's own name. +# variables before it can be told otherwise, so they are re-pointed at the +# plainer results/. The --tee and --stress raw logs write earlier and keep +# the library's name. TEST_RESULTS_DIR="$TEST_OUTPUT_DIRECTORY/results" TEST_RESULTS_BASE="$TEST_RESULTS_DIR/$TEST_NAME$TEST_STRESS_JOB_SFX" TEST_RESULTS_SAN_DIR="$TEST_RESULTS_BASE.$TEST_RESULTS_SAN_DIR_SFX" -# The library has moved into the trash directory by now, so everything the -# tests reach outside it is anchored to TEST_OUTPUT_DIRECTORY, which still -# names this directory whatever depth the trash sits at. +# The library has moved into the trash directory by now, so paths outside +# it are anchored to TEST_OUTPUT_DIRECTORY. # The tests run cgit by name, so the binary just built has to come ahead of any # copy already installed. Under Valgrind the wrappers take that place instead. @@ -138,9 +133,7 @@ enable-filter-overrides=1 repo.url=foo repo.path=$PWD/repos/foo/.git -# Do not specify a description for this repo, as it then will be assigned -# the constant value "[no description]" (which actually used to cause a -# segfault). +# No repo.desc here, so the [no description] default gets exercised. repo.url=bar repo.path=$PWD/repos/bar/.git diff --git a/tests/t0001-git-version.sh b/tests/t0001-git-version.sh index 7dd9c40..dd49002 100755 --- a/tests/t0001-git-version.sh +++ b/tests/t0001-git-version.sh @@ -1,11 +1,9 @@ #!/bin/sh # Checks that the Git version cgit says it is built for is the one it is -# actually built against. The top-level Makefile names a version, the tree -# under vendor/git records its own, and the submodule is pinned to a tag, so -# all three have to agree or cgit is being built on something other than what -# it claims. Set CGIT_TEST_NO_GIT_VERSION to YesPlease when Git comes from -# elsewhere and the comparison has nothing to say. +# built against, so the Makefile, vendor/git and the submodule pin must name +# the same version. Set CGIT_TEST_NO_GIT_VERSION to YesPlease when Git comes +# from elsewhere. if test "$CGIT_TEST_NO_GIT_VERSION" = "YesPlease" then diff --git a/tests/t0002-html-validity.sh b/tests/t0002-html-validity.sh index f22b2b6..0784092 100755 --- a/tests/t0002-html-validity.sh +++ b/tests/t0002-html-validity.sh @@ -1,11 +1,9 @@ #!/bin/sh -# Runs the tidy checker over one page of each kind cgit renders, so that markup -# broken enough to confuse a browser is caught here rather than in the browser. -# Only the shape of the markup matters, since what the pages actually say is -# the business of the t01xx scripts. Tidy is optional and old versions of it -# predate the elements cgit uses, so the whole file steps aside when a usable -# one cannot be found. +# Runs the tidy checker over one page of each kind cgit renders, caring only +# about the shape of the markup. What the pages say is the business of the +# t01xx scripts. Tidy is optional and old versions predate the elements cgit +# uses, so the file steps aside when a usable one cannot be found. test_description='Validate html with tidy' . ./setup.sh diff --git a/tests/t0003-cache.sh b/tests/t0003-cache.sh index 7ad2a79..61bf45c 100755 --- a/tests/t0003-cache.sh +++ b/tests/t0003-cache.sh @@ -1,11 +1,9 @@ #!/bin/sh -# Exercises the cache, which keeps a rendered page in a slot on disk and -# replays it for the next request that asks for the same thing. The first three -# tests set cache-size to nothing, to one slot and to the full table in turn, -# then count what the requests left behind. The rest cover the two ways a slot -# can come out wrong, a page longer than the output buffer and a key too long -# to be read back. +# Exercises the cache. The first three tests set cache-size to nothing, one +# slot and the full table in turn, then count what the requests left behind. +# The rest cover a page longer than the output buffer and a key too long to +# be read back. test_description='Validate cache' . ./setup.sh diff --git a/tests/t0004-docs.sh b/tests/t0004-docs.sh index ef12493..292223f 100755 --- a/tests/t0004-docs.sh +++ b/tests/t0004-docs.sh @@ -3,9 +3,7 @@ # Checks that the configuration documents stay consistent with each other. # The manual documents every key the reference config sets, the reference # config sets every key the manual documents, and both settings sections of -# the manual keep their entries sorted so a reader can find a key by -# scanning. An audit found the two files drifting apart, so this pins them -# together. +# the manual keep their entries sorted. test_description='Check the configuration documents' . ./setup.sh diff --git a/tests/t0101-index.sh b/tests/t0101-index.sh index be382ad..60f71e8 100755 --- a/tests/t0101-index.sh +++ b/tests/t0101-index.sh @@ -1,11 +1,9 @@ #!/bin/sh -# The index page is what cgit serves at the root of a site, one row for every -# repository named in cgitrc. These checks look for each repository the shared -# setup builds along with its description, and for the escaping a name that -# contains a plus or a space needs before it can go into a link. They also -# confirm the index stays a plain list, without the tree and log links that -# belong to a repository's own pages. +# The index page, one row per repository in cgitrc. The checks look for each +# fixture repository and its description, for the escaping a name with a +# plus or a space needs in a link, and for the absence of the tree and log +# links that belong to a repository's own pages. test_description='Check content on index page' . ./setup.sh diff --git a/tests/t0102-summary.sh b/tests/t0102-summary.sh index 5ce1fab..181040d 100755 --- a/tests/t0102-summary.sh +++ b/tests/t0102-summary.sh @@ -1,11 +1,9 @@ #!/bin/sh -# The summary page is the landing page for a single repository, holding its -# most recent commits together with its branches and its tags. The shared -# setup caps that log at five entries, so the checks against the fifty commit -# repository are really checking that the cut lands where it should. They also -# confirm the clone url template from cgitrc has had the repository name -# substituted into it. +# The summary page for a single repository. The shared setup caps its log at +# five entries, so the checks against the fifty commit repository prove the +# cut lands where it should, and the clone url template has the repository +# name substituted in. test_description='Check content on summary page' . ./setup.sh diff --git a/tests/t0103-log.sh b/tests/t0103-log.sh index dfb5176..16fabda 100755 --- a/tests/t0103-log.sh +++ b/tests/t0103-log.sh @@ -1,10 +1,8 @@ #!/bin/sh -# The log page lists the commits on a branch and can narrow that list down -# with a search. The searching checks run against the repository whose name -# contains a space and search for a term containing a space, so every link -# cgit writes back out has to escape both the path it points at and the query -# it carries, and the two are escaped differently. +# The log page and its search. The searching checks use the repository whose +# name contains a space and a term containing a space, so every link cgit +# writes back has to escape the path and the query, which escape differently. test_description='Check content on log page' . ./setup.sh diff --git a/tests/t0104-tree.sh b/tests/t0104-tree.sh index a30297f..66686c0 100755 --- a/tests/t0104-tree.sh +++ b/tests/t0104-tree.sh @@ -1,11 +1,9 @@ #!/bin/sh -# The tree page browses a repository at one revision, either as a listing of a -# directory or as a single file with an anchor on every line. The checks -# against the repository named foo+bar cover a file name and a branch name -# that both contain a plus, which cgit percent-encodes in a path as well as in -# a query, since a bare plus in a served path would read back as a space when -# the link is requested through a query string. +# The tree page. The checks against foo+bar cover a file name and a branch +# name that both contain a plus, which cgit percent-encodes in a path as +# well as in a query, since a bare plus in a served path reads back as a +# space through a query string. test_description='Check content on tree page' . ./setup.sh diff --git a/tests/t0105-plain.sh b/tests/t0105-plain.sh index b950098..2fbe87f 100755 --- a/tests/t0105-plain.sh +++ b/tests/t0105-plain.sh @@ -1,11 +1,9 @@ #!/bin/sh -# The plain page hands over a repository's own bytes rather than a rendered -# view of them, so these checks read the response headers and then compare -# the body against what git says the blob holds. A file with no better guess -# is served as text unless its bytes look binary, a directory answers with a -# bare listing of links rather than one of the themed pages, and a path that -# names nothing has to come back as a 404 rather than an empty document. +# The plain page hands over a repository's own bytes. A file with no better +# guess is served as text unless its bytes look binary, a directory answers +# with a bare listing of links, and a path that names nothing comes back as +# a 404 rather than an empty document. test_description='Check content on plain page' . ./setup.sh diff --git a/tests/t0106-commit.sh b/tests/t0106-commit.sh index ca41830..39f1822 100755 --- a/tests/t0106-commit.sh +++ b/tests/t0106-commit.sh @@ -1,10 +1,8 @@ #!/bin/sh -# The commit page shows a single commit, its message, the files it touched and -# the diff for them. Most of these checks read the markup cgit emits for the -# tree link, the parent link, the subject and the diffstat. The last few ask -# for the root commit of a repository, which has no parent and so goes through -# the code that compares a commit against an empty tree. +# The commit page. Most checks read the markup for the tree link, the parent +# link, the subject and the diffstat. The last few use a root commit, which +# has no parent and diffs against the empty tree. test_description='Check content on commit page' . ./setup.sh diff --git a/tests/t0107-diff.sh b/tests/t0107-diff.sh index 175f609..f058246 100755 --- a/tests/t0107-diff.sh +++ b/tests/t0107-diff.sh @@ -1,11 +1,10 @@ #!/bin/sh -# The diff page renders the change a commit made, either as unified text or, -# when dt=1 asks for it, as a side by side table. The first checks read the -# markup for one added file in the repository the shared setup builds. The -# rest build small repositories of their own, shaped so the side by side -# renderer meets the two cases it used to get wrong, a hunk covering a single -# line and a file where every line changed. +# The diff page, unified or side by side under dt=1. The first checks read +# the markup for one added file in the fixture repository. The rest build +# repositories of their own, shaped for the side by side renderer's two +# trickiest cases, a hunk covering a single line and a file where every +# line changed. test_description='Check content on diff page' . ./setup.sh diff --git a/tests/t0108-rawdiff.sh b/tests/t0108-rawdiff.sh index c546993..7753bdb 100755 --- a/tests/t0108-rawdiff.sh +++ b/tests/t0108-rawdiff.sh @@ -1,10 +1,8 @@ #!/bin/sh -# Checks the rawdiff page, which serves a diff as plain text with no markup -# around it so that the result can be fed straight to patch. Every test runs -# the git command the page is meant to mirror and compares the two byte for -# byte, covering an ordinary commit, the initial commit that has no parent, -# and a range spanning several commits. +# The rawdiff page serves a diff as plain text fit for patch. Every test +# runs the git command the page mirrors and compares the two byte for byte, +# over an ordinary commit, the parentless initial commit and a range. test_description='Check content on rawdiff page' . ./setup.sh diff --git a/tests/t0109-patch.sh b/tests/t0109-patch.sh index ae4dc83..0982102 100755 --- a/tests/t0109-patch.sh +++ b/tests/t0109-patch.sh @@ -1,15 +1,22 @@ #!/bin/sh -# The patch page serves a commit as a mail ready patch, so that a change read -# from cgit can be fed straight to git am. The checks compare that output -# against git format-patch for a single commit and for a range of them, once -# the CGI headers have been stripped, which means the two have to agree line -# for line. The last one sets max-patch-count so a range wider than the limit -# comes back cut short. +# The patch page serves mail ready patches, compared line for line against +# git format-patch for a single commit and for a range, once the CGI headers +# are stripped. The last check sets max-patch-count so a range wider than +# the limit comes back cut short. test_description='Check content on patch page' . ./setup.sh +# The signature sits on the second-to-last line, above the trailing blank. +check_cgit_signature() { + tail -2 tmp | head -1 | grep "^cgit" +} + +# The version the built binary signs its patches with. +CGIT_VERSION=$(sed -n "s/CGIT_VERSION = //p" \ + "$TEST_OUTPUT_DIRECTORY/../build/VERSION") + test_expect_success 'generate foo/patch' ' cgit_query "url=foo/patch" >tmp ' @@ -27,26 +34,27 @@ test_expect_success 'find `Subject:` line' ' ' test_expect_success 'find `cgit` signature' ' - tail -2 tmp | head -1 | grep "^cgit" + check_cgit_signature ' test_expect_success 'compare with output of git-format-patch(1)' ' - CGIT_VERSION=$(sed -n "s/CGIT_VERSION = //p" "$TEST_OUTPUT_DIRECTORY/../build/VERSION") && - git --git-dir="$PWD/repos/foo/.git" format-patch --subject-prefix="" --signature="cgit $CGIT_VERSION" --stdout HEAD^ >tmp2 && + git --git-dir="$PWD/repos/foo/.git" format-patch --subject-prefix="" \ + --signature="cgit $CGIT_VERSION" --stdout HEAD^ >tmp2 && strip_headers <tmp >tmp_ && test_cmp tmp_ tmp2 ' test_expect_success 'find initial commit' ' - root=$(git --git-dir="$PWD/repos/foo/.git" rev-list --max-parents=0 HEAD) + root=$(git --git-dir="$PWD/repos/foo/.git" \ + rev-list --max-parents=0 HEAD) ' test_expect_success 'generate patch for initial commit' ' cgit_query "url=foo/patch&id=$root" >tmp ' -test_expect_success 'find `cgit` signature' ' - tail -2 tmp | head -1 | grep "^cgit" +test_expect_success 'find `cgit` signature on the initial commit' ' + check_cgit_signature ' test_expect_success 'generate patches for multiple commits' ' @@ -55,13 +63,14 @@ test_expect_success 'generate patches for multiple commits' ' cgit_query "url=foo/patch&id=$id&id2=$id2" >tmp ' -test_expect_success 'find `cgit` signature' ' - tail -2 tmp | head -1 | grep "^cgit" +test_expect_success 'find `cgit` signature on the range' ' + check_cgit_signature ' -test_expect_success 'compare with output of git-format-patch(1)' ' - CGIT_VERSION=$(sed -n "s/CGIT_VERSION = //p" "$TEST_OUTPUT_DIRECTORY/../build/VERSION") && - git --git-dir="$PWD/repos/foo/.git" format-patch -N --subject-prefix="" --signature="cgit $CGIT_VERSION" --stdout HEAD~3..HEAD >tmp2 && +test_expect_success 'compare the range with git-format-patch(1)' ' + git --git-dir="$PWD/repos/foo/.git" format-patch --subject-prefix="" \ + -N --signature="cgit $CGIT_VERSION" --stdout \ + HEAD~3..HEAD >tmp2 && strip_headers <tmp >tmp_ && test_cmp tmp_ tmp2 ' @@ -74,8 +83,10 @@ test_expect_success 'max-patch-count bounds a patch range' ' echo "repo.path=$PWD/repos/foo/.git" } >patchcountrc && id=$(git --git-dir="$PWD/repos/foo/.git" rev-parse HEAD) && - root=$(git --git-dir="$PWD/repos/foo/.git" rev-list --max-parents=0 HEAD) && - CGIT_CONFIG="$PWD/patchcountrc" QUERY_STRING="url=foo/patch&id=$id&id2=$root" cgit >tmp && + root=$(git --git-dir="$PWD/repos/foo/.git" \ + rev-list --max-parents=0 HEAD) && + CGIT_CONFIG="$PWD/patchcountrc" \ + QUERY_STRING="url=foo/patch&id=$id&id2=$root" cgit >tmp && test $(grep -c "^From " tmp) -eq 2 ' diff --git a/tests/t0110-snapshot.sh b/tests/t0110-snapshot.sh index 9dc177f..9c8ce35 100755 --- a/tests/t0110-snapshot.sh +++ b/tests/t0110-snapshot.sh @@ -1,13 +1,10 @@ #!/bin/sh -# The snapshot page hands a branch back as an archive. Every tar based format -# goes through the same motions, fetch the archive, read its headers, prove -# the compression is genuine, then unpack it and compare the files inside -# against the repository the shared setup built, so one function below -# registers those checks per format. cgit pipes each format through the -# matching compressor on the server and this test needs the same program to -# unpack, so one missing tool skips that format on both grounds at once. The -# zip format follows at the end, since unzip shares no flags with the rest. +# The snapshot page hands a branch back as an archive. One function below +# registers the same checks for every tar based format, and each format +# needs the same compressor cgit pipes through on the server, so one missing +# tool skips that format on both grounds at once. zip follows at the end, +# since unzip shares no flags with the rest. test_description='Verify snapshot' . ./setup.sh diff --git a/tests/t0111-atom.sh b/tests/t0111-atom.sh index d2a678b..6ffb5b0 100755 --- a/tests/t0111-atom.sh +++ b/tests/t0111-atom.sh @@ -1,11 +1,8 @@ #!/bin/sh -# The atom page serves a repository's recent history as a feed, XML rather -# than a page, so a reader can follow the project without polling the -# browsable log. These checks read the content type, count the entries -# against the max-atom-items cap, which defaults to ten, and hand the feed to -# xmllint where one is installed, since a reader is far stricter about -# well-formedness than any of the greps here. +# The atom page serves recent history as a feed. The checks read the content +# type, count the entries against the max-atom-items cap, and hand the feed +# to xmllint where installed, a far stricter reader than any grep here. test_description='Check the atom feed' . ./setup.sh diff --git a/tests/t0201-filters.sh b/tests/t0201-filters.sh index 971ec36..a8341aa 100755 --- a/tests/t0201-filters.sh +++ b/tests/t0201-filters.sh @@ -49,13 +49,10 @@ do grep "<committer@example.com> commit C O MITTER <COMMITTER@EXAMPLE.COM>" tmp ' - # Page output is buffered, so whatever was written before a filter - # opens has to leave the buffer before the filter takes over stdout, - # and whatever was written while it was open has to leave before - # stdout is handed back, since those bytes are meant for the filter. - # A missed flush reorders the page rather than losing any of it, - # so the two tests below confirm that the markup around the filtered - # text is still on the side of it that it belongs on. + # Page output is buffered and has to be flushed as stdout is handed + # to a filter and back. A missed flush reorders the page rather than + # losing any of it, so the two tests below watch which side of the + # filtered text the surrounding markup lands on. test_expect_success "the $prefix source filter output stays inside its cell" " cgit_url 'filter-$prefix/tree/a%2bb' >tmp && tr -d '\n' <tmp >flat.out && diff --git a/tests/t0202-submodule-links.sh b/tests/t0202-submodule-links.sh index bbd99b4..6db164f 100755 --- a/tests/t0202-submodule-links.sh +++ b/tests/t0202-submodule-links.sh @@ -1,11 +1,9 @@ #!/bin/sh -# Submodule rows can derive their links from the .gitmodules entry at the -# shown revision once enable-gitmodules-links is set. The url is matched -# against the repositories this instance serves first, so ssh and relative -# urls still land on an internal page, a plain web url is linked directly, -# an ssh url to a known host is rewritten to its web form, and anything -# else stays unlinked with the url offered as a tooltip. +# Submodule rows derive their links from the .gitmodules entry at the shown +# revision once enable-gitmodules-links is set, landing on an internal page, +# a plain or rewritten web url, or an unlinked tooltip depending on the url. +# Each of those outcomes is checked against a fixture .gitmodules. test_description='Check submodule links derived from .gitmodules' . ./setup.sh diff --git a/tests/t0204-limits.sh b/tests/t0204-limits.sh index 6591106..1195c93 100755 --- a/tests/t0204-limits.sh +++ b/tests/t0204-limits.sh @@ -1,13 +1,11 @@ #!/bin/sh -# Checks the ceilings a config can put on a page, that is the caps on refs -# listed, on the lines and the files a diff renders inline, and on the size -# of a blob any view will inflate, along with the clamp on how long an index -# query may be. Crossing one of these has to trim the page or point the -# reader at somewhere better suited, never drop the request, so each case -# watches what survives as closely as what is left out. The last case is the -# same idea applied to a filter, which degrades to escaped text rather than -# failing when its highlighting library is absent. +# Checks the ceilings a config can put on a page, the caps on refs listed, +# on the lines and files a diff renders inline, on the size of a blob any +# view will inflate, and the clamp on index query length. Crossing one has +# to trim the page or point somewhere better suited, never drop the request. +# The last case is the same idea applied to a filter, which degrades to +# escaped text when its highlighting library is absent. test_description='Check the ref listing and diff size limits' . ./setup.sh diff --git a/tests/t0301-security.sh b/tests/t0301-security.sh index 9a3af3c..2e5127c 100755 --- a/tests/t0301-security.sh +++ b/tests/t0301-security.sh @@ -2,10 +2,8 @@ # Collects the regression tests for the security fixes and for the behaviour # this fork adds on top of upstream cgit. Each case builds the smallest -# repository and config that reproduce the original problem and then asks for -# the page that used to mishandle it. The comment above a case says what the -# page is being defended against, because a request that looks ordinary is -# usually the whole point of the attack. +# repository and config that reproduce the original problem and then asks +# for the page that used to mishandle it. test_description='Check security fixes and fork-specific behavior' . ./setup.sh diff --git a/tests/t0502-syntax-highlight.sh b/tests/t0502-syntax-highlight.sh index c7cbf89..a6e3fcb 100755 --- a/tests/t0502-syntax-highlight.sh +++ b/tests/t0502-syntax-highlight.sh @@ -1,13 +1,13 @@ #!/bin/sh -# Checks syntax-highlight.lua, the shipped source-filter. The real -# Scintillua collection is not assumed anywhere, because the fake lexer -# module under extensions/fake-lexers drives every path through the -# filter deterministically. The unit checks run twice per interpreter, once -# with the fake lexers found and once with an empty directory so the escaped -# fallback is what everything renders through. The run through cgit itself -# places the fake lexers beside the config file, which is the probe the -# filter documents for a scintillua directory next to cgitrc. +# Checks syntax-highlight.lua, the shipped source-filter. The real Scintillua +# collection is not assumed anywhere, because the fake lexer module under +# extensions/fake-lexers drives every path through the filter +# deterministically. The unit checks run twice per interpreter, once with the +# fake lexers found and once with an empty directory so the escaped fallback +# is what everything renders through. The run through cgit itself places the +# fake lexers beside the config file, which is the probe the filter documents +# for a scintillua directory next to cgitrc. test_description='Check the shipped syntax-highlight extension' CGIT_TEST_NO_CREATE_REPOS=YesPlease diff --git a/tests/t0505-auth.sh b/tests/t0505-auth.sh index d4dcd52..ca379de 100755 --- a/tests/t0505-auth.sh +++ b/tests/t0505-auth.sh @@ -1,15 +1,12 @@ #!/bin/sh # Checks auth-file.lua and auth-inline.lua, the shipped auth filters, which -# share their cookie signing, redirect vetting and action flows and differ -# only in where accounts live. The unit checks under a standalone Lua meet -# luaossl and luaposix with deterministic stand-ins from the harness, so -# they prove this script's own logic on any machine, tampered and expired -# cookies turned away, header injection stripped, unsafe redirects refused -# and the login flows answering as documented. The run through cgit itself -# needs the real modules inside the binary's own Lua, so it is probed for, -# and an unedited copy protects nothing, which is itself the behaviour worth -# proving end to end. +# differ only in where accounts live. The unit checks meet luaossl and +# luaposix with deterministic stand-ins from the harness, proving tampered +# and expired cookies turned away, header injection stripped, unsafe +# redirects refused and the login flows answering as documented. The run +# through cgit itself needs the real modules inside the binary's own Lua, so +# it is probed for, and proves an unedited copy protects nothing. test_description='Check the shipped auth extensions' CGIT_TEST_NO_CREATE_REPOS=YesPlease diff --git a/tools/release-build.sh b/tools/release-build.sh index 12f4260..0d9008f 100755 --- a/tools/release-build.sh +++ b/tools/release-build.sh @@ -1,17 +1,13 @@ #!/bin/sh -# Build cgit for a release with Linux hardening flags. These are ELF and GCC -# or Clang specific, so this targets a Linux deploy rather than local macOS -# development, where a plain make is enough. The flags add a stack protector, -# fortified libc calls, a position independent executable, and full RELRO. By -# default Lua is pinned off so the binary needs no Lua at runtime, and running -# it as ./tools/release-build.sh lua links in the backend behind the "lua:" -# filter prefix instead, which needs a Lua dev package installed. +# Build cgit for a release with Linux hardening flags, which are ELF and GCC or +# Clang specific. The flags add a stack protector, fortified libc calls, a +# position independent executable, and full RELRO. By default Lua is pinned off +# so the binary needs no Lua at runtime, and running it as +# ./tools/release-build.sh lua links in the backend behind the "lua:" filter +# prefix instead, which needs a Lua dev package installed. set -eu -# The argument is checked rather than assumed, since anything unrecognised -# would otherwise fall through to a quiet Lua-less build that looks like it -# worked. if [ "$#" -gt 1 ]; then echo "usage: $0 [lua]" >&2 exit 2 @@ -23,8 +19,7 @@ lua) set -- ;; exit 2 ;; esac -# Every make target below is written relative to the repository root, so go -# there rather than requiring the caller to. +# Every make target below is written relative to the repository root. cd "$(dirname "$0")/.." CC=${CC:-cc} @@ -68,11 +63,9 @@ LDFLAGS="-pie \ # These reach only the cgit objects, so git's own sources are not held to them. # -Wformat-security is an error because a non-literal format with no arguments -# is never intentional. The shape that let a repository supply its own format -# string through module-link was the other one, a non-literal that does take -# arguments, and only -Wformat-nonliteral reports that, and that one is left out because it also fires -# on forwarding a va_list and on local format constants, so it cannot be an -# error without false positives. It is still worth running by hand when +# is never intentional. -Wformat-nonliteral would catch the module-link shape, +# a non-literal that does take arguments, but it also fires on va_list +# forwarding and on local format constants, so it is left to manual runs when # touching anything that formats. # # make cgit CGIT_EXTRA_CFLAGS=-Wformat-nonliteral diff --git a/tools/serve.py b/tools/serve.py index 2b2f045..a39d03f 100755 --- a/tools/serve.py +++ b/tools/serve.py @@ -29,8 +29,20 @@ REPO_ROOT = Path(__file__).resolve().parent.parent MAX_BODY_BYTES = 8 * 1024 * 1024 CGI_TIMEOUT = 60 -STATIC_SUFFIXES = (".css", ".js", ".png", ".ico", ".gif", ".jpg", ".jpeg", - ".svg", ".webp", ".txt", ".woff", ".woff2") +STATIC_SUFFIXES = ( + ".css", + ".js", + ".png", + ".ico", + ".gif", + ".jpg", + ".jpeg", + ".svg", + ".webp", + ".txt", + ".woff", + ".woff2", +) # The request headers cgit looks at, paired with the CGI variable each one # has to arrive as. @@ -273,8 +285,11 @@ def main() -> None: config = Path(options.config).resolve() cgit = Path(options.cgit).resolve() data_dir = Path(options.data).resolve() - for label, path in (("config", config), ("cgit binary", cgit), - ("data directory", data_dir)): + for label, path in ( + ("config", config), + ("cgit binary", cgit), + ("data directory", data_dir), + ): if not path.exists(): sys.exit(f"error: {label} not found: {path}") |
