Compare commits
No commits in common. "main" and "a9f6d9fe9682189df7dabd81ed586d51bc31b656" have entirely different histories.
main
...
a9f6d9fe96
5 changed files with 36 additions and 148 deletions
2
LICENSE
2
LICENSE
|
@ -1,6 +1,6 @@
|
|||
MIT/X Consortium License
|
||||
|
||||
(c) 2015-2022 Hiltjo Posthuma <hiltjo@codemadness.org>
|
||||
(c) 2015-2021 Hiltjo Posthuma <hiltjo@codemadness.org>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
|
|
2
Makefile
2
Makefile
|
@ -1,7 +1,7 @@
|
|||
.POSIX:
|
||||
|
||||
NAME = stagit
|
||||
VERSION = 1.1
|
||||
VERSION = 0.9.6
|
||||
|
||||
# paths
|
||||
PREFIX = /usr/local
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
git update-server-info
|
||||
stagit .
|
||||
ln -sf log.html index.html
|
||||
|
||||
cd ..
|
||||
stagit-index */ > index.html
|
||||
|
|
@ -16,16 +16,6 @@ static char description[255] = "Repositories";
|
|||
static char *name = "";
|
||||
static char owner[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)
|
||||
{
|
||||
|
@ -38,28 +28,6 @@ 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 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)
|
||||
|
@ -150,7 +118,7 @@ writelog(FILE *fp)
|
|||
*p = '\0';
|
||||
|
||||
fputs("<tr><td><a href=\"", fp);
|
||||
percentencode(fp, stripped_name, strlen(stripped_name));
|
||||
xmlencode(fp, stripped_name, strlen(stripped_name));
|
||||
fputs("/log.html\">", fp);
|
||||
xmlencode(fp, stripped_name, strlen(stripped_name));
|
||||
fputs("</a></td><td>", fp);
|
||||
|
@ -183,13 +151,7 @@ main(int argc, char *argv[])
|
|||
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)
|
||||
|
@ -226,7 +188,6 @@ main(int argc, char *argv[])
|
|||
if (fp) {
|
||||
if (!fgets(description, sizeof(description), fp))
|
||||
description[0] = '\0';
|
||||
checkfileerror(fp, "description", 'r');
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
|
@ -240,9 +201,8 @@ main(int argc, char *argv[])
|
|||
if (fp) {
|
||||
if (!fgets(owner, sizeof(owner), fp))
|
||||
owner[0] = '\0';
|
||||
checkfileerror(fp, "owner", 'r');
|
||||
fclose(fp);
|
||||
owner[strcspn(owner, "\n")] = '\0';
|
||||
fclose(fp);
|
||||
}
|
||||
writelog(stdout);
|
||||
}
|
||||
|
@ -252,7 +212,5 @@ main(int argc, char *argv[])
|
|||
git_repository_free(repo);
|
||||
git_libgit2_shutdown();
|
||||
|
||||
checkfileerror(stdout, "<stdout>", 'w');
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
125
stagit.c
125
stagit.c
|
@ -71,7 +71,7 @@ static char *licensefiles[] = { "HEAD:LICENSE", "HEAD:LICENSE.md", "HEAD:COPYING
|
|||
static char *license;
|
||||
static char *readmefiles[] = { "HEAD:README", "HEAD:README.md" };
|
||||
static char *readme;
|
||||
static long long nlogcommits = -1; /* -1 indicates not used */
|
||||
static long long nlogcommits = -1; /* < 0 indicates not used */
|
||||
|
||||
/* cache */
|
||||
static git_oid lastoid;
|
||||
|
@ -79,16 +79,6 @@ static char lastoidstr[GIT_OID_HEXSZ + 2]; /* id + newline + NUL byte */
|
|||
static FILE *rcachefp, *wcachefp;
|
||||
static const char *cachefile;
|
||||
|
||||
/* 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)
|
||||
{
|
||||
|
@ -324,14 +314,8 @@ getrefs(struct referenceinfo **pris, size_t *prefcount)
|
|||
if (!(ci = commitinfo_getbyoid(id)))
|
||||
break;
|
||||
|
||||
{
|
||||
struct referenceinfo *newris;
|
||||
if (!(newris = reallocarray(ris, refcount + 1, sizeof(*ris)))) {
|
||||
free(ris);
|
||||
err(1, "realloc");
|
||||
}
|
||||
ris = newris;
|
||||
}
|
||||
if (!(ris = reallocarray(ris, refcount + 1, sizeof(*ris))))
|
||||
err(1, "realloc");
|
||||
ris[refcount].ci = ci;
|
||||
ris[refcount].ref = r;
|
||||
refcount++;
|
||||
|
@ -375,28 +359,6 @@ 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 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)
|
||||
|
@ -518,12 +480,10 @@ writeheader(FILE *fp, const char *title)
|
|||
fputs(" - ", fp);
|
||||
xmlencode(fp, description, strlen(description));
|
||||
fprintf(fp, "</title>\n<link rel=\"icon\" type=\"image/png\" href=\"%s../favicon.png\" />\n", 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);
|
||||
xmlencode(fp, name, strlen(name));
|
||||
fprintf(fp, " Atom Feed (tags)\" href=\"%stags.xml\" />\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);
|
||||
fprintf(fp, "<link rel=\"stylesheet\" type=\"text/css\" href=\"%s../style.css\" />\n", relpath);
|
||||
fputs("</head>\n<body>\n<table><tr><td>", fp);
|
||||
fprintf(fp, "<a href=\"../%s\"><img src=\"%s../logo.png\" alt=\"\" width=\"32\" height=\"32\" /></a>",
|
||||
|
@ -535,7 +495,7 @@ writeheader(FILE *fp, const char *title)
|
|||
fputs("</span></td></tr>", fp);
|
||||
if (cloneurl[0]) {
|
||||
fputs("<tr class=\"url\"><td></td><td>git clone <a href=\"", fp);
|
||||
xmlencode(fp, cloneurl, strlen(cloneurl)); /* not percent-encoded */
|
||||
xmlencode(fp, cloneurl, strlen(cloneurl));
|
||||
fputs("\">", fp);
|
||||
xmlencode(fp, cloneurl, strlen(cloneurl));
|
||||
fputs("</a></td></tr>", fp);
|
||||
|
@ -578,15 +538,14 @@ writeblobhtml(FILE *fp, const git_blob *blob)
|
|||
continue;
|
||||
n++;
|
||||
fprintf(fp, nfmt, n, n, n);
|
||||
xmlencodeline(fp, &s[prev], i - prev + 1);
|
||||
putc('\n', fp);
|
||||
xmlencode(fp, &s[prev], i - prev + 1);
|
||||
prev = i + 1;
|
||||
}
|
||||
/* trailing data */
|
||||
if ((len - prev) > 0) {
|
||||
n++;
|
||||
fprintf(fp, nfmt, n, n, n);
|
||||
xmlencodeline(fp, &s[prev], len - prev);
|
||||
xmlencode(fp, &s[prev], len - prev);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -609,7 +568,7 @@ printcommit(FILE *fp, struct commitinfo *ci)
|
|||
fputs("<b>Author:</b> ", fp);
|
||||
xmlencode(fp, ci->author->name, strlen(ci->author->name));
|
||||
fputs(" <<a href=\"mailto:", fp);
|
||||
xmlencode(fp, ci->author->email, strlen(ci->author->email)); /* not percent-encoded */
|
||||
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);
|
||||
|
@ -704,11 +663,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);
|
||||
percentencode(fp, delta->old_file.path, strlen(delta->old_file.path));
|
||||
xmlencode(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);
|
||||
percentencode(fp, delta->new_file.path, strlen(delta->new_file.path));
|
||||
xmlencode(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");
|
||||
|
@ -780,7 +739,6 @@ 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);
|
||||
|
@ -800,11 +758,8 @@ 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) {
|
||||
remcommits++;
|
||||
if (!r)
|
||||
continue;
|
||||
}
|
||||
if (!nlogcommits && !r)
|
||||
continue;
|
||||
|
||||
if (!(ci = commitinfo_getbyoid(&id)))
|
||||
break;
|
||||
|
@ -812,10 +767,15 @@ writelog(FILE *fp, const git_oid *oid)
|
|||
if (commitinfo_getstats(ci) == -1)
|
||||
goto err;
|
||||
|
||||
if (nlogcommits != 0) {
|
||||
if (nlogcommits < 0) {
|
||||
writelogline(fp, ci);
|
||||
if (nlogcommits > 0)
|
||||
nlogcommits--;
|
||||
} else 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 (cachefile)
|
||||
|
@ -830,7 +790,6 @@ writelog(FILE *fp, const git_oid *oid)
|
|||
printshowfile(fpfile, ci);
|
||||
fputs("</pre>\n", fpfile);
|
||||
writefooter(fpfile);
|
||||
checkfileerror(fpfile, path, 'w');
|
||||
fclose(fpfile);
|
||||
}
|
||||
err:
|
||||
|
@ -838,12 +797,6 @@ 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;
|
||||
|
@ -980,13 +933,14 @@ writeblob(git_object *obj, const char *fpath, const char *filename, size_t files
|
|||
fprintf(fp, " (%zuB)", filesize);
|
||||
fputs("</p><hr/>", fp);
|
||||
|
||||
if (git_blob_is_binary((git_blob *)obj))
|
||||
if (git_blob_is_binary((git_blob *)obj)) {
|
||||
fputs("<p>Binary file.</p>\n", fp);
|
||||
else
|
||||
} else {
|
||||
lc = writeblobhtml(fp, (git_blob *)obj);
|
||||
|
||||
if (ferror(fp))
|
||||
err(1, "fwrite");
|
||||
}
|
||||
writefooter(fp);
|
||||
checkfileerror(fp, fpath, 'w');
|
||||
fclose(fp);
|
||||
|
||||
relpath = "";
|
||||
|
@ -1081,7 +1035,7 @@ writefilestree(FILE *fp, git_tree *tree, const char *path)
|
|||
fputs("<tr><td>", fp);
|
||||
fputs(filemode(git_tree_entry_filemode(entry)), fp);
|
||||
fprintf(fp, "</td><td><a href=\"%s", relpath);
|
||||
percentencode(fp, filepath, strlen(filepath));
|
||||
xmlencode(fp, filepath, strlen(filepath));
|
||||
fputs("\">", fp);
|
||||
xmlencode(fp, entrypath, strlen(entrypath));
|
||||
fputs("</a></td><td class=\"num\" align=\"right\">", fp);
|
||||
|
@ -1236,13 +1190,7 @@ main(int argc, char *argv[])
|
|||
if (!realpath(repodir, repodirabs))
|
||||
err(1, "realpath");
|
||||
|
||||
/* 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 (unveil(repodir, "r") == -1)
|
||||
|
@ -1294,7 +1242,6 @@ main(int argc, char *argv[])
|
|||
if (fpread) {
|
||||
if (!fgets(description, sizeof(description), fpread))
|
||||
description[0] = '\0';
|
||||
checkfileerror(fpread, path, 'r');
|
||||
fclose(fpread);
|
||||
}
|
||||
|
||||
|
@ -1307,9 +1254,8 @@ main(int argc, char *argv[])
|
|||
if (fpread) {
|
||||
if (!fgets(cloneurl, sizeof(cloneurl), fpread))
|
||||
cloneurl[0] = '\0';
|
||||
checkfileerror(fpread, path, 'r');
|
||||
fclose(fpread);
|
||||
cloneurl[strcspn(cloneurl, "\n")] = '\0';
|
||||
fclose(fpread);
|
||||
}
|
||||
|
||||
/* check LICENSE */
|
||||
|
@ -1369,15 +1315,13 @@ main(int argc, char *argv[])
|
|||
while (!feof(rcachefp)) {
|
||||
n = fread(buf, 1, sizeof(buf), rcachefp);
|
||||
if (ferror(rcachefp))
|
||||
break;
|
||||
err(1, "fread");
|
||||
if (fwrite(buf, 1, n, fp) != n ||
|
||||
fwrite(buf, 1, n, wcachefp) != n)
|
||||
break;
|
||||
err(1, "fwrite");
|
||||
}
|
||||
checkfileerror(rcachefp, cachefile, 'r');
|
||||
fclose(rcachefp);
|
||||
}
|
||||
checkfileerror(wcachefp, tmppath, 'w');
|
||||
fclose(wcachefp);
|
||||
} else {
|
||||
if (head)
|
||||
|
@ -1386,7 +1330,6 @@ main(int argc, char *argv[])
|
|||
|
||||
fputs("</tbody></table>", fp);
|
||||
writefooter(fp);
|
||||
checkfileerror(fp, "log.html", 'w');
|
||||
fclose(fp);
|
||||
|
||||
/* files for HEAD */
|
||||
|
@ -1395,7 +1338,6 @@ main(int argc, char *argv[])
|
|||
if (head)
|
||||
writefiles(fp, head);
|
||||
writefooter(fp);
|
||||
checkfileerror(fp, "files.html", 'w');
|
||||
fclose(fp);
|
||||
|
||||
/* summary page with branches and tags */
|
||||
|
@ -1403,19 +1345,16 @@ main(int argc, char *argv[])
|
|||
writeheader(fp, "Refs");
|
||||
writerefs(fp);
|
||||
writefooter(fp);
|
||||
checkfileerror(fp, "refs.html", 'w');
|
||||
fclose(fp);
|
||||
|
||||
/* Atom feed */
|
||||
fp = efopen("atom.xml", "w");
|
||||
writeatom(fp, 1);
|
||||
checkfileerror(fp, "atom.xml", 'w');
|
||||
fclose(fp);
|
||||
|
||||
/* Atom feed for tags / releases */
|
||||
fp = efopen("tags.xml", "w");
|
||||
writeatom(fp, 0);
|
||||
checkfileerror(fp, "tags.xml", 'w');
|
||||
fclose(fp);
|
||||
|
||||
/* rename new cache file on success */
|
||||
|
|
Loading…
Reference in a new issue