commit 42eae3e1bbc5085115e948f426e6e9a52e4d2a76
parent 8684421e547f08888751ed14042b8063eadc1e45
Author: Emma Weaver <emma@waeaves.com>
Date: Wed, 20 May 2026 00:26:21 -0400
Improved sizes printing, rewrote mkdirp without strlcpy
Diffstat:
| M | TODO | 6 | ++++-- |
| M | stagit.c | 147 | +++++++++++++++++++++++++++++++++---------------------------------------------- |
| M | style.css | 31 | +++++++------------------------ |
3 files changed, 72 insertions(+), 112 deletions(-)
diff --git a/TODO b/TODO
@@ -20,8 +20,10 @@
[ ] add lines of code totals on files page
[x] add lines of code on each file's page
[x] show line/byte totals for each directory
-[ ] tweak paths that start with dots, so that they don't get hidden or 403'd (?)
-[ ] handle missing decorations (desc/logo/favicon) better
+[ ] tweak paths that start with dots, so that they don't get hidden or 403'd
+ - put parentheses around the dirnames in the urls
+[ ] while writing log, only print links to files whose pages exist
+[ ] add backlinks on files
---END NEW FEATURES---
[ ] Remove globals
diff --git a/stagit.c b/stagit.c
@@ -72,7 +72,6 @@ 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 = "";
@@ -480,17 +479,23 @@ put_xml_line(FILE *fp, const char *s, size_t len)
int
mkdirp(const char *path)
{
- char tmp[PATH_MAX], *p;
+ const char *p;
+ char tmp[PATH_MAX];
+ char *t;
- if (strlcpy(tmp, path, sizeof(tmp)) >= sizeof(tmp))
- errx(1, "path truncated: '%s'", path);
- for (p = tmp + (tmp[0] == '/'); *p; p++) {
- if (*p != '/')
+ for(t = tmp, p = path; *p != '\0'; *p++, *t++) {
+ if (*p != '/') {
+ *t = *p;
continue;
- *p = '\0';
- if (mkdir(tmp, S_IRWXU | S_IRWXG | S_IRWXO) < 0 && errno != EEXIST)
+ }
+
+ *t = '\0';
+ if (
+ mkdir(tmp, S_IRWXU | S_IRWXG | S_IRWXO) < 0 &&
+ errno != EEXIST
+ )
return -1;
- *p = '/';
+ *t = '/';
}
return 0;
}
@@ -567,7 +572,7 @@ printtimeshort(FILE *fp, const git_time *intime)
char *
put_absolute_path(FILE *fp, const char * path)
{
- fprintf(fp, "/%s%s", relpath, path);
+ fprintf(fp, "%s/%s", baseurl, path);
}
void
@@ -612,23 +617,21 @@ put_header(FILE *fp, const char *title)
SPUTF(fp, "\" />" "\n");
SPUTF(fp, "</head>" "\n" "<body>" "\n");
- // TODO handle backlink using config.h
- SPUTF(fp, "<p><a href=\"");
- put_absolute_path(fp, "files.html");
- SPUTF(fp, "\">home</a></p>" "\n");
-
- SPUTF(fp, "<table><tr><td>");
+ SPUTF(fp, "<table>" "<tr><td>");
+ /*
fprintf(fp, "<a href=\"");
- put_absolute_path(fp, ""); // TODO links to site root?
+ put_absolute_path(fp, ""); // TODO how to handle? ./.logo.png?
SPUTF(fp, "\"><img src=\"");
put_absolute_path(fp, "logo.png");
SPUTF(fp, "\" alt=\"\" width=\"32\" height=\"32\" /></a>");
+ */
- SPUTF(fp, "</td><td><h1>");
- put_xml(fp, stripped_name, strlen(stripped_name));
- SPUTF(fp, "</h1><span class=\"desc\">");
+ SPUTF(fp, "</td><td>" "<h1>");
+ if (stripped_name[0] == '\0') SPUTF(fp, "untitled");
+ else put_xml(fp, stripped_name, strlen(stripped_name));
+ SPUTF(fp, "</h1>" "<span class=\"desc\">");
put_xml(fp, description, strlen(description));
- SPUTF(fp, "</span></td></tr>");
+ SPUTF(fp, "</span>" "</td></tr>");
if (cloneurl[0]) {
SPUTF(fp, "<tr class=\"url\"><td></td><td>git clone <a href=\"");
put_xml(fp, cloneurl, strlen(cloneurl)); /* not percent-encoded */
@@ -687,8 +690,8 @@ size_t
put_file_html(FILE *fp, const git_blob *blob)
{
size_t n = 0, i, len, prev;
- // TODO use spaces + pre to right-align the line numbers, instead of css
- const char *number_format = "<a href=\"#l%zu\" class=\"line\" id=\"l%zu\">%7zu </a>";
+ const char *number_format =
+ "<a href=\"#l%zu\" class=\"line\" id=\"l%zu\">%7zu </a>";
const char *s = git_blob_rawcontent(blob);
len = git_blob_rawsize(blob);
@@ -721,13 +724,13 @@ void
print_commit(FILE *fp, struct CommitInfo *ci)
{
SPUTF(fp, "<b>commit</b> <a href=\"");
- put_absolute_path(fp, "commit/");
+ put_absolute_path(fp, "commits/");
fprintf(fp, "%s.html", ci->oid);
fprintf(fp, "\">%s</a>" "\n", ci->oid);
if (ci->parentoid[0])
SPUTF(fp, "<b>parent</b> <a href=\"");
- put_absolute_path(fp, "commit/");
+ put_absolute_path(fp, "commits/");
fprintf(fp, "%s.html", ci->parentoid);
fprintf(fp, "\">%s</a>" "\n", ci->parentoid);
@@ -907,7 +910,7 @@ put_log_line(FILE *fp, struct CommitInfo *ci)
fputs("</td><td>", fp);
if (ci->summary) {
SPUTF(fp, "<a href=\"");
- put_absolute_path(fp, "commit/");
+ put_absolute_path(fp, "commits/");
fprintf(fp, "%s.html", ci->oid);
SPUTF(fp, "\">");
put_xml(fp, ci->summary, strlen(ci->summary));
@@ -940,15 +943,13 @@ write_log(FILE *fp, const git_oid *oid)
git_revwalk_push(w, oid);
while (!git_revwalk_next(&id, w)) {
- relpath = "";
-
if (cachefile && !memcmp(&id, &lastoid, sizeof(id)))
break;
git_oid_tostr(oidstr, sizeof(oidstr), &id);
- r = snprintf(path, sizeof(path), "commit/%s.html", oidstr);
+ r = snprintf(path, sizeof(path), "commits/%s.html", oidstr);
if (r < 0 || (size_t)r >= sizeof(path))
- errx(1, "path truncated: 'commit/%s.html'", oidstr);
+ errx(1, "path truncated: 'commits/%s.html'", oidstr);
r = access(path, F_OK);
/* optimization: if there are no log lines to write and
@@ -976,7 +977,6 @@ write_log(FILE *fp, const git_oid *oid)
/* check if file exists if so skip it */
if (r) {
- relpath = "../";
fpfile = efopen(path, "w");
put_header(fpfile, ci->summary);
@@ -1002,8 +1002,6 @@ err:
remcommits
);
- relpath = "";
-
return 0;
}
@@ -1073,8 +1071,6 @@ write_log_cached(FILE *fp, const git_oid *head)
void
put_log_html(FILE *fp, const git_oid *head)
{
- relpath = "";
- mkdir("commit", S_IRWXU | S_IRWXG | S_IRWXO);
put_header(fp, "Log");
fputs(
"<table id=\"log\"><thead>" "\n"
@@ -1125,7 +1121,7 @@ print_commit_atom(FILE *fp, struct CommitInfo *ci, const char *tag)
}
fprintf(
fp,
- "<link rel=\"alternate\" type=\"text/html\" href=\"%scommit/%s.html\" />\n",
+ "<link rel=\"alternate\" type=\"text/html\" href=\"%scommits/%s.html\" />\n",
baseurl,
ci->oid
);
@@ -1226,9 +1222,10 @@ write_binary_file(const char *filename, git_blob *blob) {
}
void
-put_size(FILE *fp, size_t size)
+put_size(FILE *fp, size_t size, int fixed)
{
int order = 0;
+ int decimal;
if (size == 0) {
fprintf(fp, " - ");
@@ -1237,10 +1234,14 @@ put_size(FILE *fp, size_t size)
while (size > 1000) {
order++;
+ decimal = (size / 100) % 10;
size /= 1000;
}
- fprintf(fp, "%3zu%c", size, orders[order]);
+ if (size < 10 && order > 0) fprintf(fp, "%1zu.%1d", size, decimal);
+ else fprintf(fp, fixed ? "%3zu" : "%zu", size);
+
+ if (order != 0 || fixed) fprintf(fp, "%c", orders[order]);
}
size_t
@@ -1258,16 +1259,6 @@ put_blob(
FILE *fp;
int is_binary;
- if (strlcpy(tmp, filename, sizeof(tmp)) >= sizeof(tmp))
- errx(1, "path truncated: '%s'", filename);
-
- // wtf does this do....
- for (p = filename, tmp[0] = '\0'; *p; p++) {
- if (*p == '/' && strlcat(tmp, "../", sizeof(tmp)) >= sizeof(tmp))
- errx(1, "path truncated: '../%s'", tmp);
- }
- relpath = tmp;
-
is_binary = git_blob_is_binary((git_blob *)obj);
const char *file_url = is_binary ? entryname : "";
@@ -1275,11 +1266,7 @@ put_blob(
put_header(fp, entryname);
fprintf(fp, "<p>");
put_xml(fp, entrypath, strlen(entrypath));
- CPUT(fp, ' ');
- put_size(fp, 0);
- CPUT(fp, ' ');
- put_size(fp, bytes);
- SPUTF(fp, "B</p>");
+ SPUTF(fp, "</p>");
if (is_binary) {
fprintf(
@@ -1300,8 +1287,6 @@ put_blob(
check_file_error(fp, filename, 'w');
fclose(fp);
- relpath = "";
-
return lines;
}
@@ -1370,9 +1355,9 @@ put_filetree_file_line(
SPUTF(fp, "\">");
put_xml(fp, entryname, strlen(entryname));
SPUTF(fp, "</a></td><td class=\"num\" align=\"right\">");
- put_size(fp, lines);
+ put_size(fp, lines, 1);
SPUTF(fp, "</td><td class=\"num\" align=\"right\">");
- put_size(fp, bytes);
+ put_size(fp, bytes, 1);
SPUTF(fp, "</td></tr>\n");
return ret;
}
@@ -1394,9 +1379,9 @@ put_filetree_dir_line(
SPUTF(fp, "\">");
put_xml(fp, title, strlen(title));
SPUTF(fp, "</a></td><td class=\"num\" align=\"right\">");
- put_size(fp, lines);
+ put_size(fp, lines, 1);
SPUTF(fp, "</td><td class=\"num\" align=\"right\">");
- put_size(fp, bytes);
+ put_size(fp, bytes, 1);
SPUTF(fp, "</td></tr>\n");
}
@@ -1425,24 +1410,19 @@ void put_filetree_backlink_line(FILE *fp, const char *path) {
/* need this here to allow recursion */
struct Weight write_filetree(
- const char *filename,
const char *path,
+ const char *filename,
git_tree *tree
);
struct Weight
-put_entry_obj(
- FILE *fp,
- git_object *obj,
- const git_tree_entry *entry,
- char *entrypath,
- const char *entryname
-) {
+put_entry_obj(FILE *fp, git_object *obj, const git_tree_entry *entry, char *entrypath, const char *entryname)
+{
struct Weight ret;
git_object_t obj_type = git_object_type(obj);
char filename[PATH_MAX];
int r;
-
+
/* filename = f("files/%s.html", entrypath) */
r = snprintf(filename, sizeof(filename), "files/%s.html", entrypath);
if (r < 0 || (size_t) r >= sizeof(filename))
@@ -1451,7 +1431,7 @@ put_entry_obj(
switch (obj_type) {
case GIT_OBJ_TREE:
/* NOTE: recurses */
- ret = write_filetree(filename, entrypath, (git_tree *) obj);
+ ret = write_filetree(entrypath, filename, (git_tree *) obj);
put_filetree_dir_line(
fp,
entry,
@@ -1484,20 +1464,15 @@ put_submodule_obj(
) {
char oid[8];
- SPUTF(fp, "<tr><td>m---------</td><td><a href=\"");
+ SPUTF(fp, "<tr>" "<td>m---------</td>" "<td><a href=\"");
put_absolute_path(fp, "files/.gitmodules.html");
SPUTF(fp, "\">");
put_xml(fp, entrypath, strlen(entrypath));
SPUTF(fp, "</a> @ ");
git_oid_tostr(oid, sizeof(oid), git_tree_entry_id(entry));
put_xml(fp, oid, strlen(oid));
- SPUTF(fp, "</td><td class=\"num\" align=\"right\"></td></tr>" "\n");
-}
-
-void
-put_entry()
-{
-
+ SPUTF(fp, "</td>" "<td class=\"num\" align=\"right\"></td>" "\n");
+ SPUTF(fp, "<td class=\"num\" align=\"right\"></td>" "</tr>" "\n");
}
struct Weight
@@ -1559,7 +1534,7 @@ put_filetree_dir_title(FILE *fp, const char *path)
}
struct Weight
-write_filetree(const char *filename, const char *path, git_tree *tree)
+write_filetree(const char *path, const char *filename, git_tree *tree)
{
FILE *fp;
struct Weight ret;
@@ -1567,7 +1542,7 @@ write_filetree(const char *filename, const char *path, git_tree *tree)
fp = efopen(filename, "w");
put_header(fp, "Files");
- fprintf(fp, "<p>");
+ SPUTF(fp, "<p>");
put_filetree_dir_title(fp, path);
SPUTF(fp, "</p>");
@@ -1575,8 +1550,8 @@ write_filetree(const char *filename, const char *path, git_tree *tree)
fp,
"<table id=\"files\"><thead>" "\n" "<tr>"
"<td><b>Mode</b></td><td><b>Name</b></td>"
- "<td class=\"num\" align=\"right\"><b>L</b></td>"
- "<td class=\"num\" align=\"right\"><b>B</b></td>"
+ "<td class=\"num\" align=\"right\"><b> L </b></td>"
+ "<td class=\"num\" align=\"right\"><b> B </b></td>"
"</tr>" "\n" "</thead><tbody>" "\n"
);
@@ -1586,9 +1561,9 @@ write_filetree(const char *filename, const char *path, git_tree *tree)
SPUTF(fp, "</tbody></table>");
SPUTF(fp, "<p>Totals: ");
- put_size(fp, ret.lines);
+ put_size(fp, ret.lines, 0);
SPUTF(fp, "L ");
- put_size(fp, ret.bytes);
+ put_size(fp, ret.bytes, 0);
SPUTF(fp, "B</p>");
put_footer(fp);
@@ -1610,7 +1585,7 @@ walk_git_tree(const git_oid *id)
has_files = !git_commit_lookup(&commit, repo, id) &&
!git_commit_tree(&tree, commit);
- write_filetree("files.html", "", has_files ? tree : NULL);
+ write_filetree("", "files.html", has_files ? tree : NULL);
git_commit_free(commit);
git_tree_free(tree);
@@ -1663,7 +1638,7 @@ write_refs(FILE *fp)
put_xml(fp, ci->author->name, strlen(ci->author->name));
fputs("</td></tr>\n", fp);
}
- /* table footer */
+
if (count)
fputs("</tbody></table><br/>\n", fp);
@@ -1724,7 +1699,7 @@ usage(char *argv0)
fprintf(
stderr,
"usage: %s [-c cachefile | -l commits] "
- "[-u baseurl] repodir\n",
+ "[-u baseurl] repodir" "\n",
argv0
);
exit(1);
diff --git a/style.css b/style.css
@@ -13,7 +13,7 @@ img, h1, h2 {
}
img, object {
- border: 1px solid black;
+ border: 1px solid #ccc;
}
a {
@@ -30,10 +30,7 @@ a:target {
background-color: #ddf;
}
-a.d,
-a.h,
-a.i,
-a.line {
+a.d, a.h, a.i, a.line {
text-decoration: none;
}
@@ -53,7 +50,7 @@ table td {
#content table td {
vertical-align: top;
- white-space: pre-wrap;
+ white-space: pre;
}
#branches tr:hover td,
@@ -82,18 +79,10 @@ table#diffstat {
}
#blob a {
- border-right: 1px solid black;
- margin-right: 5px;
- padding-right: 5px;
+ margin-right: 10px;
user-select: none;
}
-/*
-table#blob {
- border-collapse: collapse;
-}
-*/
-
td.num {
text-align: right;
}
@@ -117,20 +106,14 @@ pre a.h {
color: #0072aa; /* blue */
}
-.A,
-span.i,
-pre a.i {
+.A, span.i, pre a.i {
color: #00ba53; /* green */
}
-.D,
-span.d,
-pre a.d {
+.D, span.d, pre a.d {
color: #ee3200; /* red */
}
-pre a.h:hover,
-pre a.i:hover,
-pre a.d:hover {
+pre a.h:hover, pre a.i:hover, pre a.d:hover {
text-decoration: none;
}