Initial commit
This commit is contained in:
commit
be306cd215
110
Makefile
Normal file
110
Makefile
Normal file
@ -0,0 +1,110 @@
|
||||
.POSIX:
|
||||
|
||||
NAME = stagit
|
||||
VERSION = 1.2
|
||||
|
||||
# paths
|
||||
PREFIX = /usr/local
|
||||
MANPREFIX = ${PREFIX}/man
|
||||
DOCPREFIX = ${PREFIX}/share/doc/${NAME}
|
||||
|
||||
LIB_INC = -I/usr/local/include
|
||||
LIB_LIB = -L/usr/local/lib -lgit2 -lmd4c-html
|
||||
|
||||
# use system flags.
|
||||
STAGIT_CFLAGS = ${LIB_INC} ${CFLAGS}
|
||||
STAGIT_LDFLAGS = ${LIB_LIB} ${LDFLAGS}
|
||||
STAGIT_CPPFLAGS = -D_XOPEN_SOURCE=700 -D_DEFAULT_SOURCE -D_BSD_SOURCE
|
||||
|
||||
# Uncomment to enable workaround for older libgit2 which don't support this
|
||||
# option. This workaround will be removed in the future *pinky promise*.
|
||||
#STAGIT_CFLAGS += -DGIT_OPT_SET_OWNER_VALIDATION=-1
|
||||
|
||||
SRC = \
|
||||
stagit.c\
|
||||
stagit-index.c
|
||||
COMPATSRC = \
|
||||
reallocarray.c\
|
||||
strlcat.c\
|
||||
strlcpy.c
|
||||
BIN = \
|
||||
stagit\
|
||||
stagit-index
|
||||
MAN1 = \
|
||||
stagit.1\
|
||||
stagit-index.1
|
||||
DOC = \
|
||||
LICENSE\
|
||||
README.md
|
||||
HDR = compat.h
|
||||
|
||||
COMPATOBJ = \
|
||||
reallocarray.o\
|
||||
strlcat.o\
|
||||
strlcpy.o
|
||||
|
||||
OBJ = ${SRC:.c=.o} ${COMPATOBJ}
|
||||
|
||||
all: ${BIN}
|
||||
|
||||
.o:
|
||||
${CC} -o $@ ${LDFLAGS}
|
||||
|
||||
.c.o:
|
||||
${CC} -o $@ -c $< ${STAGIT_CFLAGS} ${STAGIT_CPPFLAGS}
|
||||
|
||||
dist:
|
||||
rm -rf ${NAME}-${VERSION}
|
||||
mkdir -p ${NAME}-${VERSION}
|
||||
cp -f ${MAN1} ${HDR} ${SRC} ${COMPATSRC} ${DOC} \
|
||||
Makefile assets/favicon.png assets/logo.png assets/style.css assets/helper ${NAME}-${VERSION}
|
||||
# make tarball
|
||||
tar -cf - ${NAME}-${VERSION} | \
|
||||
gzip -c > ${NAME}-${VERSION}.tar.gz
|
||||
rm -rf ${NAME}-${VERSION}
|
||||
|
||||
${OBJ}: ${HDR}
|
||||
|
||||
stagit: stagit.o ${COMPATOBJ}
|
||||
${CC} -o $@ stagit.o ${COMPATOBJ} ${STAGIT_LDFLAGS}
|
||||
|
||||
stagit-index: stagit-index.o ${COMPATOBJ}
|
||||
${CC} -o $@ stagit-index.o ${COMPATOBJ} ${STAGIT_LDFLAGS}
|
||||
|
||||
clean:
|
||||
rm -f ${BIN} ${OBJ} ${NAME}-${VERSION}.tar.gz
|
||||
|
||||
install: all
|
||||
# installing executable files.
|
||||
mkdir -p ${DESTDIR}${PREFIX}/bin
|
||||
cp -f ${BIN} ${DESTDIR}${PREFIX}/bin
|
||||
for f in ${BIN}; do chmod 755 ${DESTDIR}${PREFIX}/bin/$$f; done
|
||||
# installing example files.
|
||||
mkdir -p ${DESTDIR}${DOCPREFIX}
|
||||
cp -f assets/style.css\
|
||||
assets/favicon.png\
|
||||
assets/logo.png\
|
||||
assets/helper\
|
||||
README.md\
|
||||
${DESTDIR}${DOCPREFIX}
|
||||
# installing manual pages.
|
||||
mkdir -p ${DESTDIR}${MANPREFIX}/man1
|
||||
cp -f ${MAN1} ${DESTDIR}${MANPREFIX}/man1
|
||||
for m in ${MAN1}; do chmod 644 ${DESTDIR}${MANPREFIX}/man1/$$m; done
|
||||
|
||||
uninstall:
|
||||
# removing executable files.
|
||||
for f in ${BIN}; do rm -f ${DESTDIR}${PREFIX}/bin/$$f; done
|
||||
# removing example files.
|
||||
rm -f \
|
||||
${DESTDIR}${DOCPREFIX}/style.css\
|
||||
${DESTDIR}${DOCPREFIX}/favicon.png\
|
||||
${DESTDIR}${DOCPREFIX}/logo.png\
|
||||
${DESTDIR}${DOCPREFIX}/example_create.sh\
|
||||
${DESTDIR}${DOCPREFIX}/example_post-receive.sh\
|
||||
${DESTDIR}${DOCPREFIX}/README.md
|
||||
-rmdir ${DESTDIR}${DOCPREFIX}
|
||||
# removing manual pages.
|
||||
for m in ${MAN1}; do rm -f ${DESTDIR}${MANPREFIX}/man1/$$m; done
|
||||
|
||||
.PHONY: all clean dist install uninstall
|
31
README.md
Normal file
31
README.md
Normal file
@ -0,0 +1,31 @@
|
||||
# stagit
|
||||
> static git page generator
|
||||
|
||||
## Information
|
||||
This is my personal fork of [stagit](https://codemadness.org/stagit.html) which is running [git.acid.vegas](https://git.acid.vegas/)
|
||||
|
||||
## Dependencies
|
||||
- C compiler *(C99)*
|
||||
- libc *(tested with OpenBSD, FreeBSD, NetBSD, Linux: glibc and musl)*
|
||||
- [libgit2](https://github.com/libgit2/libgit2) *(v0.22+)*
|
||||
- [md4c](https://github.com/mity/md4c) *(v0.4.4+)*
|
||||
- POSIX make *(optional)*
|
||||
|
||||
## Setup
|
||||
```shell
|
||||
cd stagit
|
||||
make
|
||||
sudo make install
|
||||
```
|
||||
|
||||
###### New Features
|
||||
- Markdown rendering to HTML for README files
|
||||
- Syntax hilighting
|
||||
- Repository categories
|
||||
- Direct download to repository tar.gz
|
||||
- Style changes
|
||||
|
||||
###### Props
|
||||
- Hiltjo Posthuma *(orignal author of [stagit](https://codemadness.org/git/stagit/))*
|
||||
- Larry Burns *([stagit-md](https://github.com/lmburns/stagit-md))*
|
||||
- Oscar Benedito *([md4c implementation](https://oscarbenedito.com/blog/2020/08/adding-about-pages-to-stagit/))*
|
BIN
assets/acidvegas.png
Normal file
BIN
assets/acidvegas.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
BIN
assets/favicon.png
Normal file
BIN
assets/favicon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 505 B |
62
assets/helper
Executable file
62
assets/helper
Executable file
@ -0,0 +1,62 @@
|
||||
#!/bin/sh
|
||||
# stagit setup helper - developed by acidevegas (https://git.acid.vegas/stagit)
|
||||
# sudo apt-get install libgit2-1.1 libmd4c*
|
||||
|
||||
URL="git.acid.vegas"
|
||||
PROTO="https"
|
||||
CLONE_URL="git://$URL"
|
||||
COMMIT_LIMIT=100
|
||||
HTML_DIR="$HOME/dev/git/acidvegas/git.acid.vegas"
|
||||
REPOS_DIR="$HOME/dev/git"
|
||||
REPO_EXCLUDE="git.acid.vegas"
|
||||
REPOS="$(find $REPOS_DIR -type d -name mirror -prune -o -type d -name $REPO_EXCLUDE -prune -o -type d -name .git -printf "%h\\n " | sort | xargs echo)"
|
||||
|
||||
prepair() {
|
||||
[ -d $HTML_DIR ] && rm -rf $HTML_DIR/*
|
||||
mkdir -p $HTML_DIR/assets
|
||||
cp acidvegas.png favicon.png logo.png mostdangerous.png style.css $HTML_DIR/assets
|
||||
echo $URL > $HTML_DIR/CNAME
|
||||
}
|
||||
|
||||
make_index() {
|
||||
echo "[~] creating index..."
|
||||
args=""
|
||||
for dir in $(ls $REPOS_DIR | grep -v 'mirror'); do
|
||||
echo "[~] indexing '$dir' repositories..."
|
||||
DIR_REPOS="$(find $REPOS_DIR/$dir -type d -name mirror -prune -o -type d -name $REPO_EXCLUDE -prune -o -type d -name .git -printf "%h\\n " | sort | xargs echo)"
|
||||
args="$args -c \"$dir\" $DIR_REPOS"
|
||||
done
|
||||
echo "$args" | xargs stagit-index > $HTML_DIR/index.html
|
||||
echo "[+] finished"
|
||||
}
|
||||
|
||||
make_repos() {
|
||||
for dir in $(echo $REPOS); do
|
||||
USER=$(basename $(dirname $dir))
|
||||
REPO=$(basename $dir)
|
||||
if [ -f $dir/.git/description ]; then
|
||||
if [ "$(cat $dir/.git/description)" = "Unnamed repository; edit this file 'description' to name the repository." ]; then
|
||||
read -p "description for '$USER/$REPO':" desc
|
||||
echo "$desc" > $dir/.git/description
|
||||
echo "[+] updated default 'description' file for '$REPO'"
|
||||
fi
|
||||
else
|
||||
read -p "description for '$USER/$REPO':" desc
|
||||
echo "$desc" > $dir/.git/description
|
||||
echo "[+] added missing 'description' file for '$REPO'"
|
||||
fi
|
||||
if [ ! -f $dir/.git/url ]; then
|
||||
echo "$CLONE_URL/$REPO.git" > $dir/.git/url
|
||||
echo "[+] added missing 'url' file for '$REPO'"
|
||||
fi
|
||||
echo "[~] processing '$REPO' repository..."
|
||||
mkdir -p $HTML_DIR/$REPO && cd $HTML_DIR/$REPO && stagit -l $COMMIT_LIMIT -u "$PROTO://$URL/$REPO" $dir
|
||||
ln -sf log.html index.html
|
||||
#git --git-dir $dir/.git archive --format=tar.gz -o "$HTML_DIR/$REPO/$REPO.tar.gz" --prefix="$REPO/" HEAD
|
||||
done
|
||||
}
|
||||
|
||||
# Main
|
||||
prepair
|
||||
make_repos
|
||||
make_index
|
BIN
assets/logo.png
Normal file
BIN
assets/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 505 B |
BIN
assets/mostdangerous.png
Normal file
BIN
assets/mostdangerous.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.0 KiB |
40
assets/post-recieve
Executable file
40
assets/post-recieve
Executable file
@ -0,0 +1,40 @@
|
||||
#!/bin/sh
|
||||
# stagit setup helper - developed by acidevegas (https://git.acid.vegas/stagit)
|
||||
# sudo apt-get install libgit2-1.1 libmd4c*
|
||||
|
||||
BASE_URL="https://git.acid.vegas"
|
||||
CLONE_URL="git://git.acid.vegas"
|
||||
HTML_DIR="/home/acidvegas/dev/html/stagit/out"
|
||||
REPOS="$(find $HOME/dev/git -type d -name mirror -prune -o -type d -name .git -print | sort | xargs echo)"
|
||||
|
||||
mkdir -p $HTML_DIR
|
||||
cp favicon.png logo.png style.css $HTML_DIR
|
||||
for dir in $(echo $REPOS); do
|
||||
OWNER=$(basename $(dirname $(dirname $dir)))
|
||||
REPO=$(basename $(dirname $dir))
|
||||
if [ -f $dir/description ]; then
|
||||
REPO_DESC=$(cat $dir/description)
|
||||
if [ "$REPO_DESC" = "Unnamed repository; edit this file 'description' to name the repository." ]; then
|
||||
nano $dir/description
|
||||
echo "[+] updated default 'description' file for '$REPO'"
|
||||
fi
|
||||
else
|
||||
nano $dir/description
|
||||
echo "[+] added missing 'description' file for '$REPO'"
|
||||
fi
|
||||
if [ ! -f $dir/owner ]; then
|
||||
echo $OWNER > $dir/owner
|
||||
echo "[+] added missing 'owner' file for '$REPO'"
|
||||
fi
|
||||
if [ ! -f $dir/url ]; then
|
||||
echo "$CLONE_URL/$REPO.git" > $dir/url
|
||||
echo "[+] added missing 'url' file for '$REPO'"
|
||||
fi
|
||||
echo "[~] processing '$REPO' repository..."
|
||||
mkdir -p $HTML_DIR/$REPO && cd $HTML_DIR/$REPO && stagit -c ".cache" -u "$BASE_URL/$REPO" $dir
|
||||
ln -sf log.html index.html && ln -sf ../style.css style.css && ln -sf ../logo.png logo.png && ln -sf ../favicon.png favicon.png
|
||||
git --git-dir $dir archive HEAD -o "$HTML_DIR/$REPO/$REPO.zip" # TODO: can we do this with libgit2?
|
||||
done
|
||||
echo "[~] creating index..."
|
||||
stagit-index "$REPOS" > $HTML_DIR/index.html
|
||||
echo "[+] finished"
|
42
assets/style.css
Normal file
42
assets/style.css
Normal file
@ -0,0 +1,42 @@
|
||||
*{font-family:monospace}
|
||||
a{color:#8dc;text-decoration:none}
|
||||
a:hover{color:#8dc}
|
||||
a:target {background-color:#222;}
|
||||
body{background-color:#000;color:#bdbdbd;font-size:14px;}
|
||||
h1{display:inline;}
|
||||
table thead td{font-weight:bold;}
|
||||
table td{padding:0 0.3em;}
|
||||
table tr{margin:0px;}
|
||||
img{max-width:800px}
|
||||
.border-bottom{border-bottom:1px solid #222;}
|
||||
.container{border:1px solid #222;border-radius:5px;padding-top:10px;padding-bottom:10px;overflow-x:auto;}
|
||||
.desc{color:#aaa;}
|
||||
td.num{text-align:right;}
|
||||
#blob{text-align:left;}
|
||||
#blob a{color:#555;}
|
||||
#blob a:hover{color:#56c8ff;text-decoration:none;}
|
||||
#blob a:target{color:#eee;}
|
||||
#content table td{vertical-align:top;white-space:nowrap;}
|
||||
#index .category td {font-style: italic;}
|
||||
#index .item-repo td:first-child{padding-left:1.5em;}
|
||||
#footer{font-size:smaller;padding-top:10px;}
|
||||
#files tbody tr:hover td, #index .item-repo:hover td, #log tbody tr:hover td{background-color:#111}
|
||||
#files tr td:nth-child(2), #index .item-repo td:nth-child(2), #log tr td:nth-child(2){white-space:normal;}
|
||||
.container, #container, #content, #footer, #index, #log{width:100%;max-width:800px;}
|
||||
|
||||
a.d, a.h, a.i, a.line {text-decoration:none;}
|
||||
h1, h2, h3, h4, h5, h6{font-size:1em;margin:0;}
|
||||
img, h1, h2{vertical-align middle;}
|
||||
#branches tr td:nth-child(3),
|
||||
#branches tr:hover td, #tags tr:hover td{background-color:#111;}
|
||||
#tags tr td:nth-child(3){white-space:normal;}
|
||||
.A, span.i, pre a.i{color:#00cd00;}
|
||||
.D, span.d, pre a.d {color:#cd0000;}
|
||||
.md{text-align:left;}
|
||||
.md h1{font-size:1.5em;}
|
||||
.md h2{font-size:1.25em;}
|
||||
.md table{border-collapse:collapse;margin:1em 1em;border:1px solid var(--border);}
|
||||
.md table td, .md table th{padding:0.25em 1em;border:1px solid var(--border);}
|
||||
pre a.h{color:#00cdcd;}
|
||||
pre a.h:hover, pre a.i:hover, pre a.d:hover{text-decoration:none;}
|
||||
pre:not(#readme){overflow-x:auto;border:1px solid var(--code-border);border-radius:4px;}
|
6
compat.h
Normal file
6
compat.h
Normal file
@ -0,0 +1,6 @@
|
||||
#undef strlcat
|
||||
size_t strlcat(char *, const char *, size_t);
|
||||
#undef strlcpy
|
||||
size_t strlcpy(char *, const char *, size_t);
|
||||
#undef reallocarray
|
||||
void *reallocarray(void *, size_t, size_t);
|
39
reallocarray.c
Normal file
39
reallocarray.c
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (c) 2008 Otto Moerbeek <otto@drijf.net>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "compat.h"
|
||||
|
||||
/*
|
||||
* This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX
|
||||
* if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW
|
||||
*/
|
||||
#define MUL_NO_OVERFLOW (1UL << (sizeof(size_t) * 4))
|
||||
|
||||
void *
|
||||
reallocarray(void *optr, size_t nmemb, size_t size)
|
||||
{
|
||||
if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
|
||||
nmemb > 0 && SIZE_MAX / nmemb < size) {
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
return realloc(optr, size * nmemb);
|
||||
}
|
47
stagit-index.1
Normal file
47
stagit-index.1
Normal file
@ -0,0 +1,47 @@
|
||||
.Dd August 2, 2021
|
||||
.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.
|
||||
The repos in the index are in the same order as the arguments
|
||||
.Ar repodir
|
||||
specified.
|
||||
.Pp
|
||||
The basename of the directory is used as the repository name.
|
||||
The suffix ".git" is removed from the basename, this suffix is commonly used
|
||||
for "bare" repos.
|
||||
.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 favicon.png
|
||||
favicon image.
|
||||
.It logo.png
|
||||
32x32 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
|
||||
.An Hiltjo Posthuma Aq Mt hiltjo@codemadness.org
|
216
stagit-index.c
Normal file
216
stagit-index.c
Normal file
@ -0,0 +1,216 @@
|
||||
#include <err.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <git2.h>
|
||||
|
||||
static git_repository *repo;
|
||||
static const char *relpath = "";
|
||||
static char description[255] = "Acidvegas Repositories";
|
||||
static char *name = "";
|
||||
static char category[255];
|
||||
|
||||
/* Handle read or write errors for a FILE * stream */
|
||||
void checkfileerror(FILE *fp, const char *name, int mode) {
|
||||
if (mode == 'r' && ferror(fp))
|
||||
errx(1, "read error: %s", name);
|
||||
else if (mode == 'w' && (fflush(fp) || ferror(fp)))
|
||||
errx(1, "write error: %s", name);
|
||||
}
|
||||
|
||||
void joinpath(char *buf, size_t bufsiz, const char *path, const char *path2) {
|
||||
int r;
|
||||
r = snprintf(buf, bufsiz, "%s%s%s", path, path[0] && path[strlen(path) - 1] != '/' ? "/" : "", path2);
|
||||
if (r < 0 || (size_t)r >= bufsiz)
|
||||
errx(1, "path truncated: '%s%s%s'", 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 or ",-." */
|
||||
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) {
|
||||
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: putc(*s, fp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void printtimeshort(FILE *fp, const git_time *intime) {
|
||||
struct tm *intm;
|
||||
time_t t;
|
||||
char out[32];
|
||||
t = (time_t)intime->time;
|
||||
if (!(intm = gmtime(&t)))
|
||||
return;
|
||||
strftime(out, sizeof(out), "%Y-%m-%d", intm);
|
||||
fputs(out, fp);
|
||||
}
|
||||
|
||||
void writeheader(FILE *fp) {
|
||||
fputs("<!DOCTYPE html>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>", fp);
|
||||
xmlencode(fp, description, strlen(description));
|
||||
fputs("</title>\n<meta name=\"description\" content=\"acidvegas repositories\">\n"
|
||||
"<meta name=\"keywords\" content=\"git, repositories, supernets, irc, python, stagit\">\n"
|
||||
"<meta name=\"author\" content=\"acidvegas\">\n", fp);
|
||||
fputs("<link rel=\"icon\" type=\"image/png\" href=\"/assets/favicon.png\">\n"
|
||||
"<link rel=\"stylesheet\" type=\"text/css\" href=\"/assets/style.css\">\n", fp);
|
||||
fputs("<center>\n<img src=\"/assets/acidvegas.png\"><br>\n<img src=\"/assets/mostdangerous.png\"><br><br>\n"
|
||||
"<div id=\"content\">\n\t<table id=\"index\">\n\t\t<thead>\n\t\t\t<tr><td>Name</td><td>Description</td><td>Last commit</td></tr>\n\t\t</thead>\n\t\t<tbody>", fp);
|
||||
}
|
||||
|
||||
void writefooter(FILE *fp) {
|
||||
fputs("\n\t\t</tbody>\n\t</table>\n</div>\n<div id=\"footer\">\n"
|
||||
"\t© 2023 acidvegas, inc • generated with stagit\n"
|
||||
"</div>\n</center>", fp);
|
||||
}
|
||||
|
||||
int writelog(FILE *fp) {
|
||||
git_commit *commit = NULL;
|
||||
const git_signature *author;
|
||||
git_revwalk *w = NULL;
|
||||
git_oid id;
|
||||
char *stripped_name = NULL, *p;
|
||||
int ret = 0;
|
||||
|
||||
git_revwalk_new(&w, repo);
|
||||
git_revwalk_push_head(w);
|
||||
|
||||
if (git_revwalk_next(&id, w) ||
|
||||
git_commit_lookup(&commit, repo, &id)) {
|
||||
ret = -1;
|
||||
goto err;
|
||||
}
|
||||
|
||||
author = git_commit_author(commit);
|
||||
|
||||
/* strip .git suffix */
|
||||
if (!(stripped_name = strdup(name)))
|
||||
err(1, "strdup");
|
||||
if ((p = strrchr(stripped_name, '.')))
|
||||
if (!strcmp(p, ".git"))
|
||||
*p = '\0';
|
||||
|
||||
fputs("\n\t\t\t<tr class=\"item-repo\"><td><a href=\"", fp);
|
||||
percentencode(fp, stripped_name, strlen(stripped_name));
|
||||
fputs("/log.html\">", fp);
|
||||
xmlencode(fp, stripped_name, strlen(stripped_name));
|
||||
fputs("</a></td><td>", fp);
|
||||
xmlencode(fp, description, strlen(description));
|
||||
fputs("</td><td>", fp);
|
||||
if (author)
|
||||
printtimeshort(fp, &(author->when));
|
||||
fputs("</td></tr>", fp);
|
||||
|
||||
git_commit_free(commit);
|
||||
err:
|
||||
git_revwalk_free(w);
|
||||
free(stripped_name);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
FILE *fp;
|
||||
char path[PATH_MAX], repodirabs[PATH_MAX + 1];
|
||||
const char *repodir;
|
||||
int i, ret = 0;
|
||||
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "usage: %s [repodir...]\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* do not search outside the git repository:
|
||||
GIT_CONFIG_LEVEL_APP is the highest level currently */
|
||||
git_libgit2_init();
|
||||
for (i = 1; i <= GIT_CONFIG_LEVEL_APP; i++)
|
||||
git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, i, "");
|
||||
/* do not require the git repository to be owned by the current user */
|
||||
git_libgit2_opts(GIT_OPT_SET_OWNER_VALIDATION, 0);
|
||||
|
||||
#ifdef __OpenBSD__
|
||||
if (pledge("stdio rpath", NULL) == -1)
|
||||
err(1, "pledge");
|
||||
#endif
|
||||
|
||||
writeheader(stdout);
|
||||
|
||||
for (i = 1; i < argc; i++) {
|
||||
if (!strcmp(argv[i], "-c")) {
|
||||
i++;
|
||||
if (i == argc)
|
||||
err(1, "missing argument");
|
||||
repodir = argv[i];
|
||||
fputs("\n\t\t\t<tr class=\"category\"><td colspan=\"3\">", stdout);
|
||||
xmlencode(stdout, repodir, strlen(repodir));
|
||||
fputs("</td></tr>", stdout);
|
||||
continue;
|
||||
}
|
||||
|
||||
repodir = argv[i];
|
||||
if (!realpath(repodir, repodirabs))
|
||||
err(1, "realpath");
|
||||
|
||||
if (git_repository_open_ext(&repo, repodir, GIT_REPOSITORY_OPEN_NO_SEARCH, NULL)) {
|
||||
fprintf(stderr, "%s: cannot open repository\n", argv[0]);
|
||||
ret = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* use directory name as name */
|
||||
if ((name = strrchr(repodirabs, '/')))
|
||||
name++;
|
||||
else
|
||||
name = "";
|
||||
|
||||
/* read description or .git/description */
|
||||
joinpath(path, sizeof(path), repodir, "description");
|
||||
if (!(fp = fopen(path, "r"))) {
|
||||
joinpath(path, sizeof(path), repodir, ".git/description");
|
||||
fp = fopen(path, "r");
|
||||
}
|
||||
description[0] = '\0';
|
||||
if (fp) {
|
||||
if (!fgets(description, sizeof(description), fp))
|
||||
description[0] = '\0';
|
||||
checkfileerror(fp, "description", 'r');
|
||||
fclose(fp);
|
||||
}
|
||||
writelog(stdout);
|
||||
}
|
||||
writefooter(stdout);
|
||||
|
||||
/* cleanup */
|
||||
git_repository_free(repo);
|
||||
git_libgit2_shutdown();
|
||||
|
||||
checkfileerror(stdout, "<stdout>", 'w');
|
||||
|
||||
return ret;
|
||||
}
|
125
stagit.1
Normal file
125
stagit.1
Normal file
@ -0,0 +1,125 @@
|
||||
.Dd August 2, 2021
|
||||
.Dt STAGIT 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm stagit
|
||||
.Nd static git page generator
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Op Fl c Ar cachefile
|
||||
.Op Fl l Ar commits
|
||||
.Op Fl u Ar baseurl
|
||||
.Ar repodir
|
||||
.Sh DESCRIPTION
|
||||
.Nm
|
||||
writes HTML pages for the repository
|
||||
.Ar repodir
|
||||
to the current directory.
|
||||
.Pp
|
||||
The options are as follows:
|
||||
.Bl -tag -width Ds
|
||||
.It Fl c Ar cachefile
|
||||
Cache the entries of the log page up to the point of
|
||||
the last commit.
|
||||
The
|
||||
.Ar cachefile
|
||||
will store the last commit id and the entries in the HTML table.
|
||||
It is up to the user to make sure the state of the
|
||||
.Ar cachefile
|
||||
is in sync with the history of the repository.
|
||||
.It Fl l Ar commits
|
||||
Write a maximum number of
|
||||
.Ar commits
|
||||
to the log.html file only.
|
||||
However the commit files are written as usual.
|
||||
.It Fl u Ar baseurl
|
||||
Base URL to make links in the Atom feeds absolute.
|
||||
For example: "https://git.codemadness.org/stagit/".
|
||||
.El
|
||||
.Pp
|
||||
The options
|
||||
.Fl c
|
||||
and
|
||||
.Fl l
|
||||
cannot be used at the same time.
|
||||
.Pp
|
||||
The following files will be written:
|
||||
.Bl -tag -width Ds
|
||||
.It atom.xml
|
||||
Atom XML feed of the last 100 commits.
|
||||
.It tags.xml
|
||||
Atom XML feed of the tags.
|
||||
.It files.html
|
||||
List of files in the latest tree, linking to the file.
|
||||
.It log.html
|
||||
List of commits in reverse chronological applied commit order, each commit
|
||||
links to a page with a diffstat and diff of the commit.
|
||||
.It refs.html
|
||||
Lists references of the repository such as branches and tags.
|
||||
.El
|
||||
.Pp
|
||||
For each entry 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 diffstat and diff of the commit.
|
||||
It will write the string "Binary files differ" if the data is considered to
|
||||
be non-textual.
|
||||
Too large diffs will be suppressed and a string
|
||||
"Diff is too large, output suppressed" will be written.
|
||||
.Pp
|
||||
When a commit HTML file exists it won't be overwritten again, note that if
|
||||
you've changed
|
||||
.Nm
|
||||
or changed one of the metadata files of the repository it is recommended to
|
||||
recreate all the output files because it will contain old data.
|
||||
To do this remove the output directory and
|
||||
.Ar cachefile ,
|
||||
then recreate the files.
|
||||
.Pp
|
||||
The basename of the directory is used as the repository name.
|
||||
The suffix ".git" is removed from the basename, this suffix is commonly used
|
||||
for "bare" repos.
|
||||
.Pp
|
||||
The content of the follow files specifies the metadata for each repository:
|
||||
.Bl -tag -width Ds
|
||||
.It .git/description or description (bare repo).
|
||||
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.codemadness.org/stagit
|
||||
.El
|
||||
.Pp
|
||||
When a README or LICENSE file exists in HEAD or a .gitmodules submodules file
|
||||
exists in HEAD a direct link in the menu is made.
|
||||
.Pp
|
||||
For changing the style of the page you can use the following files:
|
||||
.Bl -tag -width Ds
|
||||
.It favicon.png
|
||||
favicon image.
|
||||
.It logo.png
|
||||
32x32 logo.
|
||||
.It style.css
|
||||
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
|
||||
.An Hiltjo Posthuma Aq Mt hiltjo@codemadness.org
|
57
strlcat.c
Normal file
57
strlcat.c
Normal file
@ -0,0 +1,57 @@
|
||||
/* $OpenBSD: strlcat.c,v 1.15 2015/03/02 21:41:08 millert Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1998, 2015 Todd C. Miller <Todd.Miller@courtesan.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "compat.h"
|
||||
|
||||
/*
|
||||
* Appends src to string dst of size dsize (unlike strncat, dsize is the
|
||||
* full size of dst, not space left). At most dsize-1 characters
|
||||
* will be copied. Always NUL terminates (unless dsize <= strlen(dst)).
|
||||
* Returns strlen(src) + MIN(dsize, strlen(initial dst)).
|
||||
* If retval >= dsize, truncation occurred.
|
||||
*/
|
||||
size_t
|
||||
strlcat(char *dst, const char *src, size_t dsize)
|
||||
{
|
||||
const char *odst = dst;
|
||||
const char *osrc = src;
|
||||
size_t n = dsize;
|
||||
size_t dlen;
|
||||
|
||||
/* Find the end of dst and adjust bytes left but don't go past end. */
|
||||
while (n-- != 0 && *dst != '\0')
|
||||
dst++;
|
||||
dlen = dst - odst;
|
||||
n = dsize - dlen;
|
||||
|
||||
if (n-- == 0)
|
||||
return(dlen + strlen(src));
|
||||
while (*src != '\0') {
|
||||
if (n != 0) {
|
||||
*dst++ = *src;
|
||||
n--;
|
||||
}
|
||||
src++;
|
||||
}
|
||||
*dst = '\0';
|
||||
|
||||
return(dlen + (src - osrc)); /* count does not include NUL */
|
||||
}
|
52
strlcpy.c
Normal file
52
strlcpy.c
Normal file
@ -0,0 +1,52 @@
|
||||
/* $OpenBSD: strlcpy.c,v 1.12 2015/01/15 03:54:12 millert Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1998, 2015 Todd C. Miller <Todd.Miller@courtesan.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "compat.h"
|
||||
|
||||
/*
|
||||
* Copy string src to buffer dst of size dsize. At most dsize-1
|
||||
* chars will be copied. Always NUL terminates (unless dsize == 0).
|
||||
* Returns strlen(src); if retval >= dsize, truncation occurred.
|
||||
*/
|
||||
size_t
|
||||
strlcpy(char *dst, const char *src, size_t dsize)
|
||||
{
|
||||
const char *osrc = src;
|
||||
size_t nleft = dsize;
|
||||
|
||||
/* Copy as many bytes as will fit. */
|
||||
if (nleft != 0) {
|
||||
while (--nleft != 0) {
|
||||
if ((*dst++ = *src++) == '\0')
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Not enough room in dst, add NUL and traverse rest of src. */
|
||||
if (nleft == 0) {
|
||||
if (dsize != 0)
|
||||
*dst = '\0'; /* NUL-terminate dst */
|
||||
while (*src++)
|
||||
;
|
||||
}
|
||||
|
||||
return(src - osrc - 1); /* count does not include NUL */
|
||||
}
|
Loading…
Reference in New Issue
Block a user