stagit-index.c 4.9 KB

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