URI: 
       split libgcgi.h into a .c and .h file - libgcgi - REST library for Gopher
  HTML git clone git://bitreich.org/libgcgi git://hg6vgqziawt5s4dj.onion/libgcgi
   DIR Log
   DIR Files
   DIR Refs
   DIR Tags
   DIR README
   DIR LICENSE
       ---
   DIR commit 052f666afd7390d53ec4b3ad91882e7e76b7a49f
   DIR parent 5bc5afc6bfca4948fee87a59a87aede28f2de765
  HTML Author: Josuah Demangeon <me@josuah.net>
       Date:   Sat, 30 Jul 2022 13:38:07 +0200
       
       split libgcgi.h into a .c and .h file
       
       Diffstat:
         M Makefile                            |       6 +++---
         M index.c                             |      11 ++---------
         A libgcgi.c                           |     283 +++++++++++++++++++++++++++++++
         M libgcgi.h                           |     328 ++-----------------------------
       
       4 files changed, 303 insertions(+), 325 deletions(-)
       ---
   DIR diff --git a/Makefile b/Makefile
       @@ -1,10 +1,10 @@
        LDFLAGS = -static
       -CFLAGS = -g -pedantic -std=c99 -Wall -Wextra -Wno-unused-function
       +CFLAGS = -g -pedantic -std=c99 -Wall -Wextra
        
        all: index.cgi
        
        clean:
                rm -f *.o index.cgi
        
       -index.cgi: index.c libgcgi.h
       -        ${CC} ${LDFLAGS} ${CFLAGS} -o $@ index.c
       +index.cgi: index.c libgcgi.c libgcgi.h
       +        ${CC} ${LDFLAGS} ${CFLAGS} -o $@ index.c libgcgi.c
   DIR diff --git a/index.c b/index.c
       @@ -1,13 +1,6 @@
       -#include <assert.h>
       -#include <ctype.h>
       -#include <errno.h>
       -#include <stdarg.h>
       -#include <stdint.h>
       -#include <stdio.h>
       -#include <stdlib.h>
       -#include <string.h>
       +#include <stddef.h>
        #include <unistd.h>
       -#include <sys/stat.h>
       +#include <stdio.h>
        
        #include "libgcgi.h"
        
   DIR diff --git a/libgcgi.c b/libgcgi.c
       @@ -0,0 +1,283 @@
       +#include <assert.h>
       +#include <ctype.h>
       +#include <errno.h>
       +#include <stdarg.h>
       +#include <stdint.h>
       +#include <stdio.h>
       +#include <stdlib.h>
       +#include <string.h>
       +#include <unistd.h>
       +#include <sys/stat.h>
       +
       +#include "libgcgi.h"
       +
       +#define GCGI_MATCH_NUM 5
       +
       +char *gcgi_gopher_search;
       +char *gcgi_gopher_path;
       +char *gcgi_gopher_host;
       +char *gcgi_gopher_port;
       +struct gcgi_var_list gcgi_gopher_query;
       +
       +void
       +gcgi_fatal(char *fmt, ...)
       +{
       +        va_list va;
       +        char msg[1024];
       +
       +        va_start(va, fmt);
       +        vsnprintf(msg, sizeof msg, fmt, va);
       +        printf("error: %s\n", msg);
       +        exit(1);
       +}
       +
       +static char *
       +gcgi_fopenread(char *path)
       +{
       +        FILE *fp;
       +        char *buf;
       +        ssize_t ssz;
       +        size_t sz;
       +
       +        if ((fp = fopen(path, "r")) == NULL)
       +                return NULL;
       +        if (fseek(fp, 0, SEEK_END) == -1)
       +                return NULL;
       +        if ((ssz = ftell(fp)) == -1)
       +                return NULL;
       +        sz = ssz;
       +        if (fseek(fp, 0, SEEK_SET) == -1)
       +                return NULL;
       +        if ((buf = malloc(sz + 1)) == NULL)
       +                return NULL;
       +        if (fread(buf, sz, 1, fp) == sz) {
       +                errno = EFBIG;
       +                goto error_free;
       +        }
       +        if (ferror(fp))
       +                goto error_free;
       +        fclose(fp);
       +        buf[sz] = '\0';
       +        return buf;
       +error_free:
       +        free(buf);
       +        return NULL;
       +}
       +
       +static int
       +gcgi_cmp_var(const void *v1, const void *v2)
       +{
       +        return strcasecmp(((struct gcgi_var *)v1)->key, ((struct gcgi_var *)v2)->key);
       +}
       +
       +void
       +gcgi_add_var(struct gcgi_var_list *vars, char *key, char *val)
       +{
       +        void *mem;
       +
       +        vars->len++;
       +        if ((mem = realloc(vars->list, vars->len * sizeof *vars->list)) == NULL)
       +                 gcgi_fatal("realloc");
       +        vars->list = mem;
       +        vars->list[vars->len-1].key = key;
       +        vars->list[vars->len-1].val = val;
       +}
       +
       +void
       +gcgi_sort_var_list(struct gcgi_var_list *vars)
       +{
       +        qsort(vars->list, vars->len, sizeof *vars->list, gcgi_cmp_var);
       +}
       +
       +char *
       +gcgi_get_var(struct gcgi_var_list *vars, char *key)
       +{
       +        struct gcgi_var *v, q = { .key = key };
       +
       +        v = bsearch(&q, vars->list, vars->len, sizeof *vars->list, gcgi_cmp_var);
       +        return (v == NULL) ? NULL : v->val;
       +}
       +
       +void
       +gcgi_set_var(struct gcgi_var_list *vars, char *key, char *val)
       +{
       +        struct gcgi_var *v, q;
       +
       +        q.key = key;
       +        v = bsearch(&q, vars->list, vars->len, sizeof *vars->list, gcgi_cmp_var);
       +        if (v != NULL) {
       +                v->val = val;
       +                return;
       +        }
       +        gcgi_add_var(vars, key, val);
       +        gcgi_sort_var_list(vars);
       +}
       +
       +void
       +gcgi_read_var_list(struct gcgi_var_list *vars, char *path)
       +{
       +        char *line, *tail, *key, *s;
       +
       +        line = NULL;
       +
       +        if ((tail = vars->buf = gcgi_fopenread(path)) == NULL)
       +                gcgi_fatal("opening %s: %s", path, strerror(errno));
       +        while ((line = strsep(&tail, "\n")) != NULL) {
       +                if (line[0] == '\0')
       +                        break;
       +                key = strsep(&line, ":");
       +                if (line == NULL || *line++ != ' ')
       +                        gcgi_fatal("%s: missing ': ' separator", path);
       +                gcgi_add_var(vars, key, line);
       +        }
       +        gcgi_set_var(vars, "text", tail ? tail : "");
       +        gcgi_set_var(vars, "file", (s = strrchr(path, '/')) ? s + 1 : path);
       +        gcgi_sort_var_list(vars);
       +}
       +
       +void
       +gcgi_free_var_list(struct gcgi_var_list *vars)
       +{
       +        if (vars->buf != NULL)
       +                free(vars->buf);
       +        free(vars->list);
       +}
       +
       +int
       +gcgi_write_var_list(struct gcgi_var_list *vars, char *dst)
       +{
       +        FILE *fp;
       +        struct gcgi_var *v;
       +        size_t n;
       +        char path[1024];
       +        char *text;
       +
       +        text = NULL;
       +
       +        snprintf(path, sizeof path, "%s.tmp", dst);
       +        if ((fp = fopen(path, "w")) == NULL)
       +                gcgi_fatal("opening '%s' for writing", path);
       +
       +        for (v = vars->list, n = vars->len; n > 0; v++, n--) {
       +                if (strcasecmp(v->key, "Text") == 0) {
       +                        text = text ? text : v->val;
       +                        continue;
       +                }
       +                assert(strchr(v->key, '\n') == NULL);
       +                assert(strchr(v->val, '\n') == NULL);
       +                fprintf(fp, "%s: %s\n", v->key, v->val);
       +        }
       +        fprintf(fp, "\n%s", text ? text : "");
       +
       +        fclose(fp);
       +        if (rename(path, dst) == -1)
       +                gcgi_fatal( "renaming '%s' to '%s'", path, dst);
       +        return 0;
       +}
       +
       +static int
       +gcgi_match(char const *glob, char *path, char **matches, size_t m)
       +{
       +        if (m >= GCGI_MATCH_NUM)
       +                gcgi_fatal("too many wildcards in glob");
       +        matches[m] = NULL;
       +        while (*glob != '*' && *path != '\0' && *glob == *path)
       +                glob++, path++;
       +        if (glob[0] == '*') {
       +                if (*glob != '\0' && gcgi_match(glob + 1, path, matches, m + 1)) {
       +                        if (matches[m] == NULL)
       +                                matches[m] = path;
       +                        *path = '\0';
       +                        return 1;
       +                } else if (*path != '\0' && gcgi_match(glob, path + 1, matches, m)) {
       +                        matches[m] = (char *)path;
       +                        return 1;
       +                }
       +        }
       +        return *glob == '\0' && *path == '\0';
       +}
       +
       +static void
       +gcgi_decode_url(struct gcgi_var_list *vars, char *s)
       +{
       +        char *tok, *eq;
       +
       +        while ((tok = strsep(&s, "&"))) {
       +                //gcgi_decode_hex(tok);
       +                if ((eq = strchr(tok, '=')) == NULL)
       +                        continue;
       +                *eq = '\0';
       +                gcgi_add_var(vars, tok, eq + 1);
       +        }
       +        gcgi_sort_var_list(vars);
       +}
       +
       +void
       +gcgi_handle_request(struct gcgi_handler h[], char **argv, int argc)
       +{
       +        char *query_string;
       +
       +        if (argc != 5)
       +                gcgi_fatal("wrong number of arguments: %c", argc);
       +        assert(argv[0] && argv[1] && argv[2] && argv[3]);
       +
       +        /* executable.[d]cgi $search $arguments $host $port */
       +        gcgi_gopher_search = argv[1];
       +        gcgi_gopher_path = argv[2];
       +        gcgi_gopher_host = argv[3];
       +        gcgi_gopher_port = argv[4];
       +        query_string = strchr(gcgi_gopher_path, '?');
       +        if (query_string != NULL) {
       +                *query_string++ = '\0';
       +                gcgi_decode_url(&gcgi_gopher_query, query_string);
       +        }
       +
       +        for (; h->glob != NULL; h++) {
       +                char *matches[GCGI_MATCH_NUM + 1];
       +                if (!gcgi_match(h->glob, gcgi_gopher_path, matches, 0))
       +                        continue;
       +                h->fn(matches);
       +                return;
       +        }
       +        gcgi_fatal("no handler for '%s'", gcgi_gopher_path);
       +}
       +
       +static char*
       +gcgi_next_var(char *head, char **tail)
       +{
       +        char *beg, *end;
       +
       +        if ((beg = strstr(head, "{{")) == NULL
       +          || (end = strstr(beg, "}}")) == NULL)
       +                return NULL;
       +        *beg = *end = '\0';
       +        *tail = end + strlen("}}");
       +        return beg + strlen("{{");
       +}
       +
       +void
       +gcgi_template(char const *path, struct gcgi_var_list *vars)
       +{
       +        FILE *fp;
       +        size_t sz;
       +        char *line, *head, *tail, *key, *val;
       +
       +        if ((fp = fopen(path, "r")) == NULL)
       +                gcgi_fatal("opening template %s", path);
       +        sz = 0;
       +        line = NULL;
       +        while (getline(&line, &sz, fp) > 0) {
       +                head = tail = line;
       +                for (; (key = gcgi_next_var(head, &tail)); head = tail) {
       +                        fputs(head, stdout);
       +                        if ((val = gcgi_get_var(vars, key)))
       +                                fputs(val, stdout);
       +                        else
       +                                fprintf(stdout, "{{error:%s}}", key);
       +                }
       +                fputs(tail, stdout);
       +        }
       +        if (ferror(fp))
       +                gcgi_fatal("reading from template: %s", strerror(errno));
       +        fclose(fp);
       +}
   DIR diff --git a/libgcgi.h b/libgcgi.h
       @@ -19,328 +19,30 @@ struct gcgi_var_list {
        };
        
        /* main loop executing h->fn() if h->glob is matching */
       -static void gcgi_handle_request(struct gcgi_handler h[], char **argv, int argc);
       +void gcgi_handle_request(struct gcgi_handler h[], char **argv, int argc);
        
        /* abort the program with an error message sent to the client */
       -static void gcgi_fatal(char *fmt, ...);
       +void gcgi_fatal(char *fmt, ...);
        
        /* print a template with every "{{name}}" looked up in `vars` */
       -static void gcgi_template(char const *path, struct gcgi_var_list *vars);
       -
       -/* print `s` with all gophermap special characters escaped */
       -static void gcgi_print_gophermap(char const *s);
       +void gcgi_template(char const *path, struct gcgi_var_list *vars);
        
        /* manage a `key`-`val` pair storage `vars`, as used with gcgi_template */
       -static void gcgi_add_var(struct gcgi_var_list *vars, char *key, char *val);
       -static void gcgi_sort_var_list(struct gcgi_var_list *vars);
       -static void gcgi_set_var(struct gcgi_var_list *vars, char *key, char *val);
       -static char *gcgi_get_var(struct gcgi_var_list *vars, char *key);
       -static void gcgi_free_var_list(struct gcgi_var_list *vars);
       +void gcgi_add_var(struct gcgi_var_list *vars, char *key, char *val);
       +void gcgi_sort_var_list(struct gcgi_var_list *vars);
       +void gcgi_set_var(struct gcgi_var_list *vars, char *key, char *val);
       +char *gcgi_get_var(struct gcgi_var_list *vars, char *key);
       +void gcgi_free_var_list(struct gcgi_var_list *vars);
        
        /* store and read a list of variables onto a simple RFC822-like format */
       -static void gcgi_read_var_list(struct gcgi_var_list *vars, char *path);
       -static int gcgi_write_var_list(struct gcgi_var_list *vars, char *path);
       +void gcgi_read_var_list(struct gcgi_var_list *vars, char *path);
       +int gcgi_write_var_list(struct gcgi_var_list *vars, char *path);
        
        /* components of the gopher request */
       -char *gcgi_gopher_search;
       -char *gcgi_gopher_path;
       -char *gcgi_gopher_host;
       -char *gcgi_gopher_port;
       -static struct gcgi_var_list gcgi_gopher_query;
       -
       -
       -/// POLICE LINE /// DO NOT CROSS ///
       -
       -
       -#define GCGI_MATCH_NUM 5
       -
       -static void
       -gcgi_fatal(char *fmt, ...)
       -{
       -        va_list va;
       -        char msg[1024];
       -
       -        va_start(va, fmt);
       -        vsnprintf(msg, sizeof msg, fmt, va);
       -        printf("error: %s\n", msg);
       -        exit(1);
       -}
       -
       -static inline char *
       -gcgi_fopenread(char *path)
       -{
       -        FILE *fp;
       -        char *buf;
       -        ssize_t ssz;
       -        size_t sz;
       -
       -        if ((fp = fopen(path, "r")) == NULL)
       -                return NULL;
       -        if (fseek(fp, 0, SEEK_END) == -1)
       -                return NULL;
       -        if ((ssz = ftell(fp)) == -1)
       -                return NULL;
       -        sz = ssz;
       -        if (fseek(fp, 0, SEEK_SET) == -1)
       -                return NULL;
       -        if ((buf = malloc(sz + 1)) == NULL)
       -                return NULL;
       -        if (fread(buf, sz, 1, fp) == sz) {
       -                errno = EFBIG;
       -                goto error_free;
       -        }
       -        if (ferror(fp))
       -                goto error_free;
       -        fclose(fp);
       -        buf[sz] = '\0';
       -        return buf;
       -error_free:
       -        free(buf);
       -        return NULL;
       -}
       -
       -static int
       -gcgi_cmp_var(const void *v1, const void *v2)
       -{
       -        return strcasecmp(((struct gcgi_var *)v1)->key, ((struct gcgi_var *)v2)->key);
       -}
       -
       -static void
       -gcgi_add_var(struct gcgi_var_list *vars, char *key, char *val)
       -{
       -        void *mem;
       -
       -        vars->len++;
       -        if ((mem = realloc(vars->list, vars->len * sizeof *vars->list)) == NULL)
       -                 gcgi_fatal("realloc");
       -        vars->list = mem;
       -        vars->list[vars->len-1].key = key;
       -        vars->list[vars->len-1].val = val;
       -}
       -
       -static void
       -gcgi_sort_var_list(struct gcgi_var_list *vars)
       -{
       -        qsort(vars->list, vars->len, sizeof *vars->list, gcgi_cmp_var);
       -}
       -
       -static char *
       -gcgi_get_var(struct gcgi_var_list *vars, char *key)
       -{
       -        struct gcgi_var *v, q = { .key = key };
       -
       -        v = bsearch(&q, vars->list, vars->len, sizeof *vars->list, gcgi_cmp_var);
       -        return (v == NULL) ? NULL : v->val;
       -}
       -
       -static void
       -gcgi_set_var(struct gcgi_var_list *vars, char *key, char *val)
       -{
       -        struct gcgi_var *v, q;
       -
       -        q.key = key;
       -        v = bsearch(&q, vars->list, vars->len, sizeof *vars->list, gcgi_cmp_var);
       -        if (v != NULL) {
       -                v->val = val;
       -                return;
       -        }
       -        gcgi_add_var(vars, key, val);
       -        gcgi_sort_var_list(vars);
       -}
       -
       -static void
       -gcgi_read_var_list(struct gcgi_var_list *vars, char *path)
       -{
       -        char *line, *tail, *key, *s;
       -
       -        line = NULL;
       -
       -        if ((tail = vars->buf = gcgi_fopenread(path)) == NULL)
       -                gcgi_fatal("opening %s: %s", path, strerror(errno));
       -        while ((line = strsep(&tail, "\n")) != NULL) {
       -                if (line[0] == '\0')
       -                        break;
       -                key = strsep(&line, ":");
       -                if (line == NULL || *line++ != ' ')
       -                        gcgi_fatal("%s: missing ': ' separator", path);
       -                gcgi_add_var(vars, key, line);
       -        }
       -        gcgi_set_var(vars, "text", tail ? tail : "");
       -        gcgi_set_var(vars, "file", (s = strrchr(path, '/')) ? s + 1 : path);
       -        gcgi_sort_var_list(vars);
       -}
       -
       -static void
       -gcgi_free_var_list(struct gcgi_var_list *vars)
       -{
       -        if (vars->buf != NULL)
       -                free(vars->buf);
       -        free(vars->list);
       -}
       -
       -static int
       -gcgi_write_var_list(struct gcgi_var_list *vars, char *dst)
       -{
       -        FILE *fp;
       -        struct gcgi_var *v;
       -        size_t n;
       -        char path[1024];
       -        char *text;
       -
       -        text = NULL;
       -
       -        snprintf(path, sizeof path, "%s.tmp", dst);
       -        if ((fp = fopen(path, "w")) == NULL)
       -                gcgi_fatal("opening '%s' for writing", path);
       -
       -        for (v = vars->list, n = vars->len; n > 0; v++, n--) {
       -                if (strcasecmp(v->key, "Text") == 0) {
       -                        text = text ? text : v->val;
       -                        continue;
       -                }
       -                assert(strchr(v->key, '\n') == NULL);
       -                assert(strchr(v->val, '\n') == NULL);
       -                fprintf(fp, "%s: %s\n", v->key, v->val);
       -        }
       -        fprintf(fp, "\n%s", text ? text : "");
       -
       -        fclose(fp);
       -        if (rename(path, dst) == -1)
       -                gcgi_fatal( "renaming '%s' to '%s'", path, dst);
       -        return 0;
       -}
       -
       -static inline int
       -gcgi_match(char const *glob, char *path, char **matches, size_t m)
       -{
       -        if (m >= GCGI_MATCH_NUM)
       -                gcgi_fatal("too many wildcards in glob");
       -        matches[m] = NULL;
       -        while (*glob != '*' && *path != '\0' && *glob == *path)
       -                glob++, path++;
       -        if (glob[0] == '*') {
       -                if (*glob != '\0' && gcgi_match(glob + 1, path, matches, m + 1)) {
       -                        if (matches[m] == NULL)
       -                                matches[m] = path;
       -                        *path = '\0';
       -                        return 1;
       -                } else if (*path != '\0' && gcgi_match(glob, path + 1, matches, m)) {
       -                        matches[m] = (char *)path;
       -                        return 1;
       -                }
       -        }
       -        return *glob == '\0' && *path == '\0';
       -}
       -
       -static inline void
       -gcgi_decode_url(struct gcgi_var_list *vars, char *s)
       -{
       -        char *tok, *eq;
       -
       -        while ((tok = strsep(&s, "&"))) {
       -                //gcgi_decode_hex(tok);
       -                if ((eq = strchr(tok, '=')) == NULL)
       -                        continue;
       -                *eq = '\0';
       -                gcgi_add_var(vars, tok, eq + 1);
       -        }
       -        gcgi_sort_var_list(vars);
       -}
       -
       -static void
       -gcgi_handle_request(struct gcgi_handler h[], char **argv, int argc)
       -{
       -        char *query_string;
       -
       -        if (argc != 5)
       -                gcgi_fatal("wrong number of arguments: %c", argc);
       -        assert(argv[0] && argv[1] && argv[2] && argv[3]);
       -
       -        /* executable.[d]cgi $search $arguments $host $port */
       -        gcgi_gopher_search = argv[1];
       -        gcgi_gopher_path = argv[2];
       -        gcgi_gopher_host = argv[3];
       -        gcgi_gopher_port = argv[4];
       -        query_string = strchr(gcgi_gopher_path, '?');
       -        if (query_string != NULL) {
       -                *query_string++ = '\0';
       -                gcgi_decode_url(&gcgi_gopher_query, query_string);
       -        }
       -
       -        for (; h->glob != NULL; h++) {
       -                char *matches[GCGI_MATCH_NUM + 1];
       -                if (!gcgi_match(h->glob, gcgi_gopher_path, matches, 0))
       -                        continue;
       -                h->fn(matches);
       -                return;
       -        }
       -        gcgi_fatal("no handler for '%s'", gcgi_gopher_path);
       -}
       -
       -static void
       -gcgi_print_gophermap(char const *s)
       -{
       -        for (; *s != '\0'; s++) {
       -                switch(*s) {
       -                case '<':
       -                        fputs("&lt;", stdout);
       -                        break;
       -                case '>':
       -                        fputs("&gt;", stdout);
       -                        break;
       -                case '"':
       -                        fputs("&quot;", stdout);
       -                        break;
       -                case '\'':
       -                        fputs("&#39;", stdout);
       -                        break;
       -                case '&':
       -                        fputs("&amp;", stdout);
       -                        break;
       -                default:
       -                        fputc(*s, stdout);
       -                }
       -        }
       -}
       -
       -static inline char*
       -gcgi_next_var(char *head, char **tail)
       -{
       -        char *beg, *end;
       -
       -        if ((beg = strstr(head, "{{")) == NULL
       -          || (end = strstr(beg, "}}")) == NULL)
       -                return NULL;
       -        *beg = *end = '\0';
       -        *tail = end + strlen("}}");
       -        return beg + strlen("{{");
       -}
       -
       -static void
       -gcgi_template(char const *path, struct gcgi_var_list *vars)
       -{
       -        FILE *fp;
       -        size_t sz;
       -        char *line, *head, *tail, *key, *val;
       -
       -        if ((fp = fopen(path, "r")) == NULL)
       -                gcgi_fatal("opening template %s", path);
       -        sz = 0;
       -        line = NULL;
       -        while (getline(&line, &sz, fp) > 0) {
       -                head = tail = line;
       -                for (; (key = gcgi_next_var(head, &tail)); head = tail) {
       -                        fputs(head, stdout);
       -                        if ((val = gcgi_get_var(vars, key)))
       -                                gcgi_print_gophermap(val);
       -                        else
       -                                fprintf(stdout, "{{error:%s}}", key);
       -                }
       -                fputs(tail, stdout);
       -        }
       -        if (ferror(fp))
       -                gcgi_fatal("reading from template: %s", strerror(errno));
       -        fclose(fp);
       -}
       +extern char *gcgi_gopher_search;
       +extern char *gcgi_gopher_path;
       +extern char *gcgi_gopher_host;
       +extern char *gcgi_gopher_port;
       +extern struct gcgi_var_list gcgi_gopher_query;
        
        #endif