/* 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));}}