]> git.armaanb.net Git - stagit.git/blob - urmoms.c
read README and LICENSE from repo, escape as HTML
[stagit.git] / urmoms.c
1 #include <err.h>
2 #include <libgen.h>
3 #include <limits.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
7
8 #include "git2.h"
9
10 static git_repository *repo;
11
12 static const char *relpath;
13 static const char *repodir = ".";
14
15 static char name[255];
16 static char description[255];
17 static int hasreadme, haslicense;
18
19 FILE *
20 efopen(const char *name, const char *flags)
21 {
22         FILE *fp;
23
24         fp = fopen(name, flags);
25         if (!fp)
26                 err(1, "fopen");
27
28         return fp;
29 }
30
31 /* Escape characters below as HTML 2.0 / XML 1.0. */
32 void
33 xmlencode(FILE *fp, const char *s, size_t len)
34 {
35         size_t i;
36
37         for (i = 0; *s && i < len; s++, i++) {
38                 switch(*s) {
39                 case '<':  fputs("&lt;",   fp); break;
40                 case '>':  fputs("&gt;",   fp); break;
41                 case '\'': fputs("&apos;", fp); break;
42                 case '&':  fputs("&amp;",  fp); break;
43                 case '"':  fputs("&quot;", fp); break;
44                 default:   fputc(*s, fp);
45                 }
46         }
47 }
48
49 /* Some implementations of basename(3) return a pointer to a static
50  * internal buffer (OpenBSD). Others modify the contents of `path` (POSIX).
51  * This is a wrapper function that is compatible with both versions.
52  * The program will error out if basename(3) failed, this can only happen
53  * with the OpenBSD version. */
54 char *
55 xbasename(const char *path)
56 {
57         char *p, *b;
58
59         if (!(p = strdup(path)))
60                 err(1, "strdup");
61         if (!(b = basename(p)))
62                 err(1, "basename");
63         if (!(b = strdup(b)))
64                 err(1, "strdup");
65         free(p);
66
67         return b;
68 }
69
70 static void
71 printtime(FILE *fp, const git_time *intime, const char *prefix)
72 {
73         struct tm *intm;
74         time_t t;
75         int offset, hours, minutes;
76         char sign, out[32];
77
78         offset = intime->offset;
79         if (offset < 0) {
80                 sign = '-';
81                 offset = -offset;
82         } else {
83                 sign = '+';
84         }
85
86         hours = offset / 60;
87         minutes = offset % 60;
88
89         t = (time_t) intime->time + (intime->offset * 60);
90
91         intm = gmtime(&t);
92         strftime(out, sizeof(out), "%a %b %e %T %Y", intm);
93
94         fprintf(fp, "%s%s %c%02d%02d\n", prefix, out, sign, hours, minutes);
95 }
96
97 static void
98 printcommit(FILE *fp, git_commit *commit)
99 {
100         const git_signature *sig;
101         char buf[GIT_OID_HEXSZ + 1];
102         int i, count;
103         const char *scan, *eol;
104
105         git_oid_tostr(buf, sizeof(buf), git_commit_id(commit));
106         fprintf(fp, "commit <a href=\"commit/%s.html\">%s</a>\n", buf, buf);
107
108         if ((count = (int)git_commit_parentcount(commit)) > 1) {
109                 fprintf(fp, "Merge:");
110                 for (i = 0; i < count; ++i) {
111                         git_oid_tostr(buf, 8, git_commit_parent_id(commit, i));
112                         fprintf(fp, " %s", buf);
113                 }
114                 fprintf(fp, "\n");
115         }
116         if ((sig = git_commit_author(commit)) != NULL) {
117                 fprintf(fp, "Author: <a href=\"author/%s.html\">%s</a> <%s>\n",
118                         sig->name, sig->name, sig->email);
119                 printtime(fp, &sig->when, "Date:   ");
120         }
121         fprintf(fp, "\n");
122
123         for (scan = git_commit_message(commit); scan && *scan;) {
124                 for (eol = scan; *eol && *eol != '\n'; ++eol)   /* find eol */
125                         ;
126
127                 fprintf(fp, "    %.*s\n", (int) (eol - scan), scan);
128                 scan = *eol ? eol + 1 : NULL;
129         }
130         fprintf(fp, "\n");
131 }
132
133 static void
134 printcommitdiff(FILE *fp, git_commit *commit)
135 {
136         const git_signature *sig;
137         char buf[GIT_OID_HEXSZ + 1];
138         int i, count;
139         const char *scan, *eol;
140
141         git_oid_tostr(buf, sizeof(buf), git_commit_id(commit));
142         fprintf(fp, "commit <a href=\"commit/%s.html\">%s</a>\n", buf, buf);
143
144         if ((count = (int)git_commit_parentcount(commit)) > 1) {
145                 fprintf(fp, "Merge:");
146                 for (i = 0; i < count; ++i) {
147                         git_oid_tostr(buf, 8, git_commit_parent_id(commit, i));
148                         fprintf(fp, " %s", buf);
149                 }
150                 fprintf(fp, "\n");
151         }
152         if ((sig = git_commit_author(commit)) != NULL) {
153                 fprintf(fp, "Author: <a href=\"author/%s.html\">%s</a> <%s>\n",
154                         sig->name, sig->name, sig->email);
155                 printtime(fp, &sig->when, "Date:   ");
156         }
157         fprintf(fp, "\n");
158
159         for (scan = git_commit_message(commit); scan && *scan;) {
160                 for (eol = scan; *eol && *eol != '\n'; ++eol)   /* find eol */
161                         ;
162
163                 fprintf(fp, "    %.*s\n", (int) (eol - scan), scan);
164                 scan = *eol ? eol + 1 : NULL;
165         }
166         fprintf(fp, "\n");
167 }
168
169 int
170 writeheader(FILE *fp)
171 {
172         fprintf(fp, "<!DOCTYPE HTML>"
173                 "<html dir=\"ltr\" lang=\"en\"><head>"
174                 "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />"
175                 "<meta http-equiv=\"Content-Language\" content=\"en\" />");
176         fprintf(fp, "<title>%s%s%s</title>", name, description[0] ? " - " : "", description);
177         fprintf(fp, "<link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\" />"
178                 "</head><body><center>");
179         fprintf(fp, "<h1><img src=\"%slogo.png\" alt=\"\" /> %s</h1>", relpath, name);
180         fprintf(fp, "<span class=\"desc\">%s</span><br/>", description);
181         fprintf(fp, "<a href=\"%slog.html\">Log</a> |", relpath);
182         fprintf(fp, "<a href=\"%sfiles.html\">Files</a>| ", relpath);
183         fprintf(fp, "<a href=\"%sstats.html\">Stats</a>", relpath);
184         if (hasreadme)
185                 fprintf(fp, " | <a href=\"%sreadme.html\">README</a>", relpath);
186         if (haslicense)
187                 fprintf(fp, " | <a href=\"%slicense.html\">LICENSE</a>", relpath);
188         fprintf(fp, "</center><hr/><pre>");
189
190         return 0;
191 }
192
193 int
194 writefooter(FILE *fp)
195 {
196         fprintf(fp, "</pre></body></html>");
197
198         return 0;
199 }
200
201 int
202 writelog(FILE *fp)
203 {
204         git_revwalk *w = NULL;
205         git_oid id;
206         git_commit *c = NULL;
207
208         git_revwalk_new(&w, repo);
209         git_revwalk_push_head(w);
210
211         while (!git_revwalk_next(&id, w)) {
212                 if (git_commit_lookup(&c, repo, &id))
213                         return 1;
214                 printcommit(fp, c);
215                 git_commit_free(c);
216         }
217         git_revwalk_free(w);
218
219         return 0;
220 }
221
222 int
223 writefiles(FILE *fp)
224 {
225         git_index *index;
226         const git_index_entry *entry;
227         size_t count, i;
228
229         git_repository_index(&index, repo);
230
231         count = git_index_entrycount(index);
232         for (i = 0; i < count; i++) {
233                 entry = git_index_get_byindex(index, i);
234                 fprintf(fp, "name: %s, size: %lu, mode: %lu\n",
235                         entry->path, entry->file_size, entry->mode);
236         }
237
238         return 0;
239 }
240
241 #if 0
242 int
243 writebranches(FILE *fp)
244 {
245         git_branch_iterator *branchit = NULL;
246         git_branch_t branchtype;
247         git_reference *branchref;
248         char branchbuf[BUFSIZ] = "";
249         int status;
250
251         git_branch_iterator_new(&branchit, repo, GIT_BRANCH_LOCAL);
252
253         while ((status = git_branch_next(&branchref, &branchtype, branchit)) == GIT_ITEROVER) {
254                 git_reference_normalize_name(branchbuf, sizeof(branchbuf), git_reference_name(branchref), GIT_REF_FORMAT_ALLOW_ONELEVEL | GIT_REF_FORMAT_REFSPEC_SHORTHAND);
255
256                 /* fprintf(fp, "branch: |%s|\n", branchbuf); */
257         }
258
259         git_branch_iterator_free(branchit);
260
261         return 0;
262 }
263 #endif
264
265 void
266 writeblobhtml(FILE *fp, const git_blob *blob)
267 {
268         xmlencode(fp, git_blob_rawcontent(blob), (size_t)git_blob_rawsize(blob));
269 }
270
271 int
272 main(int argc, char *argv[])
273 {
274         git_object *obj = NULL;
275         const git_error *e = NULL;
276         FILE *fp, *fpread;
277         char path[PATH_MAX], *p;
278         int status;
279
280         if (argc != 2) {
281                 fprintf(stderr, "%s <repodir>\n", argv[0]);
282                 return 1;
283         }
284         repodir = argv[1];
285
286         git_libgit2_init();
287
288         if ((status = git_repository_open(&repo, repodir)) < 0) {
289                 e = giterr_last();
290                 fprintf(stderr, "error %d/%d: %s\n", status, e->klass, e->message);
291                 return status;
292         }
293
294         /* use directory name as name */
295         p = xbasename(repodir);
296         snprintf(name, sizeof(name), "%s", p);
297         free(p);
298
299         /* read description or .git/description */
300         snprintf(path, sizeof(path), "%s%s%s",
301                 repodir, repodir[strlen(repodir)] == '/' ? "" : "/", "description");
302         if (!(fpread = fopen(path, "r+b"))) {
303                 snprintf(path, sizeof(path), "%s%s%s",
304                         repodir, repodir[strlen(repodir)] == '/' ? "" : "/", ".git/description");
305                 fpread = fopen(path, "r+b");
306         }
307         if (fpread) {
308                 if (!fgets(description, sizeof(description), fpread))
309                         description[0] = '\0';
310                 fclose(fpread);
311         }
312
313         /* read LICENSE */
314         if (!git_revparse_single(&obj, repo, "HEAD:LICENSE")) {
315                 fp = efopen("license.html", "w+b");
316                 writeheader(fp);
317                 writeblobhtml(fp, (git_blob *)obj);
318                 if (ferror(fp))
319                         err(1, "fwrite");
320                 writefooter(fp);
321
322                 fclose(fp);
323
324                 haslicense = 1;
325         }
326
327         /* read README */
328         if (!git_revparse_single(&obj, repo, "HEAD:README")) {
329                 fp = efopen("readme.html", "w+b");
330                 writeheader(fp);
331                 writeblobhtml(fp, (git_blob *)obj);
332                 if (ferror(fp))
333                         err(1, "fwrite");
334                 writefooter(fp);
335                 fclose(fp);
336
337                 hasreadme = 1;
338         }
339
340         fp = efopen("logs.html", "w+b");
341         writeheader(fp);
342         writelog(fp);
343         writefooter(fp);
344         fclose(fp);
345
346         fp = efopen("files.html", "w+b");
347         writeheader(fp);
348         writefiles(fp);
349         writefooter(fp);
350         fclose(fp);
351
352         /* cleanup */
353         git_repository_free(repo);
354         git_libgit2_shutdown();
355
356         return 0;
357 }