List for cgit developers and users
 help / color / mirror / Atom feed
* Format orgmode files
@ 2020-08-09 16:21 Oscar Najera
  0 siblings, 0 replies; only message in thread
From: Oscar Najera @ 2020-08-09 16:21 UTC (permalink / raw)
  To: cgit

[-- Attachment #1: Type: text/plain, Size: 344 bytes --]

Hi,

I like your work. I'm using cgit now to selfhost my projects, yet as an
emacs & org-mode user I run into the need to format the readme.org
files.

Attached is a patch for my solution. It is a copy of the md2html, which
I also change a bit to reuse some code.

Happy to go over a review process for this patch to get merged.

Cheers,
Oscar

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Support-for-org-mode-files.patch --]
[-- Type: text/x-patch, Size: 19509 bytes --]

From ced85d881c4c61a27cb1eb1c189a67cd94678bef Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=93scar=20N=C3=A1jera?= <hi@oscarnajera.com>
Date: Sun, 9 Aug 2020 15:01:56 +0200
Subject: [PATCH] Support for org-mode files

---
 cgitrc.5.txt                     |   2 +
 filters/about-formatting.sh      |   2 +
 filters/html-converters/md.css   | 279 +++++++++++++++++++++++++++++
 filters/html-converters/md2html  | 295 +------------------------------
 filters/html-converters/org2html |  22 +++
 5 files changed, 313 insertions(+), 287 deletions(-)
 create mode 100644 filters/html-converters/md.css
 create mode 100755 filters/html-converters/org2html

diff --git a/cgitrc.5.txt b/cgitrc.5.txt
index 33a6a8c..04f7f9d 100644
--- a/cgitrc.5.txt
+++ b/cgitrc.5.txt
@@ -913,6 +913,8 @@ readme=:README.htm
 readme=:readme.htm
 readme=:README.txt
 readme=:readme.txt
+readme=:README.org
+readme=:readme.org
 readme=:README
 readme=:readme
 readme=:INSTALL.md
diff --git a/filters/about-formatting.sh b/filters/about-formatting.sh
index 85daf9c..476edf4 100755
--- a/filters/about-formatting.sh
+++ b/filters/about-formatting.sh
@@ -6,6 +6,7 @@
 # Markdown support requires python and markdown-python.
 # RestructuredText support requires python and docutils.
 # Man page support requires groff.
+# Org-mode support requires python and org-python.
 
 # The following environment variables can be used to retrieve the configuration
 # of the repository for which this script is called:
@@ -20,6 +21,7 @@
 cd "$(dirname $0)/html-converters/"
 case "$(printf '%s' "$1" | tr '[:upper:]' '[:lower:]')" in
 	*.markdown|*.mdown|*.md|*.mkd) exec ./md2html; ;;
+	*.org) exec ./org2html; ;;
 	*.rst) exec ./rst2html; ;;
 	*.[1-9]) exec ./man2html; ;;
 	*.htm|*.html) exec cat; ;;
