stagit

Added chroma syntax highlighting

Arjun Choudhary contact@arjunchoudhary.com

commit: d920d00 parent: 862c56d
5 files changed, 59 insertions(+), 195 deletions(-)
MMakefile+8-1
DREADME+0-186
Mstagit+0-0
Mstagit.c+51-8
Dstagit.o+0-0
M · Makefile +8, -1
 1@@ -8,9 +8,15 @@ PREFIX = /usr/local
 2 MANPREFIX = ${PREFIX}/man
 3 DOCPREFIX = ${PREFIX}/share/doc/${NAME}
 4 
 5+CHROMA = $(shell which chroma > /dev/null; echo $$?)
 6 LIBGIT_INC = -I/usr/local/include
 7 LIBGIT_LIB = -L/usr/local/lib -lgit2
 8 
 9+ifeq (${CHROMA}, 0)
10+	STAGIT_CPPFLAGS := ${STAGIT_CPPFLAGS} -DHAS_CHROMA
11+endif
12+
13+
14 # use system flags.
15 STAGIT_CFLAGS = ${LIBGIT_INC} ${CFLAGS}
16 STAGIT_LDFLAGS = ${LIBGIT_LIB} ${LDFLAGS}
17@@ -26,6 +32,7 @@ COMPATSRC = \
18 	entity.c\
19 	md4c.c\
20 	md4c-html.c
21+
22 BIN = \
23 	stagit\
24 	stagit-index
25@@ -90,7 +97,7 @@ install: all
26 		logo.png\
27 		example_create.sh\
28 		example_post-receive.sh\
29-		README\
30+		README.md\
31 		${DESTDIR}${DOCPREFIX}
32 	# installing manual pages.
33 	mkdir -p ${DESTDIR}${MANPREFIX}/man1
D · README +0, -186
  1@@ -1,186 +0,0 @@
  2-stagit
  3-------
  4-
  5-static git page generator.
  6-
  7-It generates static HTML pages for a git repository.
  8-
  9-
 10-Usage
 11------
 12-
 13-Make files per repository:
 14-
 15-	$ mkdir -p htmlroot/htmlrepo1 && cd htmlroot/htmlrepo1
 16-	$ stagit path/to/gitrepo1
 17-	repeat for other repositories
 18-	$ ...
 19-
 20-Make index file for repositories:
 21-
 22-	$ cd htmlroot
 23-	$ stagit-index path/to/gitrepo1 \
 24-	               path/to/gitrepo2 \
 25-	               path/to/gitrepo3 > index.html
 26-
 27-
 28-Build and install
 29------------------
 30-
 31-$ make
 32-# make install
 33-
 34-
 35-Dependencies
 36-------------
 37-
 38-- C compiler (C99).
 39-- libc (tested with OpenBSD, FreeBSD, NetBSD, Linux: glibc and musl).
 40-- libgit2 (v0.22+).
 41-- POSIX make (optional).
 42-
 43-
 44-Documentation
 45--------------
 46-
 47-See man pages: stagit(1) and stagit-index(1).
 48-
 49-
 50-Building a static binary
 51-------------------------
 52-
 53-It may be useful to build static binaries, for example to run in a chroot.
 54-
 55-It can be done like this at the time of writing (v0.24):
 56-
 57-cd libgit2-src
 58-
 59-# change the options in the CMake file: CMakeLists.txt
 60-BUILD_SHARED_LIBS to OFF (static)
 61-CURL to OFF              (not needed)
 62-USE_SSH OFF              (not needed)
 63-THREADSAFE OFF           (not needed)
 64-USE_OPENSSL OFF          (not needed, use builtin)
 65-
 66-mkdir -p build && cd build
 67-cmake ../
 68-make
 69-make install
 70-
 71-
 72-Extract owner field from git config
 73------------------------------------
 74-
 75-A way to extract the gitweb owner for example in the format:
 76-
 77-	[gitweb]
 78-		owner = Name here
 79-
 80-Script:
 81-
 82-	#!/bin/sh
 83-	awk '/^[ 	]*owner[ 	]=/ {
 84-		sub(/^[^=]*=[ 	]*/, "");
 85-		print $0;
 86-	}'
 87-
 88-
 89-Set clone URL for a directory of repos
 90---------------------------------------
 91-	#!/bin/sh
 92-	cd "$dir"
 93-	for i in *; do
 94-		test -d "$i" && echo "git://git.codemadness.org/$i" > "$i/url"
 95-	done
 96-
 97-
 98-Update files on git push
 99-------------------------
