some random old commits before bzr conversion
[tridge/junkcode.git] / tserver / template.c
1 /* 
2    some simple html template routines
3    Copyright (C) Andrew Tridgell 2001
4    
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2 of the License, or
8    (at your option) any later version.
9    
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14    
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20 #include "includes.h"
21
22 static void process_tag(struct template_state *tmpl, const char *tag);
23
24 /* 
25    fetch a variable from the template variables list 
26 */
27 static struct template_var *find_var(struct template_state *tmpl, const char *name)
28 {
29         struct template_var *var;
30
31         for (var = tmpl->variables; var; var = var->next) {
32                 if (strcmp(var->name, name) == 0) {
33                         return var;
34                 }
35         }
36         return NULL;
37 }
38
39 /*
40   add a name/value pair to the list of template variables 
41 */
42 static void put(struct template_state *tmpl, const char *name, 
43                 const char *value, template_fn fn)
44 {
45         struct template_var *var;
46         if (!name || !value) return;
47
48         var = find_var(tmpl, name);
49         if (var) {
50                 free(var->value);
51         } else {
52                 var = malloc(sizeof(*var));
53                 var->next = tmpl->variables;
54                 tmpl->variables = var;
55                 var->name = strdup(name);
56                 var->function = fn;
57         }
58         var->value = strdup(value);
59 }
60
61 /* fetch a variable from the template variables list */
62 static const char *get(struct template_state *tmpl, const char *name)
63 {
64         struct template_var *var;
65
66         var = find_var(tmpl, name);
67         if (var) return var->value;
68
69         return NULL;
70 }
71
72
73 /* process a template variable */
74 static void process_variable(struct template_state *tmpl, const char *tag)
75 {
76         const char *v = tmpl->get(tmpl, tag);
77         if (v) {
78                 printf("%s", v);
79         }
80 }
81
82 /* process setting a template variable */
83 static void process_set_var(struct template_state *tmpl, char *tag)
84 {
85         char *p;
86         p = strchr(tag, '=');
87         if (!p) return;
88         *p++ = 0;
89         trim_tail(tag, " \t");
90         trim_tail(p, " \t");
91         while (isspace(*p)) p++;
92         tmpl->put(tmpl, tag, p, NULL);
93 }
94
95 /* process a template variable with quote escaping */
96 static void process_escaped_variable(struct template_state *tmpl, const char *tag)
97 {
98         const char *v = tmpl->get(tmpl, tag);
99         while (v && *v) {
100                 if (*v == '"') {
101                         printf(""");
102                 } else {
103                         fputc(*v, stdout);
104                 }
105                 v++;
106         }
107 }
108
109 /* process a inline shell script
110    all current template variables are passed to the script in the environment
111    recursively processes any tags in the output of the script
112  */
113 static void process_shell(struct template_state *tmpl, const char *tag, int recurse)
114 {
115         pid_t pid;
116
117         fflush(stdout);
118
119         if ((pid=fork()) == 0) {
120                 struct template_var *var;
121                 FILE *p;
122                 char *tag1=NULL;
123                 int c, taglen=0;
124
125                 for (var = tmpl->variables; var; var = var->next) {
126                         setenv(var->name, var->value, 1);
127                 } 
128                 dup2(1, 2);
129                 p = popen(tag, "r");
130                 if (!p) {
131                         printf("Failed to execute script\n");
132                         exit(1);
133                 }
134                 while ((c = fgetc(p)) != EOF) {
135                         int depth;
136
137                         tag1 = realloc(tag1, taglen+2);
138                         tag1[taglen++] = c; tag1[taglen] = 0;
139                         if (!recurse || strncmp(tag1, START_TAG, taglen) != 0) {
140                                 fputs(tag1, stdout);
141                                 free(tag1); tag1 = NULL; taglen = 0;
142                                 continue;
143                         }
144                         if (strcmp(tag1, START_TAG) != 0) continue;
145
146                         depth = 1;
147                         /* keep going until END_TAG */
148                         while (depth && (c = fgetc(p)) != EOF) {
149                                 tag1 = realloc(tag1, taglen+2);
150                                 tag1[taglen++] = c; tag1[taglen] = 0;
151                                 if (strcmp(tag1+taglen-strlen(END_TAG), 
152                                            END_TAG) == 0) {
153                                         depth--;
154                                 }
155                                 if (strcmp(tag1+taglen-strlen(START_TAG), 
156                                            START_TAG) == 0) {
157                                         depth++;
158                                 }
159                         }
160                         if (depth) continue;
161
162                         tag1[taglen-strlen(END_TAG)] = 0;
163                         process_tag(tmpl, tag1+strlen(START_TAG));
164                         free(tag1); tag1 = NULL; taglen = 0;
165                 }
166                 if (tag1) {
167                         fputs(tag1, stdout);
168                         free(tag1);
169                 }
170                 fflush(stdout);
171                 fclose(p);
172                 exit(1);
173         }
174         waitpid(pid, NULL, 0);
175 }
176
177 /* process a call into a C function setup with put_function() */
178 static void process_c_call(struct template_state *tmpl, const char *tag)
179 {
180         struct template_var *var;
181         char *name, *args, *p, *tok;
182         char **argv;
183         int argc=0;
184
185         if (!(p=strchr(tag, '('))) return;
186
187         name = strndup(tag, strcspn(tag, "("));
188
189         var = find_var(tmpl, name);
190         if (!var || !var->function) {
191                 free(name);
192                 return;
193         }
194
195         args = strndup(p+1, strcspn(p+1, ")"));
196
197         argv = malloc(sizeof(char *));
198         for (tok = strtok_r(args, ",", &p); tok; tok = strtok_r(NULL, ",", &p)) {
199                 argv = realloc(argv, (argc+2)*sizeof(char *));
200                 while (isspace(*tok)) tok++;
201                 trim_tail(tok, " \t\r\n");
202                 argv[argc++] = tok;
203         }
204         argv[argc] = NULL;
205
206         var->function(tmpl, name, var->value, argc, argv);
207         free(args);
208         free(name);
209 }
210
211 /*
212   process a single tag
213 */
214 static void process_tag(struct template_state *tmpl, const char *tag)
215 {
216         char *tag2, *p;
217         int recurse = 1;
218
219         while (isspace(*tag)) tag++;
220
221         tag2 = strdup(tag);
222         trim_tail(tag2, " \t\n\r");
223
224         p = tag2;
225
226         if (*p == '-') {
227                 p++;
228                 recurse = 0;
229         }
230
231         switch (*p) {
232         case '$':
233                 process_variable(tmpl, p+1);
234                 break;
235         case '%':
236                 process_escaped_variable(tmpl, p+1);
237                 break;
238         case '!':
239                 process_shell(tmpl, p+1, recurse);
240                 break;
241         case '@':
242                 process_c_call(tmpl, p+1);
243                 break;
244         case '#':
245                 /* its a comment, ignore it */
246                 break;
247         default:
248                 if (strchr(tag2, '=')) {
249                         process_set_var(tmpl, p);
250                 } else {
251                         /* an include file */
252                         tmpl->process(tmpl, p, recurse);
253                 }
254         }
255         free(tag2);
256
257         fflush(stdout);
258 }
259
260 /*
261   process a template file
262 */
263 static int process(struct template_state *tmpl, const char *filename, int recurse)
264 {
265         size_t size, remaining;
266         char *m, *mp;
267         char *p, *s;
268
269         setvbuf(stdout, NULL, _IONBF, 0);
270
271         mp = map_file(filename, &size);
272         if (!mp) {
273                 fprintf(stderr,"Failed to map %s (%s)\n", 
274                         filename, strerror(errno));
275                 return -1;
276         }
277
278         remaining = size;
279         m = mp;
280
281         if (strncmp(m, "#!", 2) == 0) {
282                 /* advance past shell script tag */
283                 m = strchr(m, '\n');
284                 if (!m) return 0;
285                 m++;
286                 remaining -= (m-mp);
287         }
288
289         /* tags look like {{ TAG }} 
290            where TAG can be of several forms
291         */
292         while (recurse && remaining && (p = strstr(m, START_TAG))) {
293                 const char *m0 = m;
294                 int len;
295                 char *contents, *s2, *m2;
296                 int depth=1;
297
298                 fwrite(m, 1, (p-m), stdout);
299                 m = p + strlen(START_TAG);
300                 m2 = m;
301                 while (depth) {
302                         s2 = strstr(m2, START_TAG);
303                         s = strstr(m2, END_TAG);
304                         if (!s) break;
305                         if (s2 && s2 < s) {
306                                 depth++;
307                                 m2 = s2 + strlen(START_TAG);
308                         } else {
309                                 depth--;
310                                 m2 = s + strlen(END_TAG);
311                         }
312                 }
313                 if (!s || depth) {
314                         fprintf(stderr,"No termination of tag!\n");
315                         return -1;
316                 }
317                 len = (s-m);
318                 while (len && isspace(m[len-1])) len--;
319                 contents = strndup(m, len);
320                 process_tag(tmpl, contents);
321                 free(contents);
322                 m = s + strlen(END_TAG);
323                 remaining -= (m - m0);
324         }
325
326         if (remaining > 0) {
327                 fwrite(m, 1, remaining, stdout);
328         }
329         fflush(stdout);
330         unmap_file(mp, size);
331         return 0;
332 }
333
334 /*
335   destroy a open template
336 */
337 static void destroy(struct template_state *tmpl)
338 {
339         while (tmpl->variables) {
340                 struct template_var *var = tmpl->variables;
341                 free(var->name);
342                 free(var->value);
343                 tmpl->variables = tmpl->variables->next;
344                 free(var);
345         }
346
347         memset(tmpl, 0, sizeof(tmpl));
348         free(tmpl);
349 }
350
351
352 static struct template_state template_base = {
353         /* methods */
354         process,
355         put,
356         get,
357         destroy
358         
359         /* rest are zero */
360 };
361
362 struct template_state *template_init(void)
363 {
364         struct template_state *tmpl;
365
366         tmpl = x_malloc(sizeof(*tmpl));
367         memcpy(tmpl, &template_base, sizeof(*tmpl));
368
369         return tmpl;
370 }