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 84155dca25baf5e67288cc67a653511e555f7773
parent b325811df985e925cf473e3e5aadb95dabe7a619
Author: Emma Weaver <emma@waeaves.com>
Date:   Sun, 24 May 2026 13:38:33 -0400

Reincorporated basic index page generation w/ configs

Diffstat:
MMakefile9++++++---
MTODO8+++++---
Mconfig.h28++++++++++++++++------------
Mfiles.c86+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mfiles.h10++++++++++
Aindex.c140+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mmain.c692+++++++++++++++++++++++++++++++++++++++----------------------------------------
Mopt.c11++++++++---
Mopt.h4++--
Dstagit-index.c182-------------------------------------------------------------------------------
10 files changed, 614 insertions(+), 556 deletions(-)

diff --git a/Makefile b/Makefile @@ -1,8 +1,8 @@ include config.mk -tigit: main.c opt.o files.o config.mk - $(CC) main.c opt.o files.o -o stagit $(LDFLAGS) +stagit: main.c opt.o files.o index.o config.h config.mk + $(CC) main.c opt.o files.o index.o -o stagit $(LDFLAGS) opt.o: opt.c opt.h config.mk $(CC) -c opt.c -o opt.o $(CFLAGS) @@ -10,8 +10,11 @@ opt.o: opt.c opt.h config.mk files.o: files.c files.h config.mk $(CC) -c files.c -o files.o $(CFLAGS) +index.o: index.c config.h config.mk + $(CC) -c index.c -o index.o $(CFLAGS) + clean: - rm -f opt.o stagit + rm -f opt.o files.o index.o stagit install: stagit config.mk mkdir -p $(PREFIX)/bin diff --git a/TODO b/TODO @@ -23,11 +23,13 @@ - put parentheses around the dirnames in the urls [x] while writing log, only print links to files whose pages exist [x] add backlinks on files +[ ] handle trailing/leading slashes in -o and -b ---END NEW FEATURES--- -[ ] Incorporate index page into main.c -[ ] Separate page-generation and main's responsibilities -[ ] Remove globals +[x] Incorporate index page into main.c +[x] Correctly link to repo pages from index page, based on configuration/baseurl +[ ] Separate repo page generation out of main.c +[ ] Remove globals / replace them with passed structs [ ] Redo caching, to minimally repeat work - but it needs to also not increase the complexity. diff --git a/config.h b/config.h @@ -1,24 +1,28 @@ +/* Only macros! Nice and clean :) */ /* CONFIG FOR INDEX PAGE */ -#define INDEX_DEST "/index.html" -#define INDEX_LOGO "/logo.png" +// TODO uniformly expose repo data via struct +#define INDEX_LINK readme == NULL ? "files.html" : "files/%s.html", readme +#define INDEX_LOGO_SRC "/logo.png" +#define INDEX_LOGO_HREF "" /* CONFIG FOR REPOSITORY PAGES */ #define HEADER "<p>bonjour!</p>" #define FOOTER "<p>ciao!</p>" -/* These paths are all relative to the [-o OUT] directory, or pwd. */ -// TODO figure out how to use the [-b BASE] option along with that... -#define PAGE_DEST "/%s", stripped_name +/* Relative to the output directory (see -o). */ +// TODO make this being relative EXPLICIT here (but make sure the slashes still work out) +#define PAGE_DEST "%s", repo_name /* where pages get saved */ + +/* Absolute within the webpage */ #define STYLE_HREF "/style.css" #define LOGO_SRC "/logo.png" #define LOGO_HREF "/index.html" -static const int dir_pages = 1; /* generate pages for each directory */ -static const int backlinks = 0; /* include ".." in directories */ -static const int max_commits = 100; -static const char *license_files[] = { - "HEAD:LICENSE", "HEAD:LICENSE.md", "HEAD:LICENSE.txt", "HEAD:COPYING" -}; -static const char *readme_files[] = { "HEAD:README", "HEAD:README.md" }; +#define DIR_PAGES 1 /* generate pages for each directory */ +#define BACKLINKS 0 /* include ".." in directories */ +#define MAX_COMMITS 100 +#define LICENSE_FILES "HEAD:LICENSE", "HEAD:LICENSE.md", \ + "HEAD:LICENSE.txt", "HEAD:COPYING" +#define README_FILES "HEAD:README", "HEAD:README.md" diff --git a/files.c b/files.c @@ -0,0 +1,86 @@ +#include <sys/stat.h> +#include <sys/types.h> + +#include <stddef.h> +#include <stdio.h> +#include <err.h> +#include <errno.h> +#include <string.h> +#include <limits.h> + +typedef struct _path { + char *buf; + size_t buf_size; + int index; +} Path; + +void +join_path(char *buf, size_t buf_size, const char *path, const char *path2) +{ + int r; + char * separator = ""; + if(path[0] && path[strlen(path) - 1] != '/') + separator = "/"; + + r = snprintf(buf, buf_size, "%s%s%s", path, separator, path2); + if (r < 0 || (size_t) r >= buf_size) + errx(1, "path truncated: '%s%s%s'", path, separator, path2); +} + +void +combine_paths(char *ret, const char *path, const char *name) +{ + int r; + + if (path[0] == '\0') r = snprintf(ret, PATH_MAX, "%s", name); + else r = snprintf(ret, PATH_MAX, "%s/%s", path, name); + + if (r < 0 || (size_t) r >= PATH_MAX) + errx(1, "path truncated...?"); +} + +int +mkdirp(const char *path) +{ + const char *p; + char tmp[PATH_MAX]; + char *t; + + // TODO use strchr for this? + for(t = tmp, p = path; *p != '\0'; p++, t++) { + if (*p != '/') { + *t = *p; + continue; + } + + *t = '\0'; + if ( + mkdir(tmp, S_IRWXU | S_IRWXG | S_IRWXO) < 0 && + errno != EEXIST + ) + return -1; + *t = '/'; + } + return 0; +} + +/* populates buf with the filepath that was opened. */ +FILE * +fopen_w(const char *name) +{ + FILE *ret; + + mkdirp(name); // TODO minimize calls to this + if (!(ret = fopen(name, "w"))) err(1, "fopen: '%s'", name); + return ret; +} + +/* Handle read or write errors for a FILE * stream */ +void +check_file_error(FILE *fp, const char *path, int mode) +{ + if (mode == 'r' && ferror(fp)) + errx(1, "read error: %s", path); + else if (mode == 'w' && (fflush(fp) || ferror(fp))) + errx(1, "write error: %s", path); +} diff --git a/files.h b/files.h @@ -0,0 +1,10 @@ + +int mkdirp(const char *path); + +FILE * fopen_w(const char *name); + +void check_file_error(FILE *fp, const char *name, int mode); + +void join_path(char *buf, size_t bufsiz, const char *path, const char *path2); + +void combine_paths(char *ret, const char *path, const char *name); diff --git a/index.c b/index.c @@ -0,0 +1,140 @@ +#include <err.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +#include <git2.h> + +#include "config.h" + +// TODO extract these to a shared location (main.h?) +#define SPUTF(f, s) fputs((s), (f)) +#define put_xml(fp, s) put_xml_len((fp), (s), strlen(s)) + +static char owner[255]; + +FILE * git_fopen(char *path, size_t path_size, const char *name); +void put_xml_len(FILE *fp, const char *data, size_t len); +void put_percent_encoded(FILE *fp, const char *data); +void printtimeshort(FILE *fp, const git_time *intime); +void check_file_error(FILE *fp, const char *path, int mode); +void put_absolute_path(FILE *fp, char *name); + +void +put_index_header(FILE *fp) +{ + // TODO replace with option or config + const char *description = "Repositories"; + + SPUTF( + fp, + "<!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>" + ); + put_xml(fp, description); + // TODO make links relative to base_url or ? + SPUTF( + fp, + "</title>" "\n" "<link rel=\"icon\" type=\"image/png\" href=\"favicon.png\" />" "\n" + "<link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\" />" "\n" + "</head>" "\n" "<body>" "\n" + "<table>" "\n" "<tr><td><a href=\"" + ); + fprintf(fp, INDEX_LOGO_HREF); + SPUTF(fp, "\"><img alt=\"logo\" src=\""); + fprintf(fp, INDEX_LOGO_SRC); + SPUTF(fp, "\" alt=\"\" width=\"32\" height=\"32\" /></img></td>" "\n"); + SPUTF(fp, "<td><span class=\"desc\">"); + put_xml(fp, description); + SPUTF( + fp, + "</span></td></tr><tr><td></td><td>" "\n" + "</td></tr>" "\n" "</table>" "\n" "<hr/>" "\n" + "<div id=\"content\">" "\n" "<table id=\"index\"><thead>" "\n" + "<tr><td><b>Name</b></td><td><b>Description</b></td><td><b>Owner</b></td>" + "<td><b>Last commit</b></td></tr>" + "</thead><tbody>" "\n" + ); +} + +void +put_index_footer(FILE *fp) +{ + SPUTF( + fp, + "</tbody>" "\n" "</table>" "\n" "</div>" "\n" + "</body>" "\n" "</html>" "\n" + ); +} + +int +put_index_log( + FILE *fp, + git_repository *repo, + const char *owner, + const char *description, + const char *repo_name, + const char *readme +) { + git_commit *commit = NULL; + const git_signature *author; + git_revwalk *w = NULL; + git_oid id; + 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); + + // TODO make this link reflect the repo page configuration + SPUTF(fp, "<tr><td><a href=\""); + put_absolute_path(fp, ""); + fprintf(fp, INDEX_LINK); + SPUTF(fp, "\">"); + put_xml(fp, repo_name); + SPUTF(fp, "</a></td><td>"); + put_xml(fp, description); + SPUTF(fp, "</td><td>"); + put_xml(fp, owner); + SPUTF(fp, "</td><td>"); + if (author) printtimeshort(fp, &(author->when)); + SPUTF(fp, "</td></tr>"); + + git_commit_free(commit); + +err: + git_revwalk_free(w); + + return ret; +} + +void +find_owner() +{ + FILE *fp; + char path[PATH_MAX]; + + // TODO use git_fopen + fp = git_fopen(path, sizeof(path), ".git/owner"); + + owner[0] = '\0'; + if (fp) { + if (!fgets(owner, sizeof(owner), fp)) + owner[0] = '\0'; + check_file_error(fp, "owner", 'r'); + fclose(fp); + owner[strcspn(owner, "\n")] = '\0'; + } +} diff --git a/main.c b/main.c @@ -4,24 +4,27 @@ #include <err.h> #include <errno.h> #include <libgen.h> -#include <limits.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #include <unistd.h> +#include <limits.h> #include <git2.h> #include "compat.h" -#include "config.h" #include "opt.h" +#include "files.h" #define CPUT(f, c) putc((c), (f)) +#define put_xml(fp, s) put_xml_len((fp), (s), strlen(s)) #define SPUTF(f, s) fputs((s), (f)) #define LENGTH(s) (sizeof(s)/sizeof(*s)) +#include "config.h" + struct DeltaInfo { git_patch *patch; @@ -77,82 +80,94 @@ static git_repository *repo; static Opt opt; static const char *repodir; +static const char *license_files[] = { LICENSE_FILES }; +static const char *readme_files[] = { README_FILES }; + +// TODO extract these into a struct. LMFAO static char *name = ""; -static char *stripped_name = ""; +static char owner[255]; +static char *repo_name = ""; static char description[255]; -static char cloneurl[1024]; +static char clone_url[1024]; static char *submodules; static const char *license; static const char *readme; static long long nlogcommits = -1; /* -1 indicates not used */ -/* cache */ -static git_oid lastoid; -static char lastoidstr[GIT_OID_HEXSZ + 2]; /* id + newline + NUL byte */ -static FILE *rcachefp, *wcachefp; -static const char *cachefile; +/* bad globals for index.... */ +// TODO move this stuff to index.h? +static FILE *index_fp; +static char index_fname[255] = "index.html"; -int -mkdirp(const char *path) -{ - const char *p; - char tmp[PATH_MAX]; - char *t; - - for(t = tmp, p = path; *p != '\0'; p++, t++) { - if (*p != '/') { - *t = *p; - continue; - } - - *t = '\0'; - if ( - mkdir(tmp, S_IRWXU | S_IRWXG | S_IRWXO) < 0 && - errno != EEXIST - ) - return -1; - *t = '/'; - } - return 0; -} +void find_owner(); +int put_index_log(FILE *fp, git_repository *repo, const char *owner, const char *description, const char *repo_name, const char *readme); +void put_index_header(FILE *fp); +void put_index_footer(FILE *fp); -FILE * -efopen(const char *filename, const char *flags) +void +get_page_path(char *buf, size_t buf_size, const char *dir, char *name) { - FILE *fp; - mkdirp(filename); - if (!(fp = fopen(filename, flags))) - err(1, "fopen: '%s'", filename); - return fp; + int index = 0, chars = 0; + + // TODO tweak this so that it correctly places slashes between names + chars = snprintf(buf + index, buf_size - index, "%s", dir); + if (chars < 0 || (size_t) (index += chars) >= buf_size) + goto page_path_err; + + if (chars != 0) buf[index++] = '/'; + + chars = snprintf(buf + index, buf_size - index, PAGE_DEST); + if (chars < 0 || (size_t) (index += chars) >= buf_size) + goto page_path_err; + + if (chars != 0) buf[index++] = '/'; + + chars = snprintf(buf + index, buf_size - index, "%s", name); + if (chars < 0 || (size_t) (index += chars) >= buf_size) + goto page_path_err; + + return; + +page_path_err: + errx(1, "page path truncated! '%s' / '%s'" "\n", dir, name); } -/* Handle read or write errors for a FILE * stream */ void -check_file_error(FILE *fp, const char *name, int mode) +get_index_page_path(char *buf, size_t buf_size, const char *dir, char *name) { - 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); + int index = 0, chars = 0; + + // TODO tweak this so that it correctly places slashes between names + chars = snprintf(buf + index, buf_size - index, "%s", dir); + if (chars < 0 || (size_t) (index += chars) >= buf_size) + goto page_path_err; + + const char *format = dir[0] == '\0' ? "%s" : "/%s"; + chars = snprintf(buf + index, buf_size - index, format, name); + if (chars < 0 || (size_t) (index += chars) >= buf_size) + goto page_path_err; + + printf("Index page path: %s" "\n", buf); + + return; + +page_path_err: + errx(1, "page path truncated! '%s' / '%s'" "\n", dir, name); } -void -join_path(char *buf, size_t bufsiz, const char *path, const char *path2) +FILE * +git_fopen(char *path, int path_size, const char *file) { - int r; - char * separator = ""; - if(path[0] && path[strlen(path) - 1] != '/') - separator = "/"; - - 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, - separator, - path2 - ); + FILE *ret; + + /* try dir/(whatever), */ + /* (+5 removes the leading '.git/'.) */ + join_path(path, path_size, repodir, file + 5); + if ((ret = fopen(path, "r"))) return ret; + + /* then try dir/.git/(whatever). */ + join_path(path, path_size, repodir, file); + return fopen(path, "r"); } void @@ -175,6 +190,7 @@ find_readme() { git_object *obj = NULL; int i; + readme = NULL; for (i = 0; i < LENGTH(readme_files) && !readme; i++) { if ( !git_revparse_single(&obj, repo, readme_files[i]) && @@ -492,7 +508,7 @@ put_xml_char(FILE *fp, const char *s) } void -put_xml(FILE *fp, const char *s, size_t len) +put_xml_len(FILE *fp, const char *s, size_t len) { size_t i = 0; for (; *s && i < len; s++, i++) @@ -571,8 +587,10 @@ printtimeshort(FILE *fp, const git_time *intime) void put_absolute_path(FILE *fp, const char * path) { - fprintf(fp, opt.base_url); - fprintf(fp, PAGE_DEST); + if (opt.base_url[0] != '\0') + fprintf(fp, "/%s", opt.base_url); + CPUT(fp, '/'); + fprintf(fp, PAGE_DEST); /* should have no leading/trailing slashes */ fprintf(fp, "/%s", path); } @@ -588,13 +606,13 @@ put_header(FILE *fp, const char *title) "<title>", fp ); - put_xml(fp, title, strlen(title)); - if (title[0] && stripped_name[0]) + put_xml(fp, title); + if (title[0] && repo_name[0]) fputs(" - ", fp); - put_xml(fp, stripped_name, strlen(stripped_name)); + put_xml(fp, repo_name); if (description[0]) fputs(" - ", fp); - put_xml(fp, description, strlen(description)); + put_xml(fp, description); SPUTF(fp, "</title>" "\n"); SPUTF(fp, "<link rel=\"icon\" type=\"image/png\" href=\""); @@ -602,13 +620,13 @@ put_header(FILE *fp, const char *title) SPUTF(fp, "\" />" "\n"); SPUTF(fp, "<link rel=\"alternate\" type=\"application/atom+xml\" title=\""); - put_xml(fp, name, strlen(name)); + put_xml(fp, name); SPUTF(fp, " Atom Feed\" href=\""); put_absolute_path(fp, "atom.xml"); SPUTF(fp, "\" />" "\n"); SPUTF(fp, "<link rel=\"alternate\" type=\"application/atom+xml\" title=\""); - put_xml(fp, name, strlen(name)); + put_xml(fp, name); SPUTF(fp, " Atom Feed (tags)\" href=\""); put_absolute_path(fp, "tags.xml"); SPUTF(fp, "\" />" "\n"); @@ -629,15 +647,15 @@ put_header(FILE *fp, const char *title) SPUTF(fp, "\"></a>"); SPUTF(fp, "</td><td>" "<h1>"); - put_xml(fp, stripped_name, strlen(stripped_name)); + put_xml(fp, repo_name); SPUTF(fp, "</h1>" "<span class=\"desc\">"); - put_xml(fp, description, strlen(description)); + put_xml(fp, description); SPUTF(fp, "</span>" "</td></tr>"); - if (cloneurl[0]) { + if (clone_url[0]) { SPUTF(fp, "<tr class=\"url\"><td></td><td>git clone <a href=\""); - put_xml(fp, cloneurl, strlen(cloneurl)); /* not percent-encoded */ + put_xml(fp, clone_url); /* not percent encoded */ SPUTF(fp, "\">"); - put_xml(fp, cloneurl, strlen(cloneurl)); + put_xml(fp, clone_url); SPUTF(fp, "</a></td></tr>"); } SPUTF(fp, "<tr><td></td><td>" "\n"); @@ -740,18 +758,18 @@ print_commit(FILE *fp, struct CommitInfo *ci) if (ci->author) { SPUTF(fp, "<b>Author:</b> "); - put_xml(fp, ci->author->name, strlen(ci->author->name)); + put_xml(fp, ci->author->name); SPUTF(fp, " &lt;<a href=\"mailto:"); - put_xml(fp, ci->author->email, strlen(ci->author->email)); /* not percent-encoded */ + put_xml(fp, ci->author->email); /* not percent encoded */ SPUTF(fp, "\">"); - put_xml(fp, ci->author->email, strlen(ci->author->email)); + put_xml(fp, ci->author->email); SPUTF(fp, "</a>&gt;\n<b>Date:</b> "); printtime(fp, &(ci->author->when)); CPUT(fp, '\n'); } if (ci->msg) { CPUT(fp, '\n'); - put_xml(fp, ci->msg, strlen(ci->msg)); + put_xml(fp, ci->msg); CPUT(fp, '\n'); } } @@ -803,10 +821,10 @@ put_show_file(FILE *fp, struct CommitInfo *ci) fprintf(fp, "<tr><td class=\"%c\">%c", c, c); fprintf(fp, "</td><td><a href=\"#h%zu\">", i); - put_xml(fp, delta->old_file.path, strlen(delta->old_file.path)); + put_xml(fp, delta->old_file.path); if (strcmp(delta->old_file.path, delta->new_file.path)) { fputs(" -&gt; ", fp); - put_xml(fp, delta->new_file.path, strlen(delta->new_file.path)); + put_xml(fp, delta->new_file.path); } add = ci->deltas[i]->add_count; @@ -849,9 +867,9 @@ put_show_file(FILE *fp, struct CommitInfo *ci) delta = git_patch_get_delta(patch); SPUTF(fp, "<b>diff --git a/"); - put_xml(fp, delta->old_file.path, strlen(delta->old_file.path)); + put_xml(fp, delta->old_file.path); fprintf(fp, " b/<a id=\"h%zu\" href=\"#h%zu\">", i, i); - put_xml(fp, delta->new_file.path, strlen(delta->new_file.path)); + put_xml(fp, delta->new_file.path); SPUTF(fp, "</a></b>" "\n"); /* check binary data */ @@ -866,7 +884,7 @@ put_show_file(FILE *fp, struct CommitInfo *ci) break; fprintf(fp, "<a href=\"#h%zu-%zu\" id=\"h%zu-%zu\" class=\"h\">", i, j, i, j); - put_xml(fp, hunk->header, hunk->header_len); + put_xml_len(fp, hunk->header, hunk->header_len); SPUTF(fp, "</a>"); for (k = 0; ; k++) { @@ -907,12 +925,11 @@ put_log_line(FILE *fp, struct CommitInfo *ci) put_absolute_path(fp, "commits/"); fprintf(fp, "%s.html", ci->oid); SPUTF(fp, "\">"); - put_xml(fp, ci->summary, strlen(ci->summary)); + put_xml(fp, ci->summary); SPUTF(fp, "</a>"); } SPUTF(fp, "</td><td>"); - if (ci->author) - put_xml(fp, ci->author->name, strlen(ci->author->name)); + if (ci->author) put_xml(fp, ci->author->name); SPUTF(fp, "</td><td class=\"num\" align=\"right\">"); fprintf(fp, "%zu", ci->file_count); SPUTF(fp, "</td><td class=\"num\" align=\"right\">"); @@ -922,6 +939,25 @@ put_log_line(FILE *fp, struct CommitInfo *ci) SPUTF(fp, "</td></tr>" "\n"); } +void +write_commit_page(char *name, struct CommitInfo *ci) +{ + char fname[PATH_MAX]; + get_page_path(fname, sizeof(fname), opt.out_dir, name); + FILE *fp = fopen_w(fname); + // [old] fpfile = efopen(path, "w"); + + put_header(fp, ci->summary); + + SPUTF(fp, "<pre>"); + put_show_file(fp, ci); + SPUTF(fp, "</pre>" "\n"); + + put_footer(fp); + check_file_error(fp, fname, 'w'); + fclose(fp); +} + int put_log(FILE *fp, const git_oid *oid) { @@ -929,7 +965,6 @@ put_log(FILE *fp, const git_oid *oid) git_revwalk *w = NULL; git_oid id; char path[PATH_MAX], oidstr[GIT_OID_HEXSZ + 1]; - FILE *fpfile; size_t remcommits = 0; int r; @@ -937,9 +972,6 @@ put_log(FILE *fp, const git_oid *oid) git_revwalk_push(w, oid); while (!git_revwalk_next(&id, w)) { - if (cachefile && !memcmp(&id, &lastoid, sizeof(id))) - break; - git_oid_tostr(oidstr, sizeof(oidstr), &id); r = snprintf(path, sizeof(path), "commits/%s.html", oidstr); if (r < 0 || (size_t)r >= sizeof(path)) @@ -966,22 +998,9 @@ put_log(FILE *fp, const git_oid *oid) nlogcommits--; } - if (cachefile) - put_log_line(wcachefp, ci); - /* check if file exists if so skip it */ - if (r) { - fpfile = efopen(path, "w"); - put_header(fpfile, ci->summary); - - fputs("<pre>", fpfile); - put_show_file(fpfile, ci); - fputs("</pre>\n", fpfile); + if (r) write_commit_page(path, ci); - put_footer(fpfile); - check_file_error(fpfile, path, 'w'); - fclose(fpfile); - } err: commit_info_free(ci); } @@ -1000,68 +1019,6 @@ err: } void -put_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); - - put_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 put_log_html(FILE *fp, const git_oid *head) { put_header(fp, "Log"); @@ -1079,8 +1036,7 @@ put_log_html(FILE *fp, const git_oid *head) fp ); - if (head && !cachefile) put_log(fp, head); - if (head && cachefile) put_log_cached(fp, head); + if (head) put_log(fp, head); fputs("</tbody></table>", fp); put_footer(fp); @@ -1106,10 +1062,10 @@ print_commit_atom(FILE *fp, struct CommitInfo *ci, const char *tag) SPUTF(fp, "<title>"); if (tag && tag[0]) { SPUTF(fp, "["); - put_xml(fp, tag, strlen(tag)); + put_xml(fp, tag); SPUTF(fp, "] "); } - put_xml(fp, ci->summary, strlen(ci->summary)); + put_xml(fp, ci->summary); SPUTF(fp, "</title>\n"); } SPUTF(fp, "<link rel=\"alternate\" type=\"text/html\" href=\""); @@ -1118,9 +1074,9 @@ print_commit_atom(FILE *fp, struct CommitInfo *ci, const char *tag) if (ci->author) { SPUTF(fp, "<author>" "\n" "<name>"); - put_xml(fp, ci->author->name, strlen(ci->author->name)); + put_xml(fp, ci->author->name); SPUTF(fp, "</name>" "\n" "<email>"); - put_xml(fp, ci->author->email, strlen(ci->author->email)); + put_xml(fp, ci->author->email); SPUTF(fp, "</email>" "\n" "</author>" "\n"); } @@ -1130,16 +1086,16 @@ print_commit_atom(FILE *fp, struct CommitInfo *ci, const char *tag) fprintf(fp, "parent %s" "\n", ci->parentoid); if (ci->author) { SPUTF(fp, "Author: "); - put_xml(fp, ci->author->name, strlen(ci->author->name)); + put_xml(fp, ci->author->name); SPUTF(fp, " &lt;"); - put_xml(fp, ci->author->email, strlen(ci->author->email)); + put_xml(fp, ci->author->email); SPUTF(fp, "&gt;" "\n" "Date: "); printtime(fp, &(ci->author->when)); CPUT(fp, '\n'); } if (ci->msg) { CPUT(fp, '\n'); - put_xml(fp, ci->msg, strlen(ci->msg)); + put_xml(fp, ci->msg); } SPUTF(fp, "\n" "</content>" "\n" "</entry>" "\n"); } @@ -1159,9 +1115,9 @@ write_atom(FILE *fp, int all) "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" "\n" "<feed xmlns=\"http://www.w3.org/2005/Atom\">" "\n" "<title>" ); - put_xml(fp, stripped_name, strlen(stripped_name)); + put_xml(fp, repo_name); SPUTF(fp, ", branch HEAD</title>" "\n" "<subtitle>"); - put_xml(fp, description, strlen(description)); + put_xml(fp, description); SPUTF(fp, "</subtitle>" "\n"); /* all commits or only tags? */ @@ -1197,7 +1153,7 @@ write_atom(FILE *fp, int all) void write_binary_file(const char *filename, git_blob *blob) { - char dest[PATH_MAX] = ""; + char dest[PATH_MAX]; const char *raw = git_blob_rawcontent(blob); int size = git_blob_rawsize(blob); @@ -1205,9 +1161,12 @@ write_binary_file(const char *filename, git_blob *blob) { join_path(dest, PATH_MAX, "files", filename); /* write the file! */ - FILE *fp = efopen(dest, "w"); + char fname[PATH_MAX]; + get_page_path(fname, sizeof(fname), opt.out_dir, dest); + FILE *fp = fopen_w(fname); + fwrite(raw, size, 1, fp); - check_file_error(fp, dest, 'w'); + check_file_error(fp, fname, 'w'); fclose(fp); } @@ -1244,12 +1203,12 @@ put_file_title(FILE *fp, const char *name, int is_dir) if (name[0] == '\0') return; - if (!dir_pages) { + if (!DIR_PAGES) { fprintf(fp, "<p>%s</p>", name); return; } - if (backlinks && !is_dir) { + if (BACKLINKS && !is_dir) { for (i = 0; name[i] != '\0'; i++) if (name[i] == '/') len = i; @@ -1270,43 +1229,47 @@ put_file_title(FILE *fp, const char *name, int is_dir) if (!is_dir) SPUTF(fp, "<hr>"); } +void +put_binary_blob_stub(FILE *fp, const char *file_url) +{ + SPUTF(fp, "<p>Binary file: <a href=\""); + put_absolute_path(fp, "files/"); + fprintf(fp, "%s\">raw</a></p>" "\n", file_url); + SPUTF(fp, "<object data=\""); + put_absolute_path(fp, "files/"); + fprintf(fp, "%s\">", file_url); + SPUTF(fp, "no preview available.</object></p>" "\n"); +} + struct Weight write_blob( - git_object *obj, - const char *filename, + git_blob *blob, + char *filename, const char *entrypath, const char *name ) { + char fname[PATH_MAX]; FILE *fp; - const char *file_url; - int is_binary; struct Weight ret; + ret.lines = 0; - ret.bytes = git_blob_rawsize((git_blob *)obj); - - is_binary = git_blob_is_binary((git_blob *)obj); - file_url = is_binary ? name : ""; + ret.bytes = git_blob_rawsize(blob); - fp = efopen(filename, "w"); + get_page_path(fname, sizeof(fname), opt.out_dir, filename); + fp = fopen_w(fname); + put_header(fp, name); put_file_title(fp, entrypath, 0); - if (is_binary) { - SPUTF(fp, "<p>Binary file: <a href=\""); - put_absolute_path(fp, ""); - fprintf(fp, "%s\">raw</a></p>" "\n", file_url); - SPUTF(fp, "<object data=\""); - put_absolute_path(fp, ""); - fprintf(fp, "%s\">", file_url); - SPUTF(fp, "no preview available.</object></p>" "\n"); - - write_binary_file(entrypath, (git_blob *) obj); + if (git_blob_is_binary(blob)) { + put_binary_blob_stub(fp, entrypath); // name); + write_binary_file(entrypath, blob); } else { - ret.lines = put_file_html(fp, (git_blob *) obj); + ret.lines = put_file_html(fp, blob); } put_footer(fp); - check_file_error(fp, filename, 'w'); + check_file_error(fp, fname, 'w'); fclose(fp); return ret; @@ -1371,8 +1334,8 @@ put_filetree_file_line( put_absolute_path(fp, ""); put_percent_encoded(fp, filename, strlen(filename)); SPUTF(fp, "\">"); - if (dir_pages) put_xml(fp, name, strlen(name)); - else put_xml(fp, entrypath, strlen(entrypath)); + if (DIR_PAGES) put_xml(fp, name); + else put_xml(fp, entrypath); SPUTF(fp, "</a></td><td class=\"num\" align=\"right\">"); put_size(fp, weight.lines, 1); SPUTF(fp, "</td><td class=\"num\" align=\"right\">"); @@ -1395,7 +1358,7 @@ put_filetree_dir_line( put_absolute_path(fp, ""); put_percent_encoded(fp, destination, strlen(destination)); SPUTF(fp, "\">"); - put_xml(fp, title, strlen(title)); + put_xml(fp, title); SPUTF(fp, "</a></td><td class=\"num\" align=\"right\">"); put_size(fp, lines, 1); SPUTF(fp, "</td><td class=\"num\" align=\"right\">"); @@ -1449,7 +1412,7 @@ put_entry_obj(FILE *fp, git_object *obj, const git_tree_entry *entry, char *entr switch (obj_type) { case GIT_OBJ_TREE: - if (dir_pages) { + if (DIR_PAGES) { /* create dir page */ ret = write_filetree(entrypath, filename, (git_tree *) obj); @@ -1461,7 +1424,7 @@ put_entry_obj(FILE *fp, git_object *obj, const git_tree_entry *entry, char *entr } break; case GIT_OBJ_BLOB: - ret = write_blob(obj, filename, entrypath, name); + ret = write_blob((git_blob *) obj, filename, entrypath, name); put_filetree_file_line(fp, obj, entry, filename, entrypath, name, ret); break; default: break; @@ -1478,26 +1441,14 @@ put_submodule_obj(FILE *fp, const git_tree_entry *entry, const char *name) SPUTF(fp, "<tr>" "<td>m---------</td>" "<td><a href=\""); put_absolute_path(fp, "files/.gitmodules.html"); SPUTF(fp, "\">"); - put_xml(fp, name, strlen(name)); + put_xml(fp, name); SPUTF(fp, "</a> @ "); git_oid_tostr(oid, sizeof(oid), git_tree_entry_id(entry)); - put_xml(fp, oid, strlen(oid)); + put_xml(fp, oid); SPUTF(fp, "</td>" "<td class=\"num\" align=\"right\"></td>" "\n"); SPUTF(fp, "<td class=\"num\" align=\"right\"></td>" "</tr>" "\n"); } -void -combine_paths(char *ret, const char *path, const char *name) -{ - int r; - - if (path[0] == '\0') r = snprintf(ret, PATH_MAX, "%s", name); - else r = snprintf(ret, PATH_MAX, "%s/%s", path, name); - - if (r < 0 || (size_t) r >= PATH_MAX) - errx(1, "path truncated...?"); -} - struct Weight put_file_list(FILE *fp, git_tree *tree, const char *path) { @@ -1547,10 +1498,13 @@ put_file_list(FILE *fp, git_tree *tree, const char *path) struct Weight write_filetree(const char *path, const char *filename, git_tree *tree) { - FILE *fp; struct Weight ret; + char fname[PATH_MAX] = ""; + FILE *fp; + + get_page_path(fname, sizeof(fname), opt.out_dir, (char *) filename); + fp = fopen_w(fname); - fp = efopen(filename, "w"); put_header(fp, "Files"); put_file_title(fp, path, 1); @@ -1565,7 +1519,7 @@ write_filetree(const char *path, const char *filename, git_tree *tree) ); if (tree != NULL) { - if (backlinks && path[0] != '\0') + if (BACKLINKS && path[0] != '\0') put_filetree_backlink_line(fp, path); ret = put_file_list(fp, tree, path); @@ -1581,7 +1535,7 @@ write_filetree(const char *path, const char *filename, git_tree *tree) SPUTF(fp, "B</p>"); put_footer(fp); - check_file_error(fp, filename, 'w'); + check_file_error(fp, fname, 'w'); fclose(fp); return ret; @@ -1606,7 +1560,7 @@ walk_git_tree(const git_oid *id) } int -write_refs(FILE *fp) +put_refs(FILE *fp) { struct ReferenceInfo *ris = NULL; struct CommitInfo *ci; @@ -1643,13 +1597,13 @@ write_refs(FILE *fp) s = git_reference_shorthand(ris[i].ref); fputs("<tr><td>", fp); - put_xml(fp, s, strlen(s)); + put_xml(fp, s); fputs("</td><td>", fp); if (ci->author) printtimeshort(fp, &(ci->author->when)); fputs("</td><td>", fp); if (ci->author) - put_xml(fp, ci->author->name, strlen(ci->author->name)); + put_xml(fp, ci->author->name); fputs("</td></tr>\n", fp); } @@ -1668,188 +1622,167 @@ write_refs(FILE *fp) void write_log_page(const git_oid *head) { - const char *filename = "log.html"; - FILE *fp = efopen(filename, "w"); + FILE *fp; + char fname[PATH_MAX]; + + get_page_path(fname, sizeof(fname), opt.out_dir, "log.html"); + fp = fopen_w(fname); + put_log_html(fp, head); - check_file_error(fp, filename, 'w'); + + check_file_error(fp, fname, 'w'); fclose(fp); } void write_refs_page() { - const char *filename = "refs.html"; - FILE *fp = efopen(filename, "w"); + FILE *fp; + char fname[PATH_MAX]; + + get_page_path(fname, sizeof(fname), opt.out_dir, "refs.html"); + fp = fopen_w(fname); + put_header(fp, "Refs"); - write_refs(fp); + put_refs(fp); put_footer(fp); - check_file_error(fp, filename, 'w'); + + check_file_error(fp, fname, 'w'); fclose(fp); } void write_feed() { - const char *filename = "atom.xml"; - FILE *fp = efopen(filename, "w"); + FILE *fp; + char fname[PATH_MAX]; + + get_page_path(fname, sizeof(fname), opt.out_dir, "atom.xml"); + fp = fopen_w(fname); + write_atom(fp, 1); - check_file_error(fp, filename, 'w'); + + check_file_error(fp, fname, 'w'); fclose(fp); } void write_releases_feed() { - const char *filename = "tags.xml"; - FILE *fp = efopen(filename, "w"); + FILE *fp; + char fname[PATH_MAX]; + + get_page_path(fname, sizeof(fname), opt.out_dir, "tags.xml"); + fp = fopen_w(fname); + write_atom(fp, 0); - check_file_error(fp, filename, 'w'); + + check_file_error(fp, fname, 'w'); fclose(fp); } void -usage(char *argv0) +find_submodules() { - fprintf( - stderr, - "usage: %s [-c cachefile | -l commits] " - "repodir" "\n", - argv0 - ); - exit(1); + git_object *obj = NULL; + if ( + !git_revparse_single(&obj, repo, "HEAD:.gitmodules") && + git_object_type(obj) == GIT_OBJ_BLOB + ) + submodules = ".gitmodules"; + git_object_free(obj); } -int -main(int argc, const char *argv[]) +void +find_description() { - const git_oid *head = NULL; - FILE *fpread; - char path[PATH_MAX], repodirabs[PATH_MAX + 1], *p; - git_object *obj = NULL; - int i; + char path[PATH_MAX]; + FILE *fp; + if ((fp = git_fopen(path, sizeof(path), ".git/description"))) { + if (!fgets(description, sizeof(description), fp)) + description[0] = '\0'; + check_file_error(fp, path, 'r'); + fclose(fp); + } +} - int result = parse_opts(&opt, argc, argv); - if (result == OPT_FAIL) { - PRINT_USAGE(argv[0]); - return 1; +void +find_clone_url() +{ + char path[PATH_MAX]; + FILE *fp; + if ((fp = git_fopen(path, sizeof(path), ".git/url"))) { + if (!fgets(clone_url, sizeof(clone_url), fp)) + clone_url[0] = '\0'; + check_file_error(fp, path, 'r'); + fclose(fp); + + // TODO ??? + clone_url[strcspn(clone_url, "\n")] = '\0'; } +} - printf( - "hide?: %d" "\n" - "#repos: %d" "\n" - "indexfile: %s" "\n" - "cachedir: %s" "\n" - "outdir: %s" "\n" - "\n", - opt.hide_hidden, - opt.repo_count, - opt.index_file, - opt.cache_dir, - opt.out_dir - ); - - puts("Repos:" "\n"); - for(int a = 0; a < opt.repo_count; a++) - printf("%d: '%s'" "\n", a, opt.repo_dirs[a]); +int +write_repo_pages(const char* dir) +{ + char abs_dir[PATH_MAX + 1]; /* absolute path of dir */ - // TODO iterate over repo_dirs - repodir = opt.repo_dirs[0]; + // TODO fully stop using globals like this.... + repodir = dir; - /* parse args */ - if (!realpath(repodir, repodirabs)) + if (!realpath(dir, abs_dir)) 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) - err(1, "unveil: %s", repodir); - if (unveil(".", "rwc") == -1) - err(1, "unveil: ."); - if (cachefile && unveil(cachefile, "rwc") == -1) - err(1, "unveil: %s", cachefile); + if (unveil(repodir, "r") == -1) err(1, "unveil: %s", repodir); + if (unveil(".", "rwc") == -1) err(1, "unveil: ."); + /* if (cachefile) { - if (pledge("stdio rpath wpath cpath fattr", NULL) == -1) - err(1, "pledge"); + if (unveil(cachefile, "rwc") == -1) err(1, "unveil: %s", cachefile); + if (pledge("stdio rpath wpath cpath fattr", NULL) == -1) err(1, "pledge"); } else { - if (pledge("stdio rpath wpath cpath", NULL) == -1) - err(1, "pledge"); + if (pledge("stdio rpath wpath cpath", NULL) == -1) err(1, "pledge"); } + */ #endif - if ( - git_repository_open_ext( - &repo, - repodir, - GIT_REPOSITORY_OPEN_NO_SEARCH, - NULL - ) < 0 - ) { - fprintf(stderr, "%s: cannot open repository\n", argv[0]); + /* open the git repo */ + int result = git_repository_open_ext( + &repo, repodir, GIT_REPOSITORY_OPEN_NO_SEARCH, NULL + ); + if (result < 0) { + fprintf(stderr, "fatal: cannot open repository '%s'" "\n", dir); return 1; } /* find HEAD */ + const git_oid *head = NULL; + git_object *obj = NULL; if (!git_revparse_single(&obj, repo, "HEAD")) head = git_object_id(obj); git_object_free(obj); /* use directory name as name */ - if ((name = strrchr(repodirabs, '/'))) name++; + if ((name = strrchr(abs_dir, '/'))) name++; else name = ""; /* strip .git suffix */ - if (!(stripped_name = strdup(name))) - err(1, "strdup"); - if ((p = strrchr(stripped_name, '.'))) + char *p; + if (!(repo_name = strdup(name))) err(1, "strdup"); + if ((p = strrchr(repo_name, '.'))) if (!strcmp(p, ".git")) *p = '\0'; - if (stripped_name[0] == '\0') - stripped_name = "untitled"; - - /* read description or .git/description */ - join_path(path, sizeof(path), repodir, "description"); - if (!(fpread = fopen(path, "r"))) { - join_path(path, sizeof(path), repodir, ".git/description"); - fpread = fopen(path, "r"); - } - if (fpread) { - if (!fgets(description, sizeof(description), fpread)) - description[0] = '\0'; - check_file_error(fpread, path, 'r'); - fclose(fpread); - } - - /* read url or .git/url */ - join_path(path, sizeof(path), repodir, "url"); - if (!(fpread = fopen(path, "r"))) { - join_path(path, sizeof(path), repodir, ".git/url"); - fpread = fopen(path, "r"); - } - if (fpread) { - if (!fgets(cloneurl, sizeof(cloneurl), fpread)) - cloneurl[0] = '\0'; - check_file_error(fpread, path, 'r'); - fclose(fpread); - cloneurl[strcspn(cloneurl, "\n")] = '\0'; - } + if (repo_name[0] == '\0') + repo_name = "untitled"; + /* set global vars... */ + find_description(); + find_clone_url(); find_license(); find_readme(); - - if ( - !git_revparse_single(&obj, repo, "HEAD:.gitmodules") && - git_object_type(obj) == GIT_OBJ_BLOB - ) - submodules = ".gitmodules"; - git_object_free(obj); + find_submodules(); + if (opt.index) find_owner(); /* write pages! */ write_log_page(head); @@ -1857,9 +1790,66 @@ main(int argc, const char *argv[]) write_refs_page(); write_feed(); write_releases_feed(); + if (opt.index) put_index_log(index_fp, repo, owner, description, repo_name, readme); - /* cleanup */ + /* clean up */ git_repository_free(repo); + + return 0; +} + +void +begin_index() +{ + // TODO populate index_fname correctly based on configs + get_index_page_path(index_fname, sizeof(index_fname), opt.out_dir, "index.html"); + index_fp = fopen_w(index_fname); + put_index_header(index_fp); +} + +void +end_index() +{ + put_index_footer(index_fp); + check_file_error(index_fp, index_fname, 'w'); + fclose(index_fp); +} + +void +init_lib_git() +{ + int i; + + /* 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); +} + +int +main(int argc, const char *argv[]) +{ + int i; + + if (parse_opts(&opt, argc, argv) == OPT_FAIL) { + PRINT_USAGE(argv[0]); + return 1; + } + + init_lib_git(); + + if (opt.index) begin_index(); + for (i = 0; i < opt.repo_count; i++) { + printf("Writing repository: %s" "\n", opt.repo_dirs[i]); + if(write_repo_pages(opt.repo_dirs[i]) > 0) + return 1; + } + if (opt.index) end_index(); + git_libgit2_shutdown(); return 0; diff --git a/opt.c b/opt.c @@ -19,9 +19,9 @@ parse_opt(Opt *opt, const char *cur, const char *next) switch (flag) { case 'h': opt->hide_hidden = 1; break; - case 'i': opt->index_file = next; return OPT_SKIP; + case 'i': opt->index = next; return OPT_SKIP; case 'o': opt->out_dir = next; return OPT_SKIP; - case 'c': opt->cache_dir = next; return OPT_SKIP; + case 'c': opt->cache = next; return OPT_SKIP; case 'b': opt->base_url = next; return OPT_SKIP; default: ERROR("Unrecognized flag: -%c." "\n\n", flag); } @@ -41,9 +41,14 @@ parse_opts(Opt *opt, int argc, const char * argv[]) if (argc < 2) ERROR(""); + /* set default values */ + opt->base_url = ""; + opt->out_dir = ""; + opt->index = NULL; + for (index = 1;;) { cur = argv[index]; - printf("Parsing flag group: %s" "\n", cur); + // printf("Parsing flag group: %s" "\n", cur); next = (index+1 < argc-1) ? argv[index+1] : NULL; switch (parse_opt(opt, cur, next)) { case OPT_SKIP: index++; break; diff --git a/opt.h b/opt.h @@ -24,9 +24,9 @@ typedef struct _ { int repo_count; const char **repo_dirs; - const char *cache_dir; + const char *cache; const char *out_dir; - const char *index_file; + const char *index; const char *base_url; } Opt; diff --git a/stagit-index.c b/stagit-index.c @@ -1,182 +0,0 @@ -#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] = "Repositories"; -static char *name = ""; -static char owner[255]; - -void -writeheader(FILE *fp) -{ - 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, description, strlen(description)); - fprintf(fp, "</title>\n<link rel=\"icon\" type=\"image/png\" href=\"%sfavicon.png\" />\n", relpath); - fprintf(fp, "<link rel=\"stylesheet\" type=\"text/css\" href=\"%sstyle.css\" />\n", relpath); - fputs("</head>\n<body>\n", fp); - fprintf(fp, "<table>\n<tr><td><img src=\"%slogo.png\" alt=\"\" width=\"32\" height=\"32\" /></td>\n" - "<td><span class=\"desc\">", relpath); - xmlencode(fp, description, strlen(description)); - fputs("</span></td></tr><tr><td></td><td>\n" - "</td></tr>\n</table>\n<hr/>\n<div id=\"content\">\n" - "<table id=\"index\"><thead>\n" - "<tr><td><b>Name</b></td><td><b>Description</b></td><td><b>Owner</b></td>" - "<td><b>Last commit</b></td></tr>" - "</thead><tbody>\n", fp); -} - -void -writefooter(FILE *fp) -{ - fputs("</tbody>" "\n" "</table>" "\n" "</div>" "\n" "</body>" "\n" "</html>" "\n", 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("<tr><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); - xmlencode(fp, owner, strlen(owner)); - 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++) { - 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); - } - - /* read owner or .git/owner */ - joinpath(path, sizeof(path), repodir, "owner"); - if (!(fp = fopen(path, "r"))) { - joinpath(path, sizeof(path), repodir, ".git/owner"); - fp = fopen(path, "r"); - } - owner[0] = '\0'; - if (fp) { - if (!fgets(owner, sizeof(owner), fp)) - owner[0] = '\0'; - checkfileerror(fp, "owner", 'r'); - fclose(fp); - owner[strcspn(owner, "\n")] = '\0'; - } - writelog(stdout); - } - writefooter(stdout); - - /* cleanup */ - git_repository_free(repo); - git_libgit2_shutdown(); - - checkfileerror(stdout, "<stdout>", 'w'); - - return ret; -}

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