ctucx.git: stagit

ctucx' stagit fork

commit 64adeb03d75a920daee7442781ea1b3b1d811ef8
parent de33eb0c24e9b6b9494c882936da539e8a8a2369
Author: Oscar Benedito <oscar@oscarbenedito.com>
Date: Wed, 8 Dec 2021 18:29:57 +0100

Merge tag '1.0'
7 files changed, 94 insertions(+), 29 deletions(-)
M
Makefile
|
2
+-
M
README.md
|
2
+-
M
example_create.sh
|
2
+-
M
stagit-index.1
|
7
++++++-
M
stagit-index.c
|
24
+++++++++++++++++++++++-
M
stagit.1
|
13
+++++++++++--
M
stagit.c
|
73
+++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
diff --git a/Makefile b/Makefile
@@ -1,7 +1,7 @@
 .POSIX:
 
 NAME = stagit
-VERSION = 0.9.6
+VERSION = 1.0
 
 # paths
 PREFIX = /usr/local
diff --git a/README.md b/README.md
@@ -90,7 +90,7 @@ Script:
 	}'
 
 
-Set clone url for a directory of repos
+Set clone URL for a directory of repos
 --------------------------------------
 
 	#!/bin/sh
diff --git a/example_create.sh b/example_create.sh
@@ -7,7 +7,7 @@
 #   is included.
 # - modify the categories in the for loop with your own.
 #
-# - write clone url, for example "git://git.codemadness.org/dir" to the "url"
+# - write clone URL, for example "git://git.codemadness.org/dir" to the "url"
 #   file for each repo.
 # - write owner of repo to the "owner" file.
 # - write description in "description" file.
diff --git a/stagit-index.1 b/stagit-index.1
@@ -1,4 +1,4 @@
-.Dd December 26, 2015
+.Dd August 2, 2021
 .Dt STAGIT-INDEX 1
 .Os
 .Sh NAME

@@ -36,6 +36,11 @@ square logo.
 .It style.css
 CSS stylesheet.
 .El
+.Sh EXAMPLES
+.Bd -literal
+cd htmlroot
+stagit-index path/to/gitrepo1 path/to/gitrepo2 > index.html
+.Ed
 .Sh SEE ALSO
 .Xr stagit 1
 .Sh AUTHORS
diff --git a/stagit-index.c b/stagit-index.c
@@ -29,6 +29,28 @@ joinpath(char *buf, size_t bufsiz, const char *path, const char *path2)
 			path, path[0] && path[strlen(path) - 1] != '/' ? "/" : "", path2);
 }
 
+/* Percent-encode, see RFC3986 section 2.1. */
+void
+percentencode(FILE *fp, const char *s, size_t len)
+{
+	static char tab[] = "0123456789ABCDEF";
+	unsigned char uc;
+	size_t i;
+
+	for (i = 0; *s && i < len; s++, i++) {
+		uc = *s;
+		/* NOTE: do not encode '/' for paths */
+		if (uc < '/' || uc >= 127 || (uc >= ':' && uc <= '@') ||
+		    uc == '[' || uc == ']') {
+			putc('%', fp);
+			putc(tab[(uc >> 4) & 0x0f], fp);
+			putc(tab[uc & 0x0f], fp);
+		} else {
+			putc(uc, fp);
+		}
+	}
+}
+
 /* Escape characters below as HTML 2.0 / XML 1.0. */
 void
 xmlencode(FILE *fp, const char *s, size_t len)

@@ -122,7 +144,7 @@ writelog(FILE *fp)
 			*p = '\0';
 
 	fputs("<tr class=\"repo\"><td><a href=\"", fp);
-	xmlencode(fp, stripped_name, strlen(stripped_name));
+	percentencode(fp, stripped_name, strlen(stripped_name));
 	fputs("/\">", fp);
 	xmlencode(fp, stripped_name, strlen(stripped_name));
 	fputs("</a></td><td>", fp);
diff --git a/stagit.1 b/stagit.1
@@ -1,4 +1,4 @@
-.Dd May 18, 2021
+.Dd August 2, 2021
 .Dt STAGIT 1
 .Os
 .Sh NAME

@@ -92,7 +92,7 @@ description
 .It .git/owner or owner (bare repo).
 owner of repository
 .It .git/url or url (bare repo).
-primary clone url of the repository, for example:
+primary clone URL of the repository, for example:
 git://git.codemadness.org/stagit
 .El
 .Pp

@@ -110,6 +110,15 @@ CSS stylesheet.
 .El
 .Sh EXIT STATUS
 .Ex -std
