git home / emma home
logo

sgw

Static git web. Emma's fork of stagit.
git clone https://git.y1.nz/archives/sgw.tar.gz
README | Files | Log | Refs | LICENSE

commit 546c6d82185ea286e7365c7abe137be2ef23045d
parent 022c85fdcf44b9b8723ce6983c62d585e2563c9d
Author: Emma Weaver <emma@waeaves.com>
Date:   Sun, 17 May 2026 17:03:00 -0400

Binary file previews, more prettying

Diffstat:
MTODO35+++++++++++++++--------------------
Mstagit.c153+++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------
Mstyle.css5++---
3 files changed, 117 insertions(+), 76 deletions(-)

diff --git a/TODO b/TODO @@ -1,30 +1,25 @@ -STUFF I WANT TO CHANGE ABOUT THIS +-STUFF I WANT TO CHANGE ABOUT STAGIT- +(might rename it to "statigit" also.) [ ] 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) + - 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) +[ ] handle missing decorations (desc/logo/favicon) better [ ] create config.h file - - reused format strings (such as header) + - 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 +---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 +[ ] add lines of code on each file's page +[x] include binary files in static site + link to them +[ ] add support for projects with folders in them + - show totals "123L + 456B" for each directory +[ ] tweak paths that start with dots, so that they don't get hidden or 403'd +---END NEW FEATURES--- [ ] Redo caching, to never repeat work - but it needs to also not increase the complexity. diff --git a/stagit.c b/stagit.c @@ -16,7 +16,8 @@ #include "compat.h" -#define LEN(s) (sizeof(s)/sizeof(*s)) +#define SPUTF(f, s) fputs((s), (f)) +#define LENGTH(s) (sizeof(s)/sizeof(*s)) struct DeltaInfo { git_patch *patch; @@ -525,7 +526,7 @@ printtimeshort(FILE *fp, const git_time *intime) } void -write_header(FILE *fp, const char *title) +put_header(FILE *fp, const char *title) { fputs( "<!DOCTYPE html>\n" @@ -612,40 +613,41 @@ write_header(FILE *fp, const char *title) } void -write_footer(FILE *fp) +put_footer(FILE *fp) { fputs("</div>" "\n" "</body>" "\n" "</html>" "\n", fp); } size_t -write_blob_html(FILE *fp, const git_blob *blob) +put_file_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>"; + // 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 *s = git_blob_rawcontent(blob); len = git_blob_rawsize(blob); - fputs("<pre id=\"blob\">" "\n", fp); + SPUTF(fp, "<pre id=\"blob\">" "\n"); if (len > 0) { for (i = 0, prev = 0; i < len; i++) { if (s[i] != '\n') continue; n++; - fprintf(fp, nfmt, n, n, n); + fprintf(fp, number_format, n, n, n); put_xml_line(fp, &s[prev], i - prev + 1); - fputs("\n", fp); + SPUTF(fp, "\n"); prev = i + 1; } /* trailing data */ if ((len - prev) > 0) { n++; - fprintf(fp, nfmt, n, n, n); + fprintf(fp, number_format, n, n, n); put_xml_line(fp, &s[prev], len - prev); } } - fputs("</pre>" "\n", fp); + SPUTF(fp, "</pre>" "\n"); return n; } @@ -798,11 +800,17 @@ print_show_file(FILE *fp, struct CommitInfo *ci) if (git_patch_get_line_in_hunk(&line, patch, j, k)) break; if (line->old_lineno == -1) - fprintf(fp, "<a href=\"#h%zu-%zu-%zu\" id=\"h%zu-%zu-%zu\" class=\"i\">+", - i, j, k, i, j, k); + fprintf( + fp, + "<a href=\"#h%zu-%zu-%zu\" id=\"h%zu-%zu-%zu\" class=\"i\">+", + i, j, k, i, j, k + ); else if (line->new_lineno == -1) - fprintf(fp, "<a href=\"#h%zu-%zu-%zu\" id=\"h%zu-%zu-%zu\" class=\"d\">-", - i, j, k, i, j, k); + fprintf( + fp, + "<a href=\"#h%zu-%zu-%zu\" id=\"h%zu-%zu-%zu\" class=\"d\">-", + i, j, k, i, j, k + ); else putc(' ', fp); put_xml_line(fp, line->content, line->content_len); @@ -891,13 +899,13 @@ write_log(FILE *fp, const git_oid *oid) if (r) { relpath = "../"; fpfile = efopen(path, "w"); - write_header(fpfile, ci->summary); + put_header(fpfile, ci->summary); fputs("<pre>", fpfile); print_show_file(fpfile, ci); fputs("</pre>\n", fpfile); - write_footer(fpfile); + put_footer(fpfile); check_file_error(fpfile, path, 'w'); fclose(fpfile); } @@ -988,7 +996,7 @@ write_log_page(FILE *fp, const git_oid *head) { relpath = ""; mkdir("commit", S_IRWXU | S_IRWXG | S_IRWXO); - write_header(fp, "Log"); + put_header(fp, "Log"); fputs( "<table id=\"log\"><thead>" "\n" "<tr>" @@ -1007,7 +1015,7 @@ write_log_page(FILE *fp, const git_oid *head) if (head && cachefile) write_log_cached(fp, head); fputs("</tbody></table>", fp); - write_footer(fp); + put_footer(fp); } void @@ -1122,18 +1130,42 @@ write_atom(FILE *fp, int all) return 0; } +void +write_binary_file(const char *filepath, git_blob *blob) { + char dest[PATH_MAX] = ""; + const char *raw = git_blob_rawcontent(blob); + int size = git_blob_rawsize(blob); + + /* set dest */ + join_path(dest, PATH_MAX, "file", filepath); + + /* write the file! */ + FILE *fp = efopen(dest, "w"); + fwrite(raw, size, 1, fp); + check_file_error(fp, dest, 'w'); + fclose(fp); +} + size_t -write_blob(git_object *obj, const char *fpath, const char *filename, size_t filesize) -{ +write_blob( + git_object *obj, + const char *fpath, /* absolute path of page */ + const char *entrypath, /* directory of file */ + const char *filename, /* name of file */ + size_t filesize +) { char tmp[PATH_MAX] = "", *d; const char *p; size_t lc = 0; FILE *fp; + int is_binary; if (strlcpy(tmp, fpath, sizeof(tmp)) >= sizeof(tmp)) errx(1, "path truncated: '%s'", fpath); + if (!(d = dirname(tmp))) err(1, "dirname"); + if (mkdirp(d)) return -1; @@ -1143,20 +1175,31 @@ write_blob(git_object *obj, const char *fpath, const char *filename, size_t file } relpath = tmp; + is_binary = git_blob_is_binary((git_blob *)obj); + const char *file_url = is_binary ? filename : ""; + fp = efopen(fpath, "w"); - write_header(fp, filename); - fputs("<p> ", fp); + put_header(fp, filename); + fprintf(fp, "<p>"); put_xml(fp, filename, strlen(filename)); - fprintf(fp, " (%zuB)", filesize); - fputs("</p>", fp); + fprintf(fp, " (%zuB)</p>", filesize); // TODO also show line number (if possible) - if (git_blob_is_binary((git_blob *)obj)) - // TODO check and display image - fputs("<p>Binary file.</p>\n", fp); - else - lc = write_blob_html(fp, (git_blob *)obj); + if (is_binary) { + fprintf( + fp, + "<p>Binary file: <a href=\"%s\">raw</a></p>" "\n" + "<p><object data=\"%s\">" + "no preview available." + "</object></p>" "\n", + file_url, + file_url + ); + write_binary_file(entrypath, (git_blob *) obj); + } else { + lc = put_file_html(fp, (git_blob *) obj); + } - write_footer(fp); + put_footer(fp); check_file_error(fp, fpath, 'w'); fclose(fp); @@ -1165,37 +1208,41 @@ write_blob(git_object *obj, const char *fpath, const char *filename, size_t file return lc; } +char +filemode_type(git_filemode_t m) +{ + if (S_ISREG(m)) return '-'; + if (S_ISBLK(m)) return 'b'; + if (S_ISCHR(m)) return 'c'; + if (S_ISDIR(m)) return 'd'; + if (S_ISFIFO(m)) return 'p'; + if (S_ISLNK(m)) return 'l'; + if (S_ISSOCK(m)) return 's'; + return '?'; +} + const char * filemode(git_filemode_t m) { static char mode[11]; 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] = '?'; - + mode[0] = filemode_type(m); if (m & S_IRUSR) mode[1] = 'r'; if (m & S_IWUSR) mode[2] = 'w'; if (m & S_IXUSR) mode[3] = 'x'; + if (m & S_ISUID) mode[3] = (mode[3] == 'x') ? 's' : 'S'; if (m & S_IRGRP) mode[4] = 'r'; if (m & S_IWGRP) mode[5] = 'w'; if (m & S_IXGRP) mode[6] = 'x'; + if (m & S_ISGID) mode[6] = (mode[6] == 'x') ? 's' : 'S'; if (m & S_IROTH) mode[7] = 'r'; if (m & S_IWOTH) mode[8] = 'w'; if (m & S_IXOTH) mode[9] = 'x'; - - if (m & S_ISUID) mode[3] = (mode[3] == 'x') ? 's' : 'S'; - if (m & S_ISGID) mode[6] = (mode[6] == 'x') ? 's' : 'S'; if (m & S_ISVTX) mode[9] = (mode[9] == 'x') ? 't' : 'T'; + mode[10] = '\0'; + return mode; } @@ -1227,11 +1274,11 @@ write_file_list(FILE *fp, git_tree *tree, const char *path) case GIT_OBJ_BLOB: break; case GIT_OBJ_TREE: - /* NOTE: recurses */ // 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); + /* NOTE: recurses */ + ret = write_file_list(fp, (git_tree *)obj, entrypath); git_object_free(obj); if (ret) return ret; @@ -1242,7 +1289,7 @@ write_file_list(FILE *fp, git_tree *tree, const char *path) } filesize = git_blob_rawsize((git_blob *)obj); - lc = write_blob(obj, filepath, entryname, filesize); + lc = write_blob(obj, filepath, entrypath, entryname, filesize); fputs("<tr><td>", fp); fputs(filemode(git_tree_entry_filemode(entry)), fp); @@ -1498,7 +1545,7 @@ main(int argc, char *argv[]) } /* check LICENSE */ - for (i = 0; i < LEN(license_files) && !license; i++) { + for (i = 0; i < LENGTH(license_files) && !license; i++) { if ( !git_revparse_single(&obj, repo, license_files[i]) && git_object_type(obj) == GIT_OBJ_BLOB @@ -1508,7 +1555,7 @@ main(int argc, char *argv[]) } /* check README */ - for (i = 0; i < LEN(readme_files) && !readme; i++) { + for (i = 0; i < LENGTH(readme_files) && !readme; i++) { if ( !git_revparse_single(&obj, repo, readme_files[i]) && git_object_type(obj) == GIT_OBJ_BLOB @@ -1532,17 +1579,17 @@ main(int argc, char *argv[]) /* files for HEAD */ fp = efopen("files.html", "w"); - write_header(fp, "Files"); + put_header(fp, "Files"); if (head) write_files(fp, head); - write_footer(fp); + put_footer(fp); check_file_error(fp, "files.html", 'w'); fclose(fp); /* summary page with branches and tags */ fp = efopen("refs.html", "w"); - write_header(fp, "Refs"); + put_header(fp, "Refs"); write_refs(fp); - write_footer(fp); + put_footer(fp); check_file_error(fp, "refs.html", 'w'); fclose(fp); diff --git a/style.css b/style.css @@ -12,8 +12,8 @@ img, h1, h2 { vertical-align: middle; } -img { - border: 0; +img, object { + border: 1px solid black; } a { @@ -82,7 +82,6 @@ table#diffstat { } #blob a { - text-align: right; border-right: 1px solid black; margin-right: 5px; padding-right: 5px;

This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.