commit 022c85fdcf44b9b8723ce6983c62d585e2563c9d
parent 4d3348560faf732f9304a05a269b4d33df930988
Author: Emma Weaver <emma@waeaves.com>
Date: Fri, 15 May 2026 23:59:08 -0400
initial refactoring, visual improvements
Diffstat:
| A | .gitignore | 6 | ++++++ |
| A | TODO | 33 | +++++++++++++++++++++++++++++++++ |
| D | favicon.png | 0 | |
| D | logo.png | 0 | |
| M | stagit.c | 906 | +++++++++++++++++++++++++++++++++++++++++++++---------------------------------- |
| M | style.css | 103 | +++++++++++++++++++++++++++++++++---------------------------------------------- |
6 files changed, 603 insertions(+), 445 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -0,0 +1,6 @@
+*.o
+**.swp
+stagit
+stagit-index
+test
+
diff --git a/TODO b/TODO
@@ -0,0 +1,33 @@
+STUFF I WANT TO CHANGE ABOUT THIS
+
+[ ] Redesign options / I/O
+ - Usage: stagit [-i] [-d DESTDIR] [-c CACHEDIR] REPO [REPO_2 ... REPO_N]
+ - generates index page if -i
+ - puts repos in their own subdirectory if -i or REPO_2
+ - writes output into DESTDIR (defaults to pwd)
+
+[ ] add warnings on missing decorations
+ - flag that skips repos with incomplete metadata
+ - (possible mechanism to hide private repos)
+
+[ ] create config.h file
+ - reused format strings (such as header)
+
+[ ] handle projects with large numbers of files better
+ - show "{total LoC}L + {total binary B}B" for each directory
+ - (VERY IMPORTANT to still punish large projects)
+
+ - NEW FEATURES
+[ ] add lines of code totals on files page
+[ ] add lines of code to top of each file's page
+[ ] allow download of arbitrary binary files
+[ ] display images on their file page (png, jpg, webp, which others?)
+[ ] / add support for projects with folders in them
+[ ] remove leading dot from links, so they can be served more easily
+ - END NEW FEATURES
+
+[ ] Redo caching, to never repeat work
+ - but it needs to also not increase the complexity.
+ - it's ok to duplicate storage
+ - perhaps: save every reusable intermediate into a file.
+ then, check for changes and reuse if valid.
diff --git a/favicon.png b/favicon.png
Binary files differ.
diff --git a/logo.png b/logo.png
Binary files differ.
diff --git a/stagit.c b/stagit.c
@@ -16,16 +16,16 @@
#include "compat.h"
-#define LEN(s) (sizeof(s)/sizeof(*s))
+#define LEN(s) (sizeof(s)/sizeof(*s))
-struct deltainfo {
+struct DeltaInfo {
git_patch *patch;
- size_t addcount;
- size_t delcount;
+ size_t add_count;
+ size_t del_count;
};
-struct commitinfo {
+struct CommitInfo {
const git_oid *id;
char oid[GIT_OID_HEXSZ + 1];
@@ -33,47 +33,51 @@ struct commitinfo {
const git_signature *author;
const git_signature *committer;
- const char *summary;
- const char *msg;
+ const char *summary;
+ const char *msg;
- git_diff *diff;
+ git_diff *diff;
git_commit *commit;
git_commit *parent;
- git_tree *commit_tree;
- git_tree *parent_tree;
+ git_tree *commit_tree;
+ git_tree *parent_tree;
- size_t addcount;
- size_t delcount;
- size_t filecount;
+ size_t add_count;
+ size_t del_count;
+ size_t file_count;
- struct deltainfo **deltas;
+ struct DeltaInfo **deltas;
size_t ndeltas;
};
/* reference and associated data for sorting */
-struct referenceinfo {
+struct ReferenceInfo {
struct git_reference *ref;
- struct commitinfo *ci;
+ struct CommitInfo *ci;
};
static git_repository *repo;
+/* flags + arguments */
static const char *baseurl = ""; /* base URL to make absolute RSS/Atom URI */
static const char *relpath = "";
static const char *repodir;
static char *name = "";
-static char *strippedname = "";
+static char *stripped_name = "";
static char description[255];
static char cloneurl[1024];
static char *submodules;
-static char *licensefiles[] = { "HEAD:LICENSE", "HEAD:LICENSE.md", "HEAD:LICENSE.txt", "HEAD:COPYING" };
+static char *license_files[] = {
+ "HEAD:LICENSE", "HEAD:LICENSE.md", "HEAD:LICENSE.txt", "HEAD:COPYING"
+};
static char *license;
-static char *readmefiles[] = { "HEAD:README", "HEAD:README.md" };
+static char *readme_files[] = { "HEAD:README", "HEAD:README.md" };
static char *readme;
static long long nlogcommits = -1; /* -1 indicates not used */
/* cache */
+// TODO yuckyyy
static git_oid lastoid;
static char lastoidstr[GIT_OID_HEXSZ + 2]; /* id + newline + NUL byte */
static FILE *rcachefp, *wcachefp;
@@ -81,7 +85,7 @@ static const char *cachefile;
/* Handle read or write errors for a FILE * stream */
void
-checkfileerror(FILE *fp, const char *name, int mode)
+check_file_error(FILE *fp, const char *name, int mode)
{
if (mode == 'r' && ferror(fp))
errx(1, "read error: %s", name);
@@ -90,31 +94,38 @@ checkfileerror(FILE *fp, const char *name, int mode)
}
void
-joinpath(char *buf, size_t bufsiz, const char *path, const char *path2)
+join_path(char *buf, size_t bufsiz, const char *path, const char *path2)
{
int r;
+ char * separator = "";
+ if(path[0] && path[strlen(path) - 1] != '/')
+ separator = "/";
- r = snprintf(buf, bufsiz, "%s%s%s",
- path, path[0] && path[strlen(path) - 1] != '/' ? "/" : "", path2);
+ r = snprintf(buf, bufsiz, "%s%s%s", path, separator, path2);
if (r < 0 || (size_t)r >= bufsiz)
- errx(1, "path truncated: '%s%s%s'",
- path, path[0] && path[strlen(path) - 1] != '/' ? "/" : "", path2);
+ errx(
+ 1,
+ "path truncated: '%s%s%s'",
+ path,
+ separator,
+ path2
+ );
}
void
-deltainfo_free(struct deltainfo *di)
+delta_info_free(struct DeltaInfo *di)
{
- if (!di)
- return;
+ if (!di) return;
+
git_patch_free(di->patch);
memset(di, 0, sizeof(*di));
free(di);
}
int
-commitinfo_getstats(struct commitinfo *ci)
+commit_info_get_stats(struct CommitInfo *ci)
{
- struct deltainfo *di;
+ struct DeltaInfo *di;
git_diff_options opts;
git_diff_find_options fopts;
const git_diff_delta *delta;
@@ -135,28 +146,29 @@ commitinfo_getstats(struct commitinfo *ci)
git_diff_init_options(&opts, GIT_DIFF_OPTIONS_VERSION);
opts.flags |= GIT_DIFF_DISABLE_PATHSPEC_MATCH |
- GIT_DIFF_IGNORE_SUBMODULES |
- GIT_DIFF_INCLUDE_TYPECHANGE;
+ GIT_DIFF_IGNORE_SUBMODULES |
+ GIT_DIFF_INCLUDE_TYPECHANGE;
if (git_diff_tree_to_tree(&(ci->diff), repo, ci->parent_tree, ci->commit_tree, &opts))
goto err;
if (git_diff_find_init_options(&fopts, GIT_DIFF_FIND_OPTIONS_VERSION))
goto err;
/* find renames and copies, exact matches (no heuristic) for renames. */
- fopts.flags |= GIT_DIFF_FIND_RENAMES | GIT_DIFF_FIND_COPIES |
- GIT_DIFF_FIND_EXACT_MATCH_ONLY;
+ fopts.flags |= GIT_DIFF_FIND_RENAMES |
+ GIT_DIFF_FIND_COPIES |
+ GIT_DIFF_FIND_EXACT_MATCH_ONLY;
if (git_diff_find_similar(ci->diff, &fopts))
goto err;
ndeltas = git_diff_num_deltas(ci->diff);
- if (ndeltas && !(ci->deltas = calloc(ndeltas, sizeof(struct deltainfo *))))
+ if (ndeltas && !(ci->deltas = calloc(ndeltas, sizeof(struct DeltaInfo *))))
err(1, "calloc");
for (i = 0; i < ndeltas; i++) {
if (git_patch_from_diff(&patch, ci->diff, i))
goto err;
- if (!(di = calloc(1, sizeof(struct deltainfo))))
+ if (!(di = calloc(1, sizeof(struct DeltaInfo))))
err(1, "calloc");
di->patch = patch;
ci->deltas[i] = di;
@@ -175,17 +187,17 @@ commitinfo_getstats(struct commitinfo *ci)
if (git_patch_get_line_in_hunk(&line, patch, j, k))
break;
if (line->old_lineno == -1) {
- di->addcount++;
- ci->addcount++;
+ di->add_count++;
+ ci->add_count++;
} else if (line->new_lineno == -1) {
- di->delcount++;
- ci->delcount++;
+ di->del_count++;
+ ci->del_count++;
}
}
}
}
ci->ndeltas = i;
- ci->filecount = i;
+ ci->file_count = i;
return 0;
@@ -201,19 +213,19 @@ err:
if (ci->deltas)
for (i = 0; i < ci->ndeltas; i++)
- deltainfo_free(ci->deltas[i]);
+ delta_info_free(ci->deltas[i]);
free(ci->deltas);
ci->deltas = NULL;
ci->ndeltas = 0;
- ci->addcount = 0;
- ci->delcount = 0;
- ci->filecount = 0;
+ ci->add_count = 0;
+ ci->del_count = 0;
+ ci->file_count = 0;
return -1;
}
void
-commitinfo_free(struct commitinfo *ci)
+commit_info_free(struct CommitInfo *ci)
{
size_t i;
@@ -221,7 +233,7 @@ commitinfo_free(struct commitinfo *ci)
return;
if (ci->deltas)
for (i = 0; i < ci->ndeltas; i++)
- deltainfo_free(ci->deltas[i]);
+ delta_info_free(ci->deltas[i]);
free(ci->deltas);
git_diff_free(ci->diff);
@@ -233,12 +245,12 @@ commitinfo_free(struct commitinfo *ci)
free(ci);
}
-struct commitinfo *
-commitinfo_getbyoid(const git_oid *id)
+struct CommitInfo *
+commit_info_get_by_oid(const git_oid *id)
{
- struct commitinfo *ci;
+ struct CommitInfo *ci;
- if (!(ci = calloc(1, sizeof(struct commitinfo))))
+ if (!(ci = calloc(1, sizeof(struct CommitInfo))))
err(1, "calloc");
if (git_commit_lookup(&(ci->commit), repo, id))
@@ -256,7 +268,7 @@ commitinfo_getbyoid(const git_oid *id)
return ci;
err:
- commitinfo_free(ci);
+ commit_info_free(ci);
return NULL;
}
@@ -264,7 +276,7 @@ err:
int
refs_cmp(const void *v1, const void *v2)
{
- const struct referenceinfo *r1 = v1, *r2 = v2;
+ const struct ReferenceInfo *r1 = v1, *r2 = v2;
time_t t1, t2;
int r;
@@ -276,15 +288,17 @@ refs_cmp(const void *v1, const void *v2)
if ((r = t1 > t2 ? -1 : (t1 == t2 ? 0 : 1)))
return r;
- return strcmp(git_reference_shorthand(r1->ref),
- git_reference_shorthand(r2->ref));
+ return strcmp(
+ git_reference_shorthand(r1->ref),
+ git_reference_shorthand(r2->ref)
+ );
}
int
-getrefs(struct referenceinfo **pris, size_t *prefcount)
+get_refs(struct ReferenceInfo **pris, size_t *prefcount)
{
- struct referenceinfo *ris = NULL;
- struct commitinfo *ci = NULL;
+ struct ReferenceInfo *ris = NULL;
+ struct CommitInfo *ci = NULL;
git_reference_iterator *it = NULL;
const git_oid *id = NULL;
git_object *obj = NULL;
@@ -316,12 +330,14 @@ getrefs(struct referenceinfo **pris, size_t *prefcount)
default:
continue;
}
- if (!git_reference_target(r) ||
- git_reference_peel(&obj, r, GIT_OBJ_ANY))
+ if (
+ !git_reference_target(r) ||
+ git_reference_peel(&obj, r, GIT_OBJ_ANY)
+ )
goto err;
if (!(id = git_object_id(obj)))
goto err;
- if (!(ci = commitinfo_getbyoid(id)))
+ if (!(ci = commit_info_get_by_oid(id)))
break;
if (!(ris = reallocarray(ris, refcount + 1, sizeof(*ris))))
@@ -348,9 +364,9 @@ getrefs(struct referenceinfo **pris, size_t *prefcount)
err:
git_object_free(obj);
git_reference_free(dref);
- commitinfo_free(ci);
+ commit_info_free(ci);
for (i = 0; i < refcount; i++) {
- commitinfo_free(ris[i].ci);
+ commit_info_free(ris[i].ci);
git_reference_free(ris[i].ref);
}
free(ris);
@@ -371,7 +387,7 @@ efopen(const char *filename, const char *flags)
/* Percent-encode, see RFC3986 section 2.1. */
void
-percentencode(FILE *fp, const char *s, size_t len)
+put_percent_encoded(FILE *fp, const char *s, size_t len)
{
static char tab[] = "0123456789ABCDEF";
unsigned char uc;
@@ -380,8 +396,13 @@ percentencode(FILE *fp, const char *s, size_t len)
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 == ']') {
+ if (
+ uc < ',' ||
+ uc >= 127 ||
+ (uc >= ':' && uc <= '@') ||
+ uc == '[' ||
+ uc == ']'
+ ) {
putc('%', fp);
putc(tab[(uc >> 4) & 0x0f], fp);
putc(tab[uc & 0x0f], fp);
@@ -393,39 +414,36 @@ percentencode(FILE *fp, const char *s, size_t len)
/* Escape characters below as HTML 2.0 / XML 1.0. */
void
-xmlencode(FILE *fp, const char *s, size_t len)
+put_xml_char(FILE *fp, const char *s)
{
- 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);
- }
+ 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);
}
+
}
-/* Escape characters below as HTML 2.0 / XML 1.0, ignore printing '\r', '\n' */
void
-xmlencodeline(FILE *fp, const char *s, size_t len)
+put_xml(FILE *fp, const char *s, size_t len)
{
- size_t i;
+ size_t i = 0;
+ for (; *s && i < len; s++, i++)
+ put_xml_char(fp, s);
+}
- 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;
- case '\r': break; /* ignore CR */
- case '\n': break; /* ignore LF */
- default: putc(*s, fp);
- }
+/* terminate on, but don't print '\r' or '\n' */
+void
+put_xml_line(FILE *fp, const char *s, size_t len)
+{
+ size_t i = 0;
+ for (; *s && i < len; s++, i++) {
+ if (*s == '\r' || *s == '\n')
+ break;
+ put_xml_char(fp, s);
}
}
@@ -474,12 +492,22 @@ printtime(FILE *fp, const git_time *intime)
if (!(intm = gmtime(&t)))
return;
strftime(out, sizeof(out), "%a, %e %b %Y %H:%M:%S", intm);
- if (intime->offset < 0)
- fprintf(fp, "%s -%02d%02d", out,
- -(intime->offset) / 60, -(intime->offset) % 60);
- else
- fprintf(fp, "%s +%02d%02d", out,
- intime->offset / 60, intime->offset % 60);
+ if (intime->offset < 0) {
+ fprintf(
+ fp,
+ "%s -%02d%02d",
+ out,
+ -(intime->offset) / 60,
+ -(intime->offset) % 60
+ );
+ } else {
+ fprintf(
+ fp,
+ "%s +%02d%02d", out,
+ intime->offset / 60,
+ intime->offset % 60
+ );
+ }
}
void
@@ -497,74 +525,107 @@ printtimeshort(FILE *fp, const git_time *intime)
}
void
-writeheader(FILE *fp, const char *title)
+write_header(FILE *fp, const char *title)
{
- fputs("<!DOCTYPE html>\n"
- "<html>\n<head>\n"
+ fputs(
+ "<!DOCTYPE html>\n"
+ "<html>" "\n"
+ "<head>" "\n"
"<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />\n"
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n"
- "<title>", fp);
- xmlencode(fp, title, strlen(title));
- if (title[0] && strippedname[0])
+ "<title>",
+ fp
+ );
+ put_xml(fp, title, strlen(title));
+ if (title[0] && stripped_name[0])
fputs(" - ", fp);
- xmlencode(fp, strippedname, strlen(strippedname));
+ put_xml(fp, stripped_name, strlen(stripped_name));
if (description[0])
fputs(" - ", fp);
- xmlencode(fp, description, strlen(description));
+ put_xml(fp, description, strlen(description));
fprintf(fp, "</title>\n<link rel=\"icon\" type=\"image/png\" href=\"%sfavicon.png\" />\n", relpath);
fputs("<link rel=\"alternate\" type=\"application/atom+xml\" title=\"", fp);
- xmlencode(fp, name, strlen(name));
+ put_xml(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));
+ put_xml(fp, name, strlen(name));
fprintf(fp, " Atom Feed (tags)\" href=\"%stags.xml\" />\n", relpath);
fprintf(fp, "<link rel=\"stylesheet\" type=\"text/css\" href=\"%sstyle.css\" />\n", relpath);
- fputs("</head>\n<body>\n<table><tr><td>", fp);
- fprintf(fp, "<a href=\"../%s\"><img src=\"%slogo.png\" alt=\"\" width=\"32\" height=\"32\" /></a>",
- relpath, relpath);
+ fputs("</head>" "\n" "<body>" "\n", fp);
+
+ // TODO handle backlink using config.h
+ fprintf(fp, "<p><a href=\"%s\">home</a></p>" "\n", "/files.html");
+
+ fputs("<table><tr><td>", fp);
+ fprintf(
+ fp,
+ "<a href=\"../%s\"><img src=\"%slogo.png\" alt=\"\" width=\"32\" height=\"32\" /></a>",
+ relpath,
+ relpath
+ );
fputs("</td><td><h1>", fp);
- xmlencode(fp, strippedname, strlen(strippedname));
+ put_xml(fp, stripped_name, strlen(stripped_name));
fputs("</h1><span class=\"desc\">", fp);
- xmlencode(fp, description, strlen(description));
+ put_xml(fp, description, strlen(description));
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 */
+ put_xml(fp, cloneurl, strlen(cloneurl)); /* not percent-encoded */
fputs("\">", fp);
- xmlencode(fp, cloneurl, strlen(cloneurl));
+ put_xml(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);
+
+ if (readme)
+ fprintf(
+ fp,
+ "<a href=\"%sfile/%s.html\">README</a> | ",
+ relpath,
+ readme
+ );
+
fprintf(fp, "<a href=\"%sfiles.html\">Files</a> | ", relpath);
+ fprintf(fp, "<a href=\"%slog.html\">Log</a> | ", relpath);
fprintf(fp, "<a href=\"%srefs.html\">Refs</a>", relpath);
+
if (submodules)
- fprintf(fp, " | <a href=\"%sfile/%s.html\">Submodules</a>",
- relpath, submodules);
- if (readme)
- fprintf(fp, " | <a href=\"%sfile/%s.html\">README</a>",
- relpath, readme);
+ fprintf(
+ fp,
+ " | <a href=\"%sfile/%s.html\">Submodules</a>",
+ relpath,
+ submodules
+ );
+
if (license)
- fprintf(fp, " | <a href=\"%sfile/%s.html\">LICENSE</a>",
- relpath, license);
- fputs("</td></tr></table>\n<hr/>\n<div id=\"content\">\n", fp);
+ fprintf(
+ fp,
+ " | <a href=\"%sfile/%s.html\">LICENSE</a>",
+ relpath,
+ license
+ );
+
+ fputs("</td></tr></table>" "\n", fp);
+ fputs("<hr/>" "\n", fp);
+
+ fputs("<div id=\"content\">" "\n", fp);
}
void
-writefooter(FILE *fp)
+write_footer(FILE *fp)
{
- fputs("</div>\n</body>\n</html>\n", fp);
+ fputs("</div>" "\n" "</body>" "\n" "</html>" "\n", fp);
}
size_t
-writeblobhtml(FILE *fp, const git_blob *blob)
+write_blob_html(FILE *fp, const git_blob *blob)
{
size_t n = 0, i, len, prev;
- const char *nfmt = "<a href=\"#l%zu\" class=\"line\" id=\"l%zu\">%7zu</a> ";
+ const char *nfmt = "<a href=\"#l%zu\" class=\"line\" id=\"l%zu\">%7zu</a>";
const char *s = git_blob_rawcontent(blob);
len = git_blob_rawsize(blob);
- fputs("<pre id=\"blob\">\n", fp);
+ fputs("<pre id=\"blob\">" "\n", fp);
if (len > 0) {
for (i = 0, prev = 0; i < len; i++) {
@@ -572,25 +633,25 @@ 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);
+ put_xml_line(fp, &s[prev], i - prev + 1);
+ fputs("\n", fp);
prev = i + 1;
}
/* trailing data */
if ((len - prev) > 0) {
n++;
fprintf(fp, nfmt, n, n, n);
- xmlencodeline(fp, &s[prev], len - prev);
+ put_xml_line(fp, &s[prev], len - prev);
}
}
- fputs("</pre>\n", fp);
+ fputs("</pre>" "\n", fp);
return n;
}
void
-printcommit(FILE *fp, struct commitinfo *ci)
+print_commit(FILE *fp, struct CommitInfo *ci)
{
fprintf(fp, "<b>commit</b> <a href=\"%scommit/%s.html\">%s</a>\n",
relpath, ci->oid, ci->oid);
@@ -601,24 +662,24 @@ printcommit(FILE *fp, struct commitinfo *ci)
if (ci->author) {
fputs("<b>Author:</b> ", fp);
- xmlencode(fp, ci->author->name, strlen(ci->author->name));
+ put_xml(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 */
+ put_xml(fp, ci->author->email, strlen(ci->author->email)); /* not percent-encoded */
fputs("\">", fp);
- xmlencode(fp, ci->author->email, strlen(ci->author->email));
+ put_xml(fp, ci->author->email, strlen(ci->author->email));
fputs("</a>>\n<b>Date:</b> ", fp);
printtime(fp, &(ci->author->when));
putc('\n', fp);
}
if (ci->msg) {
putc('\n', fp);
- xmlencode(fp, ci->msg, strlen(ci->msg));
+ put_xml(fp, ci->msg, strlen(ci->msg));
putc('\n', fp);
}
}
void
-printshowfile(FILE *fp, struct commitinfo *ci)
+print_show_file(FILE *fp, struct CommitInfo *ci)
{
const git_diff_delta *delta;
const git_diff_hunk *hunk;
@@ -628,32 +689,35 @@ printshowfile(FILE *fp, struct commitinfo *ci)
char linestr[80];
int c;
- printcommit(fp, ci);
+ print_commit(fp, ci);
if (!ci->deltas)
return;
- if (ci->filecount > 1000 ||
- ci->ndeltas > 1000 ||
- ci->addcount > 100000 ||
- ci->delcount > 100000) {
+ if (
+ ci->file_count > 1000 ||
+ ci->ndeltas > 1000 ||
+ ci->add_count > 100000 ||
+ ci->del_count > 100000
+ ) {
fputs("Diff is too large, output suppressed.\n", fp);
return;
}
/* diff stat */
- fputs("<b>Diffstat:</b>\n<table>", fp);
+ fputs("<b>Diffstat:</b>" "\n", fp);
+ fputs("<table id=\"diffstat\">", fp);
for (i = 0; i < ci->ndeltas; i++) {
delta = git_patch_get_delta(ci->deltas[i]->patch);
switch (delta->status) {
- case GIT_DELTA_ADDED: c = 'A'; break;
- case GIT_DELTA_COPIED: c = 'C'; break;
- case GIT_DELTA_DELETED: c = 'D'; break;
- case GIT_DELTA_MODIFIED: c = 'M'; break;
- case GIT_DELTA_RENAMED: c = 'R'; break;
+ case GIT_DELTA_ADDED: c = 'A'; break;
+ case GIT_DELTA_COPIED: c = 'C'; break;
+ case GIT_DELTA_DELETED: c = 'D'; break;
+ case GIT_DELTA_MODIFIED: c = 'M'; break;
+ case GIT_DELTA_RENAMED: c = 'R'; break;
case GIT_DELTA_TYPECHANGE: c = 'T'; break;
- default: c = ' '; break;
+ default: c = ' '; break;
}
if (c == ' ')
fprintf(fp, "<tr><td>%c", c);
@@ -661,14 +725,14 @@ printshowfile(FILE *fp, struct commitinfo *ci)
fprintf(fp, "<tr><td class=\"%c\">%c", c, c);
fprintf(fp, "</td><td><a href=\"#h%zu\">", i);
- xmlencode(fp, delta->old_file.path, strlen(delta->old_file.path));
+ put_xml(fp, delta->old_file.path, strlen(delta->old_file.path));
if (strcmp(delta->old_file.path, delta->new_file.path)) {
fputs(" -> ", fp);
- xmlencode(fp, delta->new_file.path, strlen(delta->new_file.path));
+ put_xml(fp, delta->new_file.path, strlen(delta->new_file.path));
}
- add = ci->deltas[i]->addcount;
- del = ci->deltas[i]->delcount;
+ add = ci->deltas[i]->add_count;
+ del = ci->deltas[i]->del_count;
changed = add + del;
total = sizeof(linestr) - 2;
if (changed > total) {
@@ -680,17 +744,25 @@ printshowfile(FILE *fp, struct commitinfo *ci)
memset(&linestr, '+', add);
memset(&linestr[add], '-', del);
- fprintf(fp, "</a></td><td> | </td><td class=\"num\">%zu</td><td><span class=\"i\">",
- ci->deltas[i]->addcount + ci->deltas[i]->delcount);
+ fprintf(
+ fp,
+ "</a></td>"
+ // "<td> | </td>"
+ "<td class=\"num\">%zu</td><td><span class=\"i\">",
+ ci->deltas[i]->add_count + ci->deltas[i]->del_count
+ );
fwrite(&linestr, 1, add, fp);
fputs("</span><span class=\"d\">", fp);
fwrite(&linestr[add], 1, del, fp);
fputs("</span></td></tr>\n", fp);
}
- fprintf(fp, "</table></pre><pre>%zu file%s changed, %zu insertion%s(+), %zu deletion%s(-)\n",
- ci->filecount, ci->filecount == 1 ? "" : "s",
- ci->addcount, ci->addcount == 1 ? "" : "s",
- ci->delcount, ci->delcount == 1 ? "" : "s");
+ fprintf(
+ fp,
+ "</table></pre><pre>%zu file%s changed, %zu insertion%s(+), %zu deletion%s(-)\n",
+ ci->file_count, ci->file_count == 1 ? "" : "s",
+ ci->add_count, ci->add_count == 1 ? "" : "s",
+ ci->del_count, ci->del_count == 1 ? "" : "s"
+ );
fputs("<hr/>", fp);
@@ -698,13 +770,13 @@ 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));
+ put_percent_encoded(fp, delta->old_file.path, strlen(delta->old_file.path));
fputs(".html\">", fp);
- xmlencode(fp, delta->old_file.path, strlen(delta->old_file.path));
+ put_xml(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));
+ put_percent_encoded(fp, delta->new_file.path, strlen(delta->new_file.path));
fprintf(fp, ".html\">");
- xmlencode(fp, delta->new_file.path, strlen(delta->new_file.path));
+ put_xml(fp, delta->new_file.path, strlen(delta->new_file.path));
fprintf(fp, "</a></b>\n");
/* check binary data */
@@ -719,7 +791,7 @@ printshowfile(FILE *fp, struct commitinfo *ci)
break;
fprintf(fp, "<a href=\"#h%zu-%zu\" id=\"h%zu-%zu\" class=\"h\">", i, j, i, j);
- xmlencode(fp, hunk->header, hunk->header_len);
+ put_xml(fp, hunk->header, hunk->header_len);
fputs("</a>", fp);
for (k = 0; ; k++) {
@@ -733,7 +805,7 @@ printshowfile(FILE *fp, struct commitinfo *ci)
i, j, k, i, j, k);
else
putc(' ', fp);
- xmlencodeline(fp, line->content, line->content_len);
+ put_xml_line(fp, line->content, line->content_len);
putc('\n', fp);
if (line->old_lineno == -1 || line->new_lineno == -1)
fputs("</a>", fp);
@@ -743,7 +815,7 @@ printshowfile(FILE *fp, struct commitinfo *ci)
}
void
-writelogline(FILE *fp, struct commitinfo *ci)
+write_log_line(FILE *fp, struct CommitInfo *ci)
{
fputs("<tr><td>", fp);
if (ci->author)
@@ -751,25 +823,25 @@ writelogline(FILE *fp, struct commitinfo *ci)
fputs("</td><td>", fp);
if (ci->summary) {
fprintf(fp, "<a href=\"%scommit/%s.html\">", relpath, ci->oid);
- xmlencode(fp, ci->summary, strlen(ci->summary));
+ put_xml(fp, ci->summary, strlen(ci->summary));
fputs("</a>", fp);
}
fputs("</td><td>", fp);
if (ci->author)
- xmlencode(fp, ci->author->name, strlen(ci->author->name));
+ put_xml(fp, ci->author->name, strlen(ci->author->name));
fputs("</td><td class=\"num\" align=\"right\">", fp);
- fprintf(fp, "%zu", ci->filecount);
+ fprintf(fp, "%zu", ci->file_count);
fputs("</td><td class=\"num\" align=\"right\">", fp);
- fprintf(fp, "+%zu", ci->addcount);
+ fprintf(fp, "+%zu", ci->add_count);
fputs("</td><td class=\"num\" align=\"right\">", fp);
- fprintf(fp, "-%zu", ci->delcount);
+ fprintf(fp, "-%zu", ci->del_count);
fputs("</td></tr>\n", fp);
}
int
-writelog(FILE *fp, const git_oid *oid)
+write_log(FILE *fp, const git_oid *oid)
{
- struct commitinfo *ci;
+ struct CommitInfo *ci;
git_revwalk *w = NULL;
git_oid id;
char path[PATH_MAX], oidstr[GIT_OID_HEXSZ + 1];
@@ -800,51 +872,146 @@ writelog(FILE *fp, const git_oid *oid)
continue;
}
- if (!(ci = commitinfo_getbyoid(&id)))
+ if (!(ci = commit_info_get_by_oid(&id)))
break;
/* diffstat: for stagit HTML required for the log.html line */
- if (commitinfo_getstats(ci) == -1)
+ if (commit_info_get_stats(ci) == -1)
goto err;
if (nlogcommits != 0) {
- writelogline(fp, ci);
+ write_log_line(fp, ci);
if (nlogcommits > 0)
nlogcommits--;
}
if (cachefile)
- writelogline(wcachefp, ci);
+ write_log_line(wcachefp, ci);
/* check if file exists if so skip it */
if (r) {
relpath = "../";
fpfile = efopen(path, "w");
- writeheader(fpfile, ci->summary);
+ write_header(fpfile, ci->summary);
+
fputs("<pre>", fpfile);
- printshowfile(fpfile, ci);
+ print_show_file(fpfile, ci);
fputs("</pre>\n", fpfile);
- writefooter(fpfile);
- checkfileerror(fpfile, path, 'w');
+
+ write_footer(fpfile);
+ check_file_error(fpfile, path, 'w');
fclose(fpfile);
}
err:
- commitinfo_free(ci);
+ commit_info_free(ci);
}
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);
- }
+ 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;
}
+// TODO fix this ungodly mess. Yuck!
void
-printcommitatom(FILE *fp, struct commitinfo *ci, const char *tag)
+write_log_cached(FILE *fp, const git_oid *head)
+{
+ char tmppath[64] = "cache.XXXXXXXXXXXX";
+ char buf[BUFSIZ];
+ mode_t mask;
+ int fd, n;
+
+ /* read from cache file (does not need to exist) */
+ if ((rcachefp = fopen(cachefile, "r"))) {
+ if (!fgets(lastoidstr, sizeof(lastoidstr), rcachefp))
+ errx(1, "%s: no object id", cachefile);
+ if (git_oid_fromstr(&lastoid, lastoidstr))
+ errx(1, "%s: invalid object id", cachefile);
+ }
+
+ /* write log to (temporary) cache */
+ if ((fd = mkstemp(tmppath)) == -1)
+ err(1, "mkstemp");
+ if (!(wcachefp = fdopen(fd, "w")))
+ err(1, "fdopen: '%s'", tmppath);
+ /* write last commit id (HEAD) */
+ git_oid_tostr(buf, sizeof(buf), head);
+ fprintf(wcachefp, "%s\n", buf);
+
+ write_log(fp, head);
+
+ if (rcachefp) {
+ /* append previous log to log.html and the new cache */
+ while (!feof(rcachefp)) {
+ n = fread(buf, 1, sizeof(buf), rcachefp);
+ if (ferror(rcachefp))
+ break;
+ if (
+ fwrite(buf, 1, n, fp) != n ||
+ fwrite(buf, 1, n, wcachefp) != n
+ )
+ break;
+ }
+ check_file_error(rcachefp, cachefile, 'r');
+ fclose(rcachefp);
+ }
+ check_file_error(wcachefp, tmppath, 'w');
+ fclose(wcachefp);
+
+ // TODO previously, this would check that the log page was written
+ // successfully before renaming the cachefile... I'm probably going
+ // to blow this whole caching mess away so as long as it runs for
+ // now, it shouldn't matter.
+
+ if (rename(tmppath, cachefile))
+ err(1, "rename: '%s' to '%s'", tmppath, cachefile);
+ umask((mask = umask(0)));
+ if (
+ chmod(
+ cachefile,
+ (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH) & ~mask
+ )
+ )
+ err(1, "chmod: '%s'", cachefile);
+}
+
+void
+write_log_page(FILE *fp, const git_oid *head)
+{
+ relpath = "";
+ mkdir("commit", S_IRWXU | S_IRWXG | S_IRWXO);
+ write_header(fp, "Log");
+ fputs(
+ "<table id=\"log\"><thead>" "\n"
+ "<tr>"
+ "<td><b>Date</b></td>"
+ "<td><b>Commit message</b></td>"
+ "<td><b>Author</b></td>"
+ "<td class=\"num\" align=\"right\"><b>Files</b></td>"
+ "<td class=\"num\" align=\"right\"><b>+</b></td>"
+ "<td class=\"num\" align=\"right\"><b>-</b></td>"
+ "</tr>" "\n"
+ "</thead><tbody>" "\n",
+ fp
+ );
+
+ if (head && !cachefile) write_log(fp, head);
+ if (head && cachefile) write_log_cached(fp, head);
+
+ fputs("</tbody></table>", fp);
+ write_footer(fp);
+}
+
+void
+print_commit_atom(FILE *fp, struct CommitInfo *ci, const char *tag)
{
fputs("<entry>\n", fp);
@@ -863,20 +1030,24 @@ printcommitatom(FILE *fp, struct commitinfo *ci, const char *tag)
fputs("<title>", fp);
if (tag && tag[0]) {
fputs("[", fp);
- xmlencode(fp, tag, strlen(tag));
+ put_xml(fp, tag, strlen(tag));
fputs("] ", fp);
}
- xmlencode(fp, ci->summary, strlen(ci->summary));
+ put_xml(fp, ci->summary, strlen(ci->summary));
fputs("</title>\n", fp);
}
- fprintf(fp, "<link rel=\"alternate\" type=\"text/html\" href=\"%scommit/%s.html\" />\n",
- baseurl, ci->oid);
+ fprintf(
+ fp,
+ "<link rel=\"alternate\" type=\"text/html\" href=\"%scommit/%s.html\" />\n",
+ baseurl,
+ ci->oid
+ );
if (ci->author) {
fputs("<author>\n<name>", fp);
- xmlencode(fp, ci->author->name, strlen(ci->author->name));
+ put_xml(fp, ci->author->name, strlen(ci->author->name));
fputs("</name>\n<email>", fp);
- xmlencode(fp, ci->author->email, strlen(ci->author->email));
+ put_xml(fp, ci->author->email, strlen(ci->author->email));
fputs("</email>\n</author>\n", fp);
}
@@ -886,35 +1057,38 @@ printcommitatom(FILE *fp, struct commitinfo *ci, const char *tag)
fprintf(fp, "parent %s\n", ci->parentoid);
if (ci->author) {
fputs("Author: ", fp);
- xmlencode(fp, ci->author->name, strlen(ci->author->name));
+ put_xml(fp, ci->author->name, strlen(ci->author->name));
fputs(" <", fp);
- xmlencode(fp, ci->author->email, strlen(ci->author->email));
+ put_xml(fp, ci->author->email, strlen(ci->author->email));
fputs(">\nDate: ", fp);
printtime(fp, &(ci->author->when));
putc('\n', fp);
}
if (ci->msg) {
putc('\n', fp);
- xmlencode(fp, ci->msg, strlen(ci->msg));
+ put_xml(fp, ci->msg, strlen(ci->msg));
}
fputs("\n</content>\n</entry>\n", fp);
}
int
-writeatom(FILE *fp, int all)
+write_atom(FILE *fp, int all)
{
- struct referenceinfo *ris = NULL;
+ struct ReferenceInfo *ris = NULL;
size_t refcount = 0;
- struct commitinfo *ci;
+ struct CommitInfo *ci;
git_revwalk *w = NULL;
git_oid id;
size_t i, m = 100; /* last 'm' commits */
- fputs("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
- "<feed xmlns=\"http://www.w3.org/2005/Atom\">\n<title>", fp);
- xmlencode(fp, strippedname, strlen(strippedname));
+ fputs(
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<feed xmlns=\"http://www.w3.org/2005/Atom\">\n<title>",
+ fp
+ );
+ put_xml(fp, stripped_name, strlen(stripped_name));
fputs(", branch HEAD</title>\n<subtitle>", fp);
- xmlencode(fp, description, strlen(description));
+ put_xml(fp, description, strlen(description));
fputs("</subtitle>\n", fp);
/* all commits or only tags? */
@@ -922,20 +1096,22 @@ writeatom(FILE *fp, int all)
git_revwalk_new(&w, repo);
git_revwalk_push_head(w);
for (i = 0; i < m && !git_revwalk_next(&id, w); i++) {
- if (!(ci = commitinfo_getbyoid(&id)))
+ if (!(ci = commit_info_get_by_oid(&id)))
break;
- printcommitatom(fp, ci, "");
- commitinfo_free(ci);
+ print_commit_atom(fp, ci, "");
+ commit_info_free(ci);
}
git_revwalk_free(w);
- } else if (getrefs(&ris, &refcount) != -1) {
+ } else if (get_refs(&ris, &refcount) != -1) {
/* references: tags */
for (i = 0; i < refcount; i++) {
if (git_reference_is_tag(ris[i].ref))
- printcommitatom(fp, ris[i].ci,
- git_reference_shorthand(ris[i].ref));
+ print_commit_atom(
+ fp, ris[i].ci,
+ git_reference_shorthand(ris[i].ref)
+ );
- commitinfo_free(ris[i].ci);
+ commit_info_free(ris[i].ci);
git_reference_free(ris[i].ref);
}
free(ris);
@@ -947,7 +1123,7 @@ writeatom(FILE *fp, int all)
}
size_t
-writeblob(git_object *obj, const char *fpath, const char *filename, size_t filesize)
+write_blob(git_object *obj, const char *fpath, const char *filename, size_t filesize)
{
char tmp[PATH_MAX] = "", *d;
const char *p;
@@ -968,19 +1144,20 @@ writeblob(git_object *obj, const char *fpath, const char *filename, size_t files
relpath = tmp;
fp = efopen(fpath, "w");
- writeheader(fp, filename);
+ write_header(fp, filename);
fputs("<p> ", fp);
- xmlencode(fp, filename, strlen(filename));
+ put_xml(fp, filename, strlen(filename));
fprintf(fp, " (%zuB)", filesize);
- fputs("</p><hr/>", fp);
+ fputs("</p>", fp);
if (git_blob_is_binary((git_blob *)obj))
+ // TODO check and display image
fputs("<p>Binary file.</p>\n", fp);
else
- lc = writeblobhtml(fp, (git_blob *)obj);
+ lc = write_blob_html(fp, (git_blob *)obj);
- writefooter(fp);
- checkfileerror(fp, fpath, 'w');
+ write_footer(fp);
+ check_file_error(fp, fpath, 'w');
fclose(fp);
relpath = "";
@@ -996,22 +1173,14 @@ filemode(git_filemode_t m)
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 (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';
@@ -1031,7 +1200,7 @@ filemode(git_filemode_t m)
}
int
-writefilestree(FILE *fp, git_tree *tree, const char *path)
+write_file_list(FILE *fp, git_tree *tree, const char *path)
{
const git_tree_entry *entry = NULL;
git_object *obj = NULL;
@@ -1042,13 +1211,14 @@ writefilestree(FILE *fp, git_tree *tree, const char *path)
count = git_tree_entrycount(tree);
for (i = 0; i < count; i++) {
- if (!(entry = git_tree_entry_byindex(tree, i)) ||
- !(entryname = git_tree_entry_name(entry)))
+ if (
+ !(entry = git_tree_entry_byindex(tree, i)) ||
+ !(entryname = git_tree_entry_name(entry))
+ )
return -1;
- joinpath(entrypath, sizeof(entrypath), path, entryname);
+ join_path(entrypath, sizeof(entrypath), path, entryname);
- r = snprintf(filepath, sizeof(filepath), "file/%s.html",
- entrypath);
+ r = snprintf(filepath, sizeof(filepath), "file/%s.html", entrypath);
if (r < 0 || (size_t)r >= sizeof(filepath))
errx(1, "path truncated: 'file/%s.html'", entrypath);
@@ -1058,8 +1228,10 @@ writefilestree(FILE *fp, git_tree *tree, const char *path)
break;
case GIT_OBJ_TREE:
/* NOTE: recurses */
- ret = writefilestree(fp, (git_tree *)obj,
- entrypath);
+ // TODO create page for each dir
+ // instead of dumping all files into "files.html"
+ // or, is that a good idea? hm...
+ ret = write_file_list( fp, (git_tree *)obj, entrypath);
git_object_free(obj);
if (ret)
return ret;
@@ -1070,29 +1242,30 @@ writefilestree(FILE *fp, git_tree *tree, const char *path)
}
filesize = git_blob_rawsize((git_blob *)obj);
- lc = writeblob(obj, filepath, entryname, filesize);
+ lc = write_blob(obj, filepath, entryname, filesize);
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));
+ put_percent_encoded(fp, filepath, strlen(filepath));
fputs("\">", fp);
- xmlencode(fp, entrypath, strlen(entrypath));
+ put_xml(fp, entrypath, strlen(entrypath));
fputs("</a></td><td class=\"num\" align=\"right\">", fp);
- if (lc > 0)
- fprintf(fp, "%zuL", lc);
- else
- fprintf(fp, "%zuB", filesize);
+ if (lc > 0) fprintf(fp, "%zuL", lc);
+ else fprintf(fp, "%zuB", filesize);
fputs("</td></tr>\n", fp);
git_object_free(obj);
} else if (git_tree_entry_type(entry) == GIT_OBJ_COMMIT) {
/* commit object in tree is a submodule */
- fprintf(fp, "<tr><td>m---------</td><td><a href=\"%sfile/.gitmodules.html\">",
- relpath);
- xmlencode(fp, entrypath, strlen(entrypath));
+ fprintf(
+ fp,
+ "<tr><td>m---------</td><td><a href=\"%sfile/.gitmodules.html\">",
+ relpath
+ );
+ put_xml(fp, entrypath, strlen(entrypath));
fputs("</a> @ ", fp);
git_oid_tostr(oid, sizeof(oid), git_tree_entry_id(entry));
- xmlencode(fp, oid, strlen(oid));
+ put_xml(fp, oid, strlen(oid));
fputs("</td><td class=\"num\" align=\"right\"></td></tr>\n", fp);
}
}
@@ -1101,20 +1274,25 @@ writefilestree(FILE *fp, git_tree *tree, const char *path)
}
int
-writefiles(FILE *fp, const git_oid *id)
+write_files(FILE *fp, const git_oid *id)
{
git_tree *tree = NULL;
git_commit *commit = NULL;
int ret = -1;
- fputs("<table id=\"files\"><thead>\n<tr>"
- "<td><b>Mode</b></td><td><b>Name</b></td>"
- "<td class=\"num\" align=\"right\"><b>Size</b></td>"
- "</tr>\n</thead><tbody>\n", fp);
+ fputs(
+ "<table id=\"files\"><thead>\n<tr>"
+ "<td><b>Mode</b></td><td><b>Name</b></td>"
+ "<td class=\"num\" align=\"right\"><b>Size</b></td>"
+ "</tr>\n</thead><tbody>\n",
+ fp
+ );
- if (!git_commit_lookup(&commit, repo, id) &&
- !git_commit_tree(&tree, commit))
- ret = writefilestree(fp, tree, "");
+ if (
+ !git_commit_lookup(&commit, repo, id) &&
+ !git_commit_tree(&tree, commit)
+ )
+ ret = write_file_list(fp, tree, "");
fputs("</tbody></table>", fp);
@@ -1125,16 +1303,16 @@ writefiles(FILE *fp, const git_oid *id)
}
int
-writerefs(FILE *fp)
+write_refs(FILE *fp)
{
- struct referenceinfo *ris = NULL;
- struct commitinfo *ci;
+ struct ReferenceInfo *ris = NULL;
+ struct CommitInfo *ci;
size_t count, i, j, refcount;
const char *titles[] = { "Branches", "Tags" };
const char *ids[] = { "branches", "tags" };
const char *s;
- if (getrefs(&ris, &refcount) == -1)
+ if (get_refs(&ris, &refcount) == -1)
return -1;
for (i = 0, j = 0, count = 0; i < refcount; i++) {
@@ -1146,26 +1324,29 @@ writerefs(FILE *fp)
}
/* print header if it has an entry (first). */
- if (++count == 1) {
- fprintf(fp, "<h2>%s</h2><table id=\"%s\">"
- "<thead>\n<tr><td><b>Name</b></td>"
- "<td><b>Last commit date</b></td>"
- "<td><b>Author</b></td>\n</tr>\n"
- "</thead><tbody>\n",
- titles[j], ids[j]);
- }
+ if (++count == 1)
+ fprintf(
+ fp,
+ "<h2>%s</h2><table id=\"%s\">"
+ "<thead>\n<tr><td><b>Name</b></td>"
+ "<td><b>Last commit date</b></td>"
+ "<td><b>Author</b></td>\n</tr>\n"
+ "</thead><tbody>\n",
+ titles[j],
+ ids[j]
+ );
ci = ris[i].ci;
s = git_reference_shorthand(ris[i].ref);
fputs("<tr><td>", fp);
- xmlencode(fp, s, strlen(s));
+ put_xml(fp, s, strlen(s));
fputs("</td><td>", fp);
if (ci->author)
printtimeshort(fp, &(ci->author->when));
fputs("</td><td>", fp);
if (ci->author)
- xmlencode(fp, ci->author->name, strlen(ci->author->name));
+ put_xml(fp, ci->author->name, strlen(ci->author->name));
fputs("</td></tr>\n", fp);
}
/* table footer */
@@ -1173,7 +1354,7 @@ writerefs(FILE *fp)
fputs("</tbody></table><br/>\n", fp);
for (i = 0; i < refcount; i++) {
- commitinfo_free(ris[i].ci);
+ commit_info_free(ris[i].ci);
git_reference_free(ris[i].ref);
}
free(ris);
@@ -1184,8 +1365,12 @@ writerefs(FILE *fp)
void
usage(char *argv0)
{
- fprintf(stderr, "usage: %s [-c cachefile | -l commits] "
- "[-u baseurl] repodir\n", argv0);
+ fprintf(
+ stderr,
+ "usage: %s [-c cachefile | -l commits] "
+ "[-u baseurl] repodir\n",
+ argv0
+ );
exit(1);
}
@@ -1194,13 +1379,11 @@ main(int argc, char *argv[])
{
git_object *obj = NULL;
const git_oid *head = NULL;
- mode_t mask;
FILE *fp, *fpread;
char path[PATH_MAX], repodirabs[PATH_MAX + 1], *p;
- char tmppath[64] = "cache.XXXXXXXXXXXX", buf[BUFSIZ];
- size_t n;
- int i, fd;
+ int i;
+ /* parse args */
for (i = 1; i < argc; i++) {
if (argv[i][0] != '-') {
if (repodir)
@@ -1215,8 +1398,12 @@ main(int argc, char *argv[])
usage(argv[0]);
errno = 0;
nlogcommits = strtoll(argv[++i], &p, 10);
- if (argv[i][0] == '\0' || *p != '\0' ||
- nlogcommits <= 0 || errno)
+ if (
+ argv[i][0] == '\0' ||
+ *p != '\0' ||
+ nlogcommits <= 0 ||
+ errno
+ )
usage(argv[0]);
} else if (argv[i][1] == 'u') {
if (i + 1 >= argc)
@@ -1255,8 +1442,14 @@ main(int argc, char *argv[])
}
#endif
- if (git_repository_open_ext(&repo, repodir,
- GIT_REPOSITORY_OPEN_NO_SEARCH, NULL) < 0) {
+ if (
+ git_repository_open_ext(
+ &repo,
+ repodir,
+ GIT_REPOSITORY_OPEN_NO_SEARCH,
+ NULL
+ ) < 0
+ ) {
fprintf(stderr, "%s: cannot open repository\n", argv[0]);
return 1;
}
@@ -1267,161 +1460,104 @@ main(int argc, char *argv[])
git_object_free(obj);
/* use directory name as name */
- if ((name = strrchr(repodirabs, '/')))
- name++;
- else
- name = "";
+ if ((name = strrchr(repodirabs, '/'))) name++;
+ else name = "";
/* strip .git suffix */
- if (!(strippedname = strdup(name)))
+ if (!(stripped_name = strdup(name)))
err(1, "strdup");
- if ((p = strrchr(strippedname, '.')))
+ if ((p = strrchr(stripped_name, '.')))
if (!strcmp(p, ".git"))
*p = '\0';
/* read description or .git/description */
- joinpath(path, sizeof(path), repodir, "description");
+ join_path(path, sizeof(path), repodir, "description");
if (!(fpread = fopen(path, "r"))) {
- joinpath(path, sizeof(path), repodir, ".git/description");
+ join_path(path, sizeof(path), repodir, ".git/description");
fpread = fopen(path, "r");
}
if (fpread) {
if (!fgets(description, sizeof(description), fpread))
description[0] = '\0';
- checkfileerror(fpread, path, 'r');
+ check_file_error(fpread, path, 'r');
fclose(fpread);
}
/* read url or .git/url */
- joinpath(path, sizeof(path), repodir, "url");
+ join_path(path, sizeof(path), repodir, "url");
if (!(fpread = fopen(path, "r"))) {
- joinpath(path, sizeof(path), repodir, ".git/url");
+ join_path(path, sizeof(path), repodir, ".git/url");
fpread = fopen(path, "r");
}
if (fpread) {
if (!fgets(cloneurl, sizeof(cloneurl), fpread))
cloneurl[0] = '\0';
- checkfileerror(fpread, path, 'r');
+ check_file_error(fpread, path, 'r');
fclose(fpread);
cloneurl[strcspn(cloneurl, "\n")] = '\0';
}
/* check LICENSE */
- for (i = 0; i < LEN(licensefiles) && !license; i++) {
- if (!git_revparse_single(&obj, repo, licensefiles[i]) &&
- git_object_type(obj) == GIT_OBJ_BLOB)
- license = licensefiles[i] + strlen("HEAD:");
+ for (i = 0; i < LEN(license_files) && !license; i++) {
+ if (
+ !git_revparse_single(&obj, repo, license_files[i]) &&
+ git_object_type(obj) == GIT_OBJ_BLOB
+ )
+ license = license_files[i] + strlen("HEAD:");
git_object_free(obj);
}
/* check README */
- for (i = 0; i < LEN(readmefiles) && !readme; i++) {
- if (!git_revparse_single(&obj, repo, readmefiles[i]) &&
- git_object_type(obj) == GIT_OBJ_BLOB)
- readme = readmefiles[i] + strlen("HEAD:");
+ for (i = 0; i < LEN(readme_files) && !readme; i++) {
+ if (
+ !git_revparse_single(&obj, repo, readme_files[i]) &&
+ git_object_type(obj) == GIT_OBJ_BLOB
+ )
+ readme = readme_files[i] + strlen("HEAD:");
git_object_free(obj);
}
- if (!git_revparse_single(&obj, repo, "HEAD:.gitmodules") &&
- git_object_type(obj) == GIT_OBJ_BLOB)
+ if (
+ !git_revparse_single(&obj, repo, "HEAD:.gitmodules") &&
+ git_object_type(obj) == GIT_OBJ_BLOB
+ )
submodules = ".gitmodules";
git_object_free(obj);
/* log for HEAD */
fp = efopen("log.html", "w");
- relpath = "";
- mkdir("commit", S_IRWXU | S_IRWXG | S_IRWXO);
- writeheader(fp, "Log");
- fputs("<table id=\"log\"><thead>\n<tr><td><b>Date</b></td>"
- "<td><b>Commit message</b></td>"
- "<td><b>Author</b></td><td class=\"num\" align=\"right\"><b>Files</b></td>"
- "<td class=\"num\" align=\"right\"><b>+</b></td>"
- "<td class=\"num\" align=\"right\"><b>-</b></td></tr>\n</thead><tbody>\n", fp);
-
- if (cachefile && head) {
- /* read from cache file (does not need to exist) */
- if ((rcachefp = fopen(cachefile, "r"))) {
- if (!fgets(lastoidstr, sizeof(lastoidstr), rcachefp))
- errx(1, "%s: no object id", cachefile);
- if (git_oid_fromstr(&lastoid, lastoidstr))
- errx(1, "%s: invalid object id", cachefile);
- }
-
- /* write log to (temporary) cache */
- if ((fd = mkstemp(tmppath)) == -1)
- err(1, "mkstemp");
- if (!(wcachefp = fdopen(fd, "w")))
- err(1, "fdopen: '%s'", tmppath);
- /* write last commit id (HEAD) */
- git_oid_tostr(buf, sizeof(buf), head);
- fprintf(wcachefp, "%s\n", buf);
-
- writelog(fp, head);
-
- if (rcachefp) {
- /* append previous log to log.html and the new cache */
- while (!feof(rcachefp)) {
- n = fread(buf, 1, sizeof(buf), rcachefp);
- if (ferror(rcachefp))
- break;
- if (fwrite(buf, 1, n, fp) != n ||
- fwrite(buf, 1, n, wcachefp) != n)
- break;
- }
- checkfileerror(rcachefp, cachefile, 'r');
- fclose(rcachefp);
- }
- checkfileerror(wcachefp, tmppath, 'w');
- fclose(wcachefp);
- } else {
- if (head)
- writelog(fp, head);
- }
-
- fputs("</tbody></table>", fp);
- writefooter(fp);
- checkfileerror(fp, "log.html", 'w');
+ write_log_page(fp, head);
+ check_file_error(fp, "log.html", 'w');
fclose(fp);
/* files for HEAD */
fp = efopen("files.html", "w");
- writeheader(fp, "Files");
- if (head)
- writefiles(fp, head);
- writefooter(fp);
- checkfileerror(fp, "files.html", 'w');
+ write_header(fp, "Files");
+ if (head) write_files(fp, head);
+ write_footer(fp);
+ check_file_error(fp, "files.html", 'w');
fclose(fp);
/* summary page with branches and tags */
fp = efopen("refs.html", "w");
- writeheader(fp, "Refs");
- writerefs(fp);
- writefooter(fp);
- checkfileerror(fp, "refs.html", 'w');
+ write_header(fp, "Refs");
+ write_refs(fp);
+ write_footer(fp);
+ check_file_error(fp, "refs.html", 'w');
fclose(fp);
/* Atom feed */
fp = efopen("atom.xml", "w");
- writeatom(fp, 1);
- checkfileerror(fp, "atom.xml", 'w');
+ write_atom(fp, 1);
+ check_file_error(fp, "atom.xml", 'w');
fclose(fp);
/* Atom feed for tags / releases */
fp = efopen("tags.xml", "w");
- writeatom(fp, 0);
- checkfileerror(fp, "tags.xml", 'w');
+ write_atom(fp, 0);
+ check_file_error(fp, "tags.xml", 'w');
fclose(fp);
- /* rename new cache file on success */
- if (cachefile && head) {
- if (rename(tmppath, cachefile))
- err(1, "rename: '%s' to '%s'", tmppath, cachefile);
- umask((mask = umask(0)));
- if (chmod(cachefile,
- (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH) & ~mask))
- err(1, "chmod: '%s'", cachefile);
- }
-
/* cleanup */
git_repository_free(repo);
git_libgit2_shutdown();
diff --git a/style.css b/style.css
@@ -1,6 +1,5 @@
body {
color: #000;
- background-color: #fff;
font-family: monospace;
}
@@ -17,8 +16,18 @@ img {
border: 0;
}
+a {
+ /* color: #f000bb; */
+ color: #e00086;
+}
+
+a:visited {
+ /* color: #cd00f0; */
+ color: #9700b3;
+}
+
a:target {
- background-color: #ccc;
+ background-color: #ddf;
}
a.d,
@@ -28,12 +37,9 @@ a.line {
text-decoration: none;
}
-#blob a {
- color: #555;
-}
-
#blob a:hover {
- color: blue;
+ background-color: #fee;
+ color: #000;
text-decoration: none;
}
@@ -47,7 +53,7 @@ table td {
#content table td {
vertical-align: top;
- white-space: nowrap;
+ white-space: pre-wrap;
}
#branches tr:hover td,
@@ -55,7 +61,7 @@ table td {
#index tr:hover td,
#log tr:hover td,
#files tr:hover td {
- background-color: #eee;
+ background-color: #fee;
}
#index tr td:nth-child(2),
@@ -65,6 +71,30 @@ table td {
white-space: normal;
}
+#diffstat tr td:nth-child(2) {
+ border-right: 1px solid black;
+ margin-right: 5px;
+ padding-right: 5px;
+}
+
+table#diffstat {
+ border-collapse: collapse;
+}
+
+#blob a {
+ text-align: right;
+ border-right: 1px solid black;
+ margin-right: 5px;
+ padding-right: 5px;
+ user-select: none;
+}
+
+/*
+table#blob {
+ border-collapse: collapse;
+}
+*/
+
td.num {
text-align: right;
}
@@ -81,22 +111,23 @@ hr {
pre {
font-family: monospace;
+ margin: 0px;
}
pre a.h {
- color: #00a;
+ color: #0072aa; /* blue */
}
.A,
span.i,
pre a.i {
- color: #070;
+ color: #00ba53; /* green */
}
.D,
span.d,
pre a.d {
- color: #e00;
+ color: #ee3200; /* red */
}
pre a.h:hover,
@@ -104,51 +135,3 @@ pre a.i:hover,
pre a.d:hover {
text-decoration: none;
}
-
-@media (prefers-color-scheme: dark) {
- body {
- background-color: #000;
- color: #bdbdbd;
- }
- hr {
- border-color: #222;
- }
- a {
- color: #56c8ff;
- }
- a:target {
- background-color: #222;
- }
- .desc {
- color: #aaa;
- }
- #blob a {
- color: #555;
- }
- #blob a:target {
- color: #eee;
- }
- #blob a:hover {
- color: #56c8ff;
- }
- pre a.h {
- color: #00cdcd;
- }
- .A,
- span.i,
- pre a.i {
- color: #00cd00;
- }
- .D,
- span.d,
- pre a.d {
- color: #cd0000;
- }
- #branches tr:hover td,
- #tags tr:hover td,
- #index tr:hover td,
- #log tr:hover td,
- #files tr:hover td {
- background-color: #111;
- }
-}