stagit-responsive

My mobile friendly fork of stagit
Log | Files | Refs | README | LICENSE

stagit-index.c (4970B)


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