/* name: striteme.c
purpose: chtml_stritem_eval.c and support. A crufty but useful evaluator.
author: george carrette 19-DEC-96
$Id: striteme.c,v 1.8 1998/05/20 12:25:41 gjc Exp $
note: It was of course against my originally philosophy of CHTML to
put any programming constructs inside the HTML templates.
The templates were to be an object structure, not a control
structure. Any logical or control attributes of an application
could easily be implemented by the C programmer establishing
the appropriate string table values before invoking the
output formatter. But a quick hack to implement QUOTE and NOT
(one line of code each, in the subroutine chtml_stritem_eval)
and we are once again on the well worn path to hell.
Haeckel's 'biogenetic law' of recapitulation, ontogeny, ontology,
philology, you know the story, including the dead ends.
*/
#include
#include
#include
#include
#include "chtml.h"
/* Here we are faced with a typical c/c++-programmers conundrum, that there
is no standard protocol such that a function like chtml_stritem_eval can
inform its caller that the caller should deallocate the object
returned to it. Most of the time chtml_stritem_eval will be returning
data which is already allocated inside the string item table,
and other times it will return a constant string, but some times it
will need to allocate a buffer. So I'll give you gentle reader three
choices for how to deal with this buffer issue, by setting
CHTML_STRITEM_EVAL_BUFFER_CASE
1. The usual c-programmers implementation is chosen, which is
to use a global buffer which may be over-written on the next call.
Works ok for single thread uses of chtml.
2. A single buffer is allocated for each table, stored in the table.
This works fine for multi-threaded applications where access to
the table is otherwise interlocked or exclusive. It also has
the advantage of deallocating all storage once the table is freed.
3. As many buffers as are needed are allocated and stored in the table.
This would still require locking at the table level in multi-thread
enviroments, but it allows more flexibility in the use of the
return value of chtml_stritem_eval.
*/
#ifndef CHTML_STRITEM_EVAL_BUFFER_CASE
#define CHTML_STRITEM_EVAL_BUFFER_CASE 2
#endif
#if (CHTML_STRITEM_EVAL_BUFFER_CASE == 1)
static char *eval_buffer = NULL;
static long eval_buffer_size = 0;
static char *get_eval_buffer(long size,CHTML_STRITEM table)
{if (size <= eval_buffer_size)
return(eval_buffer);
if (eval_buffer)
free(eval_buffer);
eval_buffer_size = size;
eval_buffer = (char *) malloc(size);
return(eval_buffer);}
#endif
#if ((CHTML_STRITEM_EVAL_BUFFER_CASE == 2) || (CHTML_STRITEM_EVAL_BUFFER_CASE == 3))
static CHTML_STRITEM get_eval_scratch(CHTML_STRITEM table)
{CHTML_STRITEM scratch,l;
char *eval_scratch_key = "%%%EVAL_SCRATCH%%%";
if ((scratch = chtml_stritem_getem(eval_scratch_key,table)))
return(scratch);
if (!table)
return(NULL);
scratch = chtml_stritem_init(eval_scratch_key,NULL,NULL);
for(l=table;l->next;l = l->next);
l->next = scratch;
return(scratch);}
#endif
#if (CHTML_STRITEM_EVAL_BUFFER_CASE == 2)
struct eval_buffer_hdr
{long zero;
long size;
char data[1];};
static char *get_eval_buffer(long size,CHTML_STRITEM table)
{CHTML_STRITEM p;
struct chtml_str *s;
struct eval_buffer_hdr *hdr;
if (!(p = get_eval_scratch(table))) return(NULL);
if (!(s = p->first))
if (!(s = (struct chtml_str *) malloc(sizeof(struct chtml_str))))
return(NULL);
else
{s->next = NULL;
s->value = NULL;
p->first = p->last = p->current = s;}
if (!(hdr = (struct eval_buffer_hdr *) s->value))
/* I beleive a nice clean goto is better than cut/paste.
If C had lexical scoped procedures then I would avoid the goto */
goto newhdr;
if (size <= hdr->size)
return(&hdr->data[0]);
free(hdr);
s->value = NULL;
newhdr:
if (!(hdr = (struct eval_buffer_hdr *)
malloc(sizeof(struct eval_buffer_hdr) + size)))
return(NULL);
hdr->zero = 0;
hdr->size = size;
s->value = (char *) hdr;
return(&hdr->data[0]);}
#endif
#if (CHTML_STRITEM_EVAL_BUFFER_CASE == 3)
static char *get_eval_buffer(long size,CHTML_STRITEM table)
{CHTML_STRITEM p;
struct chtml_str *s;
if (!(p = get_eval_scratch(table))) return(NULL);
if (!(s = (struct chtml_str *) malloc(sizeof(struct chtml_str))))
return(NULL);
if (!(s->value = (char *) malloc(size+1)))
{free(s);
return(NULL);}
s->next = NULL;
if (p->last)
{p->last->next = s;
p->last = s;}
else
p->first = p->last = p->current = s;
/* make this look like a zero length string to avoid ugly debug print. */
s->value[0] = 0;
return(&s->value[1]);}
#endif
static char *ERR_NO_BUFFER_MSG = "CANNOT OBTAIN BUFFER";
static char *reterrmsg(CHTML_STRITEM env,char *fmt,...)
{char *buff;
va_list args;
if (!(buff = get_eval_buffer(1000,env)))
return(ERR_NO_BUFFER_MSG);
va_start(args,fmt);
vsprintf(buff,fmt,args);
va_end(args);
return(buff);}
#if 0
static int csv_length(char *str)
{char *start,*end;
int len;
if (str[0] == 0) return(0);
start = str;
len = 1;
while((end = strchr(start,',')))
{++len;
start = &end[1];}
return(len);}
#endif
static char *csv_element(char *str,int i,int *len)
{char *start,*end,*next;
int k;
start = str;
k = 0;
while(start)
{if ((end = strchr(start,',')))
next = &end[1];
else
{end = &start[strlen(start)];
next = NULL;}
if (k < i)
start = next;
else
{*len = end - start;
return(start);}
++k;}
return(NULL);}
static char *getsub(char *key,char *end,CHTML_STRITEM tbl)
{CHTML_STRITEM l;
char *retval = NULL,*pkey;
pkey = (key[0] == '.') ? &key[1] : key;
if ((l = chtml_stritem_getemsub(pkey,end,tbl)))
{if (l->current)
{retval = l->current->value;
if (l->current->next && (pkey == key))
l->current = l->current->next;}}
return(retval);}
static char *eval_buffer_return(char *start,char *end,CHTML_STRITEM table)
{char *tmp;
if (!(tmp = get_eval_buffer(((long)(end - start)) + 1,table)))
return(ERR_NO_BUFFER_MSG);
memcpy(tmp,start,end-start);
tmp[end-start] = 0;
return(tmp);}
static void insert(char **ptr,long *size,char *str)
{long n;
n = strlen(str);
if (n > *size) n = *size;
if (*size == 0) return;
memcpy(*ptr,str,n);
(*ptr)[n] = 0;
*ptr = &((*ptr)[n]);
*size = *size - n;}
static void debug_print_into_buffer(CHTML_STRITEM table,char *buff,long size)
{CHTML_STRITEM l;
long left;
struct chtml_str *s,*n;
char *ptr;
for(ptr=buff,left=size,l=table;l && (left > 0);l = l->next)
{insert(&ptr,&left,l->name);
insert(&ptr,&left," =");
for(s=l->first;s && (left > 0);s=n)
{n = s->next;
insert(&ptr,&left, " ");
insert(&ptr,&left,s->value);
if (n) insert(&ptr,&left,",");}
insert(&ptr,&left,"\n");}}
static char *eval_logic(char *exp,CHTML_STRITEM env,long (*oper)(long,long))
{char *end,*tmp,*arg1,*arg2;
long j,k,result;
int len;
end = strchr(exp,',');
if (!end) end = &exp[strlen(exp)];
if ((arg1 = getsub(exp,end,env)))
result = atol(arg1);
else
result = atol(exp);
for(j=1;(arg2 = csv_element(exp,j,&len));++j)
{if ((tmp = getsub(arg2,&arg2[len],env)))
k = atol(tmp);
else
k = atol(arg2);
result = (*oper)(result,k);}
if (!(tmp = get_eval_buffer(100,env)))
return(ERR_NO_BUFFER_MSG);
sprintf(tmp,"%ld",result);
return(tmp);}
static long logical_or(long x,long y)
{return(x | y);}
static long logical_and(long x,long y)
{return(x & y);}
static char *eval_if(char *exp,CHTML_STRITEM env)
{char *arg,*end,*tmp;
long j;
int len;
end = strchr(exp,',');
if (!end) return(exp);
if ((arg = getsub(exp,end,env)))
j = atol(arg);
else
j = 0;
if ((arg = csv_element(&end[1],j,&len)))
{if ((tmp = getsub(arg,&arg[len],env)))
return(tmp);
else
return(eval_buffer_return(arg,&arg[len],env));}
else
return(reterrmsg(env,"ERROR: fell off end of [%s]",exp));}
static char *eval_if_equal(char *exp,CHTML_STRITEM env)
{char *arg,*end,*arg1,*arg2;
long j;
int len,arg1len,arg2len;
end = strchr(exp,',');
if (!end) return(exp);
if ((arg1 = getsub(exp,end,env)))
arg1len = strlen(arg1);
else
{arg1 = exp;
arg1len = end - exp;}
if (!(arg = csv_element(exp,1,&len)))
return(exp);
if ((arg2 = getsub(arg,&arg[len],env)))
arg2len = strlen(arg2);
else
{arg2 = arg;
arg2len = len;}
if ((arg1len == arg2len) && (memcmp(arg1,arg2,arg1len) == 0))
j = 2;
else
j = 3;
if (!(arg = csv_element(exp,j,&len)))
return(exp);
if ((arg1 = getsub(arg,&arg[len],env)))
return(arg1);
return(eval_buffer_return(arg,&arg[len],env));}
static char *eval_atsign(char *exp,CHTML_STRITEM env)
{char *tmp,*arg,*fcn,*tmp2,*(*e_fcn)(char *,char *);
int len;
long j;
if (!(fcn = csv_element(exp,0,&len)))
return("NO FUNCTION SPECIFIED");
if (strncmp("default",fcn,len) == 0)
{if (!(arg = csv_element(exp,1,&len)))
return(reterrmsg(env,"ERROR: missing argument to %s",exp));
if ((tmp = getsub(arg,&arg[len],env)))
return(tmp);
if (!(tmp2 = csv_element(exp,2,&len)))
return("");
return(tmp2);}
else if (strncmp("debug_print",fcn,len) == 0)
{if (!(arg = csv_element(exp,1,&len)))
j = 1024;
else
j = atol(arg);
if (!(tmp = get_eval_buffer(j+1,env)))
return(ERR_NO_BUFFER_MSG);
debug_print_into_buffer(env,tmp,j);
return(tmp);}
else if ((strncmp("html",fcn,len) == 0) ||
(strncmp("url",fcn,len) == 0))
{if (strncmp("html",fcn,len) == 0)
e_fcn = chtml_html_encode;
else
e_fcn = chtml_url_encode;
/* TODO: use the cb versions of these procedures to avoid allocation */
if (!(arg = csv_element(exp,1,&len)))
return(reterrmsg(env,"ERROR: no html argument in [%s]",exp));
arg = chtml_stritem_get(arg,env);
if (!(tmp2 = (*e_fcn)(arg,NULL)))
return(ERR_NO_BUFFER_MSG);
if ((!(tmp = get_eval_buffer(strlen(tmp2)+1,env))))
{free(tmp2);
return(ERR_NO_BUFFER_MSG);}
strcpy(tmp,tmp2);
free(tmp2);
return(tmp);}
else if (strncmp("length",fcn,len) == 0)
{if (!(arg = csv_element(exp,1,&len)))
return(reterrmsg(env,"ERROR: no argument to length in [%s]",exp));
if ((!(tmp = get_eval_buffer(100,env))))
return(ERR_NO_BUFFER_MSG);
j = chtml_stritem_available_len(arg,env);
sprintf(tmp,"%ld",j);
return(tmp);}
else
return(reterrmsg(env,"ERROR: @function not implemented [%s]",exp));}
char *chtml_stritem_eval(char *exp,CHTML_STRITEM env)
{switch(*exp)
{case '\'':
return(&exp[1]);
case '!':
return((atol(chtml_stritem_get(&exp[1],env))) ? "0" : "1");
case '?':
return(eval_if(&exp[1],env));
case '@':
return(eval_atsign(&exp[1],env));
case '=':
return(eval_if_equal(&exp[1],env));
case '|':
return(eval_logic(&exp[1],env,logical_or));
case '&':
return(eval_logic(&exp[1],env,logical_and));
default:
return(chtml_stritem_get(exp,env));}}