100-
101-Using a post-receive hook the static files can be automatically updated.
102-Keep in mind git push -f can change the history and the commits may need
103-to be recreated. This is because stagit checks if a commit file already
104-exists. It also has a cache (-c) option which can conflict with the new
105-history. See stagit(1).
106-
107-git post-receive hook (repo/.git/hooks/post-receive):
108-
109-	#!/bin/sh
110-	# detect git push -f
111-	force=0
112-	while read -r old new ref; do
113-		hasrevs=$(git rev-list "$old" "^$new" | sed 1q)
114-		if test -n "$hasrevs"; then
115-			force=1
116-			break
117-		fi
118-	done
119-
120-	# remove commits and .cache on git push -f
121-	#if test "$force" = "1"; then
122-	# ...
123-	#fi
124-
125-	# see example_create.sh for normal creation of the files.
126-
127-
128-Create .tar.gz archives by tag
129-------------------------------
130-	#!/bin/sh
131-	name="stagit"
132-	mkdir -p archives
133-	git tag -l | while read -r t; do
134-		f="archives/${name}-$(echo "${t}" | tr '/' '_').tar.gz"
135-		test -f "${f}" && continue
136-		git archive \
137-			--format tar.gz \
138-			--prefix "${t}/" \
139-			-o "${f}" \
140-			-- \
141-			"${t}"
142-	done
143-
144-
145-Features
146---------
147-
148-- Log of all commits from HEAD.
149-- Log and diffstat per commit.
150-- Show file tree with linkable line numbers.
151-- Show references: local branches and tags.
152-- Detect README and LICENSE file from HEAD and link it as a webpage.
153-- Detect submodules (.gitmodules file) from HEAD and link it as a webpage.
154-- Atom feed of the commit log (atom.xml).
155-- Atom feed of the tags/refs (tags.xml).
156-- Make index page for multiple repositories with stagit-index.
157-- After generating the pages (relatively slow) serving the files is very fast,
158-  simple and requires little resources (because the content is static), only
159-  a HTTP file server is required.
160-- Usable with text-browsers such as dillo, links, lynx and w3m.
161-
162-
163-Cons
164-----
165-
166-- Not suitable for large repositories (2000+ commits), because diffstats are
167-  an expensive operation, the cache (-c flag) is a workaround for this in
168-  some cases.
169-- Not suitable for large repositories with many files, because all files are
170-  written for each execution of stagit. This is because stagit shows the lines
171-  of textfiles and there is no "cache" for file metadata (this would add more
172-  complexity to the code).
173-- Not suitable for repositories with many branches, a quite linear history is
174-  assumed (from HEAD).
175-
176-  In these cases it is better to just use cgit or possibly change stagit to
177-  run as a CGI program.
178-
179-- Relatively slow to run the first time (about 3 seconds for sbase,
180-  1500+ commits), incremental updates are faster.
181-- Does not support some of the dynamic features cgit has, like:
182-  - Snapshot tarballs per commit.
183-  - File tree per commit.
184-  - History log of branches diverged from HEAD.
185-  - Stats (git shortlog -s).
186-
187-  This is by design, just use git locally.
M · stagit +0, -0
M · stagit.c +51, -8
  1@@ -6,6 +6,7 @@
  2 #include <libgen.h>
  3 #include <limits.h>
  4 #include <stdint.h>
  5+#include <stdbool.h>
  6 #include <stdio.h>
  7 #include <stdlib.h>
  8 #include <string.h>
  9@@ -75,6 +76,9 @@ static char *readmefiles[] = { "HEAD:README", "HEAD:README.md" };
 10 static char *readme;
 11 static long long nlogcommits = -1; /* -1 indicates not used */
 12 
 13+bool htmlized; /* true if markdoown converted to HTML */
 14+
 15+
 16 /* cache */
 17 static git_oid lastoid;
 18 static char lastoidstr[GIT_OID_HEXSZ + 2]; /* id + newline + NUL byte */
 19@@ -592,9 +596,47 @@ writeblobmd(FILE *fp, const git_blob *blob)
 20     return n;
 21 }
 22 
 23-size_t
 24-writeblobhtml(FILE *fp, const git_blob *blob)
 25+int
 26+syntax_highlight(const char *filename, FILE *fp, const char *s, size_t len)
 27 {
 28+	// Flush HTML-file
 29+	fflush(fp);
 30+	// Copy STDOUT
 31+	int stdout_copy = dup(1);
 32+	// Redirect STDOUT
 33+	dup2(fileno(fp), 1);
 34+
 35+	char cmd[255] = "chroma --html --html-only --html-lines --html-lines-table --filename ";
 36+
 37+	strncat(cmd, filename, strlen(filename) + 1);
 38+	FILE *child = popen(cmd, "w");
 39+	if (child == NULL) {
 40+		printf("child is null: %s", strerror(errno));
 41+		exit(1);
 42+	}
 43+
 44+	// Give filename through STDIN:
 45+	// fprintf(child, "%s\n", filename);
 46+
 47+	// Give code to highlight through STDIN:
 48+	int lc;
 49+	size_t i;
 50+	for (i = 0; *s && i < len; s++, i++) {
 51+		if (*s == '\n') lc++;
 52+		fprintf(child, "%c", *s);
 53+	}
 54+
 55+	pclose(child);
 56+	fflush(stdout);
 57+	// Give back STDOUT.
 58+	dup2(stdout_copy, 1);
 59+	return lc;
 60+}
 61+
 62+size_t
 63+writeblobhtml(const char *filename, FILE *fp, const git_blob *blob)
 64+{	
 65+	int lc = 0;
 66 	size_t n = 0, i, len, prev;
 67 	const char *nfmt = "<a href=\"#l%zu\" class=\"line\" id=\"l%zu\">%zu</a>";
 68 	const char *s = git_blob_rawcontent(blob);
 69@@ -603,7 +645,9 @@ writeblobhtml(FILE *fp, const git_blob *blob)
 70 	fputs("<pre id=\"blob\">\n", fp);
 71 
 72 	if (len > 0) {
 73-		for (i = 0, prev = 0; i < len; i++) {
 74+		syntax_highlight(filename, fp, s, len);
 75+		/* Uncomment to render the original file (no syntax highlight) */
 76+/* 		for (i = 0, prev = 0; i < len; i++) {
 77 			if (s[i] != '\n')
 78 				continue;
 79 			n++;
 80@@ -611,15 +655,14 @@ writeblobhtml(FILE *fp, const git_blob *blob)
 81 			xmlencodeline(fp, &s[prev], i - prev + 1);
 82 			putc('\n', fp);
 83 			prev = i + 1;
 84-		}
 85+		} */
 86 		/* trailing data */
 87-		if ((len - prev) > 0) {
 88+/* 		if ((len - prev) > 0) {
 89 			n++;
 90 			fprintf(fp, nfmt, n, n, n);
 91 			xmlencodeline(fp, &s[prev], len - prev);
 92-		}
 93+		} */
 94 	}
 95-
 96 	fputs("</pre>\n", fp);
 97 
 98 	return n;
 99@@ -1029,7 +1072,7 @@ writeblob(git_object *obj, const char *fpath, const char *filename, size_t files
100         if (ferror(fp))
101             err(1, "md parse fail");
102 	} else {
103-		lc = writeblobhtml(fp, (git_blob *)obj);
104+		lc = writeblobhtml(filename, fp, (git_blob *)obj);
105 		if (ferror(fp))
106 			err(1, "fwrite");
107 	}
D · stagit.o +0, -0