12 #include <cmark-gfm.h>
17 static git_repository *repo;
19 static const char *relpath = "";
21 static char description[255] = "Repositories";
22 static char *name = "";
23 static char owner[255];
26 joinpath(char *buf, size_t bufsiz, const char *path, const char *path2)
30 r = snprintf(buf, bufsiz, "%s%s%s",
31 path, path[0] && path[strlen(path) - 1] != '/' ? "/" : "", path2);
32 if (r < 0 || (size_t)r >= bufsiz)
33 errx(1, "path truncated: '%s%s%s'",
34 path, path[0] && path[strlen(path) - 1] != '/' ? "/" : "", path2);
37 /* Escape characters below as HTML 2.0 / XML 1.0. */
39 xmlencode(FILE *fp, const char *s, size_t len)
43 for (i = 0; *s && i < len; s++, i++) {
45 case '<': fputs("<", fp); break;
46 case '>': fputs(">", fp); break;
47 case '\'': fputs("'" , fp); break;
48 case '&': fputs("&", fp); break;
49 case '"': fputs(""", fp); break;
50 default: putc(*s, fp);
56 printtimeshort(FILE *fp, const git_time *intime)
62 t = (time_t)intime->time;
63 if (!(intm = gmtime(&t)))
65 strftime(out, sizeof(out), "%Y-%m-%d %H:%M", intm);
70 writeheader(char *path)
72 FILE *fp = fopen(path, "w");
73 fputs("<!DOCTYPE html>\n"
74 "<html lang=\"en\">\n<head>\n"
75 "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />\n"
76 "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n"
78 xmlencode(fp, description, strlen(description));
79 fprintf(fp, "</title>\n<link rel=\"icon\" type=\"image/png\" href=\"%sfavicon.png\" />\n", relpath);
80 fprintf(fp, "<link rel=\"stylesheet\" type=\"text/css\" href=\"%sstyle.css\" />\n", relpath);
81 fputs("</head>\n<body>\n", fp);
82 fprintf(fp, "<img src=\"%slogo.png\" class=\"logo\" width=\"32\" height=\"32\" /></td>\n"
84 xmlencode(fp, description, strlen(description));
87 FILE *longdesc = fopen("desc.md", "r");
88 if (longdesc == NULL) longdesc = fopen("desc", "r");
89 if (longdesc != NULL) {
90 char c = fgetc(longdesc);
97 char *md = cmark_markdown_to_html(buf, strlen(buf), CMARK_OPT_DEFAULT);
98 fprintf(fp, md, relpath);
103 fprintf(fp, &c, relpath);
110 fputs("</p>\n<hr/>\n<div id=\"content\">\n"
111 "<table id=\"index\"><thead>\n"
112 "<tr><td><b>Name</b></td><td><b>Description</b></td><td><b>Owner</b></td>"
113 "<td><b>Last commit</b></td></tr>"
114 "</thead><tbody>\n", fp);
119 writefooter(char *path)
121 FILE *fp = fopen(path, "a");
122 fputs("</tbody>\n</table>\n</div>\n</body>\n</html>\n", fp);
129 FILE *fp = fopen(path, "a");
130 git_commit *commit = NULL;
131 const git_signature *author;
132 git_revwalk *w = NULL;
134 char *stripped_name = NULL, *p;
137 git_revwalk_new(&w, repo);
138 git_revwalk_push_head(w);
139 git_revwalk_simplify_first_parent(w);
141 if (git_revwalk_next(&id, w) ||
142 git_commit_lookup(&commit, repo, &id)) {
147 author = git_commit_author(commit);
149 /* strip .git suffix */
150 if (!(stripped_name = strdup(name)))
152 if ((p = strrchr(stripped_name, '.')))
153 if (!strcmp(p, ".git"))
156 fputs("<tr><td><a href=\"", fp);
157 xmlencode(fp, stripped_name, strlen(stripped_name));
158 fputs("/log.html\">", fp);
159 xmlencode(fp, stripped_name, strlen(stripped_name));
160 fputs("</a></td><td>", fp);
161 xmlencode(fp, description, strlen(description));
162 fputs("</td><td>", fp);
163 xmlencode(fp, owner, strlen(owner));
164 fputs("</td><td>", fp);
166 printtimeshort(fp, &(author->when));
167 fputs("</td></tr>", fp);
169 git_commit_free(commit);
179 main(int argc, char *argv[])
182 char path[PATH_MAX], repodirabs[PATH_MAX + 1];
187 fprintf(stderr, "%s [repodir...]\n", argv[0]);
194 for (i = 1; i < argc; i++)
195 if (unveil(argv[i], "r") == -1)
196 err(1, "unveil: %s", argv[i]);
198 if (pledge("stdio rpath", NULL) == -1)
202 writeheader("index.html");
204 for (i = 1; i < argc; i++) {
206 if (!realpath(repodir, repodirabs))
209 if (git_repository_open_ext(&repo, repodir,
210 GIT_REPOSITORY_OPEN_NO_SEARCH, NULL)) {
211 fprintf(stderr, "%s: cannot open repository\n", argv[0]);
216 /* use directory name as name */
217 if ((name = strrchr(repodirabs, '/')))
222 /* read description or .git/description */
223 joinpath(path, sizeof(path), repodir, "description");
224 if (!(fp = fopen(path, "r"))) {
225 joinpath(path, sizeof(path), repodir, ".git/description");
226 fp = fopen(path, "r");
228 description[0] = '\0';
230 if (!fgets(description, sizeof(description), fp))
231 description[0] = '\0';
235 /* read owner or .git/owner */
236 joinpath(path, sizeof(path), repodir, "owner");
237 if (!(fp = fopen(path, "r"))) {
238 joinpath(path, sizeof(path), repodir, ".git/owner");
239 fp = fopen(path, "r");
243 if (!fgets(owner, sizeof(owner), fp))
245 owner[strcspn(owner, "\n")] = '\0';
248 writelog("index.html");
250 writefooter("index.html");
254 strcpy(cwd, getcwd(cwd, sizeof(cwd)));
255 cp("/usr/local/share/stagit/style.css", strcat(cwd, "/style.css"));
258 git_repository_free(repo);
259 git_libgit2_shutdown();