#include <err.h>
#include <errno.h>
-#include <inttypes.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 <git2.h>
static FILE *rcachefp, *wcachefp;
static const char *cachefile;
+int cp(char fileSource[], char fileDestination[])
+{
+ int c;
+ FILE *stream_R, *stream_W;
+
+ stream_R = fopen(fileSource, "r");
+ if (stream_R == NULL)
+ return -1;
+ stream_W = fopen(fileDestination, "w"); //create and write to file
+ if (stream_W == NULL)
+ {
+ fclose(stream_R);
+ return -2;
+ }
+ while ((c = fgetc(stream_R)) != EOF)
+ fputc(c, stream_W);
+ fclose(stream_R);
+ fclose(stream_W);
+
+ return 0;
+}
+
void
joinpath(char *buf, size_t bufsiz, const char *path, const char *path2)
{
r = snprintf(buf, bufsiz, "%s%s%s",
path, path[0] && path[strlen(path) - 1] != '/' ? "/" : "", path2);
- if (r == -1 || (size_t)r >= bufsiz)
+ if (r < 0 || (size_t)r >= bufsiz)
errx(1, "path truncated: '%s%s%s'",
path, path[0] && path[strlen(path) - 1] != '/' ? "/" : "", path2);
}
if (git_diff_find_init_options(&fopts, GIT_DIFF_FIND_OPTIONS_VERSION))
goto err;
- fopts.flags |= GIT_DIFF_FIND_RENAMES | GIT_DIFF_FIND_COPIES;
+ /* find renames and copies, exact matches (no heuristic) for renames. */
+ fopts.flags |= GIT_DIFF_FIND_RENAMES | GIT_DIFF_FIND_COPIES |
+ GIT_DIFF_FIND_EXACT_MATCH_ONLY;
if (git_diff_find_similar(ci->diff, &fopts))
goto err;
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.0\" />\n"
"<title>", fp);
xmlencode(fp, title, strlen(title));
if (title[0] && strippedname[0])
fprintf(fp, "<link rel=\"alternate\" type=\"application/atom+xml\" title=\"%s Atom Feed\" href=\"%satom.xml\" />\n",
name, relpath);
fprintf(fp, "<link rel=\"stylesheet\" type=\"text/css\" href=\"%sstyle.css\" />\n", relpath);
+ fprintf(fp, "<link rel=\"stylesheet\" type=\"text/css\" href=\"%ssyntax.css\" />\n", relpath);
fputs("</head>\n<body>\n<table><tr><td>", fp);
fprintf(fp, "<a href=\"../%s\"><img src=\"%slogo.png\" alt=\"\" width=\"32\" height=\"32\" /></a>",
relpath, relpath);
xmlencode(fp, description, strlen(description));
fputs("</span></td></tr>", fp);
if (cloneurl[0]) {
- fputs("<tr class=\"url\"><td></td><td>git clone <a href=\"", fp);
+ fputs("<tr class=\"url\"><td></td><td><span class=\"clone\">git clone <a href=\"", fp);
xmlencode(fp, cloneurl, strlen(cloneurl));
fputs("\">", fp);
xmlencode(fp, cloneurl, strlen(cloneurl));
- fputs("</a></td></tr>", fp);
+ fputs("</a></span></td></tr>", fp);
}
fputs("<tr><td></td><td>\n", fp);
fprintf(fp, "<a href=\"%slog.html\">Log</a> | ", relpath);
}
int
-writeblobhtml(FILE *fp, const git_blob *blob)
+syntax_highlight(const char *filename, FILE *fp, const char *s, size_t len)
+{
+ // Flush HTML-file
+ fflush(fp);
+ // Copy STDOUT
+ int stdout_copy = dup(1);
+ // Redirect STDOUT
+ dup2(fileno(fp), 1);
+
+ // Python Pygments script for syntax highlighting.
+ FILE *child = popen("/usr/local/share/doc/stagit/highlight.py", "w");
+ if (child == NULL) {
+ printf("child is null: %s", strerror(errno));
+ exit(1);
+ }
+ // Give filename through STDIN:
+ fprintf(child, "%s\n", filename);
+ // Give code to highlight through STDIN:
+ int lc;
+ size_t i;
+ for (i = 0; *s && i < len; s++, i++) {
+ if (*s == '\n') lc++;
+ fprintf(child, "%c", *s);
+ }
+
+ pclose(child);
+ fflush(stdout);
+ // Give back STDOUT.
+ dup2(stdout_copy, 1);
+ return lc;
+}
+
+int
+writeblobhtml(const char *filename, FILE *fp, const git_blob *blob)
{
- size_t n = 0, i, prev;
- const char *nfmt = "<a href=\"#l%d\" class=\"line\" id=\"l%d\">%7d</a> ";
+ int lc = 0;
const char *s = git_blob_rawcontent(blob);
git_off_t len = git_blob_rawsize(blob);
- fputs("<pre id=\"blob\">\n", fp);
-
if (len > 0) {
- for (i = 0, prev = 0; i < (size_t)len; i++) {
- if (s[i] != '\n')
- continue;
- n++;
- fprintf(fp, nfmt, n, n, n);
- xmlencode(fp, &s[prev], i - prev + 1);
- prev = i + 1;
- }
- /* trailing data */
- if ((len - prev) > 0) {
- n++;
- fprintf(fp, nfmt, n, n, n);
- xmlencode(fp, &s[prev], len - prev);
- }
+ lc = syntax_highlight(filename, fp, s, len);
}
- fputs("</pre>\n", fp);
-
- return n;
+ return lc;
}
void
git_patch *patch;
size_t nhunks, nhunklines, changed, add, del, total, i, j, k;
char linestr[80];
+ int c;
printcommit(fp, ci);
for (i = 0; i < ci->ndeltas; i++) {
delta = git_patch_get_delta(ci->deltas[i]->patch);
- fprintf(fp, "<tr><td><a href=\"#h%zu\">", i);
+ switch (delta->status) {
+ case GIT_DELTA_ADDED: c = 'A'; break;
+ case GIT_DELTA_COPIED: c = 'C'; break;
+ case GIT_DELTA_DELETED: c = 'D'; break;
+ case GIT_DELTA_MODIFIED: c = 'M'; break;
+ case GIT_DELTA_RENAMED: c = 'R'; break;
+ case GIT_DELTA_TYPECHANGE: c = 'T'; break;
+ default: c = ' '; break;
+ }
+ if (c == ' ')
+ fprintf(fp, "<tr><td>%c", c);
+ else
+ fprintf(fp, "<tr><td class=\"%c\">%c", c, c);
+
+ fprintf(fp, "</td><td><a href=\"#h%zu\">", i);
xmlencode(fp, delta->old_file.path, strlen(delta->old_file.path));
if (strcmp(delta->old_file.path, delta->new_file.path)) {
fputs(" -> ", fp);
for (i = 0; i < ci->ndeltas; i++) {
patch = ci->deltas[i]->patch;
delta = git_patch_get_delta(patch);
- fprintf(fp, "<b>diff --git a/<a id=\"h%zu\" href=\"%sfile/%s.html\">%s</a> b/<a href=\"%sfile/%s.html\">%s</a></b>\n",
- i, relpath, delta->old_file.path, delta->old_file.path,
- relpath, delta->new_file.path, delta->new_file.path);
+ fprintf(fp, "<b>diff --git a/<a id=\"h%zu\" href=\"%sfile/", i, relpath);
+ xmlencode(fp, delta->old_file.path, strlen(delta->old_file.path));
+ fputs(".html\">", fp);
+ xmlencode(fp, delta->old_file.path, strlen(delta->old_file.path));
+ fprintf(fp, "</a> b/<a href=\"%sfile/", relpath);
+ xmlencode(fp, delta->new_file.path, strlen(delta->new_file.path));
+ fprintf(fp, ".html\">");
+ xmlencode(fp, delta->new_file.path, strlen(delta->new_file.path));
+ fprintf(fp, "</a></b>\n");
/* check binary data */
if (delta->flags & GIT_DIFF_FLAG_BINARY) {
git_revwalk_new(&w, repo);
git_revwalk_push(w, oid);
- git_revwalk_sorting(w, GIT_SORT_TIME);
git_revwalk_simplify_first_parent(w);
while (!git_revwalk_next(&id, w)) {
git_oid_tostr(oidstr, sizeof(oidstr), &id);
r = snprintf(path, sizeof(path), "commit/%s.html", oidstr);
- if (r == -1 || (size_t)r >= sizeof(path))
+ if (r < 0 || (size_t)r >= sizeof(path))
errx(1, "path truncated: 'commit/%s.html'", oidstr);
r = access(path, F_OK);
xmlencode(fp, ci->summary, strlen(ci->summary));
fputs("</title>\n", fp);
}
- fprintf(fp, "<link rel=\"alternate\" type=\"text/html\" href=\"commit/%s.html\" />",
+ fprintf(fp, "<link rel=\"alternate\" type=\"text/html\" href=\"commit/%s.html\" />\n",
ci->oid);
if (ci->author) {
- fputs("<author><name>", fp);
+ fputs("<author>\n<name>", fp);
xmlencode(fp, ci->author->name, strlen(ci->author->name));
fputs("</name>\n<email>", fp);
xmlencode(fp, ci->author->email, strlen(ci->author->email));
git_revwalk_new(&w, repo);
git_revwalk_push_head(w);
- git_revwalk_sorting(w, GIT_SORT_TIME);
git_revwalk_simplify_first_parent(w);
for (i = 0; i < m && !git_revwalk_next(&id, w); i++) {
if (git_blob_is_binary((git_blob *)obj)) {
fputs("<p>Binary file.</p>\n", fp);
} else {
- lc = writeblobhtml(fp, (git_blob *)obj);
+ lc = writeblobhtml(filename, fp, (git_blob *)obj);
if (ferror(fp))
err(1, "fwrite");
}
r = snprintf(filepath, sizeof(filepath), "file/%s.html",
entrypath);
- if (r == -1 || (size_t)r >= sizeof(filepath))
+ if (r < 0 || (size_t)r >= sizeof(filepath))
errx(1, "path truncated: 'file/%s.html'", entrypath);
if (!git_tree_entry_to_object(&obj, repo, entry)) {
fputs("<tr><td>", fp);
fputs(filemode(git_tree_entry_filemode(entry)), fp);
- fprintf(fp, "</td><td><a href=\"%s%s\">", relpath, filepath);
+ fprintf(fp, "</td><td><a href=\"%s", relpath);
+ xmlencode(fp, filepath, strlen(filepath));
+ fputs("\">", fp);
xmlencode(fp, entrypath, strlen(entrypath));
fputs("</a></td><td class=\"num\" align=\"right\">", fp);
if (lc > 0)
{
git_object *obj = NULL;
const git_oid *head = NULL;
- const git_error *e = NULL;
mode_t mask;
FILE *fp, *fpread;
char path[PATH_MAX], repodirabs[PATH_MAX + 1], *p;
git_libgit2_init();
#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 (cachefile) {
if (pledge("stdio rpath wpath cpath fattr", NULL) == -1)
err(1, "pledge");
if (git_repository_open_ext(&repo, repodir,
GIT_REPOSITORY_OPEN_NO_SEARCH, NULL) < 0) {
- e = giterr_last();
- fprintf(stderr, "%s: %s\n", argv[0], e->message);
+ fprintf(stderr, "%s: cannot open repository\n", argv[0]);
return 1;
}
else
name = "";
+ /* copy css */
+ char cwd[PATH_MAX];
+ strcpy(cwd, getcwd(cwd, sizeof(cwd)));
+ cp("/usr/local/share/doc/stagit/syntax.css", strcat(cwd, "/syntax.css"));
+ strcpy(cwd, getcwd(cwd, sizeof(cwd)));
+ cp("/usr/local/share/doc/stagit/style.css", strcat(cwd, "/style.css"));
+
/* strip .git suffix */
if (!(strippedname = strdup(name)))
err(1, "strdup");
mkdir("commit", S_IRWXU | S_IRWXG | S_IRWXO);
writeheader(fp, "Log");
fputs("<table id=\"log\"><thead>\n<tr><td><b>Date</b></td>"
- "<td><b>Commit message</b></td>"
+ "<td><b>Commit</b></td>"
"<td><b>Author</b></td><td class=\"num\" align=\"right\"><b>Files</b></td>"
"<td class=\"num\" align=\"right\"><b>+</b></td>"
"<td class=\"num\" align=\"right\"><b>-</b></td></tr>\n</thead><tbody>\n", fp);