tstagit-index.c - stagit - [fork] customized build of stagit, the static git page generator
 (HTM) git clone git://src.adamsgaard.dk/stagit
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
       tstagit-index.c (5961B)
       ---
            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 
            9 #include <git2.h>
           10 
           11 static git_repository *repo;
           12 
           13 static const char *relpath = "";
           14 
           15 static char description[255] = "Anders' Repositories";
           16 static char releaselink[255] = "<a href=\"https://src.adamsgaard.dk/releases/\">Releases</a>";
           17 static char homelink[255] = "<a href=\"https://adamsgaard.dk\">Back to adamsgaard.dk</a>";
           18 static char *name = "";
           19 static char owner[255];
           20 
           21 void
           22 joinpath(char *buf, size_t bufsiz, const char *path, const char *path2)
           23 {
           24         int r;
           25 
           26         r = snprintf(buf, bufsiz, "%s%s%s",
           27                 path, path[0] && path[strlen(path) - 1] != '/' ? "/" : "", path2);
           28         if (r < 0 || (size_t)r >= bufsiz)
           29                 errx(1, "path truncated: '%s%s%s'",
           30                         path, path[0] && path[strlen(path) - 1] != '/' ? "/" : "", path2);
           31 }
           32 
           33 /* Percent-encode, see RFC3986 section 2.1. */
           34 void
           35 percentencode(FILE *fp, const char *s, size_t len)
           36 {
           37         static char tab[] = "0123456789ABCDEF";
           38         unsigned char uc;
           39         size_t i;
           40 
           41         for (i = 0; *s && i < len; s++, i++) {
           42                 uc = *s;
           43                 /* NOTE: do not encode '/' for paths */
           44                 if (uc < '/' || uc >= 127 || (uc >= ':' && uc <= '@') ||
           45                     uc == '[' || uc == ']') {
           46                         putc('%', fp);
           47                         putc(tab[(uc >> 4) & 0x0f], fp);
           48                         putc(tab[uc & 0x0f], fp);
           49                 } else {
           50                         putc(uc, fp);
           51                 }
           52         }
           53 }
           54 
           55 /* Escape characters below as HTML 2.0 / XML 1.0. */
           56 void
           57 xmlencode(FILE *fp, const char *s, size_t len)
           58 {
           59         size_t i;
           60 
           61         for (i = 0; *s && i < len; s++, i++) {
           62                 switch(*s) {
           63                 case '<':  fputs("&lt;",   fp); break;
           64                 case '>':  fputs("&gt;",   fp); break;
           65                 case '\'': fputs("&#39;" , fp); break;
           66                 case '&':  fputs("&amp;",  fp); break;
           67                 case '"':  fputs("&quot;", fp); break;
           68                 default:   putc(*s, fp);
           69                 }
           70         }
           71 }
           72 
           73 void
           74 printtimeshort(FILE *fp, const git_time *intime)
           75 {
           76         struct tm *intm;
           77         time_t t;
           78         char out[32];
           79 
           80         t = (time_t)intime->time;
           81         if (!(intm = gmtime(&t)))
           82                 return;
           83         strftime(out, sizeof(out), "%Y-%m-%d %H:%M", intm);
           84         fputs(out, fp);
           85 }
           86 
           87 void
           88 writeheader(FILE *fp)
           89 {
           90         fputs("<!DOCTYPE html>\n"
           91                 "<html>\n<head>\n"
           92                 "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />\n"
           93                 "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n"
           94                 "<title>", fp);
           95         xmlencode(fp, description, strlen(description));
           96         fprintf(fp, "</title>\n<link rel=\"icon\" type=\"image/png\" href=\"%sfavicon.png\" />\n", relpath);
           97         fprintf(fp, "<link rel=\"stylesheet\" type=\"text/css\" href=\"%sstyle.css\" />\n", relpath);
           98         fputs("</head>\n<body>\n", fp);
           99         fprintf(fp, "<table style=\"width:100%%\">\n<tr><td style=\"white-space:nowrap\"><img src=\"%slogo.png\" alt=\"\" width=\"32\" height=\"32\" /></td>\n"
          100                 "<td style=\"width:99%%\"><span class=\"desc\">", relpath);
          101         xmlencode(fp, description, strlen(description));
          102         fprintf(fp, "<br />\n%s", releaselink);
          103         fputs("</span></td>", fp);
          104         fprintf(fp, "<td style=\"text-align:right;white-space:nowrap\">%s</td>", homelink);
          105         fputs("</tr><tr><td></td><td>\n"
          106                 "</td></tr>\n</table>\n<hr/>\n<div id=\"content\">\n"
          107                 "<table id=\"index\"><thead>\n"
          108                 "<tr><td><b>Name</b></td><td><b>Description</b></td><td><b>Owner</b></td>"
          109                 "<td><b>Last commit (UTC)</b></td></tr>"
          110                 "</thead><tbody>\n", fp);
          111 }
          112 
          113 void
          114 writefooter(FILE *fp)
          115 {
          116         fputs("</tbody>\n</table>\n</div>\n</body>\n</html>\n", fp);
          117 }
          118 
          119 int
          120 writelog(FILE *fp)
          121 {
          122         git_commit *commit = NULL;
          123         const git_signature *author;
          124         git_revwalk *w = NULL;
          125         git_oid id;
          126         char *stripped_name = NULL, *p;
          127         int ret = 0;
          128 
          129         git_revwalk_new(&w, repo);
          130         git_revwalk_push_head(w);
          131 
          132         if (git_revwalk_next(&id, w) ||
          133             git_commit_lookup(&commit, repo, &id)) {
          134                 ret = -1;
          135                 goto err;
          136         }
          137 
          138         author = git_commit_author(commit);
          139 
          140         /* strip .git suffix */
          141         if (!(stripped_name = strdup(name)))
          142                 err(1, "strdup");
          143         if ((p = strrchr(stripped_name, '.')))
          144                 if (!strcmp(p, ".git"))
          145                         *p = '\0';
          146 
          147         fputs("<tr><td><a href=\"", fp);
          148         percentencode(fp, stripped_name, strlen(stripped_name));
          149         fputs("/log.html\">", fp);
          150         xmlencode(fp, stripped_name, strlen(stripped_name));
          151         fputs("</a></td><td>", fp);
          152         xmlencode(fp, description, strlen(description));
          153         fputs("</td><td>", fp);
          154         xmlencode(fp, owner, strlen(owner));
          155         fputs("</td><td>", fp);
          156         if (author)
          157                 printtimeshort(fp, &(author->when));
          158         fputs("</td></tr>", fp);
          159 
          160         git_commit_free(commit);
          161 err:
          162         git_revwalk_free(w);
          163         free(stripped_name);
          164 
          165         return ret;
          166 }
          167 
          168 int
          169 main(int argc, char *argv[])
          170 {
          171         FILE *fp;
          172         char path[PATH_MAX], repodirabs[PATH_MAX + 1];
          173         const char *repodir;
          174         int i, ret = 0;
          175 
          176         if (argc < 2) {
          177                 fprintf(stderr, "%s [repodir...]\n", argv[0]);
          178                 return 1;
          179         }
          180 
          181         git_libgit2_init();
          182 
          183 #ifdef __OpenBSD__
          184         if (pledge("stdio rpath", NULL) == -1)
          185                 err(1, "pledge");
          186 #endif
          187 
          188         writeheader(stdout);
          189 
          190         for (i = 1; i < argc; i++) {
          191                 repodir = argv[i];
          192                 if (!realpath(repodir, repodirabs))
          193                         err(1, "realpath");
          194 
          195                 if (git_repository_open_ext(&repo, repodir,
          196                     GIT_REPOSITORY_OPEN_NO_SEARCH, NULL)) {
          197                         fprintf(stderr, "%s: cannot open repository\n", argv[0]);
          198                         ret = 1;
          199                         continue;
          200                 }
          201 
          202                 /* use directory name as name */
          203                 if ((name = strrchr(repodirabs, '/')))
          204                         name++;
          205                 else
          206                         name = "";
          207 
          208                 /* read description or .git/description */
          209                 joinpath(path, sizeof(path), repodir, "description");
          210                 if (!(fp = fopen(path, "r"))) {
          211                         joinpath(path, sizeof(path), repodir, ".git/description");
          212                         fp = fopen(path, "r");
          213                 }
          214                 description[0] = '\0';
          215                 if (fp) {
          216                         if (!fgets(description, sizeof(description), fp))
          217                                 description[0] = '\0';
          218                         fclose(fp);
          219                 }
          220 
          221                 /* read owner or .git/owner */
          222                 joinpath(path, sizeof(path), repodir, "owner");
          223                 if (!(fp = fopen(path, "r"))) {
          224                         joinpath(path, sizeof(path), repodir, ".git/owner");
          225                         fp = fopen(path, "r");
          226                 }
          227                 owner[0] = '\0';
          228                 if (fp) {
          229                         if (!fgets(owner, sizeof(owner), fp))
          230                                 owner[0] = '\0';
          231                         owner[strcspn(owner, "\n")] = '\0';
          232                         fclose(fp);
          233                 }
          234                 writelog(stdout);
          235         }
          236         writefooter(stdout);
          237 
          238         /* cleanup */
          239         git_repository_free(repo);
          240         git_libgit2_shutdown();
          241 
          242         return ret;
          243 }