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:
| M | Makefile | 9 | ++++++--- |
| M | TODO | 8 | +++++--- |
| M | config.h | 28 | ++++++++++++++++------------ |
| M | files.c | 86 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| M | files.h | 10 | ++++++++++ |
| A | index.c | 140 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| M | main.c | 692 | +++++++++++++++++++++++++++++++++++++++---------------------------------------- |
| M | opt.c | 11 | ++++++++--- |
| M | opt.h | 4 | ++-- |
| D | stagit-index.c | 182 | ------------------------------------------------------------------------------- |
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, " <<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>>\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(" -> ", 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, " <");
- put_xml(fp, ci->author->email, strlen(ci->author->email));
+ put_xml(fp, ci->author->email);
SPUTF(fp, ">" "\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;
-}