Introduction
Introduction Statistics Contact Development Disclaimer Help
stagit-index.c - stagit - static git page generator
git clone git://git.codemadness.org/stagit
Log
Files
Refs
README
LICENSE
---
stagit-index.c (6286B)
---
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 static char owner[255];
18
19 /* Handle read or write errors for a FILE * stream */
20 void
21 checkfileerror(FILE *fp, const char *name, int mode)
22 {
23 if (mode == 'r' && ferror(fp))
24 errx(1, "read error: %s", name);
25 else if (mode == 'w' && (fflush(fp) || ferror(fp)))
26 errx(1, "write error: %s", name);
27 }
28
29 void
30 joinpath(char *buf, size_t bufsiz, const char *path, const char *path2)
31 {
32 int r;
33
34 r = snprintf(buf, bufsiz, "%s%s%s",
35 path, path[0] && path[strlen(path) - 1] != '/' ? "/" : "…
36 if (r < 0 || (size_t)r >= bufsiz)
37 errx(1, "path truncated: '%s%s%s'",
38 path, path[0] && path[strlen(path) - 1] != '/' ?…
39 }
40
41 /* Percent-encode, see RFC3986 section 2.1. */
42 void
43 percentencode(FILE *fp, const char *s, size_t len)
44 {
45 static char tab[] = "0123456789ABCDEF";
46 unsigned char uc;
47 size_t i;
48
49 for (i = 0; *s && i < len; s++, i++) {
50 uc = *s;
51 /* NOTE: do not encode '/' for paths or ",-." */
52 if (uc < ',' || uc >= 127 || (uc >= ':' && uc <= '@') ||
53 uc == '[' || uc == ']') {
54 putc('%', fp);
55 putc(tab[(uc >> 4) & 0x0f], fp);
56 putc(tab[uc & 0x0f], fp);
57 } else {
58 putc(uc, fp);
59 }
60 }
61 }
62
63 /* Escape characters below as HTML 2.0 / XML 1.0. */
64 void
65 xmlencode(FILE *fp, const char *s, size_t len)
66 {
67 size_t i;
68
69 for (i = 0; *s && i < len; s++, i++) {
70 switch(*s) {
71 case '<': fputs("&lt;", fp); break;
72 case '>': fputs("&gt;", fp); break;
73 case '\'': fputs("&#39;" , fp); break;
74 case '&': fputs("&amp;", fp); break;
75 case '"': fputs("&quot;", fp); break;
76 default: putc(*s, fp);
77 }
78 }
79 }
80
81 void
82 printtimeshort(FILE *fp, const git_time *intime)
83 {
84 struct tm *intm;
85 time_t t;
86 char out[32];
87
88 t = (time_t)intime->time;
89 if (!(intm = gmtime(&t)))
90 return;
91 strftime(out, sizeof(out), "%Y-%m-%d %H:%M", intm);
92 fputs(out, fp);
93 }
94
95 void
96 writeheader(FILE *fp)
97 {
98 fputs("<!DOCTYPE html>\n"
99 "<html>\n<head>\n"
100 "<meta http-equiv=\"Content-Type\" content=\"text/html; …
101 "<meta name=\"viewport\" content=\"width=device-width, i…
102 "<title>", fp);
103 xmlencode(fp, description, strlen(description));
104 fprintf(fp, "</title>\n<link rel=\"icon\" type=\"image/png\" hre…
105 fprintf(fp, "<link rel=\"stylesheet\" type=\"text/css\" href=\"%…
106 fputs("</head>\n<body>\n", fp);
107 fprintf(fp, "<table>\n<tr><td><img src=\"%slogo.png\" alt=\"\" w…
108 "<td><span class=\"desc\">", relpath);
109 xmlencode(fp, description, strlen(description));
110 fputs("</span></td></tr><tr><td></td><td>\n"
111 "</td></tr>\n</table>\n<hr/>\n<div id=\"content\">\n"
112 "<table id=\"index\"><thead>\n"
113 "<tr><td><b>Name</b></td><td><b>Description</b></td><td>…
114 "<td><b>Last commit</b></td></tr>"
115 "</thead><tbody>\n", fp);
116 }
117
118 void
119 writefooter(FILE *fp)
120 {
121 fputs("</tbody>\n</table>\n</div>\n</body>\n</html>\n", fp);
122 }
123
124 int
125 writelog(FILE *fp)
126 {
127 git_commit *commit = NULL;
128 const git_signature *author;
129 git_revwalk *w = NULL;
130 git_oid id;
131 char *stripped_name = NULL, *p;
132 int ret = 0;
133
134 git_revwalk_new(&w, repo);
135 git_revwalk_push_head(w);
136
137 if (git_revwalk_next(&id, w) ||
138 git_commit_lookup(&commit, repo, &id)) {
139 ret = -1;
140 goto err;
141 }
142
143 author = git_commit_author(commit);
144
145 /* strip .git suffix */
146 if (!(stripped_name = strdup(name)))
147 err(1, "strdup");
148 if ((p = strrchr(stripped_name, '.')))
149 if (!strcmp(p, ".git"))
150 *p = '\0';
151
152 fputs("<tr><td><a href=\"", fp);
153 percentencode(fp, stripped_name, strlen(stripped_name));
154 fputs("/log.html\">", fp);
155 xmlencode(fp, stripped_name, strlen(stripped_name));
156 fputs("</a></td><td>", fp);
157 xmlencode(fp, description, strlen(description));
158 fputs("</td><td>", fp);
159 xmlencode(fp, owner, strlen(owner));
160 fputs("</td><td>", fp);
161 if (author)
162 printtimeshort(fp, &(author->when));
163 fputs("</td></tr>", fp);
164
165 git_commit_free(commit);
166 err:
167 git_revwalk_free(w);
168 free(stripped_name);
169
170 return ret;
171 }
172
173 int
174 main(int argc, char *argv[])
175 {
176 FILE *fp;
177 char path[PATH_MAX], repodirabs[PATH_MAX + 1];
178 const char *repodir;
179 int i, ret = 0;
180
181 if (argc < 2) {
182 fprintf(stderr, "usage: %s [repodir...]\n", argv[0]);
183 return 1;
184 }
185
186 /* do not search outside the git repository:
187 GIT_CONFIG_LEVEL_APP is the highest level currently */
188 git_libgit2_init();
189 for (i = 1; i <= GIT_CONFIG_LEVEL_APP; i++)
190 git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, i, "");
191 /* do not require the git repository to be owned by the current …
192 git_libgit2_opts(GIT_OPT_SET_OWNER_VALIDATION, 0);
193
194 #ifdef __OpenBSD__
195 if (pledge("stdio rpath", NULL) == -1)
196 err(1, "pledge");
197 #endif
198
199 writeheader(stdout);
200
201 for (i = 1; i < argc; i++) {
202 repodir = argv[i];
203 if (!realpath(repodir, repodirabs))
204 err(1, "realpath");
205
206 if (git_repository_open_ext(&repo, repodir,
207 GIT_REPOSITORY_OPEN_NO_SEARCH, NULL)) {
208 fprintf(stderr, "%s: cannot open repository\n", …
209 ret = 1;
210 continue;
211 }
212
213 /* use directory name as name */
214 if ((name = strrchr(repodirabs, '/')))
215 name++;
216 else
217 name = "";
218
219 /* read description or .git/description */
220 joinpath(path, sizeof(path), repodir, "description");
221 if (!(fp = fopen(path, "r"))) {
222 joinpath(path, sizeof(path), repodir, ".git/desc…
223 fp = fopen(path, "r");
224 }
225 description[0] = '\0';
226 if (fp) {
227 if (!fgets(description, sizeof(description), fp))
228 description[0] = '\0';
229 checkfileerror(fp, "description", 'r');
230 fclose(fp);
231 }
232
233 /* read owner or .git/owner */
234 joinpath(path, sizeof(path), repodir, "owner");
235 if (!(fp = fopen(path, "r"))) {
236 joinpath(path, sizeof(path), repodir, ".git/owne…
237 fp = fopen(path, "r");
238 }
239 owner[0] = '\0';
240 if (fp) {
241 if (!fgets(owner, sizeof(owner), fp))
242 owner[0] = '\0';
243 checkfileerror(fp, "owner", 'r');
244 fclose(fp);
245 owner[strcspn(owner, "\n")] = '\0';
246 }
247 writelog(stdout);
248 }
249 writefooter(stdout);
250
251 /* cleanup */
252 git_repository_free(repo);
253 git_libgit2_shutdown();
254
255 checkfileerror(stdout, "<stdout>", 'w');
256
257 return ret;
258 }
You are viewing proxied material from codemadness.org. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.