rename urmoms to stagit, improve documentation - stagit-gopher - A git gopher f… | |
git clone git://bitreich.org/stagit-gopher/ git://enlrupgkhuxnvlhsf6lc3fziv5h2h… | |
Log | |
Files | |
Refs | |
Tags | |
README | |
LICENSE | |
--- | |
commit 81dd454368b40eabf62fede213626d45512150a2 | |
parent 8c58750b636edc4ee36aff62d7c126f99b803dbb | |
Author: Hiltjo Posthuma <[email protected]> | |
Date: Sat, 26 Dec 2015 21:05:55 +0100 | |
rename urmoms to stagit, improve documentation | |
Diffstat: | |
M Makefile | 26 +++++++++++++------------- | |
M README | 2 +- | |
M TODO | 8 -------- | |
M example.sh | 4 ++-- | |
A stagit-index.1 | 37 +++++++++++++++++++++++++++++… | |
R urmoms-index.c -> stagit-index.c | 0 | |
A stagit.1 | 59 +++++++++++++++++++++++++++++… | |
D urmoms-index.1 | 27 --------------------------- | |
D urmoms.1 | 47 -----------------------------… | |
D urmoms.c | 852 -----------------------------… | |
10 files changed, 112 insertions(+), 950 deletions(-) | |
--- | |
diff --git a/Makefile b/Makefile | |
@@ -1,19 +1,19 @@ | |
include config.mk | |
-NAME = urmoms | |
+NAME = stagit | |
VERSION = 0.1 | |
SRC = \ | |
- urmoms.c\ | |
- urmoms-index.c | |
+ stagit.c\ | |
+ stagit-index.c | |
COMPATSRC = \ | |
strlcat.c\ | |
strlcpy.c | |
BIN = \ | |
- urmoms\ | |
- urmoms-index | |
+ stagit\ | |
+ stagit-index | |
MAN1 = \ | |
- urmoms.1\ | |
- urmoms-index.1 | |
+ stagit.1\ | |
+ stagit-index.1 | |
DOC = \ | |
LICENSE\ | |
README\ | |
@@ -35,9 +35,9 @@ dist: $(BIN) | |
logo.png style.css \ | |
release/${VERSION}/ | |
# make tarball | |
- rm -f urmoms-${VERSION}.tar.gz | |
+ rm -f stagit-${VERSION}.tar.gz | |
(cd release/${VERSION}; \ | |
- tar -czf ../../urmoms-${VERSION}.tar.gz .) | |
+ tar -czf ../../stagit-${VERSION}.tar.gz .) | |
${OBJ}: config.h config.mk ${HDR} | |
@@ -45,11 +45,11 @@ config.h: | |
@echo creating $@ from config.def.h | |
@cp config.def.h $@ | |
-urmoms: urmoms.o ${COMPATOBJ} | |
- ${CC} -o $@ urmoms.o ${COMPATOBJ} ${LDFLAGS} | |
+stagit: stagit.o ${COMPATOBJ} | |
+ ${CC} -o $@ stagit.o ${COMPATOBJ} ${LDFLAGS} | |
-urmoms-index: urmoms-index.o ${COMPATOBJ} | |
- ${CC} -o $@ urmoms-index.o ${COMPATOBJ} ${LDFLAGS} | |
+stagit-index: stagit-index.o ${COMPATOBJ} | |
+ ${CC} -o $@ stagit-index.o ${COMPATOBJ} ${LDFLAGS} | |
clean: | |
rm -f ${BIN} ${OBJ} | |
diff --git a/README b/README | |
@@ -4,7 +4,7 @@ Usage | |
mkdir -p htmldir | |
cd htmldir | |
-urmoms path-to-repo | |
+stagit path-to-repo | |
Install | |
diff --git a/TODO b/TODO | |
@@ -1,17 +1,9 @@ | |
-marketing: | |
-- rename project: stagit? | |
- | |
performance: | |
- optimize git_diff_get_stats. | |
- speed up generating files. | |
-- be smarter about changes (an existing commit can never change the diff page). | |
layout: | |
- make top menu look nicer in links/lynx again. | |
documentation: | |
- improve mandoc pages. | |
- - document .git/description, .git/owner and .git/url | |
- | |
-idea: | |
-- program to write index for multiple repos: urmoms-index <repodir>... | |
diff --git a/example.sh b/example.sh | |
@@ -19,7 +19,7 @@ curdir=$(pwd) | |
# make index. | |
cd "${reposdir}" | |
-find . -maxdepth 1 -type d | grep -v "^.$" | sort | xargs urmoms-index > "${cu… | |
+find . -maxdepth 1 -type d | grep -v "^.$" | sort | xargs stagit-index > "${cu… | |
# make files per repo. | |
find . -maxdepth 1 -type d | grep -v "^.$" | sort | while read -r dir; do | |
@@ -31,7 +31,7 @@ find . -maxdepth 1 -type d | grep -v "^.$" | sort | while rea… | |
test -d "${d}" || mkdir -p "${d}" | |
cd "${d}" | |
- urmoms "${reposdir}${d}" | |
+ stagit "${reposdir}${d}" | |
printf " done\n" | |
done | |
diff --git a/stagit-index.1 b/stagit-index.1 | |
@@ -0,0 +1,37 @@ | |
+.Dd December 26, 2015 | |
+.Dt STAGIT-INDEX 1 | |
+.Os | |
+.Sh NAME | |
+.Nm stagit-index | |
+.Nd static git index page generator | |
+.Sh SYNOPSIS | |
+.Nm | |
+.Op Ar repodir... | |
+.Sh DESCRIPTION | |
+.Nm | |
+will create an index HTML page for the repositories specified and writes | |
+the HTML data to stdout. | |
+.Pp | |
+The basename of the directory is used as the name. | |
+.Pp | |
+The content of the follow files specifies the meta data for each repository: | |
+.Bl -tag -width Ds | |
+.It .git/description or description (bare repos). | |
+description | |
+.It .git/owner or owner (bare repo). | |
+owner of repository | |
+.El | |
+.Pp | |
+For changing the style of the page you can use the following files: | |
+.Bl -tag -width Ds | |
+.It logo.png | |
+32x32 logo. | |
+.It favicon.png | |
+favicon image. | |
+.It style.css | |
+CSS stylesheet. | |
+.El | |
+.Sh SEE ALSO | |
+.Xr stagit 1 | |
+.Sh AUTHORS | |
+.An Hiltjo Posthuma Aq Mt [email protected] | |
diff --git a/urmoms-index.c b/stagit-index.c | |
diff --git a/stagit.1 b/stagit.1 | |
@@ -0,0 +1,59 @@ | |
+.Dd December 26, 2015 | |
+.Dt STAGIT 1 | |
+.Os | |
+.Sh NAME | |
+.Nm stagit | |
+.Nd static git page generator | |
+.Sh SYNOPSIS | |
+.Nm | |
+.Op Ar repodir | |
+.Sh DESCRIPTION | |
+.Nm | |
+writes HTML pages for the repository | |
+.Ar repodir | |
+to the current directory. The following files will be written: | |
+.Bl -tag -width Ds | |
+.It atom.xml | |
+Atom XML feed | |
+.It files.html | |
+List of files in the latest HEAD commit, linking to the file. | |
+.It log.html | |
+List of commits in order of most recent to old of the commits (top to bottom), | |
+each commit links to a page with a diff and diffstat of the commit. | |
+.El | |
+.Pp | |
+For each file in HEAD a file will be written in the format: | |
+file/filepath.html. This file will contain the textual data of the file | |
+prefixed by line numbers. The file will have the string "binary file" | |
+if the data is considered to be non-textual. | |
+.Pp | |
+For each commit a file will be written in the format: | |
+commit/commitid.html . This file will contain the diff and diffstat of the | |
+commit. It will write the string "binary files differ" if the data is | |
+considered to be non-textual. | |
+.Pp | |
+The basename of the directory is used as the name. | |
+.Pp | |
+The content of the follow files specifies the meta data for each repository: | |
+.Bl -tag -width Ds | |
+.It .git/description or description (bare repos). | |
+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: git://git.2f30.org/stagit | |
+.El | |
+.Pp | |
+For changing the style of the page you can use the following files: | |
+.Bl -tag -width Ds | |
+.It logo.png | |
+32x32 logo. | |
+.It favicon.png | |
+favicon image. | |
+.It style.css | |
+CSS stylesheet. | |
+.El | |
+.Sh SEE ALSO | |
+.Xr stagit-index 1 | |
+.Sh AUTHORS | |
+.An Hiltjo Posthuma Aq Mt [email protected] | |
diff --git a/urmoms-index.1 b/urmoms-index.1 | |
@@ -1,27 +0,0 @@ | |
-.Dd December 20, 2015 | |
-.Dt URMOMS-INDEX 1 | |
-.Os | |
-.Sh NAME | |
-.Nm urmoms-index | |
-.Nd static git index page generator | |
-.Sh SYNOPSIS | |
-.Nm | |
-.Op Ar repodir... | |
-.Sh DESCRIPTION | |
-.Nm | |
-will create an index HTML page for the repositories specified and writes | |
-the HTML data to stdout. | |
-.Pp | |
-For changing the style of the page you can use the following files: | |
-.Bl -tag -width Ds | |
-.It logo.png | |
-32x32 logo. | |
-.It favicon.png | |
-favicon image. | |
-.It style.css | |
-CSS stylesheet. | |
-.El | |
-.Sh SEE ALSO | |
-.Xr urmoms 1 | |
-.Sh AUTHORS | |
-.An Hiltjo Posthuma Aq Mt [email protected] | |
diff --git a/urmoms.1 b/urmoms.1 | |
@@ -1,47 +0,0 @@ | |
-.Dd December 20, 2015 | |
-.Dt URMOMS 1 | |
-.Os | |
-.Sh NAME | |
-.Nm urmoms | |
-.Nd static git page generator | |
-.Sh SYNOPSIS | |
-.Nm | |
-.Op Ar repodir | |
-.Sh DESCRIPTION | |
-.Nm | |
-writes HTML pages for the repository | |
-.Ar repodir | |
-to the current directory. The following files will be written: | |
-.Bl -tag -width Ds | |
-.It atom.xml | |
-Atom XML feed | |
-.It files.html | |
-List of files in the latest HEAD commit, linking to the file. | |
-.It log.html | |
-List of commits in order of most recent to old of the commits (top to bottom), | |
-each commit links to a page with a diff and diffstat of the commit. | |
-.El | |
-.Pp | |
-For each file in HEAD a file will be written in the format: | |
-file/filepath.html. This file will contain the textual data of the file | |
-prefixed by line numbers. The file will have the string "binary file" | |
-if the data is considered to be non-textual. | |
-.Pp | |
-For each commit a file will be written in the format: | |
-commit/commitid.html . This file will contain the diff and diffstat of the | |
-commit. It will write the string "binary files differ" if the data is | |
-considered to be non-textual. | |
-.Pp | |
-For changing the style of the page you can use the following files: | |
-.Bl -tag -width Ds | |
-.It logo.png | |
-32x32 logo. | |
-.It favicon.png | |
-favicon image. | |
-.It style.css | |
-CSS stylesheet. | |
-.El | |
-.Sh SEE ALSO | |
-.Xr urmoms-index 1 | |
-.Sh AUTHORS | |
-.An Hiltjo Posthuma Aq Mt [email protected] | |
diff --git a/urmoms.c b/urmoms.c | |
@@ -1,852 +0,0 @@ | |
-#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 <git2.h> | |
- | |
-#include "compat.h" | |
-#include "config.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 name[255]; | |
-static char description[255]; | |
-static char cloneurl[1024]; | |
-static int hasreadme, haslicense; | |
- | |
-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; | |
- | |
- 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; | |
- 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); | |
- | |
- 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 dirname(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 dirname(3) failed, this can only happen | |
- * with the OpenBSD version. */ | |
-char * | |
-xdirname(const char *path) | |
-{ | |
- char *p, *b; | |
- | |
- if (!(p = strdup(path))) | |
- err(1, "strdup"); | |
- if (!(b = dirname(p))) | |
- err(1, "basename"); | |
- if (!(b = strdup(b))) | |
- err(1, "strdup"); | |
- free(p); | |
- | |
- return b; | |
-} | |
- | |
-/* 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; | |
-} | |
- | |
-int | |
-mkdirp(const char *path) | |
-{ | |
- char tmp[PATH_MAX], *p; | |
- | |
- strlcpy(tmp, path, sizeof(tmp)); | |
- for (p = tmp + (tmp[0] == '/'); *p; p++) { | |
- if (*p != '/') | |
- continue; | |
- *p = '\0'; | |
- if (mkdir(tmp, S_IRWXU | S_IRWXG | S_IRWXO) < 0 && errno != EE… | |
- return -1; | |
- *p = '/'; | |
- } | |
- if (mkdir(tmp, S_IRWXU | S_IRWXG | S_IRWXO) < 0 && errno != EEXIST) | |
- return -1; | |
- return 0; | |
-} | |
- | |
-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 | |
-printtimez(FILE *fp, const git_time *intime) | |
-{ | |
- printtimeformat(fp, intime, "%Y-%m-%dT%H:%M:%SZ"); | |
-} | |
- | |
-void | |
-printtime(FILE *fp, const git_time *intime) | |
-{ | |
- printtimeformat(fp, intime, "%a %b %e %T %Y"); | |
-} | |
- | |
-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, name, strlen(name)); | |
- if (description[0]) | |
- fputs(" - ", fp); | |
- xmlencode(fp, description, strlen(description)); | |
- fprintf(fp, "</title>\n<link rel=\"icon\" type=\"image/png\" href=\"%s… | |
- fprintf(fp, "<link rel=\"alternate\" type=\"application/atom+xml\" tit… | |
- name, relpath); | |
- fprintf(fp, "<link rel=\"stylesheet\" type=\"text/css\" href=\"%sstyle… | |
- fputs("</head>\n<body>\n<table><tr><td>", fp); | |
- fprintf(fp, "<a href=\"../%s\"><img src=\"%slogo.png\" alt=\"\" width=… | |
- relpath, relpath); | |
- fputs("</td><td><h1>", fp); | |
- xmlencode(fp, name, strlen(name)); | |
- fputs("</h1><span class=\"desc\">", fp); | |
- xmlencode(fp, description, strlen(description)); | |
- fputs("</span></td></tr>", fp); | |
- if (cloneurl[0]) { | |
- fputs("<tr class=\"url\"><td></td><td>git clone <a href=\"", f… | |
- xmlencode(fp, cloneurl, strlen(cloneurl)); | |
- fputs("\">", fp); | |
- xmlencode(fp, cloneurl, strlen(cloneurl)); | |
- fputs("</a></td></tr>", fp); | |
- } | |
- fputs("<tr><td></td><td>\n", fp); | |
- fprintf(fp, "<a href=\"%slog.html\">Log</a> | ", relpath); | |
- fprintf(fp, "<a href=\"%sfiles.html\">Files</a>", relpath); | |
- if (hasreadme) | |
- fprintf(fp, " | <a href=\"%sfile/README.html\">README</a>", re… | |
- if (haslicense) | |
- fprintf(fp, " | <a href=\"%sfile/LICENSE.html\">LICENSE</a>", … | |
- fputs("</td></tr></table>\n<hr/><div id=\"content\">\n", fp); | |
- | |
- return 0; | |
-} | |
- | |
-int | |
-writefooter(FILE *fp) | |
-{ | |
- return !fputs("</div></body>\n</html>", fp); | |
-} | |
- | |
-void | |
-writeblobhtml(FILE *fp, const git_blob *blob) | |
-{ | |
- off_t i = 0; | |
- size_t n = 1; | |
- char *nfmt = "<a href=\"#l%d\" id=\"l%d\">%d</a>\n"; | |
- const char *s = git_blob_rawcontent(blob); | |
- git_off_t len = git_blob_rawsize(blob); | |
- | |
- fputs("<table id=\"blob\"><tr><td class=\"num\"><pre>\n", fp); | |
- | |
- if (len) { | |
- fprintf(fp, nfmt, n, n, n); | |
- while (i < len - 1) { | |
- if (s[i] == '\n') { | |
- n++; | |
- fprintf(fp, nfmt, n, n, n); | |
- } | |
- i++; | |
- } | |
- } | |
- | |
- fputs("</pre></td><td><pre>\n", fp); | |
- xmlencode(fp, s, (size_t)len); | |
- fputs("</pre></td></tr></table>\n", fp); | |
-} | |
- | |
-void | |
-printcommit(FILE *fp, struct commitinfo *ci) | |
-{ | |
- fprintf(fp, "<b>commit</b> <a href=\"%scommit/%s.html\">%s</a>\n", | |
- relpath, ci->oid, ci->oid); | |
- | |
- if (ci->parentoid[0]) | |
- fprintf(fp, "<b>parent</b> <a href=\"%scommit/%s.html\">%s</a>… | |
- relpath, ci->parentoid, ci->parentoid); | |
- | |
-#if 0 | |
- if ((count = (int)git_commit_parentcount(commit)) > 1) { | |
- fprintf(fp, "<b>Merge:</b>"); | |
- for (i = 0; i < count; i++) { | |
- git_oid_tostr(buf, 8, git_commit_parent_id(commit, i)); | |
- fprintf(fp, " <a href=\"%scommit/%s.html\">%s</a>", | |
- relpath, buf, buf); | |
- } | |
- fputc('\n', fp); | |
- } | |
-#endif | |
- if (ci->author) { | |
- fprintf(fp, "<b>Author:</b> "); | |
- xmlencode(fp, ci->author->name, strlen(ci->author->name)); | |
- fprintf(fp, " <<a href=\"mailto:"); | |
- xmlencode(fp, ci->author->email, strlen(ci->author->email)); | |
- fputs("\">", fp); | |
- xmlencode(fp, ci->author->email, strlen(ci->author->email)); | |
- fputs("</a>>\n<b>Date:</b> ", fp); | |
- printtime(fp, &(ci->author->when)); | |
- fputc('\n', fp); | |
- } | |
- fputc('\n', fp); | |
- | |
- if (ci->msg) | |
- xmlencode(fp, ci->msg, strlen(ci->msg)); | |
- | |
- fputc('\n', fp); | |
-} | |
- | |
-void | |
-printshowfile(struct commitinfo *ci) | |
-{ | |
- const git_diff_delta *delta; | |
- const git_diff_hunk *hunk; | |
- const git_diff_line *line; | |
- git_patch *patch; | |
- git_buf statsbuf; | |
- size_t ndeltas, nhunks, nhunklines; | |
- FILE *fp; | |
- size_t i, j, k; | |
- char path[PATH_MAX]; | |
- | |
- snprintf(path, sizeof(path), "commit/%s.html", ci->oid); | |
- /* check if file exists if so skip it */ | |
- if (!access(path, F_OK)) | |
- return; | |
- | |
- fp = efopen(path, "w"); | |
- writeheader(fp); | |
- fputs("<pre>\n", fp); | |
- printcommit(fp, ci); | |
- | |
- memset(&statsbuf, 0, sizeof(statsbuf)); | |
- | |
- /* diff stat */ | |
- if (ci->stats) { | |
- if (!git_diff_stats_to_buf(&statsbuf, ci->stats, | |
- GIT_DIFF_STATS_FULL | GIT_DIFF_STATS_SHORT, 80)) { | |
- if (statsbuf.ptr && statsbuf.ptr[0]) { | |
- fprintf(fp, "<b>Diffstat:</b>\n"); | |
- fputs(statsbuf.ptr, fp); | |
- } | |
- } | |
- } | |
- | |
- fputs("<hr/>", fp); | |
- | |
- ndeltas = git_diff_num_deltas(ci->diff); | |
- for (i = 0; i < ndeltas; i++) { | |
- if (git_patch_from_diff(&patch, ci->diff, i)) { | |
- git_patch_free(patch); | |
- break; | |
- } | |
- | |
- delta = git_patch_get_delta(patch); | |
- fprintf(fp, "<b>diff --git a/<a href=\"%sfile/%s.html\">%s</a>… | |
- relpath, delta->old_file.path, delta->old_file.path, | |
- relpath, delta->new_file.path, delta->new_file.path); | |
- | |
- /* check binary data */ | |
- if (delta->flags & GIT_DIFF_FLAG_BINARY) { | |
- fputs("Binary files differ\n", fp); | |
- git_patch_free(patch); | |
- continue; | |
- } | |
- | |
- nhunks = git_patch_num_hunks(patch); | |
- for (j = 0; j < nhunks; j++) { | |
- if (git_patch_get_hunk(&hunk, &nhunklines, patch, j)) | |
- break; | |
- | |
- fprintf(fp, "<span class=\"h\">%s</span>\n", hunk->hea… | |
- | |
- for (k = 0; ; k++) { | |
- if (git_patch_get_line_in_hunk(&line, patch, j… | |
- break; | |
- if (line->old_lineno == -1) | |
- fprintf(fp, "<a href=\"#h%zu-%zu\" id=… | |
- j, k, j, k); | |
- else if (line->new_lineno == -1) | |
- fprintf(fp, "<a href=\"#h%zu-%zu\" id=… | |
- j, k, j, k); | |
- else | |
- fputc(' ', fp); | |
- xmlencode(fp, line->content, line->content_len… | |
- if (line->old_lineno == -1 || line->new_lineno… | |
- fputs("</a>", fp); | |
- } | |
- } | |
- git_patch_free(patch); | |
- } | |
- git_buf_free(&statsbuf); | |
- | |
- fputs( "</pre>\n", fp); | |
- writefooter(fp); | |
- fclose(fp); | |
- return; | |
-} | |
- | |
-void | |
-writelog(FILE *fp) | |
-{ | |
- struct commitinfo *ci; | |
- git_revwalk *w = NULL; | |
- git_oid id; | |
- size_t len; | |
- | |
- mkdir("commit", 0755); | |
- | |
- git_revwalk_new(&w, repo); | |
- git_revwalk_push_head(w); | |
- git_revwalk_sorting(w, GIT_SORT_TIME); | |
- git_revwalk_simplify_first_parent(w); | |
- | |
- fputs("<table id=\"log\"><thead>\n<tr><td>Age</td><td>Commit message</… | |
- "<td>Author</td><td>Files</td><td class=\"num\">+</td>" | |
- "<td class=\"num\">-</td></tr>\n</thead><tbody>\n", fp); | |
- while (!git_revwalk_next(&id, w)) { | |
- relpath = ""; | |
- | |
- if (!(ci = commitinfo_getbyoid(&id))) | |
- break; | |
- | |
- fputs("<tr><td>", fp); | |
- if (ci->author) | |
- printtimeshort(fp, &(ci->author->when)); | |
- fputs("</td><td>", fp); | |
- if (ci->summary) { | |
- fprintf(fp, "<a href=\"%scommit/%s.html\">", relpath, … | |
- if ((len = strlen(ci->summary)) > summarylen) { | |
- xmlencode(fp, ci->summary, summarylen - 1); | |
- fputs("…", fp); | |
- } else { | |
- xmlencode(fp, ci->summary, len); | |
- } | |
- fputs("</a>", fp); | |
- } | |
- fputs("</td><td>", fp); | |
- if (ci->author) | |
- xmlencode(fp, ci->author->name, strlen(ci->author->nam… | |
- fputs("</td><td class=\"num\">", fp); | |
- fprintf(fp, "%zu", ci->filecount); | |
- fputs("</td><td class=\"num\">", fp); | |
- fprintf(fp, "+%zu", ci->addcount); | |
- fputs("</td><td class=\"num\">", fp); | |
- fprintf(fp, "-%zu", ci->delcount); | |
- fputs("</td></tr>\n", fp); | |
- | |
- relpath = "../"; | |
- printshowfile(ci); | |
- | |
- commitinfo_free(ci); | |
- } | |
- fprintf(fp, "</tbody></table>"); | |
- | |
- git_revwalk_free(w); | |
- relpath = ""; | |
-} | |
- | |
-void | |
-printcommitatom(FILE *fp, struct commitinfo *ci) | |
-{ | |
- fputs("<entry>\n", fp); | |
- | |
- fprintf(fp, "<id>%s</id>\n", ci->oid); | |
- if (ci->author) { | |
- fputs("<updated>", fp); | |
- printtimez(fp, &(ci->author->when)); | |
- fputs("</updated>\n", fp); | |
- } | |
- if (ci->summary) { | |
- fputs("<title type=\"text\">", fp); | |
- xmlencode(fp, ci->summary, strlen(ci->summary)); | |
- fputs("</title>\n", fp); | |
- } | |
- | |
- fputs("<content type=\"text\">", fp); | |
- fprintf(fp, "commit %s\n", ci->oid); | |
- if (ci->parentoid[0]) | |
- fprintf(fp, "parent %s\n", ci->parentoid); | |
- | |
-#if 0 | |
- if ((count = (int)git_commit_parentcount(commit)) > 1) { | |
- fprintf(fp, "Merge:"); | |
- for (i = 0; i < count; i++) { | |
- git_oid_tostr(buf, 8, git_commit_parent_id(commit, i)); | |
- fprintf(fp, " %s", buf); | |
- } | |
- fputc('\n', fp); | |
- } | |
-#endif | |
- | |
- if (ci->author) { | |
- fprintf(fp, "Author: "); | |
- xmlencode(fp, ci->author->name, strlen(ci->author->name)); | |
- fprintf(fp, " <"); | |
- xmlencode(fp, ci->author->email, strlen(ci->author->email)); | |
- fprintf(fp, ">\nDate: "); | |
- printtime(fp, &(ci->author->when)); | |
- } | |
- fputc('\n', fp); | |
- | |
- if (ci->msg) | |
- xmlencode(fp, ci->msg, strlen(ci->msg)); | |
- fputs("\n</content>\n", fp); | |
- if (ci->author) { | |
- fputs("<author><name>", fp); | |
- xmlencode(fp, ci->author->name, strlen(ci->author->name)); | |
- fputs("</name>\n<email>", fp); | |
- xmlencode(fp, ci->author->email, strlen(ci->author->email)); | |
- fputs("</email>\n</author>\n", fp); | |
- } | |
- fputs("</entry>\n", fp); | |
-} | |
- | |
-int | |
-writeatom(FILE *fp) | |
-{ | |
- struct commitinfo *ci; | |
- git_revwalk *w = NULL; | |
- git_oid id; | |
- size_t i, m = 100; /* max */ | |
- | |
- fputs("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" | |
- "<feed xmlns=\"http://www.w3.org/2005/Atom\">\n<title>", fp); | |
- xmlencode(fp, name, strlen(name)); | |
- fputs(", branch master</title>\n<subtitle>", fp); | |
- | |
- xmlencode(fp, description, strlen(description)); | |
- fputs("</subtitle>\n", fp); | |
- | |
- git_revwalk_new(&w, repo); | |
- git_revwalk_push_head(w); | |
- git_revwalk_sorting(w, GIT_SORT_TIME); | |
- git_revwalk_simplify_first_parent(w); | |
- | |
- for (i = 0; i < m && !git_revwalk_next(&id, w); i++) { | |
- if (!(ci = commitinfo_getbyoid(&id))) | |
- break; | |
- printcommitatom(fp, ci); | |
- commitinfo_free(ci); | |
- } | |
- git_revwalk_free(w); | |
- | |
- fputs("</feed>", fp); | |
- | |
- return 0; | |
-} | |
- | |
-int | |
-writeblob(git_object *obj, const char *filename, git_off_t filesize) | |
-{ | |
- char fpath[PATH_MAX]; | |
- char tmp[PATH_MAX] = ""; | |
- char *d, *p; | |
- FILE *fp; | |
- | |
- snprintf(fpath, sizeof(fpath), "file/%s.html", filename); | |
- d = xdirname(fpath); | |
- if (mkdirp(d)) { | |
- free(d); | |
- return 1; | |
- } | |
- free(d); | |
- | |
- p = fpath; | |
- while (*p) { | |
- if (*p == '/') | |
- strlcat(tmp, "../", sizeof(tmp)); | |
- p++; | |
- } | |
- relpath = tmp; | |
- | |
- fp = efopen(fpath, "w"); | |
- writeheader(fp); | |
- fputs("<p> ", fp); | |
- xmlencode(fp, filename, strlen(filename)); | |
- fprintf(fp, " (%" PRIu32 "b)", filesize); | |
- fputs("</p><hr/>", fp); | |
- | |
- if (git_blob_is_binary((git_blob *)obj)) { | |
- fprintf(fp, "<p>Binary file</p>\n"); | |
- } else { | |
- writeblobhtml(fp, (git_blob *)obj); | |
- if (ferror(fp)) | |
- err(1, "fwrite"); | |
- } | |
- writefooter(fp); | |
- fclose(fp); | |
- | |
- relpath = ""; | |
- | |
- return 0; | |
-} | |
- | |
-const char * | |
-filemode(git_filemode_t m) | |
-{ | |
- static char mode[11]; | |
- | |
- memset(mode, '-', sizeof(mode) - 1); | |
- mode[10] = '\0'; | |
- | |
- if (S_ISREG(m)) | |
- mode[0] = '-'; | |
- else if (S_ISBLK(m)) | |
- mode[0] = 'b'; | |
- else if (S_ISCHR(m)) | |
- mode[0] = 'c'; | |
- else if (S_ISDIR(m)) | |
- mode[0] = 'd'; | |
- else if (S_ISFIFO(m)) | |
- mode[0] = 'p'; | |
- else if (S_ISLNK(m)) | |
- mode[0] = 'l'; | |
- else if (S_ISSOCK(m)) | |
- mode[0] = 's'; | |
- else | |
- mode[0] = '?'; | |
- | |
- if (m & S_IRUSR) mode[1] = 'r'; | |
- if (m & S_IWUSR) mode[2] = 'w'; | |
- if (m & S_IXUSR) mode[3] = 'x'; | |
- if (m & S_IRGRP) mode[4] = 'r'; | |
- if (m & S_IWGRP) mode[5] = 'w'; | |
- if (m & S_IXGRP) mode[6] = 'x'; | |
- if (m & S_IROTH) mode[7] = 'r'; | |
- if (m & S_IWOTH) mode[8] = 'w'; | |
- if (m & S_IXOTH) mode[9] = 'x'; | |
- | |
- if (m & S_ISUID) mode[3] = (mode[3] == 'x') ? 's' : 'S'; | |
- if (m & S_ISGID) mode[6] = (mode[6] == 'x') ? 's' : 'S'; | |
- if (m & S_ISVTX) mode[9] = (mode[9] == 'x') ? 't' : 'T'; | |
- | |
- return mode; | |
-} | |
- | |
-int | |
-writefilestree(FILE *fp, git_tree *tree, const char *path) | |
-{ | |
- const git_tree_entry *entry = NULL; | |
- const char *filename; | |
- char filepath[PATH_MAX]; | |
- git_object *obj = NULL; | |
- git_off_t filesize; | |
- size_t count, i; | |
- int ret; | |
- | |
- count = git_tree_entrycount(tree); | |
- for (i = 0; i < count; i++) { | |
- if (!(entry = git_tree_entry_byindex(tree, i))) | |
- return -1; | |
- | |
- filename = git_tree_entry_name(entry); | |
- if (git_tree_entry_to_object(&obj, repo, entry)) | |
- return -1; | |
- switch (git_object_type(obj)) { | |
- case GIT_OBJ_BLOB: | |
- break; | |
- case GIT_OBJ_TREE: | |
- ret = writefilestree(fp, (git_tree *)obj, filename); | |
- git_object_free(obj); | |
- if (ret) | |
- return ret; | |
- continue; | |
- default: | |
- git_object_free(obj); | |
- continue; | |
- } | |
- if (path[0]) { | |
- snprintf(filepath, sizeof(filepath), "%s/%s", path, fi… | |
- filename = filepath; | |
- } | |
- | |
- filesize = git_blob_rawsize((git_blob *)obj); | |
- | |
- fputs("<tr><td>", fp); | |
- fprintf(fp, "%s", filemode(git_tree_entry_filemode(entry))); | |
- fprintf(fp, "</td><td><a href=\"%sfile/", relpath); | |
- xmlencode(fp, filename, strlen(filename)); | |
- fputs(".html\">", fp); | |
- xmlencode(fp, filename, strlen(filename)); | |
- fputs("</a></td><td class=\"num\">", fp); | |
- fprintf(fp, "%" PRIu32, filesize); | |
- fputs("</td></tr>\n", fp); | |
- | |
- writeblob(obj, filename, filesize); | |
- } | |
- | |
- return 0; | |
-} | |
- | |
-int | |
-writefiles(FILE *fp) | |
-{ | |
- const git_oid *id; | |
- git_tree *tree = NULL; | |
- git_object *obj = NULL; | |
- git_commit *commit = NULL; | |
- | |
- fputs("<table id=\"files\"><thead>\n<tr>" | |
- "<td>Mode</td><td>Name</td><td class=\"num\">Size</td>" | |
- "</tr>\n</thead><tbody>\n", fp); | |
- | |
- if (git_revparse_single(&obj, repo, "HEAD")) | |
- return -1; | |
- id = git_object_id(obj); | |
- if (git_commit_lookup(&commit, repo, id)) | |
- return -1; | |
- if (git_commit_tree(&tree, commit)) { | |
- git_commit_free(commit); | |
- return -1; | |
- } | |
- git_commit_free(commit); | |
- | |
- writefilestree(fp, tree, ""); | |
- | |
- git_commit_free(commit); | |
- git_tree_free(tree); | |
- | |
- fputs("</tbody></table>", fp); | |
- | |
- return 0; | |
-} | |
- | |
-int | |
-main(int argc, char *argv[]) | |
-{ | |
- git_object *obj = NULL; | |
- const git_error *e = NULL; | |
- FILE *fp, *fpread; | |
- char path[PATH_MAX], *p; | |
- int status; | |
- | |
- if (argc != 2) { | |
- fprintf(stderr, "%s <repodir>\n", argv[0]); | |
- return 1; | |
- } | |
- repodir = argv[1]; | |
- | |
- git_libgit2_init(); | |
- | |
- 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, e->mess… | |
- return status; | |
- } | |
- | |
- /* use directory name as name */ | |
- p = xbasename(repodir); | |
- snprintf(name, sizeof(name), "%s", p); | |
- free(p); | |
- | |
- /* read description or .git/description */ | |
- snprintf(path, sizeof(path), "%s%s%s", | |
- repodir, repodir[strlen(repodir)] == '/' ? "" : "/", "descript… | |
- if (!(fpread = fopen(path, "r"))) { | |
- snprintf(path, sizeof(path), "%s%s%s", | |
- repodir, repodir[strlen(repodir)] == '/' ? "" : "/", "… | |
- fpread = fopen(path, "r"); | |
- } | |
- if (fpread) { | |
- if (!fgets(description, sizeof(description), fpread)) | |
- description[0] = '\0'; | |
- fclose(fpread); | |
- } | |
- | |
- /* read url or .git/url */ | |
- snprintf(path, sizeof(path), "%s%s%s", | |
- repodir, repodir[strlen(repodir)] == '/' ? "" : "/", "url"); | |
- if (!(fpread = fopen(path, "r"))) { | |
- snprintf(path, sizeof(path), "%s%s%s", | |
- repodir, repodir[strlen(repodir)] == '/' ? "" : "/", "… | |
- fpread = fopen(path, "r"); | |
- } | |
- if (fpread) { | |
- if (!fgets(cloneurl, sizeof(cloneurl), fpread)) | |
- cloneurl[0] = '\0'; | |
- fclose(fpread); | |
- } | |
- | |
- /* check LICENSE */ | |
- haslicense = !git_revparse_single(&obj, repo, "HEAD:LICENSE"); | |
- git_object_free(obj); | |
- /* check README */ | |
- hasreadme = !git_revparse_single(&obj, repo, "HEAD:README"); | |
- git_object_free(obj); | |
- | |
- fp = efopen("log.html", "w"); | |
- writeheader(fp); | |
- writelog(fp); | |
- writefooter(fp); | |
- fclose(fp); | |
- | |
- fp = efopen("files.html", "w"); | |
- writeheader(fp); | |
- writefiles(fp); | |
- writefooter(fp); | |
- fclose(fp); | |
- | |
- /* Atom feed */ | |
- fp = efopen("atom.xml", "w"); | |
- writeatom(fp); | |
- fclose(fp); | |
- | |
- /* cleanup */ | |
- git_repository_free(repo); | |
- git_libgit2_shutdown(); | |
- | |
- return 0; | |
-} |