/* name: chtml.c
purpose: C programmer interface to compiled chunk html templates.
author: george j. carrette,
$Id: chtml.c,v 1.18 1998/06/22 12:43:44 gjc Exp $
COPYRIGHT (c) 1995-1996 BY NEWS INTERNET SERVICES
ALL RIGHTS RESERVED.
Permission to use, copy, modify, distribute and sell this software
and its documentation for any purpose and without fee is hereby
granted, provided that the above copyright notice appear in all
copies and that both the copyright notice and this permission
notice appear in supporting documentation, and that the name of
News Internet Services not be used in advertising or publicity
pertaining to distribution of the software without specific,
written prior permission.
THIS SOFTWARE IS MADE AVAILABLE WITHOUT CHARGE, AS-IS. NEWS
INTERNET SERVICES DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS, IN NO EVENT SHALL NEWS INTERNET BE LIABLE FOR ANY SPECIAL,
INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include
#include
#include
#ifndef WIN32
#include
#endif
#include
#include
#include "chtml.h"
char *chtml_ident(void)
{return("$Id: chtml.c,v 1.18 1998/06/22 12:43:44 gjc Exp $");}
#ifndef WIN32
static char *find_filename(char *fname,char *path)
/* It is actually fairly suprising that there is no
common C runtime library procedure for this kind of
functionality. Another example of why Bill Gates rules? */
{static char try[PATH_MAX+1];
char *start,*end,*next;
if ((fname[0] == '/') || (fname[0] == '.'))
return(fname);
start = path;
while(*start)
{if ((end = strchr(start,':')))
next = end+1;
else
{end = &start[strlen(start)];
next = end;}
sprintf(try,"%.*s/%s",end-start,start,fname);
if (access(try,R_OK) == 0)
return(try);
start = next;}
return(fname);}
#endif
#ifdef WIN32
static char *find_filename(char *fname,char *path)
/* This would call for using the WIN32
SearchPath function. */
{return(fname);}
#endif
static struct chtml *new_chtml(int type,int length)
{struct chtml *p;
if ((p = (struct chtml *) malloc(sizeof(struct chtml))))
{p->type = type;
p->length = length;}
return(p);}
struct chtml *chtml_load(char *filename,char *path)
{FILE *f;
struct chtml *value;
char *fname;
fname = find_filename(filename,(path)?path:".:/usr/local/lib/html");
if (!(f = fopen(fname,"rb"))) return(NULL);
value = chtml_fload(f);
fclose(f);
return(value);}
static int chtml_comment(FILE *f)
{int c;
while((c = getc(f)) != EOF)
if (c == '\n') return(0);
return(1);}
static long get_long(FILE *f)
{long i;
fread(&i,sizeof(long),1,f);
return(i);}
static double get_double(FILE *f)
{double d;
fread(&d,sizeof(d),1,f);
return(d);}
static void get_stringz(char *buffer,size_t len,FILE *f)
{fread(buffer,len,1,f);
buffer[len] = 0;}
struct chtml *chtml_fload(FILE *f)
{int c;
struct chtml *p;
long j,len;
c = getc(f);
switch(c)
{case EOF:
return(NULL);
case '#':
if (chtml_comment(f))
return(NULL);
return(chtml_fload(f));
case 2:
if ((p = new_chtml(CHTML_NUMBER,0)))
p->storage_as.number.value = (int) get_double(f);
return(p);
case 3:
len = get_long(f);
if ((p = new_chtml(CHTML_SYMBOL,len)) &&
(p->storage_as.symbol.name = (char *) malloc(len+1)))
get_stringz(p->storage_as.symbol.name,len,f);
return(p);
case 13:
len = get_long(f);
if ((p = new_chtml(CHTML_STRING,len)) &&
(p->storage_as.string.value = (char *) malloc(len+1)))
get_stringz(p->storage_as.string.value,len,f);
return(p);
case 16:
len = get_long(f);
if ((p = new_chtml(CHTML_ARRAY,len)) &&
(p->storage_as.array.data = (struct chtml **)
malloc(len * sizeof(struct chtml *))))
for(j=0;jstorage_as.array.data[j] = chtml_fload(f);
return(p);
default:
return(new_chtml(CHTML_ILLEGAL,c));}}
void chtml_free(struct chtml *p)
{int j;
if (p == NULL) return;
switch(p->type)
{case CHTML_NUMBER:
break;
case CHTML_SYMBOL:
if (p->storage_as.symbol.name)
free(p->storage_as.symbol.name);
p->storage_as.symbol.name = NULL;
break;
case CHTML_STRING:
if (p->storage_as.string.value)
free(p->storage_as.string.value);
p->storage_as.string.value = NULL;
break;
case CHTML_ARRAY:
if (p->storage_as.array.data)
{for(j=0;jlength;++j)
{chtml_free(p->storage_as.array.data[j]);
p->storage_as.array.data[j] = NULL;}
free(p->storage_as.array.data);}
p->storage_as.array.data = NULL;
break;
default:
break;}
free(p);}
struct chtml *chtml_object(char *name,struct chtml *obj)
/* this is coded as a depth-first search, but no doubt
a breadth-first search would be more efficient in use. */
{int j,n;
struct chtml *value;
if (!obj) return(NULL);
switch(obj->type)
{case CHTML_NUMBER:
case CHTML_SYMBOL:
case CHTML_STRING:
return(NULL);
case CHTML_ARRAY:
if (((n = obj->length) < 1) || !obj->storage_as.array.data)
return(NULL);
if ((value = obj->storage_as.array.data[0]) &&
(value->type == CHTML_SYMBOL) &&
(strcmp(name,value->storage_as.symbol.name) == 0))
return(obj);
for(j=1;jstorage_as.array.data[j])))
return(value);
return(NULL);
default:
return(NULL);}}
static void debug_print_str(FILE *f,char *str)
{char *p,c;
if (!str)
fprintf(f,"NULL");
else
for(p=str;(c = *p);++p)
if (isprint(c))
putc(c,f);
else if (c == '\n')
fputs("\\n",f);
else
putc('.',f);}
static void chtml_debug_print_1(struct chtml *p,FILE *f,int level)
{int j;
for(j=0;jtype)
{case CHTML_NUMBER:
fprintf(f,"number(%d)\n",p->storage_as.number.value);
break;
case CHTML_SYMBOL:
debug_print_str(f,p->storage_as.symbol.name);
fprintf(f," (%d)\n",p->length);
break;
case CHTML_STRING:
fprintf(f,"\"");
debug_print_str(f,p->storage_as.string.value);
fprintf(f,"\" (%d)\n",p->length);
break;
case CHTML_ARRAY:
fprintf(f,"array(%d)\n",p->length);
if (p->storage_as.array.data)
for(j=0;jlength;++j)
chtml_debug_print_1(p->storage_as.array.data[j],f,level+1);
break;
default:
fprintf(f,"**BAD DATA**\n");}}
void chtml_debug_print(struct chtml *p,FILE *f)
{chtml_debug_print_1(p,(f) ? f : stdout,0);}
void chtml_do_write(char *(*get)(char *,void *),
void *tbl,
struct chtml *p,
void (*fcn)(char *,void *),
void *arg)
{int j,k,m,n;
struct chtml *repeat;
char *tmp;
switch((p) ? p->type : CHTML_ILLEGAL)
{case CHTML_SYMBOL:
if ((tmp = p->storage_as.symbol.name))
(*fcn)((*get)(tmp,tbl),arg);
break;
case CHTML_STRING:
if ((tmp = p->storage_as.string.value))
(*fcn)(tmp,arg);
break;
case CHTML_ARRAY:
if (((n = p->length) < 1) || !p->storage_as.array.data) return;
repeat = p->storage_as.array.data[0];
if (repeat->type == CHTML_NUMBER)
m = repeat->storage_as.number.value;
else if (repeat->type == CHTML_SYMBOL)
m = atoi((*get)(repeat->storage_as.symbol.name,tbl));
else
return;
for(j=0;jstorage_as.array.data[k],
fcn,
arg);
break;}}
long chtml_size_write(char *(*get)(char *,void *),void *arg,struct chtml *p)
{int j,k,m,n;
long total,inc;
struct chtml *repeat;
char *tmp;
switch((p) ? p->type : CHTML_ILLEGAL)
{case CHTML_SYMBOL:
if ((tmp = p->storage_as.symbol.name))
return(strlen((*get)(tmp,arg)));
else
return(-1);
case CHTML_STRING:
if ((tmp = p->storage_as.string.value))
return(strlen(tmp));
else
return(-1);
case CHTML_ARRAY:
n = p->length;
if ((n < 1) || !p->storage_as.array.data) return(-1);
repeat = p->storage_as.array.data[0];
if (repeat->type == CHTML_NUMBER)
m = repeat->storage_as.number.value;
else if (repeat->type == CHTML_SYMBOL)
m = atoi((*get)(repeat->storage_as.symbol.name,arg));
else
return(-1);
for(total=0,j=0;jstorage_as.array.data[k]);
if (inc < 0) return(-1);
total += inc;}
return(total);
default:
return(-1);}}
static long chtml_size_write_limit_1(char *(*get)(char *,void *),
void *arg,
struct chtml *p,
long *left)
{int j,k,m,n;
long total,inc;
struct chtml *repeat;
char *tmp;
switch((p) ? p->type : CHTML_ILLEGAL)
{case CHTML_SYMBOL:
if ((tmp = p->storage_as.symbol.name))
{inc = strlen((*get)(tmp,arg));
*left -= inc;
if (*left < 0) return(-1);
return(inc);}
else
return(-1);
case CHTML_STRING:
if ((tmp = p->storage_as.string.value))
{inc = strlen(tmp);
*left -= inc;
if (*left < 0) return(-1);
return(inc);}
else
return(-1);
case CHTML_ARRAY:
n = p->length;
if ((n < 1) || !p->storage_as.array.data) return(-1);
repeat = p->storage_as.array.data[0];
if (repeat->type == CHTML_NUMBER)
m = repeat->storage_as.number.value;
else if (repeat->type == CHTML_SYMBOL)
m = atoi((*get)(repeat->storage_as.symbol.name,arg));
else
return(-1);
for(total=0,j=0;jstorage_as.array.data[k],
left);
if (inc < 0) return(-1);
total += inc;}
return(total);
default:
return(-1);}}
long chtml_size_write_limit(char *(*get)(char *,void *),
void *arg,
struct chtml *p,
long lim)
{long acc;
if (lim < 0)
return(chtml_size_write(get,arg,p));
acc = lim;
return(chtml_size_write_limit_1(get,arg,p,&acc));}
static char *mallocator(size_t len,void *cb_arg)
{return(malloc(len));}
void chtml_memfree(void *ptr)
/* Use this on the return value of url encode, decode, etc.
The Microsoft Debug Heap runtime library can complain
under some switch settings about ptrs which had been created by
malloc in one DLL and called free in a main program.
Hence this special function. In classic C programming style,
made dogma by C++ it is in fact almost always the case that each
new time will have its own new/free functions anyway */
{free(ptr);}
char *chtml_url_encode(char *str,char *aend)
{return(chtml_url_encode_cb(str,aend,mallocator,NULL));}
char *chtml_url_encode_cb(char *str,char *aend,
char *(*fcn)(size_t,void *),void *cb_arg)
{int spaces=0,specials=0,regulars=0,c;
char *p,*r,*end = (aend) ? aend : &str[strlen(str)],*out;
for(p=str,spaces=0,specials=0,regulars=0;p < end;++p)
{c = *p;
if (c == ' ') ++spaces;
else if (!(isalnum(c) || strchr("*-._@",c))) ++specials;
else ++regulars;}
if (!(out = (char *) (*fcn)(spaces + regulars + specials * 3 + 1,cb_arg)))
return(NULL);
for(p=str,r=out;p < end;++p)
{c = *p;
if (c == ' ')
*r++ = '+';
else if (!(isalnum(c) || strchr("*-._@",c)))
{sprintf(r,"%%%02X",c & 0xFF);
r += 3;}
else
*r++ = c;}
*r = 0;
return(out);}
char *chtml_url_decode(char *str,char *aend)
{return(chtml_url_decode_cb(str,aend,mallocator,NULL));}
char *chtml_url_decode_cb(char *str,char *aend,
char *(*fcn)(size_t,void *),void *cb_arg)
{int pluses=0,specials=0,regulars=0,c,j;
char *p,*r,*end = (aend) ? aend : &str[strlen(str)],*out;
for(p=str,pluses=0,specials=0,regulars=0;p < end;++p)
{c = *p;
if (c == '+') ++pluses;
else if (c == '%')
{if (isxdigit(p[1]) && isxdigit(p[2]))
++specials;
else
return(NULL);}
else
++regulars;}
if (!(out = (char *) (*fcn)(regulars + pluses + specials + 1,cb_arg)))
return(NULL);
for(p=str,r=out;p < end;++p)
{c = *p;
if (c == '+')
*r++ = ' ';
else if (c == '%')
{for(*r = 0,j=1;j<3;++j)
*r = *r * 16 + ((isdigit(p[j]))
? (p[j] - '0')
: (toupper(p[j]) - 'A' + 10));
p += 2;
++r;}
else
*r++ = c;}
*r = 0;
return(out);}
char *chtml_html_encode(char *str,char *aend)
{return(chtml_html_encode_cb(str,aend,mallocator,NULL));}
char *chtml_html_encode_cb(char *str,char *aend,
char *(*fcn)(size_t,void *),void *cb_arg)
{long j,n,m;
char *ptr,*end = (aend) ? aend : &str[strlen(str)],*out;
n = end - str;
for(j=0,m=0;j < n; ++j)
switch(str[j])
{case '>':
case '<':
m += 4;
break;
case '&':
m += 5;
break;
case '"':
m += 6;
break;
default:
++m;}
if (!(out = (char *)(*fcn)(m+1,cb_arg)))
return(NULL);
for(j=0,ptr=out;j < n; ++j)
switch(str[j])
{case '>':
strcpy(ptr,">");
ptr += 4;
break;
case '<':
strcpy(ptr,"<");
ptr += 4;
break;
case '&':
strcpy(ptr,"&");
ptr += 5;
break;
case '"':
strcpy(ptr,""");
ptr += 6;
break;
default:
*ptr++ = str[j];}
*ptr = 0;
return(out);}