split libgcgi.h into a .c and .h file - libgcgi - REST library for Gopher
(HTM) 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
(HTM) 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("<", stdout);
- break;
- case '>':
- fputs(">", stdout);
- break;
- case '"':
- fputs(""", stdout);
- break;
- case '\'':
- fputs("'", stdout);
- break;
- case '&':
- fputs("&", 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