commit 98deb9605ffe190673a1d0d7ca8f653c74c7be80
parent 60e6b177730ef068415639bf48471431dc69870b
Author: Michael Neuper <michael@michaelneuper.com>
Date: Sun, 25 Oct 2026 22:13:31 +0200
render README.org with pandoc
README.md is preferred, then README.org, then a plain README. The org
file is converted by pandoc as a standalone document, so #+title is kept,
and its body goes through the same link and image rewriting as markdown.
Without pandoc it is shown as plain text, with a warning.
Diffstat:
4 files changed, 87 insertions(+), 18 deletions(-)
diff --git a/.github/workflows/mirror.yml b/.github/workflows/mirror.yml
@@ -20,10 +20,10 @@ jobs:
steps:
- uses: actions/checkout@v7
- - name: Install libgit2 and md4c
+ - name: Install libgit2, md4c and pandoc
run: |
sudo apt-get update
- sudo apt-get install -y --no-install-recommends libgit2-dev libmd4c-dev libmd4c-html0-dev
+ sudo apt-get install -y --no-install-recommends libgit2-dev libmd4c-dev libmd4c-html0-dev pandoc
- name: Build stagit
run: make
diff --git a/README.md b/README.md
@@ -6,10 +6,11 @@ mirror of my public GitHub repos. The upstream README is in `README`.
## Changes from upstream
-- Each repo opens on its README (`index.html`), with markdown rendered by
- [md4c](https://github.com/mity/md4c). `README.md` is preferred over
- `README`, which is shown as plain text. Repos without either open on the
- log.
+- Each repo opens on its README (`index.html`). `README.md` is rendered by
+ [md4c](https://github.com/mity/md4c) and `README.org` by
+ [pandoc](https://pandoc.org); a plain `README`, or a `README.org` when
+ pandoc isn't installed, is shown as plain text. They're looked for in that
+ order, and repos without one open on the log.
- stagit-index links to each repo's front page instead of its log.
- The menu reads README | Files | Log | Refs.
- Files are browsed by directory: `files.html` lists the top level, each
@@ -25,7 +26,8 @@ To see only the code changes: `git diff 1.3 -- '*.c' Makefile`.
## Building the site
-Needs libgit2, md4c and the GitHub CLI (`brew install libgit2 md4c gh`).
+Needs libgit2, md4c, pandoc and the GitHub CLI
+(`brew install libgit2 md4c pandoc gh`).
./build.sh
python3 -m http.server -d public
diff --git a/stagit.c b/stagit.c
@@ -71,7 +71,7 @@ static char cloneurl[1024];
static char *submodules;
static char *licensefiles[] = { "HEAD:LICENSE", "HEAD:LICENSE.md", "HEAD:LICENSE.txt", "HEAD:COPYING" };
static char *license;
-static char *readmefiles[] = { "HEAD:README.md", "HEAD:README" };
+static char *readmefiles[] = { "HEAD:README.md", "HEAD:README.org", "HEAD:README" };
static char *readme;
static long long nlogcommits = -1; /* -1 indicates not used */
@@ -1143,14 +1143,76 @@ writereadmehtml(FILE *fp, const char *s)
fputs(s, fp);
}
+char *
+markdownhtml(const char *s, size_t len)
+{
+ char *html = NULL;
+ size_t htmllen;
+ FILE *htmlfp;
+
+ if (!(htmlfp = open_memstream(&html, &htmllen)))
+ err(1, "open_memstream");
+ if (md_html(s, len, writemarkdown, htmlfp, MD_DIALECT_GITHUB, 0))
+ errx(1, "md_html: '%s'", readme);
+ if (fclose(htmlfp))
+ err(1, "fclose: '%s'", readme);
+
+ return html;
+}
+
+/* README.org converted by pandoc, or NULL if that fails, for example
+ * because pandoc is not installed */
+char *
+orghtml(const char *s, size_t len)
+{
+ char tmppath[] = "/tmp/stagit.XXXXXX", cmd[128], buf[BUFSIZ];
+ char *html = NULL, *body, *end;
+ size_t n, htmllen;
+ FILE *tmpfp, *pp, *htmlfp;
+ int fd, status;
+
+ if ((fd = mkstemp(tmppath)) < 0)
+ err(1, "mkstemp");
+ if (!(tmpfp = fdopen(fd, "w")))
+ err(1, "fdopen: '%s'", tmppath);
+ fwrite(s, 1, len, tmpfp);
+ checkfileerror(tmpfp, tmppath, 'w');
+ fclose(tmpfp);
+
+ /* a standalone document keeps #+title; only its body is used */
+ snprintf(cmd, sizeof(cmd),
+ "pandoc -f org -t html5 -s -M pagetitle=README '%s'", tmppath);
+ if (!(pp = popen(cmd, "r")))
+ err(1, "popen");
+ if (!(htmlfp = open_memstream(&html, &htmllen)))
+ err(1, "open_memstream");
+ while ((n = fread(buf, 1, sizeof(buf), pp)) > 0)
+ fwrite(buf, 1, n, htmlfp);
+ status = pclose(pp);
+ if (fclose(htmlfp))
+ err(1, "fclose: '%s'", readme);
+ unlink(tmppath);
+
+ if (status || !(body = strstr(html, "<body>")) ||
+ !(end = strstr(body, "</body>"))) {
+ warnx("pandoc failed for '%s', showing it as plain text", readme);
+ free(html);
+ return NULL;
+ }
+ body += strlen("<body>");
+ memmove(html, body, end - body);
+ html[end - body] = '\0';
+
+ return html;
+}
+
void
writereadme(FILE *fp)
{
git_object *obj = NULL;
char spec[PATH_MAX], *html = NULL;
const char *s, *ext;
- size_t len, htmllen;
- FILE *htmlfp;
+ size_t len;
snprintf(spec, sizeof(spec), "HEAD:%s", readme);
if (git_revparse_single(&obj, repo, spec))
@@ -1158,15 +1220,14 @@ writereadme(FILE *fp)
s = git_blob_rawcontent((git_blob *)obj);
len = git_blob_rawsize((git_blob *)obj);
+ /* rendered to memory first, to rewrite links and image sources */
+ if ((ext = strrchr(readme, '.')) && !strcmp(ext, ".md"))
+ html = markdownhtml(s, len);
+ else if (ext && !strcmp(ext, ".org"))
+ html = orghtml(s, len);
+
fputs("<div id=\"readme\">\n", fp);
- if ((ext = strrchr(readme, '.')) && !strcmp(ext, ".md")) {
- /* render to memory first, to rewrite image sources */
- if (!(htmlfp = open_memstream(&html, &htmllen)))
- err(1, "open_memstream");
- if (md_html(s, len, writemarkdown, htmlfp, MD_DIALECT_GITHUB, 0))
- errx(1, "md_html: '%s'", readme);
- if (fclose(htmlfp))
- err(1, "fclose: '%s'", readme);
+ if (html) {
writereadmehtml(fp, html);
free(html);
} else {
diff --git a/style.css b/style.css
@@ -289,3 +289,9 @@ pre a.d {
#readme th {
background: var(--row);
}
+
+/* checklists: ul.task-list from pandoc, li.task-list-item from md4c */
+#readme .task-list,
+#readme .task-list-item {
+ list-style: none;
+}