Introduction
Introduction Statistics Contact Development Disclaimer Help
build-page.c - sites - public wiki contents of suckless.org
git clone git://git.suckless.org/sites
Log
Files
Refs
---
build-page.c (10322B)
---
1 #define _POSIX_C_SOURCE 200809L
2
3 #include <sys/stat.h>
4 #include <sys/types.h>
5 #include <sys/wait.h>
6
7 #include <dirent.h>
8 #include <limits.h>
9 #include <stdarg.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <unistd.h>
14
15 #define CONVERTER "smu","-n"
16 #define LEN(x) (sizeof(x) / sizeof(x[0]))
17 #define TITLE_MAX 1024
18 #define TITLE_DEFAULT "suckless.org"
19
20 #define GOPHER_ROW_MAX 80
21 #define GOPHER_PORT 70
22
23 char *html_header =
24 "<!doctype html>\n"
25 "<html>\n"
26 "<head>\n"
27 "\t<meta charset=\"utf-8\"/>\n"
28 "\t<title>%1$s | suckless.org software that sucks less</title>\n"
29 "\t<link rel=\"stylesheet\" type=\"text/css\" href=\"//suckless.…
30 "</head>\n"
31 "\n"
32 "<div id=\"header\">\n"
33 "\t<a href=\"//suckless.org/\"><img src=\"//suckless.org/logo.sv…
34 "\t<a id=\"headerLink\" href=\"//suckless.org/\">suckless.org</a…
35 "\t<span class=\"hidden\"> - </span>\n"
36 "\t<span id=\"headerSubtitle\">%1$s</span>\n"
37 "</div>\n"
38 "<hr class=\"hidden\"/>\n";
39
40 char *html_nav_bar =
41 "\t<span class=\"right\">\n"
42 "\t\t<a href=\"//dl.suckless.org\">download</a>\n"
43 "\t\t<a href=\"//git.suckless.org\">source</a>\n"
44 "\t</span>\n";
45
46 char *html_footer = "</html>\n";
47
48 char *gopher_header = "suckless.org %1$s\n\n";
49
50 struct domain {
51 char *label;
52 char *dir;
53 } domain_list[] = {
54 { "home", "suckless.org" },
55 { "dwm", "dwm.suckless.org", },
56 { "st", "st.suckless.org", },
57 { "core", "core.suckless.org", },
58 { "surf", "surf.suckless.org", },
59 { "tools", "tools.suckless.org", },
60 { "libs", "libs.suckless.org", },
61 { "e.V.", "ev.suckless.org" },
62 { NULL, NULL }
63 };
64
65 void
66 die_perror(char *fmt, ...)
67 {
68 va_list ap;
69
70 va_start(ap, fmt);
71 vfprintf(stderr, fmt, ap);
72 va_end(ap);
73 fputs(": ", stderr);
74 perror(NULL);
75 exit(1);
76 }
77
78 void
79 die(char *fmt, ...)
80 {
81 va_list ap;
82
83 va_start(ap, fmt);
84 vfprintf(stderr, fmt, ap);
85 va_end(ap);
86 fputc('\n', stderr);
87 exit(1);
88 }
89
90 char *
91 xstrdup(const char *s)
92 {
93 char *p = strdup(s);
94
95 if (!p)
96 die_perror("strdup");
97
98 return p;
99 }
100
101 int
102 stat_isdir(const char *f)
103 {
104 struct stat s;
105
106 if (stat(f, &s) == -1) {
107 perror(f);
108 return 0;
109 }
110 return S_ISDIR(s.st_mode);
111 }
112
113 int
114 stat_isfile(const char *f)
115 {
116 struct stat s;
117
118 if (stat(f, &s) == -1) {
119 perror(f);
120 return 0;
121 }
122 return S_ISREG(s.st_mode);
123 }
124
125 int
126 spawn_wait(char **argv)
127 {
128 int status;
129
130 switch (fork()) {
131 case 0:
132 execvp(argv[0], argv);
133 exit(126);
134 case -1:
135 return -1;
136 }
137 if (wait(&status) == -1)
138 return -1;
139
140 return WIFEXITED(status) ? 0 : -1;
141 }
142
143 int
144 oneline(char *buf, size_t bufsiz, const char *path)
145 {
146 char *r;
147 FILE *fp;
148
149 if (!buf || bufsiz == 0)
150 return 0;
151 if (!(fp = fopen(path, "r"))) {
152 perror(path);
153 return 0;
154 }
155
156 fgets(buf, bufsiz, fp);
157 if (ferror(fp))
158 die_perror("fgets: %s", path);
159
160 fclose(fp);
161
162 for (r = buf; *r && *r != '\n'; ++r)
163 ;
164 *r = '\0';
165
166 return 1;
167 }
168
169 void
170 print_name(const char *name)
171 {
172 int c;
173
174 for (; (c = *name); ++name)
175 putchar((c == '_' || c == '-') ? ' ' : c);
176 }
177
178 void
179 print_gopher_name(const char *name)
180 {
181 int c;
182
183 for (; (c = *name); ++name) {
184 switch (c) {
185 case '\r': /* ignore CR */
186 case '\n': /* ignore LF */
187 break;
188 case '_':
189 case '-':
190 putchar(' ');
191 break;
192 case '\t':
193 printf(" ");
194 break;
195 case '|': /* escape separators */
196 printf("\\|");
197 break;
198 default:
199 putchar(c);
200 }
201 }
202 }
203
204 void
205 print_header(void)
206 {
207 char title[TITLE_MAX];
208
209 printf(html_header, oneline(title, sizeof(title), ".title") ?
210 title : TITLE_DEFAULT);
211 }
212
213 void
214 print_nav_bar(char *domain)
215 {
216 struct domain *d;
217
218 puts("<div id=\"menu\">");
219 for (d = domain_list; d->dir; ++d) {
220 if (strcmp(domain, d->dir) == 0)
221 printf("\t<a href=\"//%s/\"><b>%s</b></a>\n",
222 d->dir, d->label);
223 else
224 printf("\t<a href=\"//%s/\">%s</a>\n",
225 d->dir, d->label);
226
227 }
228 fputs(html_nav_bar, stdout);
229 puts("</div>");
230 puts("<hr class=\"hidden\"/>");
231 }
232
233 int
234 qsort_strcmp(const void *a, const void *b)
235 {
236 return strcmp(*(const char **)a, *(const char **)b);
237 }
238
239 int
240 has_subdirs(char *this)
241 {
242 DIR *dp;
243 struct dirent *de;
244 char newdir[PATH_MAX];
245 int dir;
246
247 if ((dp = opendir(this ? this : ".")) == NULL)
248 die_perror("opendir: %s", this ? this : ".");
249
250 dir = 0;
251 while (dir == 0 && (de = readdir(dp))) {
252 if (de->d_name[0] == '.')
253 continue;
254 snprintf(newdir, sizeof(newdir), this ? "%2$s/%1$s" : "%…
255 if (stat_isdir(newdir))
256 dir = 1;
257 }
258 closedir(dp);
259
260 return dir;
261 }
262
263 void
264 menu_panel(char *domain, char *page, char *this, int depth)
265 {
266 DIR *dp;
267 struct dirent *de;
268 char newdir[PATH_MAX];
269 char *d_list[PATH_MAX], *d;
270 size_t d_len, l;
271 int i, highlight;
272
273 if ((dp = opendir(this ? this : ".")) == NULL)
274 die_perror("opendir: %s", this ? this : ".");
275
276 d_len = 0;
277 while (d_len < LEN(d_list) && (de = readdir(dp)))
278 d_list[d_len++] = xstrdup(de->d_name);
279 closedir(dp);
280
281 qsort(d_list, d_len, sizeof *d_list, qsort_strcmp);
282
283 for (l = 0; l < d_len; free(d_list[l++])) {
284 d = d_list[l];
285 if (*d == '.')
286 continue;
287 snprintf(newdir, sizeof(newdir), this ? "%2$s/%1$s" : "%…
288 d, this);
289 if (!stat_isdir(newdir))
290 continue;
291
292 highlight = page && !strncmp(newdir, page, strlen(newdir…
293 strchr("/", page[strlen(newdir)]); /* / or NUL */
294
295 for (i = 0; i < depth + 1; ++i)
296 putchar('\t');
297 fputs("<li>", stdout);
298 if (highlight) {
299 printf("<a href=\"//%s/%s/\"><b>", domain, newdi…
300 print_name(d);
301 fputs("/</b></a>", stdout);
302 } else {
303 printf("<a href=\"//%s/%s/\">", domain, newdir);
304 print_name(d);
305 fputs("/</a>", stdout);
306 }
307
308 if (highlight && has_subdirs(newdir)) {
309 putchar('\n');
310 for (i = 0; i < depth + 2; ++i)
311 putchar('\t');
312 puts("<ul>");
313 menu_panel(domain, page, newdir, depth + 1);
314 for (i = 0; i < depth + 2; ++i)
315 putchar('\t');
316 puts("</ul>");
317 for (i = 0; i < depth + 1; ++i)
318 putchar('\t');
319 }
320 puts("</li>");
321 }
322 }
323
324 void
325 print_menu_panel(char *domain, char *page)
326 {
327 fputs("<div id=\"nav\">\n\t<ul>\n\t<li>", stdout);
328 if (!page)
329 puts("<a href=\"/\"><b>about</b></a></li>");
330 else
331 puts("<a href=\"/\">about</a></li>");
332 menu_panel(domain, page, NULL, 0);
333 puts("\t</ul>");
334 puts("</div>");
335 puts("<hr class=\"hidden\"/>");
336 }
337
338 void
339 print_content(char *domain, char *page)
340 {
341 char index[PATH_MAX];
342 char *argv[] = { CONVERTER, index, NULL };
343
344 snprintf(index, sizeof(index), page ? "%2$s/%1$s" : "%s",
345 "index.md", page);
346
347 puts("<div id=\"main\">\n");
348
349 if (stat_isfile(index)) {
350 fflush(stdout);
351 if (spawn_wait(argv) == -1)
352 die_perror("spawn: %s/%s/%s", domain, page, inde…
353 }
354 puts("</div>\n");
355 }
356
357 void
358 print_footer(void)
359 {
360 fputs(html_footer, stdout);
361 }
362
363 void
364 print_gopher_item(char type, char *disp, char *domain, char *path,
365 char * file, int port, int level)
366 {
367 char d[GOPHER_ROW_MAX];
368 int l;
369
370 strncpy(d, disp, sizeof(d) - 1);
371 d[sizeof(d) - 1] = '\0';
372
373 printf("[%c|", type);
374
375 for (l = 0; l < level; ++l)
376 printf(" ");
377 print_gopher_name(d);
378 if (type == '1')
379 putchar('/');
380 putchar('|');
381
382 if (path)
383 printf("/%s", path);
384 if (file)
385 printf("/%s", file);
386
387 printf("|%s|%d]\n", domain, port);
388 }
389
390 void
391 print_gopher_header(void)
392 {
393 char title[GOPHER_ROW_MAX];
394
395 printf(gopher_header, oneline(title, sizeof(title), ".title") ?
396 title : TITLE_DEFAULT);
397 }
398
399 int
400 has_index(char *this)
401 {
402 DIR *dp;
403 struct dirent *de;
404 char newdir[PATH_MAX];
405 int index;
406
407 if ((dp = opendir(this ? this : ".")) == NULL)
408 die_perror("opendir: %s", this ? this : ".");
409
410 index = 0;
411 while (index == 0 && (de = readdir(dp))) {
412 if (de->d_name[0] == '.')
413 continue;
414 snprintf(newdir, sizeof(newdir), this ? "%2$s/%1$s" : "%…
415 if (stat_isfile(newdir) && strcmp(de->d_name, "index.md"…
416 index = 1;
417 }
418 closedir(dp);
419
420 return index;
421 }
422
423 void
424 print_gopher_menu(char *domain, char *this)
425 {
426 DIR *dp;
427 struct dirent *de;
428 char newdir[PATH_MAX];
429 char *d_list[PATH_MAX], *d;
430 size_t d_len, l;
431 int depth = this ? 1 : 0;
432
433 if ((dp = opendir(this ? this : ".")) == NULL)
434 die_perror("opendir: %s", this ? this : ".");
435
436 d_len = 0;
437 while (d_len < LEN(d_list) && (de = readdir(dp))) {
438 d_list[d_len++] = xstrdup(de->d_name);
439 }
440 closedir(dp);
441
442 qsort(d_list, d_len, sizeof *d_list, qsort_strcmp);
443
444 printf("%s/\n", this ? this : "");
445
446 if (has_index(this))
447 print_gopher_item('0', "about", domain, this ? this : NU…
448 "index.md", GOPHER_PORT, depth);
449
450 for (l = 0; l < d_len; free(d_list[l++])) {
451 d = d_list[l];
452 if (*d == '.')
453 continue;
454 snprintf(newdir, sizeof(newdir), this ? "%2$s/%1$s" : "%…
455 d, this);
456 if (!stat_isdir(newdir))
457 continue;
458
459 if (has_subdirs(newdir))
460 print_gopher_item('1', d, domain, newdir, NULL,
461 GOPHER_PORT, depth);
462 else
463 print_gopher_item('0', d, domain, newdir, "index…
464 GOPHER_PORT, depth);
465 }
466 }
467
468 void
469 print_gopher_nav(char *domain)
470 {
471 struct domain *d;
472
473 for (d = domain_list; d->dir; ++d) {
474 if (strcmp(domain, d->dir) == 0)
475 printf("%s\n", d->label);
476 else
477 print_gopher_item('1', d->label, d->dir, NULL, N…
478 GOPHER_PORT, 0);
479 }
480
481 putchar('\n');
482 print_gopher_item('1', "download", "dl.suckless.org", NULL, NULL,
483 GOPHER_PORT, 0);
484 print_gopher_item('1', "source", "git.suckless.org", NULL, NULL,
485 GOPHER_PORT, 0);
486 }
487
488 void
489 usage(char *argv0)
490 {
491 die("usage: %s [-g] directory", argv0);
492 }
493
494 int
495 main(int argc, char *argv[])
496 {
497 char *domain = NULL, *page;
498 int gopher = 0, i, j;
499
500 for (i = 1; i < argc; i++) {
501 if (argv[i][0] != '-') {
502 if (domain)
503 usage(argv[0]);
504 domain = argv[i];
505 continue;
506 }
507 for (j = 1; j < argv[i][j]; j++) {
508 switch (argv[i][j]) {
509 case 'g':
510 gopher = 1;
511 break;
512 default:
513 usage(argv[0]);
514 }
515 }
516 }
517 if (domain == NULL)
518 usage(argv[0]);
519
520 domain = xstrdup(domain);
521 if ((page = strchr(domain, '/'))) {
522 *page++ = '\0';
523 if (strlen(page) == 0)
524 page = NULL;
525 }
526 if (chdir(domain) == -1)
527 die_perror("chdir: %s", domain);
528
529 if (gopher) {
530 print_gopher_header();
531 print_gopher_menu(domain, page);
532 printf("-------------\n");
533 print_gopher_nav(domain);
534 } else {
535 print_header();
536 print_nav_bar(domain);
537 puts("<div id=\"content\">");
538 print_menu_panel(domain, page);
539 print_content(domain, page);
540 puts("</div>\n");
541 print_footer();
542 }
543
544 return 0;
545 }
You are viewing proxied material from suckless.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.