diff --git a/filters/html-converters/md.css b/filters/html-converters/md.css
new file mode 100644
index 0000000..41aa5e5
--- /dev/null
+++ b/filters/html-converters/md.css
@@ -0,0 +1,279 @@
+.markdown-body {
+    font-size: 14px;
+    line-height: 1.6;
+    overflow: hidden;
+}
+.markdown-body>*:first-child {
+    margin-top: 0 !important;
+}
+.markdown-body>*:last-child {
+    margin-bottom: 0 !important;
+}
+.markdown-body a.absent {
+    color: #c00;
+}
+.markdown-body a.anchor {
+    display: block;
+    padding-left: 30px;
+    margin-left: -30px;
+    cursor: pointer;
+    position: absolute;
+    top: 0;
+    left: 0;
+    bottom: 0;
+}
+.markdown-body h1, .markdown-body h2, .markdown-body h3, .markdown-body h4, .markdown-body h5, .markdown-body h6 {
+    margin: 20px 0 10px;
+    padding: 0;
+    font-weight: bold;
+    -webkit-font-smoothing: antialiased;
+    cursor: text;
+    position: relative;
+}
+.markdown-body h1 .mini-icon-link, .markdown-body h2 .mini-icon-link, .markdown-body h3 .mini-icon-link, .markdown-body h4 .mini-icon-link, .markdown-body h5 .mini-icon-link, .markdown-body h6 .mini-icon-link {
+    display: none;
+    color: #000;
+}
+.markdown-body h1:hover a.anchor, .markdown-body h2:hover a.anchor, .markdown-body h3:hover a.anchor, .markdown-body h4:hover a.anchor, .markdown-body h5:hover a.anchor, .markdown-body h6:hover a.anchor {
+    text-decoration: none;
+    line-height: 1;
+    padding-left: 0;
+    margin-left: -22px;
+    top: 15%;
+}
+.markdown-body h1:hover a.anchor .mini-icon-link, .markdown-body h2:hover a.anchor .mini-icon-link, .markdown-body h3:hover a.anchor .mini-icon-link, .markdown-body h4:hover a.anchor .mini-icon-link, .markdown-body h5:hover a.anchor .mini-icon-link, .markdown-body h6:hover a.anchor .mini-icon-link {
+    display: inline-block;
+}
+div#cgit .markdown-body h1 a.toclink, div#cgit .markdown-body h2 a.toclink, div#cgit .markdown-body h3 a.toclink, div#cgit .markdown-body h4 a.toclink, div#cgit .markdown-body h5 a.toclink, div#cgit .markdown-body h6 a.toclink {
+    color: black;
+}
+.markdown-body h1 tt, .markdown-body h1 code, .markdown-body h2 tt, .markdown-body h2 code, .markdown-body h3 tt, .markdown-body h3 code, .markdown-body h4 tt, .markdown-body h4 code, .markdown-body h5 tt, .markdown-body h5 code, .markdown-body h6 tt, .markdown-body h6 code {
+    font-size: inherit;
+}
+.markdown-body h1 {
+    font-size: 28px;
+    color: #000;
+}
+.markdown-body h2 {
+    font-size: 24px;
+    border-bottom: 1px solid #ccc;
+    color: #000;
+}
+.markdown-body h3 {
+    font-size: 18px;
+}
+.markdown-body h4 {
+    font-size: 16px;
+}
+.markdown-body h5 {
+    font-size: 14px;
+}
+.markdown-body h6 {
+    color: #777;
+    font-size: 14px;
+}
+.markdown-body p, .markdown-body blockquote, .markdown-body ul, .markdown-body ol, .markdown-body dl, .markdown-body table, .markdown-body pre {
+    margin: 15px 0;
+}
+.markdown-body hr {
+    background: transparent url("/dirty-shade.png") repeat-x 0 0;
+    border: 0 none;
+    color: #ccc;
+    height: 4px;
+    padding: 0;
+}
+.markdown-body>h2:first-child, .markdown-body>h1:first-child, .markdown-body>h1:first-child+h2, .markdown-body>h3:first-child, .markdown-body>h4:first-child, .markdown-body>h5:first-child, .markdown-body>h6:first-child {
+    margin-top: 0;
+    padding-top: 0;
+}
+.markdown-body a:first-child h1, .markdown-body a:first-child h2, .markdown-body a:first-child h3, .markdown-body a:first-child h4, .markdown-body a:first-child h5, .markdown-body a:first-child h6 {
+    margin-top: 0;
+    padding-top: 0;
+}
+.markdown-body h1+p, .markdown-body h2+p, .markdown-body h3+p, .markdown-body h4+p, .markdown-body h5+p, .markdown-body h6+p {
+    margin-top: 0;
+}
+.markdown-body li p.first {
+    display: inline-block;
+}
+.markdown-body ul, .markdown-body ol {
+    padding-left: 30px;
+}
+.markdown-body ul.no-list, .markdown-body ol.no-list {
+    list-style-type: none;
+    padding: 0;
+}
+.markdown-body ul li>:first-child, .markdown-body ul li ul:first-of-type, .markdown-body ul li ol:first-of-type, .markdown-body ol li>:first-child, .markdown-body ol li ul:first-of-type, .markdown-body ol li ol:first-of-type {
+    margin-top: 0px;
+}
+.markdown-body ul li p:last-of-type, .markdown-body ol li p:last-of-type {
+    margin-bottom: 0;
+}
+.markdown-body ul ul, .markdown-body ul ol, .markdown-body ol ol, .markdown-body ol ul {
+    margin-bottom: 0;
+}
+.markdown-body dl {
+    padding: 0;
+}
+.markdown-body dl dt {
+    font-size: 14px;
+    font-weight: bold;
+    font-style: italic;
+    padding: 0;
+    margin: 15px 0 5px;
+}
+.markdown-body dl dt:first-child {
+    padding: 0;
+}
+.markdown-body dl dt>:first-child {
+    margin-top: 0px;
+}
+.markdown-body dl dt>:last-child {
+    margin-bottom: 0px;
+}
+.markdown-body dl dd {
+    margin: 0 0 15px;
+    padding: 0 15px;
+}
+.markdown-body dl dd>:first-child {
+    margin-top: 0px;
+}
+.markdown-body dl dd>:last-child {
+    margin-bottom: 0px;
+}
+.markdown-body blockquote {
+    border-left: 4px solid #DDD;
+    padding: 0 15px;
+    color: #777;
+}
+.markdown-body blockquote>:first-child {
+    margin-top: 0px;
+}
+.markdown-body blockquote>:last-child {
+    margin-bottom: 0px;
+}
+.markdown-body table th {
+    font-weight: bold;
+}
+.markdown-body table th, .markdown-body table td {
+    border: 1px solid #ccc;
+    padding: 6px 13px;
+}
+.markdown-body table tr {
+    border-top: 1px solid #ccc;
+    background-color: #fff;
+}
+.markdown-body table tr:nth-child(2n) {
+    background-color: #f8f8f8;
+}
+.markdown-body img {
+    max-width: 100%;
+    -moz-box-sizing: border-box;
+    box-sizing: border-box;
+}
+.markdown-body span.frame {
+    display: block;
+    overflow: hidden;
+}
+.markdown-body span.frame>span {
+    border: 1px solid #ddd;
+    display: block;
+    float: left;
+    overflow: hidden;
+    margin: 13px 0 0;
+    padding: 7px;
+    width: auto;
+}
+.markdown-body span.frame span img {
+    display: block;
+    float: left;
+}
+.markdown-body span.frame span span {
+    clear: both;
+    color: #333;
+    display: block;
+    padding: 5px 0 0;
+}
+.markdown-body span.align-center {
+    display: block;
+    overflow: hidden;
+    clear: both;
+}
+.markdown-body span.align-center>span {
+    display: block;
+    overflow: hidden;
+    margin: 13px auto 0;
+    text-align: center;
+}
+.markdown-body span.align-center span img {
+    margin: 0 auto;
+    text-align: center;
+}
+.markdown-body span.align-right {
+    display: block;
+    overflow: hidden;
+    clear: both;
+}
+.markdown-body span.align-right>span {
+    display: block;
+    overflow: hidden;
+    margin: 13px 0 0;
+    text-align: right;
+}
+.markdown-body span.align-right span img {
+    margin: 0;
+    text-align: right;
+}
+.markdown-body span.float-left {
+    display: block;
+    margin-right: 13px;
+    overflow: hidden;
+    float: left;
+}
+.markdown-body span.float-left span {
+    margin: 13px 0 0;
+}
+.markdown-body span.float-right {
+    display: block;
+    margin-left: 13px;
+    overflow: hidden;
+    float: right;
+}
+.markdown-body span.float-right>span {
+    display: block;
+    overflow: hidden;
+    margin: 13px auto 0;
+    text-align: right;
+}
+.markdown-body code, .markdown-body tt {
+    margin: 0 2px;
+    padding: 0px 5px;
+    border: 1px solid #eaeaea;
+    background-color: #f8f8f8;
+    border-radius: 3px;
+}
+.markdown-body code {
+    white-space: nowrap;
+}
+.markdown-body pre>code {
+    margin: 0;
+    padding: 0;
+    white-space: pre;
+    border: none;
+    background: transparent;
+}
+.markdown-body .highlight pre, .markdown-body pre {
+    background-color: #f8f8f8;
+    border: 1px solid #ccc;
+    font-size: 13px;
+    line-height: 19px;
+    overflow: auto;
+    padding: 6px 10px;
+    border-radius: 3px;
+}
+.markdown-body pre code, .markdown-body pre tt {
+    margin: 0;
+    padding: 0;
+    background-color: transparent;
+    border: none;
+}
diff --git a/filters/html-converters/md2html b/filters/html-converters/md2html
index dc20f42..1a1ff24 100755
--- a/filters/html-converters/md2html
+++ b/filters/html-converters/md2html
@@ -1,297 +1,18 @@
 #!/usr/bin/env python3
