SMOLNET PORTAL home about changes
iinitial commit - libgcgi - REST library for Gopher	Err	bitreich.org	70
hgit clone git://bitreich.org/libgcgi git://hg6vgqziawt5s4dj.onion/libgcgi	URL:git://bitreich.org/libgcgi git://hg6vgqziawt5s4dj.onion/libgcgi	bitreich.org	70
1Log	/scm/libgcgi/log.gph	bitreich.org	70
1Files	/scm/libgcgi/files.gph	bitreich.org	70
1Refs	/scm/libgcgi/refs.gph	bitreich.org	70
1Tags	/scm/libgcgi/tag	bitreich.org	70
1README	/scm/libgcgi/file/README.gph	bitreich.org	70
1LICENSE	/scm/libgcgi/file/LICENSE.gph	bitreich.org	70
i---	Err	bitreich.org	70
1commit 758508d75c969333c3f72f0e01faa0a5129153b9	/scm/libgcgi/commit/758508d75c969333c3f72f0e01faa0a5129153b9.gph	bitreich.org	70
hAuthor: Josuah Demangeon <me@josuah.net>	URL:mailto:me@josuah.net	bitreich.org	70
iDate:   Sat, 30 Jul 2022 11:12:39 +0200	Err	bitreich.org	70
i	Err	bitreich.org	70
iinitial commit	Err	bitreich.org	70
i	Err	bitreich.org	70
iDiffstat:	Err	bitreich.org	70
i  A .gitignore                          |       1 +	Err	bitreich.org	70
i  A Makefile                            |      13 +++++++++++++	Err	bitreich.org	70
i  A index.c                             |      32 +++++++++++++++++++++++++++++++	Err	bitreich.org	70
i  A libgcgi.h                           |     336 +++++++++++++++++++++++++++++++	Err	bitreich.org	70
i	Err	bitreich.org	70
i4 files changed, 382 insertions(+), 0 deletions(-)	Err	bitreich.org	70
i---	Err	bitreich.org	70
1diff --git a/.gitignore b/.gitignore	/scm/libgcgi/file/.gitignore.gph	bitreich.org	70
i@@ -0,0 +1 @@	Err	bitreich.org	70
i+index.cgi	Err	bitreich.org	70
1diff --git a/Makefile b/Makefile	/scm/libgcgi/file/Makefile.gph	bitreich.org	70
i@@ -0,0 +1,13 @@	Err	bitreich.org	70
i+LDFLAGS = -static	Err	bitreich.org	70
i+CFLAGS = -g -pedantic -std=c99 -Wall -Wextra -Wno-unused-function	Err	bitreich.org	70
i+	Err	bitreich.org	70
i+V = v0.0	Err	bitreich.org	70
i+	Err	bitreich.org	70
i+all: index.cgi tmp db/category db/item db/image	Err	bitreich.org	70
i+	Err	bitreich.org	70
i+tmp db/category db/item db/image:	Err	bitreich.org	70
i+        mkdir -p -m 700 $@	Err	bitreich.org	70
i+        chown www:www $@	Err	bitreich.org	70
i+	Err	bitreich.org	70
i+index.cgi: index.c libgcgi.h	Err	bitreich.org	70
i+        ${CC} ${LDFLAGS} ${CFLAGS} -o $@ index.c	Err	bitreich.org	70
1diff --git a/index.c b/index.c	/scm/libgcgi/file/index.c.gph	bitreich.org	70
i@@ -0,0 +1,32 @@	Err	bitreich.org	70
i+#include <assert.h>	Err	bitreich.org	70
i+#include <ctype.h>	Err	bitreich.org	70
i+#include <errno.h>	Err	bitreich.org	70
i+#include <stdarg.h>	Err	bitreich.org	70
i+#include <stdint.h>	Err	bitreich.org	70
i+#include <stdio.h>	Err	bitreich.org	70
i+#include <stdlib.h>	Err	bitreich.org	70
i+#include <string.h>	Err	bitreich.org	70
i+#include <unistd.h>	Err	bitreich.org	70
i+#include <sys/stat.h>	Err	bitreich.org	70
i+#include "libgcgi.h"	Err	bitreich.org	70
i+	Err	bitreich.org	70
i+static struct gcgi_handler handlers[] = {	Err	bitreich.org	70
i+//        { "*",                                error_404 },	Err	bitreich.org	70
i+        { NULL,                                NULL },	Err	bitreich.org	70
i+};	Err	bitreich.org	70
i+	Err	bitreich.org	70
i+int	Err	bitreich.org	70
i+main(int argc, char **argv)	Err	bitreich.org	70
i+{	Err	bitreich.org	70
i+        /* restrict allowed paths */	Err	bitreich.org	70
i+        unveil("gph", "r");	Err	bitreich.org	70
i+        unveil("tmp", "rwc");	Err	bitreich.org	70
i+        unveil("db", "rwc");	Err	bitreich.org	70
i+	Err	bitreich.org	70
i+        /* restrict allowed system calls */	Err	bitreich.org	70
i+        pledge("stdio rpath wpath cpath", NULL);	Err	bitreich.org	70
i+	Err	bitreich.org	70
i+        /* handle the request with the handlers */	Err	bitreich.org	70
i+        gcgi_handle_request(handlers, argv, argc);	Err	bitreich.org	70
i+        return 0;	Err	bitreich.org	70
i+}	Err	bitreich.org	70
1diff --git a/libgcgi.h b/libgcgi.h	/scm/libgcgi/file/libgcgi.h.gph	bitreich.org	70
i@@ -0,0 +1,336 @@	Err	bitreich.org	70
i+#ifndef LIBGCGI_H	Err	bitreich.org	70
i+#define LIBGCGI_H	Err	bitreich.org	70
i+	Err	bitreich.org	70
i+/* Gopher CGI library to use in CGI scripts */	Err	bitreich.org	70
i+	Err	bitreich.org	70
i+/* maps glob pattern */	Err	bitreich.org	70
i+struct gcgi_handler {	Err	bitreich.org	70
i+        char const *glob;	Err	bitreich.org	70
i+        void (*fn)(char **matches);	Err	bitreich.org	70
i+};	Err	bitreich.org	70
i+	Err	bitreich.org	70
i+/* storage for key-value pair */	Err	bitreich.org	70
i+struct gcgi_var_list {	Err	bitreich.org	70
i+        struct gcgi_var {	Err	bitreich.org	70
i+                char *key, *val;	Err	bitreich.org	70
i+        } *list;	Err	bitreich.org	70
i+        size_t len;	Err	bitreich.org	70
i+        char *buf;	Err	bitreich.org	70
i+};	Err	bitreich.org	70
i+	Err	bitreich.org	70
i+/* main loop executing h->fn() if h->glob is matching */	Err	bitreich.org	70
i+static void gcgi_handle_request(struct gcgi_handler h[], char **argv, int argc);	Err	bitreich.org	70
i+	Err	bitreich.org	70
i+/* abort the program with an error message sent to the client */	Err	bitreich.org	70
i+static void gcgi_fatal(char *fmt, ...);	Err	bitreich.org	70
i+	Err	bitreich.org	70
i+/* receive a file payload from the client onto the disk at `path` */	Err	bitreich.org	70
i+static void gcgi_receive_file(char const *path);	Err	bitreich.org	70
i+	Err	bitreich.org	70
i+/* print a template with every "{{name}}" looked up in `vars` */	Err	bitreich.org	70
i+static void gcgi_template(char const *path, struct gcgi_var_list *vars);	Err	bitreich.org	70
i+	Err	bitreich.org	70
i+/* print `s` with all gophermap special characters escaped */	Err	bitreich.org	70
i+static void gcgi_print_gophermap(char const *s);	Err	bitreich.org	70
i+	Err	bitreich.org	70
i+/* manage a `key`-`val` pair storage `vars`, as used with gcgi_template */	Err	bitreich.org	70
i+static void gcgi_add_var(struct gcgi_var_list *vars, char *key, char *val);	Err	bitreich.org	70
i+static void gcgi_sort_var_list(struct gcgi_var_list *vars);	Err	bitreich.org	70
i+static void gcgi_set_var(struct gcgi_var_list *vars, char *key, char *val);	Err	bitreich.org	70
i+static char *gcgi_get_var(struct gcgi_var_list *vars, char *key);	Err	bitreich.org	70
i+static void gcgi_free_var_list(struct gcgi_var_list *vars);	Err	bitreich.org	70
i+	Err	bitreich.org	70
i+/* store and read a list of variables onto a simple RFC822-like format */	Err	bitreich.org	70
i+static void gcgi_read_var_list(struct gcgi_var_list *vars, char *path);	Err	bitreich.org	70
i+static int gcgi_write_var_list(struct gcgi_var_list *vars, char *path);	Err	bitreich.org	70
i+	Err	bitreich.org	70
i+/* parse various components of the Gopher request */	Err	bitreich.org	70
i+static struct gcgi_var_list * gcgi_parse_query_string(void);	Err	bitreich.org	70
i+	Err	bitreich.org	70
i+/* components of the gopher request */	Err	bitreich.org	70
i+char *gcgi_gopher_search;	Err	bitreich.org	70
i+char *gcgi_gopher_path;	Err	bitreich.org	70
i+char *gcgi_gopher_host;	Err	bitreich.org	70
i+char *gcgi_gopher_port;	Err	bitreich.org	70
i+char *gcgi_gopher_args;	Err	bitreich.org	70
i+	Err	bitreich.org	70
i+	Err	bitreich.org	70
i+/// POLICE LINE /// DO NOT CROSS ///	Err	bitreich.org	70
i+	Err	bitreich.org	70
i+	Err	bitreich.org	70
i+#define GCGI_MATCH_NUM 5	Err	bitreich.org	70
i+	Err	bitreich.org	70
i+static void	Err	bitreich.org	70
i+gcgi_fatal(char *fmt, ...)	Err	bitreich.org	70
i+{	Err	bitreich.org	70
i+        va_list va;	Err	bitreich.org	70
i+        char msg[1024];	Err	bitreich.org	70
i+	Err	bitreich.org	70
i+        va_start(va, fmt);	Err	bitreich.org	70
i+        vsnprintf(msg, sizeof msg, fmt, va);	Err	bitreich.org	70
i+        printf("Status: 500 Server Error\n\n");	Err	bitreich.org	70
i+        printf("error: %s\n", msg);	Err	bitreich.org	70
i+        exit(1);	Err	bitreich.org	70
i+}	Err	bitreich.org	70
i+	Err	bitreich.org	70
i+static inline char *	Err	bitreich.org	70
i+gcgi_fopenread(char *path)	Err	bitreich.org	70
i+{	Err	bitreich.org	70
i+        FILE *fp;	Err	bitreich.org	70
i+        char *buf;	Err	bitreich.org	70
i+        ssize_t ssz;	Err	bitreich.org	70
i+        size_t sz;	Err	bitreich.org	70
i+	Err	bitreich.org	70
i+        if ((fp = fopen(path, "r")) == NULL)	Err	bitreich.org	70
i+                return NULL;	Err	bitreich.org	70
i+        if (fseek(fp, 0, SEEK_END) == -1)	Err	bitreich.org	70
i+                return NULL;	Err	bitreich.org	70
i+        if ((ssz = ftell(fp)) == -1)	Err	bitreich.org	70
i+                return NULL;	Err	bitreich.org	70
i+        sz = ssz;	Err	bitreich.org	70
i+        if (fseek(fp, 0, SEEK_SET) == -1)	Err	bitreich.org	70
i+                return NULL;	Err	bitreich.org	70
i+        if ((buf = malloc(sz + 1)) == NULL)	Err	bitreich.org	70
i+                return NULL;	Err	bitreich.org	70
i+        if (fread(buf, sz, 1, fp) != sz)	Err	bitreich.org	70
i+                goto error_free;	Err	bitreich.org	70
i+        if (ferror(fp))	Err	bitreich.org	70
i+                goto error_free;	Err	bitreich.org	70
i+        fclose(fp);	Err	bitreich.org	70
i+        buf[sz] = '\0';	Err	bitreich.org	70
i+        return buf;	Err	bitreich.org	70
i+error_free:	Err	bitreich.org	70
i+        free(buf);	Err	bitreich.org	70
i+        return NULL;	Err	bitreich.org	70
i+}	Err	bitreich.org	70
i+	Err	bitreich.org	70
i+static int	Err	bitreich.org	70
i+gcgi_cmp_var(const void *v1, const void *v2)	Err	bitreich.org	70
i+{	Err	bitreich.org	70
i+        return strcasecmp(((struct gcgi_var *)v1)->key, ((struct gcgi_var *)v2)->key);	Err	bitreich.org	70
i+}	Err	bitreich.org	70
i+	Err	bitreich.org	70
i+static void	Err	bitreich.org	70
i+gcgi_add_var(struct gcgi_var_list *vars, char *key, char *val)	Err	bitreich.org	70
i+{	Err	bitreich.org	70
i+        void *mem;	Err	bitreich.org	70
i+	Err	bitreich.org	70
i+        vars->len++;	Err	bitreich.org	70
i+        if ((mem = realloc(vars->list, vars->len * sizeof *vars->list)) == NULL)	Err	bitreich.org	70
i+                 gcgi_fatal("realloc");	Err	bitreich.org	70
i+        vars->list = mem;	Err	bitreich.org	70
i+        vars->list[vars->len-1].key = key;	Err	bitreich.org	70
i+        vars->list[vars->len-1].val = val;	Err	bitreich.org	70
i+}	Err	bitreich.org	70
i+	Err	bitreich.org	70
i+static void	Err	bitreich.org	70
i+gcgi_sort_var_list(struct gcgi_var_list *vars)	Err	bitreich.org	70
i+{	Err	bitreich.org	70
i+        qsort(vars->list, vars->len, sizeof *vars->list, gcgi_cmp_var);	Err	bitreich.org	70
i+}	Err	bitreich.org	70
i+	Err	bitreich.org	70
i+static char *	Err	bitreich.org	70
i+gcgi_get_var(struct gcgi_var_list *vars, char *key)	Err	bitreich.org	70
i+{	Err	bitreich.org	70
i+        struct gcgi_var *v, q = { .key = key };	Err	bitreich.org	70
i+	Err	bitreich.org	70
i+        v = bsearch(&q, vars->list, vars->len, sizeof *vars->list, gcgi_cmp_var);	Err	bitreich.org	70
i+        return (v == NULL) ? NULL : v->val;	Err	bitreich.org	70
i+}	Err	bitreich.org	70
i+	Err	bitreich.org	70
i+static void	Err	bitreich.org	70
i+gcgi_set_var(struct gcgi_var_list *vars, char *key, char *val)	Err	bitreich.org	70
i+{	Err	bitreich.org	70
i+        struct gcgi_var *v, q;	Err	bitreich.org	70
i+	Err	bitreich.org	70
i+        q.key = key;	Err	bitreich.org	70
i+        v = bsearch(&q, vars->list, vars->len, sizeof *vars->list, gcgi_cmp_var);	Err	bitreich.org	70
i+        if (v != NULL) {	Err	bitreich.org	70
i+                v->val = val;	Err	bitreich.org	70
i+                return;	Err	bitreich.org	70
i+        }	Err	bitreich.org	70
i+        gcgi_add_var(vars, key, val);	Err	bitreich.org	70
i+        gcgi_sort_var_list(vars);	Err	bitreich.org	70
i+}	Err	bitreich.org	70
i+	Err	bitreich.org	70
i+static void	Err	bitreich.org	70
i+gcgi_read_var_list(struct gcgi_var_list *vars, char *path)	Err	bitreich.org	70
i+{	Err	bitreich.org	70
i+        char *line, *tail, *key, *s;	Err	bitreich.org	70
i+	Err	bitreich.org	70
i+        line = NULL;	Err	bitreich.org	70
i+	Err	bitreich.org	70
i+        if ((tail = vars->buf = gcgi_fopenread(path)) == NULL)	Err	bitreich.org	70
i+                gcgi_fatal("opening %s: %s", path, strerror(errno));	Err	bitreich.org	70
i+        while ((line = strsep(&tail, "\n")) != NULL) {	Err	bitreich.org	70
i+                if (line[0] == '\0')	Err	bitreich.org	70
i+                        break;	Err	bitreich.org	70
i+                key = strsep(&line, ":");	Err	bitreich.org	70
i+                if (line == NULL || *line++ != ' ')	Err	bitreich.org	70
i+                        gcgi_fatal("%s: missing ': ' separator", path);	Err	bitreich.org	70
i+                gcgi_add_var(vars, key, line);	Err	bitreich.org	70
i+        }	Err	bitreich.org	70
i+        gcgi_set_var(vars, "text", tail ? tail : "");	Err	bitreich.org	70
i+        gcgi_set_var(vars, "file", (s = strrchr(path, '/')) ? s + 1 : path);	Err	bitreich.org	70
i+        gcgi_sort_var_list(vars);	Err	bitreich.org	70
i+}	Err	bitreich.org	70
i+	Err	bitreich.org	70
i+static void	Err	bitreich.org	70
i+gcgi_free_var_list(struct gcgi_var_list *vars)	Err	bitreich.org	70
i+{	Err	bitreich.org	70
i+        if (vars->buf != NULL)	Err	bitreich.org	70
i+                free(vars->buf);	Err	bitreich.org	70
i+        free(vars->list);	Err	bitreich.org	70
i+}	Err	bitreich.org	70
i+	Err	bitreich.org	70
i+static int	Err	bitreich.org	70
i+gcgi_write_var_list(struct gcgi_var_list *vars, char *dst)	Err	bitreich.org	70
i+{	Err	bitreich.org	70
i+        FILE *fp;	Err	bitreich.org	70
i+        struct gcgi_var *v;	Err	bitreich.org	70
i+        size_t n;	Err	bitreich.org	70
i+        char path[1024];	Err	bitreich.org	70
i+        char *text;	Err	bitreich.org	70
i+	Err	bitreich.org	70
i+        text = NULL;	Err	bitreich.org	70
i+	Err	bitreich.org	70
i+        snprintf(path, sizeof path, "%s.tmp", dst);	Err	bitreich.org	70
i+        if ((fp = fopen(path, "w")) == NULL)	Err	bitreich.org	70
i+                gcgi_fatal("opening '%s' for writing", path);	Err	bitreich.org	70
i+	Err	bitreich.org	70
i+        for (v = vars->list, n = vars->len; n > 0; v++, n--) {	Err	bitreich.org	70
i+                if (strcasecmp(v->key, "Text") == 0) {	Err	bitreich.org	70
i+                        text = text ? text : v->val;	Err	bitreich.org	70
i+                        continue;	Err	bitreich.org	70
i+                }	Err	bitreich.org	70
i+                assert(strchr(v->key, '\n') == NULL);	Err	bitreich.org	70
i+                assert(strchr(v->val, '\n') == NULL);	Err	bitreich.org	70
i+                fprintf(fp, "%s: %s\n", v->key, v->val);	Err	bitreich.org	70
i+        }	Err	bitreich.org	70
i+        fprintf(fp, "\n%s", text ? text : "");	Err	bitreich.org	70
i+	Err	bitreich.org	70
i+        fclose(fp);	Err	bitreich.org	70
i+        if (rename(path, dst) == -1)	Err	bitreich.org	70
i+                gcgi_fatal( "renaming '%s' to '%s'", path, dst);	Err	bitreich.org	70
i+        return 0;	Err	bitreich.org	70
i+}	Err	bitreich.org	70
i+	Err	bitreich.org	70
i+static inline int	Err	bitreich.org	70
i+gcgi_match(char const *glob, char *path, char **matches, size_t m)	Err	bitreich.org	70
i+{	Err	bitreich.org	70
i+        if (m >= GCGI_MATCH_NUM)	Err	bitreich.org	70
i+                gcgi_fatal("too many wildcards in glob");	Err	bitreich.org	70
i+        matches[m] = NULL;	Err	bitreich.org	70
i+        while (*glob != '*' && *path != '\0' && *glob == *path)	Err	bitreich.org	70
i+                glob++, path++;	Err	bitreich.org	70
i+        if (glob[0] == '*') {	Err	bitreich.org	70
i+                if (*glob != '\0' && gcgi_match(glob + 1, path, matches, m + 1)) {	Err	bitreich.org	70
i+                        if (matches[m] == NULL)	Err	bitreich.org	70
i+                                matches[m] = path;	Err	bitreich.org	70
i+                        *path = '\0';	Err	bitreich.org	70
i+                        return 1;	Err	bitreich.org	70
i+                } else if (*path != '\0' && gcgi_match(glob, path + 1, matches, m)) {	Err	bitreich.org	70
i+                        matches[m] = (char *)path;	Err	bitreich.org	70
i+                        return 1;	Err	bitreich.org	70
i+                }	Err	bitreich.org	70
i+        }	Err	bitreich.org	70
i+        return *glob == '\0' && *path == '\0';	Err	bitreich.org	70
i+}	Err	bitreich.org	70
i+	Err	bitreich.org	70
i+static void	Err	bitreich.org	70
i+gcgi_handle_request(struct gcgi_handler h[], char **argv, int argc)	Err	bitreich.org	70
i+{	Err	bitreich.org	70
i+        if (argc != 5)	Err	bitreich.org	70
i+                gcgi_fatal("wrong number of arguments: %c", argc);	Err	bitreich.org	70
i+        assert(argv[0] && argv[1] && argv[2] && argv[3]);	Err	bitreich.org	70
i+	Err	bitreich.org	70
i+        /* executable.[d]cgi $search $arguments $host $port */	Err	bitreich.org	70
i+        gcgi_gopher_search = argv[1];	Err	bitreich.org	70
i+        gcgi_gopher_path = argv[2];	Err	bitreich.org	70
i+        gcgi_gopher_host = argv[3];	Err	bitreich.org	70
i+        gcgi_gopher_port = argv[4];	Err	bitreich.org	70
i+        gcgi_gopher_args = strchr(gcgi_gopher_path, '?');	Err	bitreich.org	70
i+        if (gcgi_gopher_args == NULL) {	Err	bitreich.org	70
i+                gcgi_gopher_args = "";	Err	bitreich.org	70
i+        } else {	Err	bitreich.org	70
i+                *gcgi_gopher_args++ = '\0';	Err	bitreich.org	70
i+        }	Err	bitreich.org	70
i+	Err	bitreich.org	70
i+        for (; h->glob != NULL; h++) {	Err	bitreich.org	70
i+                char *matches[GCGI_MATCH_NUM + 1];	Err	bitreich.org	70
i+                if (!gcgi_match(h->glob, gcgi_gopher_path, matches, 0))	Err	bitreich.org	70
i+                        continue;	Err	bitreich.org	70
i+                h->fn(matches);	Err	bitreich.org	70
i+                return;	Err	bitreich.org	70
i+        }	Err	bitreich.org	70
i+        gcgi_fatal("no handler for '%s'", gcgi_gopher_path);	Err	bitreich.org	70
i+}	Err	bitreich.org	70
i+	Err	bitreich.org	70
i+static void	Err	bitreich.org	70
i+gcgi_print_gophermap(char const *s)	Err	bitreich.org	70
i+{	Err	bitreich.org	70
i+        for (; *s != '\0'; s++) {	Err	bitreich.org	70
i+                switch(*s) {	Err	bitreich.org	70
i+                case '<':	Err	bitreich.org	70
i+                        fputs("&lt;", stdout);	Err	bitreich.org	70
i+                        break;	Err	bitreich.org	70
i+                case '>':	Err	bitreich.org	70
i+                        fputs("&gt;", stdout);	Err	bitreich.org	70
i+                        break;	Err	bitreich.org	70
i+                case '"':	Err	bitreich.org	70
i+                        fputs("&quot;", stdout);	Err	bitreich.org	70
i+                        break;	Err	bitreich.org	70
i+                case '\'':	Err	bitreich.org	70
i+                        fputs("&#39;", stdout);	Err	bitreich.org	70
i+                        break;	Err	bitreich.org	70
i+                case '&':	Err	bitreich.org	70
i+                        fputs("&amp;", stdout);	Err	bitreich.org	70
i+                        break;	Err	bitreich.org	70
i+                default:	Err	bitreich.org	70
i+                        fputc(*s, stdout);	Err	bitreich.org	70
i+                }	Err	bitreich.org	70
i+        }	Err	bitreich.org	70
i+}	Err	bitreich.org	70
i+	Err	bitreich.org	70
i+static inline char*	Err	bitreich.org	70
i+gcgi_next_var(char *head, char **tail)	Err	bitreich.org	70
i+{	Err	bitreich.org	70
i+        char *beg, *end;	Err	bitreich.org	70
i+	Err	bitreich.org	70
i+        if ((beg = strstr(head, "{{")) == NULL	Err	bitreich.org	70
i+          || (end = strstr(beg, "}}")) == NULL)	Err	bitreich.org	70
i+                return NULL;	Err	bitreich.org	70
i+        *beg = *end = '\0';	Err	bitreich.org	70
i+        *tail = end + strlen("}}");	Err	bitreich.org	70
i+        return beg + strlen("{{");	Err	bitreich.org	70
i+}	Err	bitreich.org	70
i+	Err	bitreich.org	70
i+static void	Err	bitreich.org	70
i+gcgi_template(char const *path, struct gcgi_var_list *vars)	Err	bitreich.org	70
i+{	Err	bitreich.org	70
i+        FILE *fp;	Err	bitreich.org	70
i+        size_t sz;	Err	bitreich.org	70
i+        char *line, *head, *tail, *key;	Err	bitreich.org	70
i+        char *val;	Err	bitreich.org	70
i+	Err	bitreich.org	70
i+        sz = 0;	Err	bitreich.org	70
i+        line = NULL;	Err	bitreich.org	70
i+	Err	bitreich.org	70
i+        if ((fp = fopen(path, "r")) == NULL)	Err	bitreich.org	70
i+                gcgi_fatal("opening template %s", path);	Err	bitreich.org	70
i+	Err	bitreich.org	70
i+        while (getline(&line, &sz, fp) > 0) {	Err	bitreich.org	70
i+                head = tail = line;	Err	bitreich.org	70
i+                for (; (key = gcgi_next_var(head, &tail)); head = tail) {	Err	bitreich.org	70
i+                        fputs(head, stdout);	Err	bitreich.org	70
i+                        if ((val = gcgi_get_var(vars, key)))	Err	bitreich.org	70
i+                                gcgi_print_gophermap(val);	Err	bitreich.org	70
i+                        else	Err	bitreich.org	70
i+                                fprintf(stdout, "{{error:%s}}", key);	Err	bitreich.org	70
i+                }	Err	bitreich.org	70
i+                fputs(tail, stdout);	Err	bitreich.org	70
i+        }	Err	bitreich.org	70
i+        fclose(fp);	Err	bitreich.org	70
i+}	Err	bitreich.org	70
i+	Err	bitreich.org	70
i+#endif	Err	bitreich.org	70
.
Response: text/plain
Original URLgopher://bitreich.org/0/scm/libgcgi/commit/758508d75c9693...
Content-Typetext/plain; charset=utf-8