- commit
- 0b28d0832592ca51a81730779b917f63b42a1642
- parent
- b24cdfc14a898af0cca18bfb1be1fe825ae480aa
- Author
- Tobias Bengfort <tobias.bengfort@posteo.de>
- Date
- 2020-03-26 16:54
major refactoring
Diffstat
| M | stagit.c | 922 | ++++++++++++++++++++++++++++--------------------------------- |
1 files changed, 417 insertions, 505 deletions
diff --git a/stagit.c b/stagit.c
@@ -28,28 +28,22 @@ struct deltainfo {
28 28 };
29 29
30 30 struct commitinfo {
31 -1 const git_oid *id;
-1 31 git_commit *commit;
32 32
33 33 char oid[GIT_OID_HEXSZ + 1];
34 34 char parentoid[GIT_OID_HEXSZ + 1];
35 35
36 36 const git_signature *author;
37 37 const git_signature *committer;
38 -1 const char *summary;
39 -1 const char *msg;
40 -1
41 -1 git_diff *diff;
42 -1 git_commit *commit;
43 -1 git_commit *parent;
44 -1 git_tree *commit_tree;
45 -1 git_tree *parent_tree;
-1 38 const char *summary;
-1 39 const char *msg;
-1 40 };
46 41
-1 42 struct commitstats {
47 43 size_t addcount;
48 44 size_t delcount;
49 -1 size_t filecount;
50 -1
51 -1 struct deltainfo **deltas;
52 45 size_t ndeltas;
-1 46 struct deltainfo **deltas;
53 47 };
54 48
55 49 static git_repository *repo;
@@ -57,19 +51,13 @@ static git_repository *repo;
57 51 static const char *relpath = "";
58 52 static const char *repodir;
59 53
60 -1 static char *name = "";
61 -1 static char *strippedname = "";
-1 54 /* reponame is a pointer into repodirabs */
-1 55 char repodirabs[PATH_MAX + 1];
-1 56 static char *reponame = "";
62 57 static char description[255];
63 -1 static char cloneurl[1024];
-1 58 static char *cloneurl = "git@git.example.com:";
64 59 static char *readmefiles[] = { "HEAD:README", "HEAD:README.md", "HEAD:README.rst" };
65 -1 static char *readme;
66 -1 static long long nlogcommits = -1; /* < 0 indicates not used */
67 -1
68 -1 /* cache */
69 -1 static git_oid lastoid;
70 -1 static char lastoidstr[GIT_OID_HEXSZ + 2]; /* id + newline + NUL byte */
71 -1 static FILE *rcachefp, *wcachefp;
72 -1 static const char *cachefile;
-1 60 static int force = 1;
73 61
74 62 void
75 63 joinpath(char *buf, size_t bufsiz, const char *path, const char *path2)
@@ -125,22 +113,24 @@ deltainfo_free(struct deltainfo *di)
125 113 }
126 114
127 115 void
128 -1 commitinfo_free(struct commitinfo *ci)
-1 116 commitstats_free(struct commitstats *cs)
129 117 {
130 118 size_t i;
-1 119 if (!cs)
-1 120 return;
-1 121 for (i = 0; i < cs->ndeltas; i++)
-1 122 deltainfo_free(cs->deltas[i]);
-1 123 free(cs->deltas);
-1 124 memset(cs, 0, sizeof(*cs));
-1 125 free(cs);
-1 126 }
131 127
-1 128 void
-1 129 commitinfo_free(struct commitinfo *ci)
-1 130 {
132 131 if (!ci)
133 132 return;
134 -1 if (ci->deltas)
135 -1 for (i = 0; i < ci->ndeltas; i++)
136 -1 deltainfo_free(ci->deltas[i]);
137 -1
138 -1 free(ci->deltas);
139 -1 git_diff_free(ci->diff);
140 -1 git_tree_free(ci->commit_tree);
141 -1 git_tree_free(ci->parent_tree);
142 133 git_commit_free(ci->commit);
143 -1 git_commit_free(ci->parent);
144 134 memset(ci, 0, sizeof(*ci));
145 135 free(ci);
146 136 }
@@ -155,7 +145,6 @@ commitinfo_getbyoid(const git_oid *id)
155 145
156 146 if (git_commit_lookup(&(ci->commit), repo, id))
157 147 goto err;
158 -1 ci->id = id;
159 148
160 149 git_oid_tostr(ci->oid, sizeof(ci->oid), git_commit_id(ci->commit));
161 150 git_oid_tostr(ci->parentoid, sizeof(ci->parentoid), git_commit_parent_id(ci->commit, 0));
@@ -169,56 +158,84 @@ commitinfo_getbyoid(const git_oid *id)
169 158
170 159 err:
171 160 commitinfo_free(ci);
172 -1
173 161 return NULL;
174 162 }
175 163
176 164 int
177 -1 commitinfo_getstats(struct commitinfo *ci)
-1 165 git_commit_get_diff(git_diff **diff, git_commit *commit)
178 166 {
179 -1 struct deltainfo *di;
-1 167 git_commit *parent;
-1 168 git_tree *commit_tree;
-1 169 git_tree *parent_tree;
180 170 git_diff_options opts;
181 171 git_diff_find_options fopts;
182 -1 const git_diff_delta *delta;
183 -1 const git_diff_hunk *hunk;
184 -1 const git_diff_line *line;
185 -1 git_patch *patch = NULL;
186 -1 size_t ndeltas, nhunks, nhunklines;
187 -1 size_t i, j, k;
188 172
189 -1 if (git_tree_lookup(&(ci->commit_tree), repo, git_commit_tree_id(ci->commit)))
-1 173 if (git_tree_lookup(&commit_tree, repo, git_commit_tree_id(commit)))
190 174 goto err;
191 -1 if (!git_commit_parent(&(ci->parent), ci->commit, 0)) {
192 -1 if (git_tree_lookup(&(ci->parent_tree), repo, git_commit_tree_id(ci->parent))) {
193 -1 ci->parent = NULL;
194 -1 ci->parent_tree = NULL;
-1 175 if (!git_commit_parent(&parent, commit, 0)) {
-1 176 if (git_tree_lookup(&parent_tree, repo, git_commit_tree_id(parent))) {
-1 177 parent = NULL;
-1 178 parent_tree = NULL;
195 179 }
196 180 }
197 181
198 -1 git_diff_init_options(&opts, GIT_DIFF_OPTIONS_VERSION);
199 -1 opts.flags |= GIT_DIFF_DISABLE_PATHSPEC_MATCH |
200 -1 GIT_DIFF_INCLUDE_TYPECHANGE;
201 -1 if (git_diff_tree_to_tree(&(ci->diff), repo, ci->parent_tree, ci->commit_tree, &opts))
-1 182 if (git_diff_init_options(&opts, GIT_DIFF_OPTIONS_VERSION))
202 183 goto err;
-1 184 opts.flags |= GIT_DIFF_DISABLE_PATHSPEC_MATCH | GIT_DIFF_INCLUDE_TYPECHANGE;
-1 185 if (git_diff_tree_to_tree(diff, repo, parent_tree, commit_tree, &opts))
-1 186 goto err;
-1 187
-1 188 git_tree_free(commit_tree);
-1 189 git_tree_free(parent_tree);
-1 190 git_commit_free(parent);
203 191
204 192 if (git_diff_find_init_options(&fopts, GIT_DIFF_FIND_OPTIONS_VERSION))
205 193 goto err;
206 194 fopts.flags |= GIT_DIFF_FIND_RENAMES | GIT_DIFF_FIND_COPIES;
207 -1 if (git_diff_find_similar(ci->diff, &fopts))
-1 195 if (git_diff_find_similar(*diff, &fopts)) {
-1 196 git_diff_free(*diff);
208 197 goto err;
-1 198 }
-1 199
-1 200 return 0;
209 201
210 -1 ndeltas = git_diff_num_deltas(ci->diff);
211 -1 if (ndeltas && !(ci->deltas = calloc(ndeltas, sizeof(struct deltainfo *))))
-1 202 err:
-1 203 git_tree_free(commit_tree);
-1 204 git_tree_free(parent_tree);
-1 205 git_commit_free(parent);
-1 206 return -1;
-1 207 }
-1 208
-1 209 struct commitstats *
-1 210 commitinfo_getstats(struct commitinfo *ci)
-1 211 {
-1 212 struct commitstats *cs;
-1 213 struct deltainfo *di;
-1 214 const git_diff_delta *delta;
-1 215 const git_diff_hunk *hunk;
-1 216 const git_diff_line *line;
-1 217 git_diff *diff;
-1 218 git_patch *patch = NULL;
-1 219 size_t ndeltas, nhunks, nhunklines, i, j, k;
-1 220
-1 221 if (!(cs = calloc(1, sizeof(struct commitstats))))
-1 222 err(1, "calloc");
-1 223
-1 224 if (git_commit_get_diff(&diff, ci->commit))
-1 225 goto err;
-1 226
-1 227 ndeltas = git_diff_num_deltas(diff);
-1 228 if (ndeltas && !(cs->deltas = calloc(ndeltas, sizeof(struct deltainfo *))))
212 229 err(1, "calloc");
213 230
214 231 for (i = 0; i < ndeltas; i++) {
215 -1 if (git_patch_from_diff(&patch, ci->diff, i))
216 -1 goto err;
-1 232 if (git_patch_from_diff(&patch, diff, i))
-1 233 break;
217 234
218 235 if (!(di = calloc(1, sizeof(struct deltainfo))))
219 236 err(1, "calloc");
220 237 di->patch = patch;
221 -1 ci->deltas[i] = di;
-1 238 cs->deltas[i] = di;
222 239
223 240 delta = git_patch_get_delta(patch);
224 241
@@ -235,43 +252,22 @@ commitinfo_getstats(struct commitinfo *ci)
235 252 break;
236 253 if (line->old_lineno == -1) {
237 254 di->addcount++;
238 -1 ci->addcount++;
-1 255 cs->addcount++;
239 256 } else if (line->new_lineno == -1) {
240 257 di->delcount++;
241 -1 ci->delcount++;
-1 258 cs->delcount++;
242 259 }
243 260 }
244 261 }
245 262 }
246 -1 ci->ndeltas = i;
247 -1 ci->filecount = i;
-1 263 cs->ndeltas = i;
248 264
249 -1 return 0;
-1 265 git_diff_free(diff);
250 266
251 267 err:
252 -1 git_diff_free(ci->diff);
253 -1 ci->diff = NULL;
254 -1 git_tree_free(ci->commit_tree);
255 -1 ci->commit_tree = NULL;
256 -1 git_tree_free(ci->parent_tree);
257 -1 ci->parent_tree = NULL;
258 -1 git_commit_free(ci->parent);
259 -1 ci->parent = NULL;
260 -1
261 -1 if (ci->deltas)
262 -1 for (i = 0; i < ci->ndeltas; i++)
263 -1 deltainfo_free(ci->deltas[i]);
264 -1 free(ci->deltas);
265 -1 ci->deltas = NULL;
266 -1 ci->ndeltas = 0;
267 -1 ci->addcount = 0;
268 -1 ci->delcount = 0;
269 -1 ci->filecount = 0;
270 -1
271 -1 return -1;
-1 268 return cs;
272 269 }
273 270
274 -1 /* Escape characters below as HTML 2.0 / XML 1.0. */
275 271 void
276 272 xmlencode(FILE *fp, const char *s, size_t len)
277 273 {
@@ -325,21 +321,21 @@ write_header(FILE *fp, const char *title)
325 321 "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />\n"
326 322 "<title>", fp);
327 323 xmlencode(fp, title, strlen(title));
328 -1 if (title[0] && strippedname[0])
-1 324 if (title[0] && reponame[0])
329 325 fputs(" - ", fp);
330 -1 xmlencode(fp, strippedname, strlen(strippedname));
-1 326 xmlencode(fp, reponame, strlen(reponame));
331 327 if (description[0])
332 328 fputs(" - ", fp);
333 329 xmlencode(fp, description, strlen(description));
334 330 fprintf(fp, "</title>\n<link rel=\"icon\" type=\"image/png\" href=\"%sfavicon.png\" />\n", relpath);
335 331 fprintf(fp, "<link rel=\"alternate\" type=\"application/atom+xml\" title=\"%s Atom Feed\" href=\"%satom.xml\" />\n",
336 -1 name, relpath);
-1 332 reponame, relpath);
337 333 fprintf(fp, "<link rel=\"stylesheet\" type=\"text/css\" href=\"%sstyle.css\" />\n", relpath);
338 334 fputs("</head>\n<body>\n<table><tr><td>", fp);
339 335 fprintf(fp, "<a href=\"../%s\"><img src=\"%slogo.png\" alt=\"\" width=\"32\" height=\"32\" /></a>",
340 336 relpath, relpath);
341 337 fputs("</td><td><h1>", fp);
342 -1 xmlencode(fp, strippedname, strlen(strippedname));
-1 338 xmlencode(fp, reponame, strlen(reponame));
343 339 fputs("</h1><span class=\"desc\">", fp);
344 340 xmlencode(fp, description, strlen(description));
345 341 fputs("</span></td></tr>", fp);
@@ -382,8 +378,107 @@ write_readme(FILE *fp, const git_blob *blob)
382 378 }
383 379
384 380 void
-1 381 write_commit_statline(FILE *fp, struct deltainfo *di, size_t i)
-1 382 {
-1 383 char c;
-1 384 int total;
-1 385 size_t j;
-1 386 const git_diff_delta *delta;
-1 387
-1 388 delta = git_patch_get_delta(di->patch);
-1 389
-1 390 switch (delta->status) {
-1 391 case GIT_DELTA_ADDED: c = 'A'; break;
-1 392 case GIT_DELTA_COPIED: c = 'C'; break;
-1 393 case GIT_DELTA_DELETED: c = 'D'; break;
-1 394 case GIT_DELTA_MODIFIED: c = 'M'; break;
-1 395 case GIT_DELTA_RENAMED: c = 'R'; break;
-1 396 case GIT_DELTA_TYPECHANGE: c = 'T'; break;
-1 397 default: c = ' '; break;
-1 398 }
-1 399
-1 400 fputs("<tr>\n", fp);
-1 401 fprintf(fp, "<td class=\"%c\">%c</td>\n", c, c);
-1 402 fprintf(fp, "<td><a href=\"#h%zu\">", i);
-1 403 xmlencode(fp, delta->old_file.path, strlen(delta->old_file.path));
-1 404 if (strcmp(delta->old_file.path, delta->new_file.path)) {
-1 405 fputs(" -> ", fp);
-1 406 xmlencode(fp, delta->new_file.path, strlen(delta->new_file.path));
-1 407 }
-1 408 fputs("</a></td>\n", fp);
-1 409
-1 410 total = di->addcount + di->delcount;
-1 411 fprintf(fp, "<td class=\"num\">%i</td>\n", total);
-1 412 fputs("<td><span class=\"i\">", fp);
-1 413 for (j = 0; j < di->addcount && j * total < di->addcount * 60; j++)
-1 414 fputc('+', fp);
-1 415 fputs("</span><span class=\"d\">", fp);
-1 416 for (j = 0; j < di->delcount && j * total < di->delcount * 60; j++)
-1 417 fputc('-', fp);
-1 418
-1 419 fputs("</tr>\n", fp);
-1 420 }
-1 421
-1 422 void
-1 423 write_commit_file(FILE *fp, struct deltainfo *di, size_t i)
-1 424 {
-1 425 const git_diff_delta *delta;
-1 426 const git_diff_hunk *hunk;
-1 427 const git_diff_line *line;
-1 428 size_t nhunks, nhunklines, j, k;
-1 429
-1 430 delta = git_patch_get_delta(di->patch);
-1 431 nhunks = git_patch_num_hunks(di->patch);
-1 432
-1 433 fprintf(fp, "<h2 id=\"h%zu\">\n", i);
-1 434 fprintf(fp, "<b>diff --git a/<a id=\"h%zu\" href=\"%sfile/", i, relpath);
-1 435 xmlencode(fp, delta->old_file.path, strlen(delta->old_file.path));
-1 436 fputs(".html\">", fp);
-1 437 xmlencode(fp, delta->old_file.path, strlen(delta->old_file.path));
-1 438 fprintf(fp, "</a> b/<a href=\"%sfile/", relpath);
-1 439 xmlencode(fp, delta->new_file.path, strlen(delta->new_file.path));
-1 440 fprintf(fp, ".html\">");
-1 441 xmlencode(fp, delta->new_file.path, strlen(delta->new_file.path));
-1 442 fprintf(fp, "</a></b>\n");
-1 443
-1 444 if (delta->flags & GIT_DIFF_FLAG_BINARY) {
-1 445 fputs("Binary files differ.\n", fp);
-1 446 return;
-1 447 }
-1 448
-1 449 if (nhunks == 0)
-1 450 return;
-1 451
-1 452 for (j = 0; j < nhunks; j++) {
-1 453 if (git_patch_get_hunk(&hunk, &nhunklines, di->patch, j))
-1 454 break;
-1 455 fprintf(fp, "<a href=\"#h%zu-%zu\" id=\"h%zu-%zu\" class=\"h\">", i, j, i, j);
-1 456 xmlencode(fp, hunk->header, hunk->header_len - 1);
-1 457 fputs("</a>\n", fp);
-1 458 for (k = 0; k < nhunklines; k++) {
-1 459 if (git_patch_get_line_in_hunk(&line, di->patch, j, k))
-1 460 break;
-1 461 if (line->old_lineno == -1)
-1 462 fprintf(fp, "<a href=\"#h%zu-%zu-%zu\" id=\"h%zu-%zu-%zu\" class=\"i\">+",
-1 463 i, j, k, i, j, k);
-1 464 else if (line->new_lineno == -1)
-1 465 fprintf(fp, "<a href=\"#h%zu-%zu-%zu\" id=\"h%zu-%zu-%zu\" class=\"d\">-",
-1 466 i, j, k, i, j, k);
-1 467 else
-1 468 fputc(' ', fp);
-1 469 xmlencode(fp, line->content, line->content_len);
-1 470 if (line->old_lineno == -1 || line->new_lineno == -1)
-1 471 fputs("</a>", fp);
-1 472 }
-1 473 }
-1 474 }
-1 475
-1 476 void
385 477 write_commit(FILE *fp, struct commitinfo *ci)
386 478 {
-1 479 size_t i;
-1 480 struct commitstats *cs = commitinfo_getstats(ci);
-1 481
387 482 fprintf(fp, "<b>commit</b> <a href=\"%scommit/%s.html\">%s</a>\n",
388 483 relpath, ci->oid, ci->oid);
389 484
@@ -407,130 +502,51 @@ write_commit(FILE *fp, struct commitinfo *ci)
407 502 xmlencode(fp, ci->msg, strlen(ci->msg));
408 503 fputc('\n', fp);
409 504 }
410 -1 }
411 -1
412 -1 void
413 -1 write_commit_file(FILE *fp, struct commitinfo *ci)
414 -1 {
415 -1 const git_diff_delta *delta;
416 -1 const git_diff_hunk *hunk;
417 -1 const git_diff_line *line;
418 -1 git_patch *patch;
419 -1 size_t nhunks, nhunklines, changed, add, del, total, i, j, k;
420 -1 char linestr[80];
421 -1 int c;
422 -1
423 -1 write_commit(fp, ci);
424 505
425 -1 if (!ci->deltas)
426 -1 return;
-1 506 if (!cs->deltas)
-1 507 goto err;
427 508
428 -1 if (ci->filecount > 1000 ||
429 -1 ci->ndeltas > 1000 ||
430 -1 ci->addcount > 100000 ||
431 -1 ci->delcount > 100000) {
-1 509 if (
-1 510 cs->ndeltas > 1000 ||
-1 511 cs->addcount > 100000 ||
-1 512 cs->delcount > 100000
-1 513 ) {
432 514 fputs("Diff is too large, output suppressed.\n", fp);
433 -1 return;
-1 515 goto err;
434 516 }
435 517
436 -1 /* diff stat */
437 -1 fputs("<b>Diffstat:</b>\n<table>", fp);
438 -1 for (i = 0; i < ci->ndeltas; i++) {
439 -1 delta = git_patch_get_delta(ci->deltas[i]->patch);
440 -1
441 -1 switch (delta->status) {
442 -1 case GIT_DELTA_ADDED: c = 'A'; break;
443 -1 case GIT_DELTA_COPIED: c = 'C'; break;
444 -1 case GIT_DELTA_DELETED: c = 'D'; break;
445 -1 case GIT_DELTA_MODIFIED: c = 'M'; break;
446 -1 case GIT_DELTA_RENAMED: c = 'R'; break;
447 -1 case GIT_DELTA_TYPECHANGE: c = 'T'; break;
448 -1 default: c = ' '; break;
449 -1 }
450 -1 if (c == ' ')
451 -1 fprintf(fp, "<tr><td>%c", c);
452 -1 else
453 -1 fprintf(fp, "<tr><td class=\"%c\">%c", c, c);
454 -1
455 -1 fprintf(fp, "</td><td><a href=\"#h%zu\">", i);
456 -1 xmlencode(fp, delta->old_file.path, strlen(delta->old_file.path));
457 -1 if (strcmp(delta->old_file.path, delta->new_file.path)) {
458 -1 fputs(" -> ", fp);
459 -1 xmlencode(fp, delta->new_file.path, strlen(delta->new_file.path));
460 -1 }
-1 518 fputs("<h2>Diffstat</h2>\n", fp);
-1 519 fputs("<table>\n", fp);
461 520
462 -1 add = ci->deltas[i]->addcount;
463 -1 del = ci->deltas[i]->delcount;
464 -1 changed = add + del;
465 -1 total = sizeof(linestr) - 2;
466 -1 if (changed > total) {
467 -1 if (add)
468 -1 add = ((float)total / changed * add) + 1;
469 -1 if (del)
470 -1 del = ((float)total / changed * del) + 1;
471 -1 }
472 -1 memset(&linestr, '+', add);
473 -1 memset(&linestr[add], '-', del);
474 -1
475 -1 fprintf(fp, "</a></td><td> | </td><td class=\"num\">%zu</td><td><span class=\"i\">",
476 -1 ci->deltas[i]->addcount + ci->deltas[i]->delcount);
477 -1 fwrite(&linestr, 1, add, fp);
478 -1 fputs("</span><span class=\"d\">", fp);
479 -1 fwrite(&linestr[add], 1, del, fp);
480 -1 fputs("</span></td></tr>\n", fp);
-1 521 for (i = 0; i < cs->ndeltas; i++) {
-1 522 write_commit_statline(fp, cs->deltas[i], i);
481 523 }
482 524 fprintf(fp, "</table></pre><pre>%zu file%s changed, %zu insertion%s(+), %zu deletion%s(-)\n",
483 -1 ci->filecount, ci->filecount == 1 ? "" : "s",
484 -1 ci->addcount, ci->addcount == 1 ? "" : "s",
485 -1 ci->delcount, ci->delcount == 1 ? "" : "s");
-1 525 cs->ndeltas, cs->ndeltas == 1 ? "" : "s",
-1 526 cs->addcount, cs->addcount == 1 ? "" : "s",
-1 527 cs->delcount, cs->delcount == 1 ? "" : "s");
486 528
-1 529 fputs("</table>\n", fp);
-1 530 fputs("<p>", fp);
-1 531 fputs("</p>\n", fp);
487 532 fputs("<hr/>", fp);
488 533
489 -1 for (i = 0; i < ci->ndeltas; i++) {
490 -1 patch = ci->deltas[i]->patch;
491 -1 delta = git_patch_get_delta(patch);
492 -1 fprintf(fp, "<b>diff --git a/<a id=\"h%zu\" href=\"%sfile/", i, relpath);
493 -1 xmlencode(fp, delta->old_file.path, strlen(delta->old_file.path));
494 -1 fputs(".html\">", fp);
495 -1 xmlencode(fp, delta->old_file.path, strlen(delta->old_file.path));
496 -1 fprintf(fp, "</a> b/<a href=\"%sfile/", relpath);
497 -1 xmlencode(fp, delta->new_file.path, strlen(delta->new_file.path));
498 -1 fprintf(fp, ".html\">");
499 -1 xmlencode(fp, delta->new_file.path, strlen(delta->new_file.path));
500 -1 fprintf(fp, "</a></b>\n");
501 -1
502 -1 /* check binary data */
503 -1 if (delta->flags & GIT_DIFF_FLAG_BINARY) {
504 -1 fputs("Binary files differ.\n", fp);
505 -1 continue;
506 -1 }
507 -1
508 -1 nhunks = git_patch_num_hunks(patch);
509 -1 for (j = 0; j < nhunks; j++) {
510 -1 if (git_patch_get_hunk(&hunk, &nhunklines, patch, j))
511 -1 break;
-1 534 for (i = 0; i < cs->ndeltas; i++) {
-1 535 write_commit_file(fp, cs->deltas[i], i);
-1 536 }
512 537
513 -1 fprintf(fp, "<a href=\"#h%zu-%zu\" id=\"h%zu-%zu\" class=\"h\">", i, j, i, j);
514 -1 xmlencode(fp, hunk->header, hunk->header_len);
515 -1 fputs("</a>", fp);
-1 538 err:
-1 539 commitstats_free(cs);
-1 540 }
516 541
517 -1 for (k = 0; ; k++) {
518 -1 if (git_patch_get_line_in_hunk(&line, patch, j, k))
519 -1 break;
520 -1 if (line->old_lineno == -1)
521 -1 fprintf(fp, "<a href=\"#h%zu-%zu-%zu\" id=\"h%zu-%zu-%zu\" class=\"i\">+",
522 -1 i, j, k, i, j, k);
523 -1 else if (line->new_lineno == -1)
524 -1 fprintf(fp, "<a href=\"#h%zu-%zu-%zu\" id=\"h%zu-%zu-%zu\" class=\"d\">-",
525 -1 i, j, k, i, j, k);
526 -1 else
527 -1 fputc(' ', fp);
528 -1 xmlencode(fp, line->content, line->content_len);
529 -1 if (line->old_lineno == -1 || line->new_lineno == -1)
530 -1 fputs("</a>", fp);
531 -1 }
532 -1 }
533 -1 }
-1 542 void
-1 543 write_log_header(FILE *fp)
-1 544 {
-1 545 fputs("<table id=\"log\">\n", fp);
-1 546 fputs("<thead>\n", fp);
-1 547 fputs("<tr><th>Date</th><th class=\"text\">Commit message</th><th>Author</th></tr>\n", fp);
-1 548 fputs("</thead>\n", fp);
-1 549 fputs("<tbody>\n", fp);
534 550 }
535 551
536 552 void
@@ -548,85 +564,26 @@ write_log_line(FILE *fp, struct commitinfo *ci)
548 564 fputs("</td><td>", fp);
549 565 if (ci->author)
550 566 xmlencode(fp, ci->author->name, strlen(ci->author->name));
551 -1 fputs("</td><td class=\"num\" align=\"right\">", fp);
552 -1 fprintf(fp, "%zu", ci->filecount);
553 -1 fputs("</td><td class=\"num\" align=\"right\">", fp);
554 -1 fprintf(fp, "+%zu", ci->addcount);
555 -1 fputs("</td><td class=\"num\" align=\"right\">", fp);
556 -1 fprintf(fp, "-%zu", ci->delcount);
557 567 fputs("</td></tr>\n", fp);
558 568 }
559 569
560 -1 int
561 -1 write_log(FILE *fp, const git_oid *oid)
-1 570 void
-1 571 write_log_footer(FILE *fp)
562 572 {
563 -1 struct commitinfo *ci;
564 -1 git_revwalk *w = NULL;
565 -1 git_oid id;
566 -1 char path[PATH_MAX], oidstr[GIT_OID_HEXSZ + 1];
567 -1 FILE *fpfile;
568 -1 int r;
569 -1
570 -1 git_revwalk_new(&w, repo);
571 -1 git_revwalk_push(w, oid);
572 -1 git_revwalk_simplify_first_parent(w);
573 -1
574 -1 while (!git_revwalk_next(&id, w)) {
575 -1 relpath = "";
576 -1
577 -1 if (cachefile && !memcmp(&id, &lastoid, sizeof(id)))
578 -1 break;
579 -1
580 -1 git_oid_tostr(oidstr, sizeof(oidstr), &id);
581 -1 r = snprintf(path, sizeof(path), "commit/%s.html", oidstr);
582 -1 if (r < 0 || (size_t)r >= sizeof(path))
583 -1 errx(1, "path truncated: 'commit/%s.html'", oidstr);
584 -1 r = access(path, F_OK);
585 -1
586 -1 /* optimization: if there are no log lines to write and
587 -1 the commit file already exists: skip the diffstat */
588 -1 if (!nlogcommits && !r)
589 -1 continue;
590 -1
591 -1 if (!(ci = commitinfo_getbyoid(&id)))
592 -1 break;
593 -1 /* diffstat: for stagit HTML required for the log.html line */
594 -1 if (commitinfo_getstats(ci) == -1)
595 -1 goto err;
596 -1
597 -1 if (nlogcommits < 0) {
598 -1 write_log_line(fp, ci);
599 -1 } else if (nlogcommits > 0) {
600 -1 write_log_line(fp, ci);
601 -1 nlogcommits--;
602 -1 if (!nlogcommits && ci->parentoid[0])
603 -1 fputs("<tr><td></td><td colspan=\"5\">"
604 -1 "More commits remaining [...]</td>"
605 -1 "</tr>\n", fp);
606 -1 }
607 -1
608 -1 if (cachefile)
609 -1 write_log_line(wcachefp, ci);
610 -1
611 -1 /* check if file exists if so skip it */
612 -1 if (r) {
613 -1 relpath = "../";
614 -1 fpfile = efopen(path, "w");
615 -1 write_header(fpfile, ci->summary);
616 -1 fputs("<pre>", fpfile);
617 -1 write_commit_file(fpfile, ci);
618 -1 fputs("</pre>\n", fpfile);
619 -1 write_footer(fpfile);
620 -1 fclose(fpfile);
621 -1 }
622 -1 err:
623 -1 commitinfo_free(ci);
624 -1 }
625 -1 git_revwalk_free(w);
626 -1
627 -1 relpath = "";
-1 573 fputs("</tbody>\n", fp);
-1 574 fputs("</table>\n", fp);
-1 575 }
628 576
629 -1 return 0;
-1 577 void
-1 578 write_atom_header(FILE *fp)
-1 579 {
-1 580 fputs("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n", fp);
-1 581 fputs("<feed xmlns=\"http://www.w3.org/2005/Atom\">\n", fp);
-1 582 fputs("<title>", fp);
-1 583 xmlencode(fp, reponame, strlen(reponame));
-1 584 fputs(", branch HEAD</title>\n<subtitle>", fp);
-1 585 xmlencode(fp, description, strlen(description));
-1 586 fputs("</subtitle>\n", fp);
630 587 }
631 588
632 589 void
@@ -650,8 +607,7 @@ write_atom_entry(FILE *fp, struct commitinfo *ci)
650 607 xmlencode(fp, ci->summary, strlen(ci->summary));
651 608 fputs("</title>\n", fp);
652 609 }
653 -1 fprintf(fp, "<link rel=\"alternate\" type=\"text/html\" href=\"commit/%s.html\" />\n",
654 -1 ci->oid);
-1 610 fprintf(fp, "<link rel=\"alternate\" type=\"text/html\" href=\"commit/%s.html\" />\n", ci->oid);
655 611
656 612 if (ci->author) {
657 613 fputs("<author>\n<name>", fp);
@@ -669,65 +625,84 @@ write_atom_entry(FILE *fp, struct commitinfo *ci)
669 625 fputs("</entry>\n", fp);
670 626 }
671 627
672 -1 int
673 -1 write_atom(FILE *fp)
-1 628 void
-1 629 write_atom_footer(FILE *fp)
674 630 {
675 -1 struct commitinfo *ci;
676 -1 git_revwalk *w = NULL;
677 -1 git_oid id;
678 -1 size_t i, m = 100; /* last 'm' commits */
679 -1
680 -1 fputs("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
681 -1 "<feed xmlns=\"http://www.w3.org/2005/Atom\">\n<title>", fp);
682 -1 xmlencode(fp, strippedname, strlen(strippedname));
683 -1 fputs(", branch HEAD</title>\n<subtitle>", fp);
684 -1 xmlencode(fp, description, strlen(description));
685 -1 fputs("</subtitle>\n", fp);
686 -1
687 -1 git_revwalk_new(&w, repo);
688 -1 git_revwalk_push_head(w);
689 -1 git_revwalk_simplify_first_parent(w);
690 -1
691 -1 for (i = 0; i < m && !git_revwalk_next(&id, w); i++) {
692 -1 if (!(ci = commitinfo_getbyoid(&id)))
693 -1 break;
694 -1 write_atom_entry(fp, ci);
695 -1 commitinfo_free(ci);
696 -1 }
697 -1 git_revwalk_free(w);
698 -1
699 631 fputs("</feed>\n", fp);
700 -1
701 -1 return 0;
702 632 }
703 633
704 -1 int
705 -1 copy_blob(git_object *obj, const char *fpath, const char *filename, git_off_t filesize)
-1 634 void
-1 635 copy_blob(git_object *obj, const char *fpath)
706 636 {
707 -1 char tmp[PATH_MAX] = "", *d;
708 -1 int lc = 0;
-1 637 char tmp[PATH_MAX] = "";
-1 638 char *d;
709 639 git_off_t len;
710 640 const void *raw;
711 641 FILE *fp;
712 642
713 643 if (strlcpy(tmp, fpath, sizeof(tmp)) >= sizeof(tmp))
714 644 errx(1, "path truncated: '%s'", fpath);
-1 645
715 646 if (!(d = dirname(tmp)))
716 647 err(1, "dirname");
717 648 if (mkdirp(d))
718 -1 return -1;
-1 649 return;
719 650
720 651 fp = efopen(fpath, "w");
721 652 len = git_blob_rawsize((git_blob *)obj);
722 653 raw = git_blob_rawcontent((git_blob *)obj);
723 654 fwrite(raw, 1, len, fp);
724 655 fclose(fp);
-1 656 }
-1 657
-1 658 void
-1 659 write_files_header(FILE *fp)
-1 660 {
-1 661 fputs("<table id=\"files\"><thead>\n<tr>"
-1 662 "<td><b>Name</b></td>"
-1 663 "<td class=\"num\" align=\"right\"><b>Size</b></td>"
-1 664 "</tr>\n</thead><tbody>\n", fp);
-1 665 }
-1 666
-1 667 void
-1 668 write_files_line(FILE *fp, char *entrypath, char *filepath, uintmax_t size)
-1 669 {
-1 670 fprintf(fp, "<tr><td><a href=\"%s", relpath);
-1 671 xmlencode(fp, filepath, strlen(filepath));
-1 672 fputs("\">", fp);
-1 673 xmlencode(fp, entrypath, strlen(entrypath));
-1 674 fputs("</a></td><td class=\"num\" align=\"right\">", fp);
-1 675 fprintf(fp, "%juB", (uintmax_t)size);
-1 676 fputs("</td></tr>\n", fp);
-1 677 }
-1 678
-1 679 void
-1 680 write_files_footer(FILE *fp)
-1 681 {
-1 682 fputs("</tbody>\n", fp);
-1 683 fputs("</table>\n", fp);
-1 684 }
-1 685
-1 686 void
-1 687 process_readme(FILE *fp)
-1 688 {
-1 689 int i;
-1 690 git_object *obj = NULL;
725 691
726 -1 return lc;
-1 692 for (i = 0; i < sizeof(readmefiles) / sizeof(*readmefiles); i++) {
-1 693 if (
-1 694 !git_revparse_single(&obj, repo, readmefiles[i]) &&
-1 695 git_object_type(obj) == GIT_OBJ_BLOB
-1 696 ) {
-1 697 write_readme(fp, (git_blob *)obj);
-1 698 break;
-1 699 }
-1 700 git_object_free(obj);
-1 701 }
727 702 }
728 703
729 704 int
730 -1 writefilestree(FILE *fp, git_tree *tree, const char *path)
-1 705 _process_files(FILE *fp, git_tree *tree, const char *path)
731 706 {
732 707 const git_tree_entry *entry = NULL;
733 708 git_object *obj = NULL;
@@ -735,19 +710,17 @@ writefilestree(FILE *fp, git_tree *tree, const char *path)
735 710 const char *entryname;
736 711 char filepath[PATH_MAX], entrypath[PATH_MAX];
737 712 size_t count, i;
738 -1 int lc, r, ret;
-1 713 int ret;
739 714
740 715 count = git_tree_entrycount(tree);
741 716 for (i = 0; i < count; i++) {
742 -1 if (!(entry = git_tree_entry_byindex(tree, i)) ||
743 -1 !(entryname = git_tree_entry_name(entry)))
-1 717 if (
-1 718 !(entry = git_tree_entry_byindex(tree, i)) ||
-1 719 !(entryname = git_tree_entry_name(entry))
-1 720 )
744 721 return -1;
745 722 joinpath(entrypath, sizeof(entrypath), path, entryname);
746 -1
747 -1 r = snprintf(filepath, sizeof(filepath), "file/%s",
748 -1 entrypath);
749 -1 if (r < 0 || (size_t)r >= sizeof(filepath))
750 -1 errx(1, "path truncated: 'file/%s.html'", entrypath);
-1 723 joinpath(filepath, sizeof(filepath), "blob", entrypath);
751 724
752 725 if (!git_tree_entry_to_object(&obj, repo, entry)) {
753 726 switch (git_object_type(obj)) {
@@ -755,8 +728,7 @@ writefilestree(FILE *fp, git_tree *tree, const char *path)
755 728 break;
756 729 case GIT_OBJ_TREE:
757 730 /* NOTE: recurses */
758 -1 ret = writefilestree(fp, (git_tree *)obj,
759 -1 entrypath);
-1 731 ret = _process_files(fp, (git_tree *)obj, entrypath);
760 732 git_object_free(obj);
761 733 if (ret)
762 734 return ret;
@@ -766,19 +738,11 @@ writefilestree(FILE *fp, git_tree *tree, const char *path)
766 738 continue;
767 739 }
768 740
-1 741 copy_blob(obj, filepath);
-1 742
769 743 filesize = git_blob_rawsize((git_blob *)obj);
770 -1 lc = copy_blob(obj, filepath, entryname, filesize);
771 -1
772 -1 fprintf(fp, "<tr><td><a href=\"%s", relpath);
773 -1 xmlencode(fp, filepath, strlen(filepath));
774 -1 fputs("\">", fp);
775 -1 xmlencode(fp, entrypath, strlen(entrypath));
776 -1 fputs("</a></td><td class=\"num\" align=\"right\">", fp);
777 -1 if (lc > 0)
778 -1 fprintf(fp, "%dL", lc);
779 -1 else
780 -1 fprintf(fp, "%juB", (uintmax_t)filesize);
781 -1 fputs("</td></tr>\n", fp);
-1 744 write_files_line(fp, entrypath, filepath, filesize);
-1 745
782 746 git_object_free(obj);
783 747 }
784 748 }
@@ -787,22 +751,25 @@ writefilestree(FILE *fp, git_tree *tree, const char *path)
787 751 }
788 752
789 753 int
790 -1 write_files(FILE *fp, const git_oid *id)
-1 754 process_files(FILE *fp)
791 755 {
792 756 git_tree *tree = NULL;
793 757 git_commit *commit = NULL;
-1 758 git_object *obj = NULL;
-1 759 const git_oid *head = NULL;
794 760 int ret = -1;
795 761
796 -1 fputs("<table id=\"files\"><thead>\n<tr>"
797 -1 "<td><b>Name</b></td>"
798 -1 "<td class=\"num\" align=\"right\"><b>Size</b></td>"
799 -1 "</tr>\n</thead><tbody>\n", fp);
800 -1
801 -1 if (!git_commit_lookup(&commit, repo, id) &&
802 -1 !git_commit_tree(&tree, commit))
803 -1 ret = writefilestree(fp, tree, "");
-1 762 /* find HEAD */
-1 763 if (!git_revparse_single(&obj, repo, "HEAD"))
-1 764 head = git_object_id(obj);
-1 765 git_object_free(obj);
-1 766 if (!head) {
-1 767 fprintf(stderr, "no HEAD found\n");
-1 768 return 1;
-1 769 }
804 770
805 -1 fputs("</tbody></table>", fp);
-1 771 if (!git_commit_lookup(&commit, repo, head) && !git_commit_tree(&tree, commit))
-1 772 ret = _process_files(fp, tree, "");
806 773
807 774 git_commit_free(commit);
808 775 git_tree_free(tree);
@@ -811,49 +778,97 @@ write_files(FILE *fp, const git_oid *id)
811 778 }
812 779
813 780 void
-1 781 process_commits(FILE *fp_log, FILE *fp_atom, size_t m)
-1 782 {
-1 783 struct commitinfo *ci;
-1 784 git_revwalk *w = NULL;
-1 785 git_oid id;
-1 786 FILE *fp_commit;
-1 787 char path[PATH_MAX], oidstr[GIT_OID_HEXSZ + 1];
-1 788
-1 789 git_revwalk_new(&w, repo);
-1 790 git_revwalk_push_head(w);
-1 791
-1 792 while (!git_revwalk_next(&id, w)) {
-1 793 if (m == 0) {
-1 794 fputs("<tr><td colspan=\"3\">More commits remaining…</td></tr>\n", fp_log);
-1 795 break;
-1 796 }
-1 797 if (!(ci = commitinfo_getbyoid(&id)))
-1 798 break;
-1 799
-1 800 write_log_line(fp_log, ci);
-1 801 write_atom_entry(fp_atom, ci);
-1 802
-1 803 git_oid_tostr(oidstr, sizeof(oidstr), &id);
-1 804 snprintf(path, sizeof(path), "commit/%s.html", oidstr);
-1 805
-1 806 if (force || access(path, F_OK)) {
-1 807 relpath = "../";
-1 808 mkdirp("commit");
-1 809 fp_commit = efopen(path, "w");
-1 810 write_header(fp_commit, ci->summary);
-1 811 write_commit(fp_commit, ci);
-1 812 write_footer(fp_commit);
-1 813 fclose(fp_commit);
-1 814 relpath = "";
-1 815 }
-1 816
-1 817 m -= 1;
-1 818 commitinfo_free(ci);
-1 819 }
-1 820 git_revwalk_free(w);
-1 821 }
-1 822
-1 823 void
814 824 usage(char *argv0)
815 825 {
816 -1 fprintf(stderr, "%s [-c cachefile | -l commits] repodir\n", argv0);
-1 826 fprintf(stderr, "%s repodir\n", argv0);
817 827 exit(1);
818 828 }
819 829
820 -1 int
821 -1 main(int argc, char *argv[])
-1 830 void
-1 831 get_repo_name(const char *path)
822 832 {
823 -1 git_object *obj = NULL;
824 -1 const git_oid *head = NULL;
825 -1 mode_t mask;
826 -1 FILE *fp, *fpread;
827 -1 char path[PATH_MAX], repodirabs[PATH_MAX + 1], *p;
828 -1 char tmppath[64] = "cache.XXXXXXXXXXXX", buf[BUFSIZ];
829 -1 size_t n;
830 -1 int i, fd;
831 -1
832 -1 for (i = 1; i < argc; i++) {
833 -1 if (argv[i][0] != '-') {
834 -1 if (repodir)
835 -1 usage(argv[0]);
836 -1 repodir = argv[i];
837 -1 } else if (argv[i][1] == 'c') {
838 -1 if (nlogcommits > 0 || i + 1 >= argc)
839 -1 usage(argv[0]);
840 -1 cachefile = argv[++i];
841 -1 } else if (argv[i][1] == 'l') {
842 -1 if (cachefile || i + 1 >= argc)
843 -1 usage(argv[0]);
844 -1 errno = 0;
845 -1 nlogcommits = strtoll(argv[++i], &p, 10);
846 -1 if (argv[i][0] == '\0' || *p != '\0' ||
847 -1 nlogcommits <= 0 || errno)
848 -1 usage(argv[0]);
849 -1 }
850 -1 }
851 -1 if (!repodir)
852 -1 usage(argv[0]);
-1 833 char *p;
853 834
854 835 if (!realpath(repodir, repodirabs))
855 836 err(1, "realpath");
856 837
-1 838 if (!(reponame = strrchr(repodirabs, '/'))) {
-1 839 fprintf(stderr, "could not use directory name\n");
-1 840 return;
-1 841 }
-1 842 reponame++;
-1 843 if ((p = strrchr(reponame, '.')))
-1 844 if (!strcmp(p, ".git"))
-1 845 *p = '\0';
-1 846 }
-1 847
-1 848 void
-1 849 get_metadata(void)
-1 850 {
-1 851 char path[PATH_MAX];
-1 852 FILE *fpread;
-1 853
-1 854 joinpath(path, sizeof(path), repodir, "description");
-1 855 if ((fpread = fopen(path, "r"))) {
-1 856 if (!fgets(description, sizeof(description), fpread))
-1 857 description[0] = '\0';
-1 858 fclose(fpread);
-1 859 }
-1 860 }
-1 861
-1 862 int
-1 863 main(int argc, char *argv[])
-1 864 {
-1 865 static FILE *fp_index, *fp_log, *fp_atom;
-1 866
-1 867 if (argc == 2)
-1 868 repodir = argv[1];
-1 869 else
-1 870 usage(argv[0]);
-1 871
857 872 git_libgit2_init();
858 873
859 874 #ifdef __OpenBSD__
@@ -861,152 +876,49 @@ main(int argc, char *argv[])
861 876 err(1, "unveil: %s", repodir);
862 877 if (unveil(".", "rwc") == -1)
863 878 err(1, "unveil: .");
864 -1 if (cachefile && unveil(cachefile, "rwc") == -1)
865 -1 err(1, "unveil: %s", cachefile);
866 -1
867 -1 if (cachefile) {
868 -1 if (pledge("stdio rpath wpath cpath fattr", NULL) == -1)
869 -1 err(1, "pledge");
870 -1 } else {
871 -1 if (pledge("stdio rpath wpath cpath", NULL) == -1)
872 -1 err(1, "pledge");
873 -1 }
-1 879
-1 880 if (pledge("stdio rpath wpath cpath", NULL) == -1)
-1 881 err(1, "pledge");
874 882 #endif
875 883
876 -1 if (git_repository_open_ext(&repo, repodir,
877 -1 GIT_REPOSITORY_OPEN_NO_SEARCH, NULL) < 0) {
-1 884 if (git_repository_open_ext(
-1 885 &repo, repodir, GIT_REPOSITORY_OPEN_NO_SEARCH, NULL
-1 886 ) < 0) {
878 887 fprintf(stderr, "%s: cannot open repository\n", argv[0]);
879 888 return 1;
880 889 }
881 890
882 -1 /* find HEAD */
883 -1 if (!git_revparse_single(&obj, repo, "HEAD"))
884 -1 head = git_object_id(obj);
885 -1 git_object_free(obj);
-1 891 get_repo_name(repodir);
-1 892 get_metadata();
886 893
887 -1 /* use directory name as name */
888 -1 if ((name = strrchr(repodirabs, '/')))
889 -1 name++;
890 -1 else
891 -1 name = "";
-1 894 fp_index = efopen("index.html", "w");
-1 895 fp_log = efopen("log.html", "w");
-1 896 fp_atom = efopen("atom.xml", "w");
892 897
893 -1 /* strip .git suffix */
894 -1 if (!(strippedname = strdup(name)))
895 -1 err(1, "strdup");
896 -1 if ((p = strrchr(strippedname, '.')))
897 -1 if (!strcmp(p, ".git"))
898 -1 *p = '\0';
899 -1
900 -1 /* read description or .git/description */
901 -1 joinpath(path, sizeof(path), repodir, "description");
902 -1 if (!(fpread = fopen(path, "r"))) {
903 -1 joinpath(path, sizeof(path), repodir, ".git/description");
904 -1 fpread = fopen(path, "r");
905 -1 }
906 -1 if (fpread) {
907 -1 if (!fgets(description, sizeof(description), fpread))
908 -1 description[0] = '\0';
909 -1 fclose(fpread);
910 -1 }
-1 898 write_header(fp_index, "Files");
-1 899 write_files_header(fp_index);
911 900
912 -1 /* read url or .git/url */
913 -1 joinpath(path, sizeof(path), repodir, "url");
914 -1 if (!(fpread = fopen(path, "r"))) {
915 -1 joinpath(path, sizeof(path), repodir, ".git/url");
916 -1 fpread = fopen(path, "r");
917 -1 }
918 -1 if (fpread) {
919 -1 if (!fgets(cloneurl, sizeof(cloneurl), fpread))
920 -1 cloneurl[0] = '\0';
921 -1 cloneurl[strcspn(cloneurl, "\n")] = '\0';
922 -1 fclose(fpread);
923 -1 }
-1 901 write_header(fp_log, "Log");
-1 902 write_log_header(fp_log);
924 903
925 -1 /* log for HEAD */
926 -1 fp = efopen("log.html", "w");
927 -1 relpath = "";
928 -1 mkdir("commit", S_IRWXU | S_IRWXG | S_IRWXO);
929 -1 write_header(fp, "Log");
930 -1 fputs("<table id=\"log\"><thead>\n<tr><td><b>Date</b></td>"
931 -1 "<td><b>Commit message</b></td>"
932 -1 "<td><b>Author</b></td><td class=\"num\" align=\"right\"><b>Files</b></td>"
933 -1 "<td class=\"num\" align=\"right\"><b>+</b></td>"
934 -1 "<td class=\"num\" align=\"right\"><b>-</b></td></tr>\n</thead><tbody>\n", fp);
935 -1
936 -1 if (cachefile && head) {
937 -1 /* read from cache file (does not need to exist) */
938 -1 if ((rcachefp = fopen(cachefile, "r"))) {
939 -1 if (!fgets(lastoidstr, sizeof(lastoidstr), rcachefp))
940 -1 errx(1, "%s: no object id", cachefile);
941 -1 if (git_oid_fromstr(&lastoid, lastoidstr))
942 -1 errx(1, "%s: invalid object id", cachefile);
943 -1 }
-1 904 write_atom_header(fp_atom);
944 905
945 -1 /* write log to (temporary) cache */
946 -1 if ((fd = mkstemp(tmppath)) == -1)
947 -1 err(1, "mkstemp");
948 -1 if (!(wcachefp = fdopen(fd, "w")))
949 -1 err(1, "fdopen: '%s'", tmppath);
950 -1 /* write last commit id (HEAD) */
951 -1 git_oid_tostr(buf, sizeof(buf), head);
952 -1 fprintf(wcachefp, "%s\n", buf);
953 -1
954 -1 write_log(fp, head);
955 -1
956 -1 if (rcachefp) {
957 -1 /* append previous log to log.html and the new cache */
958 -1 while (!feof(rcachefp)) {
959 -1 n = fread(buf, 1, sizeof(buf), rcachefp);
960 -1 if (ferror(rcachefp))
961 -1 err(1, "fread");
962 -1 if (fwrite(buf, 1, n, fp) != n ||
963 -1 fwrite(buf, 1, n, wcachefp) != n)
964 -1 err(1, "fwrite");
965 -1 }
966 -1 fclose(rcachefp);
967 -1 }
968 -1 fclose(wcachefp);
969 -1 } else {
970 -1 if (head)
971 -1 write_log(fp, head);
972 -1 }
-1 906 process_files(fp_index);
-1 907 write_files_footer(fp_index);
-1 908 process_readme(fp_index);
-1 909 process_commits(fp_log, fp_atom, 100);
973 910
974 -1 fputs("</tbody></table>", fp);
975 -1 write_footer(fp);
976 -1 fclose(fp);
-1 911 write_atom_footer(fp_atom);
977 912
978 -1 /* files for HEAD */
979 -1 fp = efopen("files.html", "w");
980 -1 write_header(fp, "Files");
981 -1 if (head)
982 -1 write_files(fp, head);
983 -1 for (i = 0; i < sizeof(readmefiles) / sizeof(*readmefiles) && !readme; i++) {
984 -1 if (!git_revparse_single(&obj, repo, readmefiles[i]) &&
985 -1 git_object_type(obj) == GIT_OBJ_BLOB) {
986 -1 readme = readmefiles[i];
987 -1 write_readme(fp, (git_blob *)obj);
988 -1 }
989 -1 git_object_free(obj);
990 -1 }
991 -1 write_footer(fp);
992 -1 fclose(fp);
-1 913 write_log_footer(fp_log);
-1 914 write_footer(fp_log);
993 915
994 -1 /* Atom feed */
995 -1 fp = efopen("atom.xml", "w");
996 -1 write_atom(fp);
997 -1 fclose(fp);
-1 916 write_footer(fp_index);
998 917
999 -1 /* rename new cache file on success */
1000 -1 if (cachefile && head) {
1001 -1 if (rename(tmppath, cachefile))
1002 -1 err(1, "rename: '%s' to '%s'", tmppath, cachefile);
1003 -1 umask((mask = umask(0)));
1004 -1 if (chmod(cachefile,
1005 -1 (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH) & ~mask))
1006 -1 err(1, "chmod: '%s'", cachefile);
1007 -1 }
-1 918 fclose(fp_index);
-1 919 fclose(fp_log);
-1 920 fclose(fp_atom);
1008 921
1009 -1 /* cleanup */
1010 922 git_repository_free(repo);
1011 923 git_libgit2_shutdown();
1012 924