-import markdown
 import sys
 import io
 from pygments.formatters import HtmlFormatter
+import markdown
 from markdown.extensions.toc import TocExtension
 sys.stdin = io.TextIOWrapper(sys.stdin.buffer, encoding='utf-8')
 sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
-sys.stdout.write('''
-<style>
-.markdown-body {
-    font-size: 14px;
-    line-height: 1.6;
-    overflow: hidden;
-}
-.markdown-body>*:first-child {
-    margin-top: 0 !important;
-}
-.markdown-body>*:last-child {
-    margin-bottom: 0 !important;
-}
-.markdown-body a.absent {
-    color: #c00;
-}
-.markdown-body a.anchor {
-    display: block;
-    padding-left: 30px;
-    margin-left: -30px;
-    cursor: pointer;
-    position: absolute;
-    top: 0;
-    left: 0;
-    bottom: 0;
-}
-.markdown-body h1, .markdown-body h2, .markdown-body h3, .markdown-body h4, .markdown-body h5, .markdown-body h6 {
-    margin: 20px 0 10px;
-    padding: 0;
-    font-weight: bold;
-    -webkit-font-smoothing: antialiased;
-    cursor: text;
-    position: relative;
-}
-.markdown-body h1 .mini-icon-link, .markdown-body h2 .mini-icon-link, .markdown-body h3 .mini-icon-link, .markdown-body h4 .mini-icon-link, .markdown-body h5 .mini-icon-link, .markdown-body h6 .mini-icon-link {
-    display: none;
-    color: #000;
-}
-.markdown-body h1:hover a.anchor, .markdown-body h2:hover a.anchor, .markdown-body h3:hover a.anchor, .markdown-body h4:hover a.anchor, .markdown-body h5:hover a.anchor, .markdown-body h6:hover a.anchor {
-    text-decoration: none;
-    line-height: 1;
-    padding-left: 0;
-    margin-left: -22px;
-    top: 15%;
-}
-.markdown-body h1:hover a.anchor .mini-icon-link, .markdown-body h2:hover a.anchor .mini-icon-link, .markdown-body h3:hover a.anchor .mini-icon-link, .markdown-body h4:hover a.anchor .mini-icon-link, .markdown-body h5:hover a.anchor .mini-icon-link, .markdown-body h6:hover a.anchor .mini-icon-link {
-    display: inline-block;
-}
-div#cgit .markdown-body h1 a.toclink, div#cgit .markdown-body h2 a.toclink, div#cgit .markdown-body h3 a.toclink, div#cgit .markdown-body h4 a.toclink, div#cgit .markdown-body h5 a.toclink, div#cgit .markdown-body h6 a.toclink {
-    color: black;
-}
-.markdown-body h1 tt, .markdown-body h1 code, .markdown-body h2 tt, .markdown-body h2 code, .markdown-body h3 tt, .markdown-body h3 code, .markdown-body h4 tt, .markdown-body h4 code, .markdown-body h5 tt, .markdown-body h5 code, .markdown-body h6 tt, .markdown-body h6 code {
-    font-size: inherit;
-}
-.markdown-body h1 {
-    font-size: 28px;
-    color: #000;
-}
-.markdown-body h2 {
-    font-size: 24px;
-    border-bottom: 1px solid #ccc;
-    color: #000;
-}
-.markdown-body h3 {
-    font-size: 18px;
-}
-.markdown-body h4 {
-    font-size: 16px;
-}
-.markdown-body h5 {
-    font-size: 14px;
-}
-.markdown-body h6 {
-    color: #777;
-    font-size: 14px;
-}
-.markdown-body p, .markdown-body blockquote, .markdown-body ul, .markdown-body ol, .markdown-body dl, .markdown-body table, .markdown-body pre {
-    margin: 15px 0;
-}
-.markdown-body hr {
-    background: transparent url("/dirty-shade.png") repeat-x 0 0;
-    border: 0 none;
-    color: #ccc;
-    height: 4px;
-    padding: 0;
-}
-.markdown-body>h2:first-child, .markdown-body>h1:first-child, .markdown-body>h1:first-child+h2, .markdown-body>h3:first-child, .markdown-body>h4:first-child, .markdown-body>h5:first-child, .markdown-body>h6:first-child {
-    margin-top: 0;
-    padding-top: 0;
-}
-.markdown-body a:first-child h1, .markdown-body a:first-child h2, .markdown-body a:first-child h3, .markdown-body a:first-child h4, .markdown-body a:first-child h5, .markdown-body a:first-child h6 {
-    margin-top: 0;
-    padding-top: 0;
-}
-.markdown-body h1+p, .markdown-body h2+p, .markdown-body h3+p, .markdown-body h4+p, .markdown-body h5+p, .markdown-body h6+p {
-    margin-top: 0;
-}
-.markdown-body li p.first {
-    display: inline-block;
-}
-.markdown-body ul, .markdown-body ol {
-    padding-left: 30px;
-}
-.markdown-body ul.no-list, .markdown-body ol.no-list {
-    list-style-type: none;
-    padding: 0;
-}
-.markdown-body ul li>:first-child, .markdown-body ul li ul:first-of-type, .markdown-body ul li ol:first-of-type, .markdown-body ol li>:first-child, .markdown-body ol li ul:first-of-type, .markdown-body ol li ol:first-of-type {
-    margin-top: 0px;
-}
-.markdown-body ul li p:last-of-type, .markdown-body ol li p:last-of-type {
-    margin-bottom: 0;
-}
-.markdown-body ul ul, .markdown-body ul ol, .markdown-body ol ol, .markdown-body ol ul {
-    margin-bottom: 0;
-}
-.markdown-body dl {
-    padding: 0;
-}
-.markdown-body dl dt {
-    font-size: 14px;
-    font-weight: bold;
-    font-style: italic;
-    padding: 0;
-    margin: 15px 0 5px;
-}
-.markdown-body dl dt:first-child {
-    padding: 0;
-}
-.markdown-body dl dt>:first-child {
-    margin-top: 0px;
-}
-.markdown-body dl dt>:last-child {
-    margin-bottom: 0px;
-}
-.markdown-body dl dd {
-    margin: 0 0 15px;
-    padding: 0 15px;
-}
-.markdown-body dl dd>:first-child {
-    margin-top: 0px;
-}
-.markdown-body dl dd>:last-child {
-    margin-bottom: 0px;
-}
-.markdown-body blockquote {
-    border-left: 4px solid #DDD;
-    padding: 0 15px;
-    color: #777;
-}
-.markdown-body blockquote>:first-child {
-    margin-top: 0px;
-}
-.markdown-body blockquote>:last-child {
-    margin-bottom: 0px;
-}
-.markdown-body table th {
-    font-weight: bold;
-}
-.markdown-body table th, .markdown-body table td {
-    border: 1px solid #ccc;
-    padding: 6px 13px;
-}
-.markdown-body table tr {
-    border-top: 1px solid #ccc;
-    background-color: #fff;
-}
-.markdown-body table tr:nth-child(2n) {
-    background-color: #f8f8f8;
-}
-.markdown-body img {
-    max-width: 100%;
-    -moz-box-sizing: border-box;
-    box-sizing: border-box;
-}
-.markdown-body span.frame {
-    display: block;
-    overflow: hidden;
-}
-.markdown-body span.frame>span {
-    border: 1px solid #ddd;
-    display: block;
-    float: left;
-    overflow: hidden;
-    margin: 13px 0 0;
-    padding: 7px;
-    width: auto;
-}
-.markdown-body span.frame span img {
-    display: block;
-    float: left;
-}
-.markdown-body span.frame span span {
-    clear: both;
-    color: #333;
-    display: block;
-    padding: 5px 0 0;
-}
-.markdown-body span.align-center {
-    display: block;
-    overflow: hidden;
-    clear: both;
-}
-.markdown-body span.align-center>span {
-    display: block;
-    overflow: hidden;
-    margin: 13px auto 0;
-    text-align: center;
-}
-.markdown-body span.align-center span img {
-    margin: 0 auto;
-    text-align: center;
-}
-.markdown-body span.align-right {
-    display: block;
-    overflow: hidden;
-    clear: both;
-}
-.markdown-body span.align-right>span {
-    display: block;
-    overflow: hidden;
-    margin: 13px 0 0;
-    text-align: right;
-}
-.markdown-body span.align-right span img {
-    margin: 0;
-    text-align: right;
-}
-.markdown-body span.float-left {
-    display: block;
-    margin-right: 13px;
-    overflow: hidden;
-    float: left;
-}
-.markdown-body span.float-left span {
-    margin: 13px 0 0;
-}
-.markdown-body span.float-right {
-    display: block;
-    margin-left: 13px;
-    overflow: hidden;
-    float: right;
-}
-.markdown-body span.float-right>span {
-    display: block;
-    overflow: hidden;
-    margin: 13px auto 0;
-    text-align: right;
-}
-.markdown-body code, .markdown-body tt {
-    margin: 0 2px;
-    padding: 0px 5px;
-    border: 1px solid #eaeaea;
-    background-color: #f8f8f8;
-    border-radius: 3px;
-}
-.markdown-body code {
-    white-space: nowrap;
-}
-.markdown-body pre>code {
-    margin: 0;
-    padding: 0;
-    white-space: pre;
-    border: none;
-    background: transparent;
-}
-.markdown-body .highlight pre, .markdown-body pre {
-    background-color: #f8f8f8;
-    border: 1px solid #ccc;
-    font-size: 13px;
-    line-height: 19px;
-    overflow: auto;
-    padding: 6px 10px;
-    border-radius: 3px;
-}
-.markdown-body pre code, .markdown-body pre tt {
-    margin: 0;
-    padding: 0;
-    background-color: transparent;
-    border: none;
-}
-''')
-sys.stdout.write(HtmlFormatter(style='pastie').get_style_defs('.highlight'))
-sys.stdout.write('''
-</style>   
-''')
+
+with open("md.css") as css:
+    sys.stdout.write('<style>')
+    sys.stdout.write(css.read())
+    sys.stdout.write(HtmlFormatter(style='pastie').get_style_defs('.highlight'))
+    sys.stdout.write('</style>')
+
 sys.stdout.write("<div class='markdown-body'>")
 sys.stdout.flush()
 # Note: you may want to run this through bleach for sanitization
