/*
some simple html template routines
Copyright (C) Andrew Tridgell 2001
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
static void process_tag(struct template_state *tmpl, const char *tag);
/*
fetch a variable from the template variables list
*/
static struct template_var *find_var(struct template_state *tmpl, const char *name)
{
struct template_var *var;
for (var = tmpl->variables; var; var = var->next) {
if (strcmp(var->name, name) == 0) {
return var;
}
}
return NULL;
}
/*
add a name/value pair to the list of template variables
*/
static void put(struct template_state *tmpl, const char *name,
const char *value, template_fn fn)
{
struct template_var *var;
if (!name || !value) return;
var = find_var(tmpl, name);
if (var) {
free(var->value);
} else {
var = malloc(sizeof(*var));
var->next = tmpl->variables;
tmpl->variables = var;
var->name = strdup(name);
var->function = fn;
}
var->value = strdup(value);
}
/* fetch a variable from the template variables list */
static const char *get(struct template_state *tmpl, const char *name)
{
struct template_var *var;
var = find_var(tmpl, name);
if (var) return var->value;
return NULL;
}
/* process a template variable */
static void process_variable(struct template_state *tmpl, const char *tag)
{
const char *v = tmpl->get(tmpl, tag);
if (v) {
printf("%s", v);
}
}
/* process setting a template variable */
static void process_set_var(struct template_state *tmpl, char *tag)
{
char *p;
p = strchr(tag, '=');
if (!p) return;
*p++ = 0;
trim_tail(tag, " \t");
trim_tail(p, " \t");
while (isspace(*p)) p++;
tmpl->put(tmpl, tag, p, NULL);
}
/* process a template variable with quote escaping */
static void process_escaped_variable(struct template_state *tmpl, const char *tag)
{
const char *v = tmpl->get(tmpl, tag);
while (v && *v) {
if (*v == '"') {
printf(""");
} else {
fputc(*v, stdout);
}
v++;
}
}
/* process a inline shell script
all current template variables are passed to the script in the environment
recursively processes any tags in the output of the script
*/
static void process_shell(struct template_state *tmpl, const char *tag, int recurse)
{
pid_t pid;
fflush(stdout);
if ((pid=fork()) == 0) {
struct template_var *var;
FILE *p;
char *tag1=NULL;
int c, taglen=0;
for (var = tmpl->variables; var; var = var->next) {
setenv(var->name, var->value, 1);
}
dup2(1, 2);
p = popen(tag, "r");
if (!p) {
printf("Failed to execute script\n");
exit(1);
}
while ((c = fgetc(p)) != EOF) {
int depth;
tag1 = realloc(tag1, taglen+2);
tag1[taglen++] = c; tag1[taglen] = 0;
if (!recurse || strncmp(tag1, START_TAG, taglen) != 0) {
fputs(tag1, stdout);
free(tag1); tag1 = NULL; taglen = 0;
continue;
}
if (strcmp(tag1, START_TAG) != 0) continue;
depth = 1;
/* keep going until END_TAG */
while (depth && (c = fgetc(p)) != EOF) {
tag1 = realloc(tag1, taglen+2);
tag1[taglen++] = c; tag1[taglen] = 0;
if (strcmp(tag1+taglen-strlen(END_TAG),
END_TAG) == 0) {
depth--;
}
if (strcmp(tag1+taglen-strlen(START_TAG),
START_TAG) == 0) {
depth++;
}
}
if (depth) continue;
tag1[taglen-strlen(END_TAG)] = 0;
process_tag(tmpl, tag1+strlen(START_TAG));
free(tag1); tag1 = NULL; taglen = 0;
}
if (tag1) {
fputs(tag1, stdout);
free(tag1);
}
fflush(stdout);
fclose(p);
exit(1);
}
waitpid(pid, NULL, 0);
}
/* process a call into a C function setup with put_function() */
static void process_c_call(struct template_state *tmpl, const char *tag)
{
struct template_var *var;
char *name, *args, *p, *tok;
char **argv;
int argc=0;
if (!(p=strchr(tag, '('))) return;
name = strndup(tag, strcspn(tag, "("));
var = find_var(tmpl, name);
if (!var || !var->function) {
free(name);
return;
}
args = strndup(p+1, strcspn(p+1, ")"));
argv = malloc(sizeof(char *));
for (tok = strtok_r(args, ",", &p); tok; tok = strtok_r(NULL, ",", &p)) {
argv = realloc(argv, (argc+2)*sizeof(char *));
while (isspace(*tok)) tok++;
trim_tail(tok, " \t\r\n");
argv[argc++] = tok;
}
argv[argc] = NULL;
var->function(tmpl, name, var->value, argc, argv);
free(args);
free(name);
}
/*
process a single tag
*/
static void process_tag(struct template_state *tmpl, const char *tag)
{
char *tag2, *p;
int recurse = 1;
while (isspace(*tag)) tag++;
tag2 = strdup(tag);
trim_tail(tag2, " \t\n\r");
p = tag2;
if (*p == '-') {
p++;
recurse = 0;
}
switch (*p) {
case '$':
process_variable(tmpl, p+1);
break;
case '%':
process_escaped_variable(tmpl, p+1);
break;
case '!':
process_shell(tmpl, p+1, recurse);
break;
case '@':
process_c_call(tmpl, p+1);
break;
case '#':
/* its a comment, ignore it */
break;
default:
if (strchr(tag2, '=')) {
process_set_var(tmpl, p);
} else {
/* an include file */
tmpl->process(tmpl, p, recurse);
}
}
free(tag2);
fflush(stdout);
}
/*
process a template file
*/
static int process(struct template_state *tmpl, const char *filename, int recurse)
{
size_t size, remaining;
char *m, *mp;
char *p, *s;
setvbuf(stdout, NULL, _IONBF, 0);
mp = map_file(filename, &size);
if (!mp) {
fprintf(stderr,"Failed to map %s (%s)\n",
filename, strerror(errno));
return -1;
}
remaining = size;
m = mp;
if (strncmp(m, "#!", 2) == 0) {
/* advance past shell script tag */
m = strchr(m, '\n');
if (!m) return 0;
m++;
remaining -= (m-mp);
}
/* tags look like {{ TAG }}
where TAG can be of several forms
*/
while (recurse && remaining && (p = strstr(m, START_TAG))) {
const char *m0 = m;
int len;
char *contents, *s2, *m2;
int depth=1;
fwrite(m, 1, (p-m), stdout);
m = p + strlen(START_TAG);
m2 = m;
while (depth) {
s2 = strstr(m2, START_TAG);
s = strstr(m2, END_TAG);
if (!s) break;
if (s2 && s2 < s) {
depth++;
m2 = s2 + strlen(START_TAG);
} else {
depth--;
m2 = s + strlen(END_TAG);
}
}
if (!s || depth) {
fprintf(stderr,"No termination of tag!\n");
return -1;
}
len = (s-m);
while (len && isspace(m[len-1])) len--;
contents = strndup(m, len);
process_tag(tmpl, contents);
free(contents);
m = s + strlen(END_TAG);
remaining -= (m - m0);
}
if (remaining > 0) {
fwrite(m, 1, remaining, stdout);
}
fflush(stdout);
unmap_file(mp, size);
return 0;
}
/*
destroy a open template
*/
static void destroy(struct template_state *tmpl)
{
while (tmpl->variables) {
struct template_var *var = tmpl->variables;
free(var->name);
free(var->value);
tmpl->variables = tmpl->variables->next;
free(var);
}
memset(tmpl, 0, sizeof(tmpl));
free(tmpl);
}
static struct template_state template_base = {
/* methods */
process,
put,
get,
destroy
/* rest are zero */
};
struct template_state *template_init(void)
{
struct template_state *tmpl;
tmpl = x_malloc(sizeof(*tmpl));
memcpy(tmpl, &template_base, sizeof(*tmpl));
return tmpl;
}