Introduction
Introduction Statistics Contact Development Disclaimer Help
libgcgi.c - libgcgi - REST library for Gopher
git clone git://bitreich.org/libgcgi/ git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinw…
Log
Files
Refs
Tags
README
LICENSE
---
libgcgi.c (6502B)
---
1 #include <assert.h>
2 #include <ctype.h>
3 #include <errno.h>
4 #include <stdarg.h>
5 #include <stdint.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <strings.h>
10 #include <unistd.h>
11 #include <sys/stat.h>
12
13 #include "libgcgi.h"
14
15 #define GCGI_MATCH_NUM 5
16
17 char *gcgi_gopher_search;
18 char *gcgi_gopher_path;
19 char *gcgi_gopher_host;
20 char *gcgi_gopher_port;
21 struct gcgi_var_list gcgi_gopher_query;
22
23 void
24 gcgi_fatal(char *fmt, ...)
25 {
26 va_list va;
27 char msg[1024];
28
29 va_start(va, fmt);
30 vsnprintf(msg, sizeof msg, fmt, va);
31 printf("error: %s\n", msg);
32 exit(1);
33 }
34
35 static char *
36 gcgi_fopenread(char *path)
37 {
38 FILE *fp;
39 char *buf;
40 ssize_t ssz;
41 size_t sz;
42
43 if ((fp = fopen(path, "r")) == NULL)
44 return NULL;
45 if (fseek(fp, 0, SEEK_END) == -1)
46 return NULL;
47 if ((ssz = ftell(fp)) == -1)
48 return NULL;
49 sz = ssz;
50 if (fseek(fp, 0, SEEK_SET) == -1)
51 return NULL;
52 if ((buf = malloc(sz + 1)) == NULL)
53 return NULL;
54 if (fread(buf, sz, 1, fp) == sz) {
55 errno = EFBIG;
56 goto error_free;
57 }
58 if (ferror(fp))
59 goto error_free;
60 fclose(fp);
61 buf[sz] = '\0';
62 return buf;
63 error_free:
64 free(buf);
65 return NULL;
66 }
67
68 static int
69 gcgi_cmp_var(const void *v1, const void *v2)
70 {
71 return strcasecmp(((struct gcgi_var *)v1)->key, ((struct gcgi_va…
72 }
73
74 static void
75 gcgi_add_var(struct gcgi_var_list *vars, char *key, char *val)
76 {
77 void *mem;
78
79 vars->len++;
80 if ((mem = realloc(vars->list, vars->len * sizeof *vars->list)) …
81 gcgi_fatal("realloc");
82 vars->list = mem;
83 vars->list[vars->len-1].key = key;
84 vars->list[vars->len-1].val = val;
85 }
86
87 static void
88 gcgi_sort_var_list(struct gcgi_var_list *vars)
89 {
90 qsort(vars->list, vars->len, sizeof *vars->list, gcgi_cmp_var);
91 }
92
93 char *
94 gcgi_get_var(struct gcgi_var_list *vars, char *key)
95 {
96 struct gcgi_var *v, q = { .key = key };
97
98 v = bsearch(&q, vars->list, vars->len, sizeof *vars->list, gcgi_…
99 return (v == NULL) ? NULL : v->val;
100 }
101
102 void
103 gcgi_set_var(struct gcgi_var_list *vars, char *key, char *val)
104 {
105 struct gcgi_var *v, q;
106
107 q.key = key;
108 v = bsearch(&q, vars->list, vars->len, sizeof *vars->list, gcgi_…
109 if (v != NULL) {
110 v->val = val;
111 return;
112 }
113 gcgi_add_var(vars, key, val);
114 gcgi_sort_var_list(vars);
115 }
116
117 void
118 gcgi_read_var_list(struct gcgi_var_list *vars, char *path)
119 {
120 char *line, *tail, *key, *s;
121
122 line = NULL;
123
124 if ((tail = vars->buf = gcgi_fopenread(path)) == NULL)
125 gcgi_fatal("opening %s: %s", path, strerror(errno));
126 while ((line = strsep(&tail, "\n")) != NULL) {
127 if (line[0] == '\0')
128 break;
129 key = strsep(&line, ":");
130 if (line == NULL || *line++ != ' ')
131 gcgi_fatal("%s: missing ': ' separator", path);
132 gcgi_add_var(vars, key, line);
133 }
134 gcgi_set_var(vars, "text", tail ? tail : "");
135 gcgi_set_var(vars, "file", (s = strrchr(path, '/')) ? s + 1 : pa…
136 gcgi_sort_var_list(vars);
137 }
138
139 void
140 gcgi_free_var_list(struct gcgi_var_list *vars)
141 {
142 free(vars->buf);
143 free(vars->list);
144 }
145
146 int
147 gcgi_write_var_list(struct gcgi_var_list *vars, char *dst)
148 {
149 FILE *fp;
150 struct gcgi_var *v;
151 size_t n;
152 char path[1024];
153 char *text;
154
155 text = NULL;
156
157 snprintf(path, sizeof path, "%s.tmp", dst);
158 if ((fp = fopen(path, "w")) == NULL)
159 gcgi_fatal("opening '%s' for writing", path);
160
161 for (v = vars->list, n = vars->len; n > 0; v++, n--) {
162 if (strcasecmp(v->key, "text") == 0) {
163 text = text ? text : v->val;
164 continue;
165 }
166 assert(strchr(v->key, '\n') == NULL);
167 assert(strchr(v->val, '\n') == NULL);
168 fprintf(fp, "%s: %s\n", v->key, v->val);
169 }
170 fprintf(fp, "\n%s", text ? text : "");
171
172 fclose(fp);
173 if (rename(path, dst) == -1)
174 gcgi_fatal( "renaming '%s' to '%s'", path, dst);
175 return 0;
176 }
177
178 static int
179 gcgi_match(char const *glob, char *path, char **matches, size_t m)
180 {
181 if (m >= GCGI_MATCH_NUM)
182 gcgi_fatal("too many wildcards in glob");
183 matches[m] = NULL;
184 while (*glob != '*' && *path != '\0' && *glob == *path)
185 glob++, path++;
186 if (glob[0] == '*') {
187 if (*glob != '\0' && gcgi_match(glob + 1, path, matches,…
188 if (matches[m] == NULL)
189 matches[m] = path;
190 *path = '\0';
191 return 1;
192 } else if (*path != '\0' && gcgi_match(glob, path + 1, m…
193 matches[m] = (char *)path;
194 return 1;
195 }
196 }
197 return *glob == '\0' && *path == '\0';
198 }
199
200 static void
201 gcgi_decode_url(struct gcgi_var_list *vars, char *s)
202 {
203 char *tok, *eq;
204
205 while ((tok = strsep(&s, "&"))) {
206 if ((eq = strchr(tok, '=')) == NULL)
207 continue;
208 *eq = '\0';
209 gcgi_add_var(vars, tok, eq + 1);
210 }
211 gcgi_sort_var_list(vars);
212 }
213
214 void
215 gcgi_handle_request(struct gcgi_handler h[], char **argv, int argc)
216 {
217 char *query_string;
218
219 if (argc != 5)
220 gcgi_fatal("wrong number of arguments: %c", argc);
221 assert(argv[0] && argv[1] && argv[2] && argv[3]);
222
223 /* executable.[d]cgi $search $arguments $host $port */
224 gcgi_gopher_search = argv[1];
225 gcgi_gopher_path = argv[2];
226 gcgi_gopher_host = argv[3];
227 gcgi_gopher_port = argv[4];
228 query_string = strchr(gcgi_gopher_path, '?');
229 if (query_string != NULL) {
230 *query_string++ = '\0';
231 gcgi_decode_url(&gcgi_gopher_query, query_string);
232 }
233
234 for (; h->glob != NULL; h++) {
235 char *matches[GCGI_MATCH_NUM + 1];
236 if (!gcgi_match(h->glob, gcgi_gopher_path, matches, 0))
237 continue;
238 h->fn(matches);
239 return;
240 }
241 gcgi_fatal("no handler for '%s'", gcgi_gopher_path);
242 }
243
244 static char*
245 gcgi_next_var(char *head, char **tail)
246 {
247 char *beg, *end;
248
249 if ((beg = strstr(head, "{{")) == NULL
250 || (end = strstr(beg, "}}")) == NULL)
251 return NULL;
252 *beg = *end = '\0';
253 *tail = end + strlen("}}");
254 return beg + strlen("{{");
255 }
256
257 void
258 gcgi_print_gophermap(char type, char *desc, char *path, char *host, char…
259 {
260 assert(type >= 0x30);
261 printf("%c%s\t%s\t%s\t%s\n", type, desc, path, host, port);
262 }
263
264 void
265 gcgi_print_gph(char type, char *desc, char *path, char *host, char *port)
266 {
267 assert(type >= 0x30);
268 if (host == NULL)
269 host = "server";
270 if (port == NULL)
271 port = "port";
272 printf("[%c|%s|%s|%s|%s]\n", type, desc, path, host, port);
273 }
274
275 void
276 gcgi_template(char const *path, struct gcgi_var_list *vars)
277 {
278 FILE *fp;
279 size_t sz;
280 char *line, *head, *tail, *key, *val;
281
282 if ((fp = fopen(path, "r")) == NULL)
283 gcgi_fatal("opening template %s", path);
284 sz = 0;
285 line = NULL;
286 while (getline(&line, &sz, fp) > 0) {
287 head = tail = line;
288 for (; (key = gcgi_next_var(head, &tail)); head = tail) {
289 fputs(head, stdout);
290 if ((val = gcgi_get_var(vars, key)))
291 fputs(val, stdout);
292 else
293 fprintf(stdout, "{{error:%s}}", key);
294 }
295 fputs(tail, stdout);
296 }
297 if (ferror(fp))
298 gcgi_fatal("reading from template: %s", strerror(errno));
299 fclose(fp);
300 }
You are viewing proxied material from bitreich.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.