add urmoms-index, initial version - stagit-gopher - A git gopher frontend. (mir… | |
git clone git://bitreich.org/stagit-gopher/ git://enlrupgkhuxnvlhsf6lc3fziv5h2h… | |
Log | |
Files | |
Refs | |
Tags | |
README | |
LICENSE | |
--- | |
commit 7102cfd3f76ebf6f6caf774f2209000c22233d39 | |
parent 372fe8d86e621b09dadb893b2153f643a70cab7f | |
Author: Hiltjo Posthuma <[email protected]> | |
Date: Sun, 20 Dec 2015 17:11:25 +0100 | |
add urmoms-index, initial version | |
Diffstat: | |
M Makefile | 12 +++++++++--- | |
A urmoms-index.1 | 14 ++++++++++++++ | |
A urmoms-index.c | 319 +++++++++++++++++++++++++++++… | |
3 files changed, 342 insertions(+), 3 deletions(-) | |
--- | |
diff --git a/Makefile b/Makefile | |
@@ -3,11 +3,14 @@ include config.mk | |
NAME = urmoms | |
VERSION = 0.1 | |
SRC = \ | |
- urmoms.c | |
+ urmoms.c\ | |
+ urmoms-index.c | |
BIN = \ | |
- urmoms | |
+ urmoms\ | |
+ urmoms-index | |
MAN1 = \ | |
- urmoms.1 | |
+ urmoms.1\ | |
+ urmoms-index.1 | |
DOC = \ | |
LICENSE\ | |
README\ | |
@@ -42,6 +45,9 @@ config.h: | |
urmoms: urmoms.o | |
${CC} -o $@ urmoms.o ${LDFLAGS} | |
+urmoms-index: urmoms-index.o | |
+ ${CC} -o $@ urmoms-index.o ${LDFLAGS} | |
+ | |
clean: | |
rm -f ${BIN} ${OBJ} | |
diff --git a/urmoms-index.1 b/urmoms-index.1 | |
@@ -0,0 +1,14 @@ | |
+.Dd December 15, 2015 | |
+.Dt URMOMS-INDEX 1 | |
+.Os | |
+.Sh NAME | |
+.Nm urmoms-index | |
+.Nd static git page generator (repo list) | |
+.Sh SYNOPSIS | |
+.Nm | |
+.Op Ar repodir... | |
+.Sh DESCRIPTION | |
+.Nm | |
+is undocumented | |
+.Sh AUTHORS | |
+.An Hiltjo Posthuma Aq Mt [email protected] | |
diff --git a/urmoms-index.c b/urmoms-index.c | |
@@ -0,0 +1,319 @@ | |
+#include <sys/stat.h> | |
+ | |
+#include <err.h> | |
+#include <errno.h> | |
+#include <inttypes.h> | |
+#include <libgen.h> | |
+#include <limits.h> | |
+#include <stdio.h> | |
+#include <stdlib.h> | |
+#include <string.h> | |
+#include <unistd.h> | |
+ | |
+#include "config.h" | |
+#include "git2.h" | |
+ | |
+struct commitinfo { | |
+ const git_oid *id; | |
+ | |
+ char oid[GIT_OID_HEXSZ + 1]; | |
+ char parentoid[GIT_OID_HEXSZ + 1]; | |
+ | |
+ const git_signature *author; | |
+ const char *summary; | |
+ const char *msg; | |
+ | |
+ git_diff_stats *stats; | |
+ git_diff *diff; | |
+ git_commit *commit; | |
+ git_commit *parent; | |
+ git_tree *commit_tree; | |
+ git_tree *parent_tree; | |
+ | |
+ size_t addcount; | |
+ size_t delcount; | |
+ size_t filecount; | |
+}; | |
+ | |
+static git_repository *repo; | |
+ | |
+static const char *relpath = ""; | |
+static const char *repodir; | |
+ | |
+static char description[255] = "Repositories"; | |
+static char name[255]; | |
+static char owner[255]; | |
+ | |
+void | |
+commitinfo_free(struct commitinfo *ci) | |
+{ | |
+ if (!ci) | |
+ return; | |
+ | |
+ git_diff_stats_free(ci->stats); | |
+ git_diff_free(ci->diff); | |
+ git_tree_free(ci->commit_tree); | |
+ git_tree_free(ci->parent_tree); | |
+ git_commit_free(ci->commit); | |
+} | |
+ | |
+struct commitinfo * | |
+commitinfo_getbyoid(const git_oid *id) | |
+{ | |
+ struct commitinfo *ci; | |
+ git_diff_options opts; | |
+ int error; | |
+ | |
+ if (!(ci = calloc(1, sizeof(struct commitinfo)))) | |
+ err(1, "calloc"); | |
+ | |
+ ci->id = id; | |
+ if (git_commit_lookup(&(ci->commit), repo, id)) | |
+ goto err; | |
+ | |
+ /* TODO: show tags when commit has it */ | |
+ git_oid_tostr(ci->oid, sizeof(ci->oid), git_commit_id(ci->commit)); | |
+ git_oid_tostr(ci->parentoid, sizeof(ci->parentoid), git_commit_parent_… | |
+ | |
+ ci->author = git_commit_author(ci->commit); | |
+ ci->summary = git_commit_summary(ci->commit); | |
+ ci->msg = git_commit_message(ci->commit); | |
+ | |
+ if ((error = git_commit_tree(&(ci->commit_tree), ci->commit))) | |
+ goto err; /* TODO: handle error */ | |
+ if (!(error = git_commit_parent(&(ci->parent), ci->commit, 0))) { | |
+ if ((error = git_commit_tree(&(ci->parent_tree), ci->parent))) | |
+ goto err; | |
+ } else { | |
+ ci->parent = NULL; | |
+ ci->parent_tree = NULL; | |
+ } | |
+ | |
+ git_diff_init_options(&opts, GIT_DIFF_OPTIONS_VERSION); | |
+ opts.flags |= GIT_DIFF_DISABLE_PATHSPEC_MATCH; | |
+ if ((error = git_diff_tree_to_tree(&(ci->diff), repo, ci->parent_tree,… | |
+ goto err; | |
+ if (git_diff_get_stats(&(ci->stats), ci->diff)) | |
+ goto err; | |
+ | |
+ ci->addcount = git_diff_stats_insertions(ci->stats); | |
+ ci->delcount = git_diff_stats_deletions(ci->stats); | |
+ ci->filecount = git_diff_stats_files_changed(ci->stats); | |
+ | |
+ /* TODO: show tag when commit has it */ | |
+ | |
+ return ci; | |
+ | |
+err: | |
+ commitinfo_free(ci); | |
+ free(ci); | |
+ | |
+ return NULL; | |
+} | |
+ | |
+FILE * | |
+efopen(const char *name, const char *flags) | |
+{ | |
+ FILE *fp; | |
+ | |
+ if (!(fp = fopen(name, flags))) | |
+ err(1, "fopen"); | |
+ | |
+ return fp; | |
+} | |
+ | |
+/* Escape characters below as HTML 2.0 / XML 1.0. */ | |
+void | |
+xmlencode(FILE *fp, const char *s, size_t len) | |
+{ | |
+ size_t i; | |
+ | |
+ for (i = 0; *s && i < len; s++, i++) { | |
+ switch(*s) { | |
+ case '<': fputs("<", fp); break; | |
+ case '>': fputs(">", fp); break; | |
+ case '\'': fputs("'", fp); break; | |
+ case '&': fputs("&", fp); break; | |
+ case '"': fputs(""", fp); break; | |
+ default: fputc(*s, fp); | |
+ } | |
+ } | |
+} | |
+ | |
+/* Some implementations of basename(3) return a pointer to a static | |
+ * internal buffer (OpenBSD). Others modify the contents of `path` (POSIX). | |
+ * This is a wrapper function that is compatible with both versions. | |
+ * The program will error out if basename(3) failed, this can only happen | |
+ * with the OpenBSD version. */ | |
+char * | |
+xbasename(const char *path) | |
+{ | |
+ char *p, *b; | |
+ | |
+ if (!(p = strdup(path))) | |
+ err(1, "strdup"); | |
+ if (!(b = basename(p))) | |
+ err(1, "basename"); | |
+ if (!(b = strdup(b))) | |
+ err(1, "strdup"); | |
+ free(p); | |
+ | |
+ return b; | |
+} | |
+ | |
+void | |
+printtimeformat(FILE *fp, const git_time *intime, const char *fmt) | |
+{ | |
+ struct tm *intm; | |
+ time_t t; | |
+ char out[32]; | |
+ | |
+ t = (time_t) intime->time + (intime->offset * 60); | |
+ intm = gmtime(&t); | |
+ strftime(out, sizeof(out), fmt, intm); | |
+ fputs(out, fp); | |
+} | |
+ | |
+void | |
+printtimeshort(FILE *fp, const git_time *intime) | |
+{ | |
+ printtimeformat(fp, intime, "%Y-%m-%d %H:%M"); | |
+} | |
+ | |
+int | |
+writeheader(FILE *fp) | |
+{ | |
+ fputs("<!DOCTYPE HTML>" | |
+ "<html dir=\"ltr\" lang=\"en\">\n<head>\n" | |
+ "<meta http-equiv=\"Content-Type\" content=\"text/html; charse… | |
+ "<meta http-equiv=\"Content-Language\" content=\"en\" />\n<tit… | |
+ xmlencode(fp, description, strlen(description)); | |
+ fprintf(fp, "</title>\n<link rel=\"icon\" type=\"image/png\" href=\"%s… | |
+ fprintf(fp, "<link rel=\"stylesheet\" type=\"text/css\" href=\"%sstyle… | |
+ fputs("</head>\n<body>\n\n", fp); | |
+ fprintf(fp, "<table>\n<tr><td><img src=\"%slogo.png\" alt=\"\" width=\… | |
+ "<td><h1>%s</h1><span class=\"desc\">%s</span></td></tr><tr><t… | |
+ relpath, name, description); | |
+ fputs("</td></tr>\n</table>\n<hr/><div id=\"content\">\n" | |
+ "<table><thead>\n" | |
+ "<tr><td>Name</td><td>Description</td><td>Owner</td><td>Last com… | |
+ "</thead><tbody>\n", fp); | |
+ | |
+ return 0; | |
+} | |
+ | |
+int | |
+writefooter(FILE *fp) | |
+{ | |
+ return !fputs("</tbody></table></div></body>\n</html>", fp); | |
+} | |
+ | |
+int | |
+writelog(FILE *fp) | |
+{ | |
+ struct commitinfo *ci; | |
+ git_revwalk *w = NULL; | |
+ git_oid id; | |
+ int ret = 0; | |
+ | |
+ git_revwalk_new(&w, repo); | |
+ git_revwalk_push_head(w); | |
+ git_revwalk_sorting(w, GIT_SORT_TIME); | |
+ git_revwalk_simplify_first_parent(w); | |
+ | |
+ if (git_revwalk_next(&id, w) || | |
+ !(ci = commitinfo_getbyoid(&id))) { | |
+ ret = -1; | |
+ goto err; | |
+ } | |
+ | |
+ fputs("<tr><td><a href=\"", fp); | |
+ xmlencode(fp, name, strlen(name)); | |
+ fputs("/log.html\">", fp); | |
+ xmlencode(fp, name, strlen(name)); | |
+ fputs("</a></td><td>", fp); | |
+ xmlencode(fp, description, strlen(description)); | |
+ fputs("</td><td>", fp); | |
+ xmlencode(fp, owner, strlen(owner)); | |
+ fputs("</td><td>", fp); | |
+ if (ci->author) | |
+ printtimeshort(fp, &(ci->author->when)); | |
+ fputs("</td></tr>", fp); | |
+ | |
+err: | |
+ git_revwalk_free(w); | |
+ | |
+ return ret; | |
+} | |
+ | |
+int | |
+main(int argc, char *argv[]) | |
+{ | |
+ const git_error *e = NULL; | |
+ FILE *fp; | |
+ char path[PATH_MAX], *p; | |
+ int status; | |
+ size_t i; | |
+ | |
+ if (argc < 2) { | |
+ fprintf(stderr, "%s [repodir...]\n", argv[0]); | |
+ return 1; | |
+ } | |
+ git_libgit2_init(); | |
+ | |
+ writeheader(stdout); | |
+ | |
+ for (i = 1; i < argc; i++) { | |
+ repodir = argv[i]; | |
+ | |
+ if ((status = git_repository_open_ext(&repo, repodir, | |
+ GIT_REPOSITORY_OPEN_NO_SEARCH, NULL)) < 0) { | |
+ e = giterr_last(); | |
+ fprintf(stderr, "error %d/%d: %s\n", status, e->klass,… | |
+ return status; | |
+ } | |
+ | |
+ /* use directory name as name */ | |
+ p = xbasename(repodir); | |
+ snprintf(name, sizeof(name), "%s", p); | |
+ free(p); | |
+ | |
+ /* read description or .git/description */ | |
+ description[0] = '\0'; | |
+ snprintf(path, sizeof(path), "%s%s%s", | |
+ repodir, repodir[strlen(repodir)] == '/' ? "" : "/", "… | |
+ if (!(fp = fopen(path, "r"))) { | |
+ snprintf(path, sizeof(path), "%s%s%s", | |
+ repodir, repodir[strlen(repodir)] == '/' ? "" … | |
+ fp = fopen(path, "r"); | |
+ } | |
+ if (fp) { | |
+ if (!fgets(description, sizeof(description), fp)) | |
+ description[0] = '\0'; | |
+ fclose(fp); | |
+ } | |
+ | |
+ /* read owner or .git/owner */ | |
+ owner[0] = '\0'; | |
+ snprintf(path, sizeof(path), "%s%s%s", | |
+ repodir, repodir[strlen(repodir)] == '/' ? "" : "/", "… | |
+ if (!(fp = fopen(path, "r"))) { | |
+ snprintf(path, sizeof(path), "%s%s%s", | |
+ repodir, repodir[strlen(repodir)] == '/' ? "" … | |
+ fp = fopen(path, "r"); | |
+ } | |
+ if (fp) { | |
+ if (!fgets(owner, sizeof(owner), fp)) | |
+ owner[0] = '\0'; | |
+ fclose(fp); | |
+ } | |
+ writelog(stdout); | |
+ } | |
+ writefooter(stdout); | |
+ | |
+ /* cleanup */ | |
+ git_repository_free(repo); | |
+ git_libgit2_shutdown(); | |
+ | |
+ return 0; | |
+} |