stagit

My fork of stagit

git clone git://git.shimmy1996.com/stagit.git

stagit-index.c (5373B)

    1 #include <err.h>
    2 #include <limits.h>
    3 #include <stdbool.h>
    4 #include <stdio.h>
    5 #include <stdlib.h>
    6 #include <string.h>
    7 #include <time.h>
    8 #include <unistd.h>
    9 
   10 #include <git2.h>
   11 
   12 static git_repository *repo;
   13 
   14 static const char *relpath = "";
   15 
   16 static char description[255] = "Secrets from the Second Foundation.";
   17 static char const title[255] = "Trantor Holocron";
   18 static char *name = "";
   19 static char owner[255];
   20 
   21 void
   22 joinpath(char *buf, size_t bufsiz, const char *path, const char *path2)
   23 {
   24 	int r;
   25 
   26 	r = snprintf(buf, bufsiz, "%s%s%s",
   27 		path, path[0] && path[strlen(path) - 1] != '/' ? "/" : "", path2);
   28 	if (r < 0 || (size_t)r >= bufsiz)
   29 		errx(1, "path truncated: '%s%s%s'",
   30 			path, path[0] && path[strlen(path) - 1] != '/' ? "/" : "", path2);
   31 }
   32 
   33 /* Escape characters below as HTML 2.0 / XML 1.0. */
   34 void
   35 xmlencode(FILE *fp, const char *s, size_t len)
   36 {
   37 	size_t i;
   38 
   39 	for (i = 0; *s && i < len; s++, i++) {
   40 		switch(*s) {
   41 		case '<':  fputs("&lt;",   fp); break;
   42 		case '>':  fputs("&gt;",   fp); break;
   43 		case '\'': fputs("&#39;" , fp); break;
   44 		case '&':  fputs("&amp;",  fp); break;
   45 		case '"':  fputs("&quot;", fp); break;
   46 		default:   fputc(*s, fp);
   47 		}
   48 	}
   49 }
   50 
   51 void
   52 printtimeshort(FILE *fp, const git_time *intime)
   53 {
   54 	struct tm *intm;
   55 	time_t t;
   56 	char out[32];
   57 
   58 	t = (time_t)intime->time;
   59 	if (!(intm = gmtime(&t)))
   60 		return;
   61 	strftime(out, sizeof(out), "%Y-%m-%d %H:%M", intm);
   62 	fputs(out, fp);
   63 }
   64 
   65 void
   66 writeheader(FILE *fp)
   67 {
   68 	fputs("<!DOCTYPE html>\n"
   69 		"<html lang=en>\n<head>\n"
   70 		"<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />\n"
   71 		"<title>", fp);
   72 	xmlencode(fp, title, strlen(title));
   73 	fprintf(fp, "</title>\n<link rel=\"icon\" type=\"image/png\" href=\"%sfavicon.png\" />\n", relpath);
   74 	fprintf(fp, "<link rel=\"stylesheet\" type=\"text/css\" href=\"%sstyle.css\" />\n", relpath);
   75 	fputs("</head>\n<body>\n<header>\n", fp);
   76 	fprintf(fp, "<img style=\"float:right\" width=\"80\" height=\"80\" alt=\"\" src=\"/%slogo.svg\">\n", relpath);
   77     fprintf(fp, "<h1>%s</h1>\n", title);
   78     fprintf(fp, "<p><em>");
   79 	xmlencode(fp, description, strlen(description));
   80 	fputs("</em></p>\n"
   81 		"</header>\n<main>\n"
   82 		"<table id=\"index\"><thead><tr>\n"
   83 		"<th>Name</th><th>Description</th><th>Owner</th><th>Last commit</th>"
   84 		"</tr></thead><tbody>\n", fp);
   85 }
   86 
   87 void
   88 writefooter(FILE *fp)
   89 {
   90 	fputs("</tbody>\n</table>\n</main>\n</body>\n</html>\n", fp);
   91 }
   92 
   93 int
   94 writelog(FILE *fp, bool is_public)
   95 {
   96 	git_commit *commit = NULL;
   97 	const git_signature *author;
   98 	git_revwalk *w = NULL;
   99 	git_oid id;
  100 	char *stripped_name = NULL, *p;
  101 	int ret = 0;
  102 
  103 	git_revwalk_new(&w, repo);
  104 	git_revwalk_push_head(w);
  105 	git_revwalk_simplify_first_parent(w);
  106 
  107 	if (git_revwalk_next(&id, w) ||
  108 	    git_commit_lookup(&commit, repo, &id)) {
  109 		ret = -1;
  110 		goto err;
  111 	}
  112 
  113 	author = git_commit_author(commit);
  114 
  115 	/* strip .git suffix */
  116 	if (!(stripped_name = strdup(name)))
  117 		err(1, "strdup");
  118 	if ((p = strrchr(stripped_name, '.')))
  119 		if (!strcmp(p, ".git"))
  120 			*p = '\0';
  121 
  122 	fputs("<tr><td>", fp);
  123     if (is_public) {
  124 		fputs("<a href=\"", fp);
  125 		xmlencode(fp, stripped_name, strlen(stripped_name));
  126 		fputs("/\">", fp);
  127 		xmlencode(fp, stripped_name, strlen(stripped_name));
  128 		fputs("</a>", fp);
  129     } else {
  130 		xmlencode(fp, stripped_name, strlen(stripped_name));
  131     }
  132 	fputs("</td><td>", fp);
  133 	xmlencode(fp, description, strlen(description));
  134 	fputs("</td><td>", fp);
  135 	xmlencode(fp, owner, strlen(owner));
  136 	fputs("</td><td>", fp);
  137 	if (author)
  138 		printtimeshort(fp, &(author->when));
  139 	fputs("</td></tr>", fp);
  140 
  141 	git_commit_free(commit);
  142 err:
  143 	git_revwalk_free(w);
  144 	free(stripped_name);
  145 
  146 	return ret;
  147 }
  148 
  149 int
  150 main(int argc, char *argv[])
  151 {
  152 	FILE *fp;
  153 	char path[PATH_MAX], repodirabs[PATH_MAX + 1];
  154 	const char *repodir;
  155 	int i, ret = 0;
  156     bool is_public = false;
  157 
  158 	if (argc < 2) {
  159 		fprintf(stderr, "%s [repodir...]\n", argv[0]);
  160 		return 1;
  161 	}
  162 
  163 	git_libgit2_init();
  164 
  165 #ifdef __OpenBSD__
  166 	if (pledge("stdio rpath", NULL) == -1)
  167 		err(1, "pledge");
  168 #endif
  169 
  170 	writeheader(stdout);
  171 
  172 	for (i = 1; i < argc; i++) {
  173 		repodir = argv[i];
  174 		if (!realpath(repodir, repodirabs))
  175 			err(1, "realpath");
  176 
  177 		if (git_repository_open_ext(&repo, repodir,
  178 		    GIT_REPOSITORY_OPEN_NO_SEARCH, NULL)) {
  179 			fprintf(stderr, "%s: cannot open repository\n", argv[0]);
  180 			ret = 1;
  181 			continue;
  182 		}
  183 
  184 		/* use directory name as name */
  185 		if ((name = strrchr(repodirabs, '/')))
  186 			name++;
  187 		else
  188 			name = "";
  189 
  190 		/* read description or .git/description */
  191 		joinpath(path, sizeof(path), repodir, "description");
  192 		if (!(fp = fopen(path, "r"))) {
  193 			joinpath(path, sizeof(path), repodir, ".git/description");
  194 			fp = fopen(path, "r");
  195 		}
  196 		description[0] = '\0';
  197 		if (fp) {
  198 			if (!fgets(description, sizeof(description), fp))
  199 				description[0] = '\0';
  200 			fclose(fp);
  201 		}
  202 
  203 		/* read owner or .git/owner */
  204 		joinpath(path, sizeof(path), repodir, "owner");
  205 		if (!(fp = fopen(path, "r"))) {
  206 			joinpath(path, sizeof(path), repodir, ".git/owner");
  207 			fp = fopen(path, "r");
  208 		}
  209 		owner[0] = '\0';
  210 		if (fp) {
  211 			if (!fgets(owner, sizeof(owner), fp))
  212 				owner[0] = '\0';
  213 			owner[strcspn(owner, "\n")] = '\0';
  214 			fclose(fp);
  215 		}
  216 
  217 		/* check if repo is public */
  218 		joinpath(path, sizeof(path), repodir, "git-daemon-export-ok");
  219 		is_public = fopen(path, "r");
  220 
  221 		writelog(stdout, is_public);
  222 	}
  223 	writefooter(stdout);
  224 
  225 	/* cleanup */
  226 	git_repository_free(repo);
  227 	git_libgit2_shutdown();
  228 
  229 	return ret;
  230 }