+.Sh EXAMPLES
+.Bd -literal
+mkdir -p htmlroot/htmlrepo1 && cd htmlroot/htmlrepo1
+stagit path/to/gitrepo1
+# repeat for other repositories.
+.Ed
+.Pp
+To update the HTML files when the repository is changed a git post-receive hook
+can be used, see the file example_post-receive.sh for an example.
 .Sh SEE ALSO
 .Xr stagit-index 1
 .Sh AUTHORS
diff --git a/stagit.c b/stagit.c
@@ -74,7 +74,7 @@ static char *readmefiles[] = { "HEAD:README", "HEAD:README.md" };
 static char *readme;
 static char *contributefiles[] = { "HEAD:CONTRIBUTING", "HEAD:CONTRIBUTING.md" };
 static char *contribute;
-static long long nlogcommits = -1; /* < 0 indicates not used */
+static long long nlogcommits = -1; /* -1 indicates not used */
 
 /* cache */
 static git_oid lastoid;

@@ -362,6 +362,28 @@ efopen(const char *filename, const char *flags)
 	return fp;
 }
 
+/* Percent-encode, see RFC3986 section 2.1. */
+void
+percentencode(FILE *fp, const char *s, size_t len)
+{
+	static char tab[] = "0123456789ABCDEF";
+	unsigned char uc;
+	size_t i;
+
+	for (i = 0; *s && i < len; s++, i++) {
+		uc = *s;
+		/* NOTE: do not encode '/' for paths */
+		if (uc < '/' || uc >= 127 || (uc >= ':' && uc <= '@') ||
+		    uc == '[' || uc == ']') {
+			putc('%', fp);
+			putc(tab[(uc >> 4) & 0x0f], fp);
+			putc(tab[uc & 0x0f], fp);
+		} else {
+			putc(uc, fp);
+		}
+	}
+}
+
 /* Escape characters below as HTML 2.0 / XML 1.0. */
 void
 xmlencode(FILE *fp, const char *s, size_t len)

@@ -498,10 +520,11 @@ writeheader(FILE *fp, const char *title)
 	xmlencode(fp, description, strlen(description));
 	fprintf(fp, "</title>\n<link rel=\"icon\" type=\"image/svg+xml\" href=\"../%slogo.svg\" />\n", relpath);
 	fprintf(fp, "<link rel=\"alternate icon\" href=\"../%sfavicon.ico\" />\n", relpath);
-	fprintf(fp, "<link rel=\"alternate\" type=\"application/atom+xml\" title=\"%s Atom Feed\" href=\"%satom.xml\" />\n",
-	        name, relpath);
-	fprintf(fp, "<link rel=\"alternate\" type=\"application/atom+xml\" title=\"%s Atom Feed (tags)\" href=\"%stags.xml\" />\n",
-	        name, relpath);
+	fputs("<link rel=\"alternate\" type=\"application/atom+xml\" title=\"", fp);
+	xmlencode(fp, name, strlen(name));
+	fprintf(fp, " Atom Feed\" href=\"%satom.xml\" />\n", relpath);
+	fputs("<link rel=\"alternate\" type=\"application/atom+xml\" title=\"", fp);
+	fprintf(fp, " Atom Feed (tags)\" href=\"%stags.xml\" />\n", relpath);
 	fprintf(fp, "<link rel=\"stylesheet\" type=\"text/css\" href=\"../%sstyle.css\" />\n", relpath);
 	fputs("</head>\n<body>\n<div id=\"head\"><table><tr><td>", fp);
 	fprintf(fp, "<a class=\"logo\" href=\"../%s\"><img src=\"../%slogo.svg\" alt=\"\" width=\"32\" height=\"32\" /></a>",

@@ -513,7 +536,7 @@ writeheader(FILE *fp, const char *title)
 	fputs("</span>\n", fp);
 	if (cloneurl[0]) {
 		fputs("<p class=\"url\">git clone <a href=\"", fp);
-		xmlencode(fp, cloneurl, strlen(cloneurl));
+		xmlencode(fp, cloneurl, strlen(cloneurl)); /* not percent-encoded */
 		fputs("\">", fp);
 		xmlencode(fp, cloneurl, strlen(cloneurl));
 		fputs("</a></p>\n", fp);

@@ -558,14 +581,15 @@ writeblobhtml(FILE *fp, const git_blob *blob)
 				continue;
 			n++;
 			fprintf(fp, nfmt, n, n, n);
-			xmlencode(fp, &s[prev], i - prev + 1);
+			xmlencodeline(fp, &s[prev], i - prev + 1);
+			putc('\n', fp);
 			prev = i + 1;
 		}
 		/* trailing data */
 		if ((len - prev) > 0) {
 			n++;
 			fprintf(fp, nfmt, n, n, n);
-			xmlencode(fp, &s[prev], len - prev);
+			xmlencodeline(fp, &s[prev], len - prev);
 		}
 	}
 

