| tstagit-index.c - stagit - [fork] customized build of stagit, the static git pa… | |
| git clone git://src.adamsgaard.dk/stagit | |
| Log | |
| Files | |
| Refs | |
| README | |
| 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/rele… | |
| 17 static char homelink[255] = "<a href=\"https://adamsgaard.dk\">Back to 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] != '/' ? "/" : "… | |
| 28 if (r < 0 || (size_t)r >= bufsiz) | |
| 29 errx(1, "path truncated: '%s%s%s'", | |
| 30 path, path[0] && path[strlen(path) - 1] != '/' ?… | |
| 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("<", fp); break; | |
| 64 case '>': fputs(">", fp); break; | |
| 65 case '\'': fputs("'" , fp); break; | |
| 66 case '&': fputs("&", fp); break; | |
| 67 case '"': fputs(""", 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; … | |
| 93 "<meta name=\"viewport\" content=\"width=device-width, i… | |
| 94 "<title>", fp); | |
| 95 xmlencode(fp, description, strlen(description)); | |
| 96 fprintf(fp, "</title>\n<link rel=\"icon\" type=\"image/png\" hre… | |
| 97 fprintf(fp, "<link rel=\"stylesheet\" type=\"text/css\" href=\"%… | |
| 98 fputs("</head>\n<body>\n", fp); | |
| 99 fprintf(fp, "<table style=\"width:100%%\">\n<tr><td style=\"whit… | |
| 100 "<td style=\"width:99%%\"><span class=\"desc\">", relpat… | |
| 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\">%… | |
| 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>… | |
| 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", … | |
| 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/desc… | |
| 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/owne… | |
| 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 } |