diff --git a/filters/html-converters/org2html b/filters/html-converters/org2html
new file mode 100755
index 0000000..01e3641
--- /dev/null
+++ b/filters/html-converters/org2html
@@ -0,0 +1,22 @@
+#!/usr/bin/env python3
+
+import sys
+import io
+
+from pygments.formatters import HtmlFormatter
+from orgpython import org_to_html
+
+sys.stdin = io.TextIOWrapper(sys.stdin.buffer, encoding='utf-8')
+sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
+
+with open("md.css") as css:
+    sys.stdout.write('<style>')
+    sys.stdout.write(css.read())
+    sys.stdout.write(HtmlFormatter(style='pastie').get_style_defs('.highlight'))
+    sys.stdout.write('</style>')
+
+sys.stdout.write("<div class='markdown-body'>")
+sys.stdout.flush()
+# Note: you may want to run this through bleach for sanitization
+sys.stdout.write(org_to_html(sys.stdin.read()))
+sys.stdout.write("</div>")
-- 
2.28.0


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2020-08-09 16:21 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-08-09 16:21 Format orgmode files Oscar Najera

List for cgit developers and users

This inbox may be cloned and mirrored by anyone:

	git clone --mirror http://inbox.vuxu.org/cgit

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V1 cgit cgit/ http://inbox.vuxu.org/cgit \
		cgit@lists.zx2c4.com
	public-inbox-index cgit

Example config snippet for mirrors.
Newsgroup available over NNTP:
	nntp://inbox.vuxu.org/vuxu.archive.cgit


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git