@@ -588,7 +612,7 @@ printcommit(FILE *fp, struct commitinfo *ci)
 		fputs("<b>Author:</b> ", fp);
 		xmlencode(fp, ci->author->name, strlen(ci->author->name));
 		fputs(" &lt;<a href=\"mailto:", fp);
-		xmlencode(fp, ci->author->email, strlen(ci->author->email));
+		xmlencode(fp, ci->author->email, strlen(ci->author->email)); /* not percent-encoded */
 		fputs("\">", fp);
 		xmlencode(fp, ci->author->email, strlen(ci->author->email));
 		fputs("</a>&gt;\n<b>Date:</b>   ", fp);

@@ -683,11 +707,11 @@ printshowfile(FILE *fp, struct commitinfo *ci)
 		patch = ci->deltas[i]->patch;
 		delta = git_patch_get_delta(patch);
 		fprintf(fp, "<b>diff --git a/<a id=\"h%zu\" href=\"%sfile/", i, relpath);
-		xmlencode(fp, delta->old_file.path, strlen(delta->old_file.path));
+		percentencode(fp, delta->old_file.path, strlen(delta->old_file.path));
 		fputs(".html\">", fp);
 		xmlencode(fp, delta->old_file.path, strlen(delta->old_file.path));
 		fprintf(fp, "</a> b/<a href=\"%sfile/", relpath);
-		xmlencode(fp, delta->new_file.path, strlen(delta->new_file.path));
+		percentencode(fp, delta->new_file.path, strlen(delta->new_file.path));
 		fprintf(fp, ".html\">");
 		xmlencode(fp, delta->new_file.path, strlen(delta->new_file.path));
 		fprintf(fp, "</a></b>\n");

@@ -759,6 +783,7 @@ writelog(FILE *fp, const git_oid *oid)
 	git_oid id;
 	char path[PATH_MAX], oidstr[GIT_OID_HEXSZ + 1];
 	FILE *fpfile;
+	size_t remcommits = 0;
 	int r;
 
 	git_revwalk_new(&w, repo);

@@ -778,8 +803,11 @@ writelog(FILE *fp, const git_oid *oid)
 
 		/* optimization: if there are no log lines to write and
 		   the commit file already exists: skip the diffstat */
-		if (!nlogcommits && !r)
-			continue;
+		if (!nlogcommits) {
+			remcommits++;
+			if (!r)
+				continue;
+		}
 
 		if (!(ci = commitinfo_getbyoid(&id)))
 			break;

@@ -787,15 +815,10 @@ writelog(FILE *fp, const git_oid *oid)
 		if (commitinfo_getstats(ci) == -1)
 			goto err;
 
-		if (nlogcommits < 0) {
-			writelogline(fp, ci);
-		} else if (nlogcommits > 0) {
+		if (nlogcommits != 0) {
 			writelogline(fp, ci);
-			nlogcommits--;
-			if (!nlogcommits && ci->parentoid[0])
-				fputs("<tr><td></td><td colspan=\"5\">"
-				      "More commits remaining [...]</td>"
-				      "</tr>\n", fp);
+			if (nlogcommits > 0)
+				nlogcommits--;
 		}
 
 		if (cachefile)

@@ -817,6 +840,12 @@ err:
 	}
 	git_revwalk_free(w);
 
+	if (nlogcommits == 0 && remcommits != 0) {
+		fprintf(fp, "<tr><td></td><td colspan=\"5\">"
+		        "%zu more commits remaining, fetch the repository"
+		        "</td></tr>\n", remcommits);
+	}
+
 	relpath = "";
 
 	return 0;

@@ -1135,7 +1164,7 @@ writefilestree(FILE *fp, git_tree *tree, const char *path)
 			if (git_object_type(obj) == GIT_OBJ_TREE)
 				fputs("class=\"dir\" ", fp);
 			fprintf(fp, "href=\"%s", relpath);
-			xmlencode(fp, filepath, strlen(filepath));
+			percentencode(fp, filepath, strlen(filepath));
 			fputs("\">", fp);
 			xmlencode(fp, entryname, strlen(entryname));
 			fputs("</a></td><td class=\"num\" align=\"right\">", fp);