build-page.c - sites - public wiki contents of suckless.org
 (HTM) git clone git://git.suckless.org/sites
 (DIR) Log
 (DIR) Files
 (DIR) 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.org/pub/style.css\"/>\n"
           30         "</head>\n"
           31         "\n"
           32         "<div id=\"header\">\n"
           33         "\t<a href=\"//suckless.org/\"><img src=\"//suckless.org/logo.svg\" alt=\"\"/></a>&nbsp;\n"
           34         "\t<a id=\"headerLink\" href=\"//suckless.org/\">suckless.org</a>\n"
           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" : "%s", de->d_name, this);
          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" : "%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, newdir);
          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, index);
          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" : "%s", de->d_name, this);
          415                 if (stat_isfile(newdir) && strcmp(de->d_name, "index.md") == 0)
          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 : NULL,
          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" : "%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.md",
          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, NULL,
          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 }