logo

stagit

STAtic GIT web view generator (in C) git clone https://hacktivis.me/git/stagit.git

stagit-index.c (5577B)


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