stagit-gopher

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

commit e46c746c435114ae3e7541ca93ffa7aacf4aaff3
parent daa814e5c59ef7dcadfe779b46bd305e0d93f7a1
Author: Hiltjo Posthuma <hiltjo@codemadness.org>
Date:   Fri, 17 Nov 2017 16:06:51 +0100

fixes for escaping and printing

- if the index or project description is empty don't print an empty line.
- escape | in gph links.
- when a column is not set / empty print it aligned.
- pad text, then print it escaped.
- print left-aligned headers of last column in a simpler way.

Diffstat:
stagit-gopher-index.c | 107++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------
stagit-gopher.c | 130++++++++++++++++++++++++++++++++++++-------------------------------------------
2 files changed, 127 insertions(+), 110 deletions(-)

diff --git a/stagit-gopher-index.c b/stagit-gopher-index.c @@ -25,17 +25,17 @@ static char *name = ""; #define pledge(p1,p2) 0 #endif -/* print `len' columns of characters. If string is shorter pad the rest +/* format `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) +int +utf8pad(char *buf, size_t bufsiz, const char *s, size_t len, int pad) { wchar_t w; - size_t col = 0, i, slen; + size_t col = 0, i, slen, siz = 0; int rl, wc; if (!len) - return; + return -1; slen = strlen(s); for (i = 0; i < slen && col < len + 1; i += rl) { @@ -43,43 +43,74 @@ printutf8pad(FILE *fp, const char *s, size_t len, int pad) break; if ((wc = wcwidth(w)) == -1) wc = 1; - col += (size_t)wc; + col += wc; if (col >= len && s[i + rl]) { - fputs("\xe2\x80\xa6", fp); + if (siz + 4 >= bufsiz) + return -1; + memcpy(&buf[siz], "\xe2\x80\xa6", 4); + return 0; + } + if (siz + rl + 1 >= bufsiz) + return -1; + memcpy(&buf[siz], &s[i], rl); + siz += rl; + buf[siz] = '\0'; + } + + len -= col; + if (siz + len + 1 >= bufsiz) + return -1; + memset(&buf[siz], pad, len); + siz += len; + buf[siz] = '\0'; + + return 0; +} + +/* Escape characters in text in geomyidae .gph format, + newlines are ignored */ +void +gphtext(FILE *fp, const char *s, size_t len) +{ + size_t i; + + for (i = 0; *s && i < len; i++) { + switch (s[i]) { + case '\r': /* ignore CR */ + case '\n': /* ignore LF */ + break; + case '\t': + fputs(" ", fp); + break; + default: + fputc(s[i], fp); break; } - fwrite(&s[i], 1, rl, fp); } - for (; col < len; col++) - putc(pad, fp); } +/* Escape characters in links in geomyidae .gph format */ void -trim(char *buf, size_t bufsiz, const char *src) +gphlink(FILE *fp, const char *s, size_t len) { - size_t d = 0, i, len, s; + size_t i; - len = strlen(src); - for (s = 0; s < len && d < bufsiz - 1; s++) { - switch (src[s]) { + for (i = 0; *s && i < len; i++) { + switch (s[i]) { + case '\r': /* ignore CR */ + case '\n': /* ignore LF */ + break; case '\t': - if (d + 8 >= bufsiz - 1) - goto end; - for (i = 0; i < 8; i++) - buf[d++] = ' '; + fputs(" ", fp); break; - case '|': - case '\n': - case '\r': - buf[d++] = ' '; + case '|': /* escape separators */ + fputs("\\|", fp); break; default: - buf[d++] = src[s]; + fputc(s[i], fp); break; } } -end: - buf[d] = '\0'; } void @@ -111,16 +142,15 @@ printtimeshort(FILE *fp, const git_time *intime) void writeheader(FILE *fp) { - char buf[256]; - - trim(buf, sizeof(buf), description); - if (buf[0] == 't' || buf[0] == '[') - fputc('t', fp); - fprintf(fp, "%s\n\n", buf); + if (description[0]) { + putchar('t'); + gphtext(fp, description, strlen(description)); + fputs("\n\n", fp); + } fprintf(fp, "%-20.20s ", "Name"); fprintf(fp, "%-50.50s ", "Description"); - fprintf(fp, "%-16.16s\n", "Last commit"); + fprintf(fp, "%s\n", "Last commit"); } int @@ -155,16 +185,15 @@ writelog(FILE *fp) *p = '\0'; fputs("[1|", fp); - trim(buf, sizeof(buf), stripped_name); - printutf8pad(fp, buf, 20, ' '); + utf8pad(buf, sizeof(buf), stripped_name, 20, ' '); + gphlink(fp, buf, strlen(buf)); fputs(" ", fp); - trim(buf, sizeof(buf), description); - printutf8pad(fp, buf, 50, ' '); + utf8pad(buf, sizeof(buf), description, 50, ' '); + gphlink(fp, buf, strlen(buf)); fputs(" ", fp); if (author) printtimeshort(fp, &(author->when)); - trim(buf, sizeof(buf), stripped_name); - fprintf(fp, "|%s/%s/log.gph|server|port]\n", relpath, buf); + fprintf(fp, "|%s/%s/log.gph|server|port]\n", relpath, stripped_name); git_commit_free(commit); err: diff --git a/stagit-gopher.c b/stagit-gopher.c @@ -69,17 +69,17 @@ static const char *cachefile; #define pledge(p1,p2) 0 #endif -/* print `len' columns of characters. If string is shorter pad the rest +/* format `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) +int +utf8pad(char *buf, size_t bufsiz, const char *s, size_t len, int pad) { wchar_t w; - size_t col = 0, i, slen; + size_t col = 0, i, slen, siz = 0; int rl, wc; if (!len) - return; + return -1; slen = strlen(s); for (i = 0; i < slen && col < len + 1; i += rl) { @@ -87,15 +87,28 @@ printutf8pad(FILE *fp, const char *s, size_t len, int pad) break; if ((wc = wcwidth(w)) == -1) wc = 1; - col += (size_t)wc; + col += wc; if (col >= len && s[i + rl]) { - fputs("\xe2\x80\xa6", fp); - break; + if (siz + 4 >= bufsiz) + return -1; + memcpy(&buf[siz], "\xe2\x80\xa6", 4); + return 0; } - fwrite(&s[i], 1, rl, fp); + if (siz + rl + 1 >= bufsiz) + return -1; + memcpy(&buf[siz], &s[i], rl); + siz += rl; + buf[siz] = '\0'; } - for (; col < len; col++) - putc(pad, fp); + + len -= col; + if (siz + len + 1 >= bufsiz) + return -1; + memset(&buf[siz], pad, len); + siz += len; + buf[siz] = '\0'; + + return 0; } void @@ -277,35 +290,6 @@ xmlencode(FILE *fp, const char *s, size_t len) } } -void -trim(char *buf, size_t bufsiz, const char *src) -{ - size_t d = 0, i, len, s; - - len = strlen(src); - for (s = 0; s < len && d < bufsiz - 1; s++) { - switch (src[s]) { - case '\t': - if (d + 8 >= bufsiz - 1) - goto end; - for (i = 0; i < 8; i++) - buf[d++] = ' '; - break; - case '\r': /* ignore CR */ - case '|': /* ignore separators here */ - break; - case '\n': - buf[d++] = ' '; - break; - default: - buf[d++] = src[s]; - break; - } - } -end: - buf[d] = '\0'; -} - /* Escape characters in text in geomyidae .gph format, with newlines */ void gphtextnl(FILE *fp, const char *s, size_t len) @@ -316,7 +300,7 @@ gphtextnl(FILE *fp, const char *s, size_t len) if (s[i] == '\n') n = 0; - /* escape 't' at the start of a line */ + /* escape with 't' at the start of a line */ if (!n && (s[i] == 't' || s[i] == '[')) { fputc('t', fp); n = 1; @@ -340,10 +324,15 @@ gphtext(FILE *fp, const char *s, size_t len) for (i = 0; *s && i < len; i++) { switch (s[i]) { - case '\r': - case '\n': break; - case '\t': fputs(" ", fp); break; - default: fputc(s[i], fp); + case '\r': /* ignore CR */ + case '\n': /* ignore LF */ + break; + case '\t': + fputs(" ", fp); + break; + default: + fputc(s[i], fp); + break; } } } @@ -356,16 +345,15 @@ gphlink(FILE *fp, const char *s, size_t len) for (i = 0; *s && i < len; i++) { switch (s[i]) { - case '\n': - /* in this context replace newline with space */ - fputc(' ', fp); - break; case '\r': /* ignore CR */ - case '|': /* ignore separators here */ + case '\n': /* ignore LF */ break; case '\t': fputs(" ", fp); break; + case '|': /* escape separators */ + fputs("\\|", fp); + break; default: fputc(s[i], fp); break; @@ -575,12 +563,12 @@ printshowfile(FILE *fp, struct commitinfo *ci) if (strcmp(delta->old_file.path, delta->new_file.path)) { snprintf(filename, sizeof(filename), "%s -> %s", delta->old_file.path, delta->new_file.path); - trim(buf, sizeof(buf), filename); + utf8pad(buf, sizeof(buf), filename, 35, ' '); } else { - trim(buf, sizeof(buf), delta->old_file.path); + utf8pad(buf, sizeof(buf), delta->old_file.path, 35, ' '); } fputs(" ", fp); - printutf8pad(fp, buf, 35, ' '); + gphtext(fp, buf, strlen(buf)); add = ci->deltas[i]->addcount; del = ci->deltas[i]->delcount; @@ -655,16 +643,14 @@ writelogline(FILE *fp, struct commitinfo *ci) fputs("[1|", fp); if (ci->author) printtimeshort(fp, &(ci->author->when)); + else + fputs(" ", fp); fputs(" ", fp); - if (ci->summary) { - trim(buf, sizeof(buf), ci->summary); - printutf8pad(fp, buf, 50, ' '); - } + utf8pad(buf, sizeof(buf), ci->summary ? ci->summary : "", 50, ' '); + gphlink(fp, buf, strlen(buf)); fputs(" ", fp); - if (ci->author) { - trim(buf, sizeof(buf), ci->author->name); - printutf8pad(fp, buf, 25, ' '); - } + utf8pad(buf, sizeof(buf), ci->author ? ci->author->name : "", 25, ' '); + gphlink(fp, buf, strlen(buf)); fprintf(fp, "|%s/commit/%s.gph", relpath, ci->oid); fputs("|server|port]\n", fp); } @@ -926,8 +912,8 @@ writefilestree(FILE *fp, git_tree *tree, const char *path) fputs("[1|", fp); fputs(filemode(git_tree_entry_filemode(entry)), fp); fputs(" ", fp); - trim(buf, sizeof(buf), entrypath); - printutf8pad(fp, buf, 50, ' '); + utf8pad(buf, sizeof(buf), entrypath, 50, ' '); + gphlink(fp, buf, strlen(buf)); fputs(" ", fp); if (lc > 0) fprintf(fp, "%7dL", lc); @@ -938,8 +924,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); - trim(buf, sizeof(buf), entrypath); - printutf8pad(fp, buf, 50, ' '); + utf8pad(buf, sizeof(buf), entrypath, 50, ' '); + gphlink(fp, buf, strlen(buf)); fprintf(fp, "|%s/file/.gitmodules.gph|server|port]\n", relpath); /* NOTE: linecount omitted */ git_submodule_free(module); @@ -1042,21 +1028,23 @@ writerefs(FILE *fp) fprintf(fp, "%s\n", titles[j]); fprintf(fp, " %-20.20s", "Name"); fprintf(fp, " %-16.16s", "Last commit date"); - fprintf(fp, " %-25.25s\n", "Author"); + fprintf(fp, " %s\n", "Author"); } name = git_reference_shorthand(r); fputs(" ", fp); - trim(buf, sizeof(buf), name); - printutf8pad(fp, buf, 20, ' '); + utf8pad(buf, sizeof(buf), name, 20, ' '); + gphlink(fp, buf, strlen(buf)); fputs(" ", fp); if (ci->author) printtimeshort(fp, &(ci->author->when)); + else + fputs(" ", fp); fputs(" ", fp); if (ci->author) { - trim(buf, sizeof(buf), ci->author->name); - printutf8pad(fp, buf, 25, ' '); + utf8pad(buf, sizeof(buf), ci->author->name, 25, ' '); + gphlink(fp, buf, strlen(buf)); } fputs("\n", fp); @@ -1204,7 +1192,7 @@ main(int argc, char *argv[]) fprintf(fp, "%-16.16s ", "Date"); fprintf(fp, "%-50.50s ", "Commit message"); - fprintf(fp, "%-25.25s\n", "Author"); + fprintf(fp, "%s\n", "Author"); if (cachefile) { /* read from cache file (does not need to exist) */