stagit-gopher

static git page generator for gopher
git clone git://git.2f30.org/stagit-gopher.git
Log | Files | Refs | README | LICENSE

commit e13c9e5eec3eca1670477033fa276e9b48e78f58
parent e95a7d9de8805d9264b1d7cdcf43f15d2ec6b49c
Author: Hiltjo Posthuma <hiltjo@codemadness.org>
Date:   Wed, 14 Jun 2017 20:33:16 +0200

align widechars, optimize space a bit for 80x25, fix mailto: link

Diffstat:
TODO | 3++-
stagit-index.c | 40+++++++++++++++++++++++++++++++++++-----
stagit.c | 56++++++++++++++++++++++++++++++++++++++++++--------------
3 files changed, 79 insertions(+), 20 deletions(-)

diff --git a/TODO b/TODO @@ -1,6 +1,7 @@ gopher: -- align UTF-8 characters (and wide?). +- cleanup code: escaping. +? printutf8pad: print '...' when truncated? - update documentation: - document new gopher-specific options. diff --git a/stagit-index.c b/stagit-index.c @@ -8,6 +8,7 @@ #include <stdlib.h> #include <string.h> #include <unistd.h> +#include <wchar.h> #include <git2.h> @@ -25,6 +26,31 @@ static char owner[255]; #define pledge(p1,p2) 0 #endif +#define ISUTF8(c) (((c) & 0xc0) != 0x80) + +/* print `len' columns of characters. If string is shorter pad the rest + * with characters `pad`. */ +void +printutf8pad(FILE *fp, const char *s, size_t len, int pad) +{ + wchar_t w; + size_t n = 0, i; + int r; + + for (i = 0; *s && n < len; i++, s++) { + if (ISUTF8(*s)) { + if ((r = mbtowc(&w, s, 4)) == -1) + break; + if ((r = wcwidth(w)) == -1) + r = 1; + n += (size_t)r; + } + putc(*s, fp); + } + for (; n < len; n++) + putc(pad, fp); +} + void trim(char *buf, size_t bufsiz, const char *src) { @@ -87,9 +113,9 @@ writeheader(FILE *fp) trim(buf, sizeof(buf), description); if (buf[0] == 't') fputc('t', fp); - fprintf(fp, "%s\n", buf); + fprintf(fp, "%s\n\n", buf); - fprintf(fp, "%-25.25s ", "Name"); + fprintf(fp, "%-20.20s ", "Name"); fprintf(fp, "%-50.50s ", "Description"); fprintf(fp, "%-25.25s ", "Owner"); fprintf(fp, "%-16.16s\n", "Last commit"); @@ -131,12 +157,16 @@ writelog(FILE *fp) if (!strcmp(p, ".git")) *p = '\0'; + fputs("[1|", fp); trim(buf, sizeof(buf), stripped_name); - fprintf(fp, "[1|%-25.25s ", buf); + printutf8pad(fp, buf, 20, ' '); + fputs(" ", fp); trim(buf, sizeof(buf), description); - fprintf(fp, "%-50.50s ", buf); + printutf8pad(fp, buf, 50, ' '); + fputs(" ", fp); trim(buf, sizeof(buf), owner); - fprintf(fp, "%-25.25s ", buf); + printutf8pad(fp, buf, 25, ' '); + fputs(" ", fp); if (author) printtimeshort(fp, &(author->when)); trim(buf, sizeof(buf), stripped_name); diff --git a/stagit.c b/stagit.c @@ -9,6 +9,7 @@ #include <stdlib.h> #include <string.h> #include <unistd.h> +#include <wchar.h> #include <git2.h> @@ -67,6 +68,31 @@ static const char *cachefile; #define pledge(p1,p2) 0 #endif +#define ISUTF8(c) (((c) & 0xc0) != 0x80) + +/* print `len' columns of characters. If string is shorter pad the rest + * with characters `pad`. */ +void +printutf8pad(FILE *fp, const char *s, size_t len, int pad) +{ + wchar_t w; + size_t n = 0, i; + int r; + + for (i = 0; *s && n < len; i++, s++) { + if (ISUTF8(*s)) { + if ((r = mbtowc(&w, s, 4)) == -1) + break; + if ((r = wcwidth(w)) == -1) + r = 1; + n += (size_t)r; + } + putc(*s, fp); + } + for (; n < len; n++) + putc(pad, fp); +} + void joinpath(char *buf, size_t bufsiz, const char *path, const char *path2) { @@ -420,7 +446,7 @@ writeheader(FILE *fp, const char *title) fprintf(fp, "[1|README|%sfile/README.gph|server|port]\n", relpath); if (haslicense) fprintf(fp, "[1|LICENSE|%sfile/LICENSE.gph|server|port]\n", relpath); - fputs("===\n", fp); + fputs("---\n", fp); } void @@ -432,7 +458,7 @@ int writeblobgph(FILE *fp, const git_blob *blob) { size_t n = 0, i, j, prev; - const char *nfmt = "%8d "; + const char *nfmt = "%6d "; const char *s = git_blob_rawcontent(blob); git_off_t len = git_blob_rawsize(blob); @@ -479,12 +505,11 @@ printcommit(FILE *fp, struct commitinfo *ci) ci->parentoid, relpath, ci->parentoid); if (ci->author) { - /* TODO: fix author email link to redirect as mailto: */ - fputs("[1|Author: ", fp); + fputs("[h|Author: ", fp); gphlink(fp, ci->author->name, strlen(ci->author->name)); fputs(" <", fp); gphlink(fp, ci->author->email, strlen(ci->author->email)); - fputs(">|mailto:", fp); + fputs(">|URL:mailto:", fp); gphlink(fp, ci->author->email, strlen(ci->author->email)); fputs("|server|port]\n", fp); fputs("Date: ", fp); @@ -556,7 +581,7 @@ printshowfile(FILE *fp, struct commitinfo *ci) ci->addcount, ci->addcount == 1 ? "" : "s", ci->delcount, ci->delcount == 1 ? "" : "s"); - fputs("===\n", fp); + fputs("---\n", fp); for (i = 0; i < ci->ndeltas; i++) { patch = ci->deltas[i]->patch; @@ -605,12 +630,12 @@ writelogline(FILE *fp, struct commitinfo *ci) fputs(" ", fp); if (ci->summary) { trim(buf, sizeof(buf), ci->summary); - fprintf(fp, "%-50.50s", buf); + printutf8pad(fp, buf, 50, ' '); } fputs(" ", fp); if (ci->author) { trim(buf, sizeof(buf), ci->author->name); - fprintf(fp, "%-25.25s", buf); + printutf8pad(fp, buf, 25, ' '); } fprintf(fp, " %5zu", ci->filecount); fprintf(fp, " %5zu+", ci->addcount); @@ -766,7 +791,7 @@ writeblob(git_object *obj, const char *fpath, const char *filename, git_off_t fi writeheader(fp, filename); gphtext(fp, filename, strlen(filename)); fprintf(fp, " (%juB)\n", (uintmax_t)filesize); - fputs("===\n", fp); + fputs("---\n", fp); if (git_blob_is_binary((git_blob *)obj)) { fputs("Binary file.\n", fp); @@ -872,7 +897,8 @@ writefilestree(FILE *fp, git_tree *tree, const char *path) fputs(filemode(git_tree_entry_filemode(entry)), fp); fputs(" ", fp); trim(buf, sizeof(buf), entrypath); - fprintf(fp, "%-50.50s ", buf); + printutf8pad(fp, buf, 50, ' '); + fputs(" ", fp); if (lc > 0) fprintf(fp, "%7dL", lc); else @@ -882,7 +908,8 @@ writefilestree(FILE *fp, git_tree *tree, const char *path) git_object_free(obj); } else if (!git_submodule_lookup(&module, repo, entryname)) { fputs("[1|m--------- ", fp); - gphlink(fp, entrypath, strlen(entrypath)); + trim(buf, sizeof(buf), entrypath); + printutf8pad(fp, buf, 50, ' '); fprintf(fp, "|%sfile/.gitmodules.gph|server|port]\n", relpath); /* NOTE: linecount omitted */ git_submodule_free(module); @@ -983,7 +1010,7 @@ writerefs(FILE *fp) /* print header if it has an entry (first). */ if (++count == 1) { fprintf(fp, "%s\n", titles[j]); - fprintf(fp, " %-25.25s", "Name"); + fprintf(fp, " %-20.20s", "Name"); fprintf(fp, " %-16.16s", "Last commit date"); fprintf(fp, " %-25.25s\n", "Author"); } @@ -992,13 +1019,14 @@ writerefs(FILE *fp) fputs(" ", fp); trim(buf, sizeof(buf), name); - fprintf(fp, "%-25.25s ", name); + printutf8pad(fp, buf, 20, ' '); + fputs(" ", fp); if (ci->author) printtimeshort(fp, &(ci->author->when)); fputs(" ", fp); if (ci->author) { trim(buf, sizeof(buf), ci->author->name); - fprintf(fp, "%-25.25s\n", buf); + printutf8pad(fp, buf, 25, ' '); } fputs("\n", fp);