/*
openisis - an open implementation of the CDS/ISIS database
Version 0.8.x (patchlevel see file Version)
Copyright (C) 2001-2003 by Erik Grziwotz, erik@openisis.org
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
see README for more information
EOH */
/*
$Id: luti.c,v 1.48 2003/06/23 14:47:35 kripke Exp $
utilities
*/
#include <string.h> /* strlen */
#include "openisis.h"
#include "luti.h"
/* ************************************************************
private types
*/
typedef struct {
char *name;
int id;
} LutiLTEnt;
typedef struct OpenIsisLT {
LutiLTEnt *arr;
int siz; /* malloc'ed */
int num; /* used */
} LutiLTNod;
#define LT_NUM 27
#define LT_SIZE (LT_NUM * sizeof (LutiLTNod))
#define LTIDX(name) ( \
('a' <= *(name) && 'z' >= *(name)) ? *(name) - 'a' : ( \
('A' <= *(name) && 'Z' >= *(name)) ? *(name) - 'A' : 26 \
))
#define LT_INCR 2
/* ************************************************************
private data
*/
/* ************************************************************
private functions
*/
/* ************************************************************
package data
*/
const char luti_hex[16] = {
'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'
};
/* ************************************************************
package functions
*/
void log_rec (
int level, Rec *rec, const char *msg, const char *delim
) {
Field *F;
int ch = 2;
if (0 > level) {
level = -level;
ch = 1;
}
if ((unsigned)level > log_lev) {
return;
}
if (msg && *msg) {
sMsg (ch, msg);
}
sMsg (ch, "%p", rec);
if (rec) {
if (! delim) {
delim = "; ";
}
sMsg (ch, " #f=%d", (int)rec->len);
for (F = rec->field; rec->len > F - rec->field; ++F) {
sMsg (ch, delim);
sMsg (ch, "%d", (int)F->tag);
sMsg (ch, " %.*s", (int)F->len, F->val);
}
}
sMsg (ch, "\n");
}
int lprint ( void *buf, int i )
{
char *c = buf;
if ( 0 > i ) {
*c++ = '-';
i = -i;
}
if ( ! i )
*c++ = '0';
else {
int dig = 0, j=i;
char *p;
for ( ; j; j /= 10 )
dig++;
p = c += dig;
for ( ; i; i /= 10 )
*--p = '0' + i % 10;
}
*c = 0;
return c - (char*)buf;
} /* lprint */
int a2il ( const char *p, int l, int *res )
{
const char *s = p;
const char *e = p + (0<=l ? l : (int)strlen(p));
char op = 0;
int ret = 0;
while ( p < e && ' ' >= (unsigned char)*p ) /* skip white */
p++;
if ( p < e )
switch ( *p ) {
case '-':
if ( p+1 == e ) ret = 1; /* so sole '-' is -1 */
case '~': /* sole ~ is ~0 */
case '+':
op = *p++;
}
if ( p < e && '0' == *p && ++p < e && 'x' == *p ) {
while ( ++p < e ) {
int v = *p;
if ( '0' <= v && v <= '9' )
v -= '0';
else if ( 'a' <= v && v <= 'f' )
v -= 'a'-10;
else if ( 'A' <= v && v <= 'F' )
v -= 'A'-10;
else
break;
ret = (ret<<4) + v;
}
} else {
while ( p < e && '0' <= *p && *p <= '9' )
ret = 10*ret + *p++ - '0';
}
if (0 != res)
switch (op) {
case '-': *res = -ret; break;
case '~': *res = ~ret; break;
default: *res = ret;
}
return p - s;
} /* a2il */
int a2i ( const char *p, int l ) {
int res;
a2il (p, l, &res);
return res;
}
int a2id ( const char *p, int l, int dflt ) {
int res;
if (0 > l) {
l = (int) strlen (p);
}
if (l != a2il (p, l, &res)) {
return dflt;
}
return res;
}
int i2a ( char *p, int i )
{
if ( i ) {
int min, dig = 0, j;
if ( 0 < i )
min = 0;
else {
min = 1;
*p++ = '-';
i = -i;
}
for ( j=i; j; j/=10 )
dig++;
for ( *(p+=dig)=0; i; i/=10 )
*--p = '0' + (i % 10);
return min+dig;
}
*p++ = '0';
*p = 0;
return 1;
} /* i2a */
int u2a ( char *p, unsigned u )
{
if ( u ) {
int dig = 0, j;
for ( j=u; j; j/=10 )
dig++;
for ( *(p+=dig)=0; u; u/=10 )
*--p = '0' + (u % 10);
return dig;
}
*p++ = '0';
*p = 0;
return 1;
} /* u2a */
LutiLT luti_ltnew () {
LutiLT res = (LutiLT) mAlloc (LT_SIZE);
return res;
}
void luti_ltdel (LutiLT lt) {
LutiLTNod *N;
LutiLTEnt *E;
if (lt) {
for (N = lt + LT_NUM; --N >= lt; ) {
if (N->arr) {
for (E = N->arr + N->num; --E >= N->arr; ) {
if (E->name) {
mFree (E->name);
}
}
mFree (N->arr);
}
}
mFree (lt);
}
}
void luti_ltadd (LutiLT lt, const char *name, int id) {
if (0 != lt) {
LutiLTNod *N;
LutiLTEnt *E;
int idx;
if (!name) {
name = "";
}
idx = LTIDX (name);
N = lt + idx;
if (N->siz == N->num) {
E = mAlloc ( (LT_INCR + N->siz) * sizeof (LutiLTEnt));
if (N->siz) {
memcpy (E, N->arr, N->siz * sizeof (LutiLTEnt));
mFree (N->arr);
}
N->arr = E;
N->siz += LT_INCR;
}
N->arr[N->num].name = mDup (name, -1);
N->arr[N->num].id = id;
++N->num;
}
}
int luti_ltget (LutiLT lt, const char *name) {
if (0 != lt) {
LutiLTNod *N;
LutiLTEnt *E;
int idx;
if (!name) {
name = "";
}
idx = LTIDX (name);
N = lt + idx;
if (N->num) {
for (E = N->arr + N->num; --E >= N->arr; ) {
if (! strcmp (E->name, name)) {
return E->id;
}
}
}
}
return -1;
}
void luti_ltrmv (LutiLT lt, const char *name) {
if (0 != lt) {
LutiLTNod *N;
LutiLTEnt *E;
int idx;
if (!name) {
name = "";
}
idx = LTIDX (name);
N = lt + idx;
if (N->num) {
for (E = N->arr + N->num; --E >= N->arr; ) {
if (! strcmp (E->name, name)) {
int len = N->num - (E - N->arr) - 1;
mFree (E->name);
if (len) {
memmove (E, E + 1, len * sizeof (LutiLTEnt));
}
--N->num;
return;
}
}
}
log_msg (LOG_WARN, "luti_ltrmv: no such entry <%s>", name);
}
}
static Rec *_unwrap ( Rec *env, int *pos, int tag, int dbid, int rdonly )
{
Field *F;
Rec *rec = 0;
int rlen, np;
if (! env ) {
return 0;
}
if (! pos) {
np = 0;
pos = &np;
}
F = rGet (env, tag, pos);
if (!F) {
return 0;
}
rlen = a2id (F->val, (int)F->len, -1);
if (0 > rlen) {
sMsg (LOG_ERROR,
"luti_unwrap: illegal reclen %d, pos %d, tag %d",
rlen, *pos, F->tag);
return 0;
}
++F;
if (env->len < *pos + rlen) {
sMsg (LOG_ERROR,
"luti_unwrap: illegal reclen %d, pos %d, tag %d, env %d",
rlen, *pos, F->tag, env->len);
return 0;
}
if (! rlen && ! rdonly) {
OPENISIS_RSPACE (rec, 0, 0);
if (0 == rec) {
return 0;
}
}
while (0 <= --rlen) {
if (! rdonly) {
RADD (rec, F->tag, F->val, F->len, !0);
if (0 == rec) {
return 0;
}
}
++F;
++(*pos);
}
if (rdonly) {
return (Rec*)1;
}
rec->dbid = dbid;
return rec;
}
Rec *luti_unwrap ( Rec *env, int *pos, int tag, int dbid)
{
return _unwrap (env, pos, tag, dbid, 0);
}
Rec* luti_append (Rec *tgt, Rec *src) {
Field *F, *E;
if (!src) {
return tgt;
}
E = src->field + src->len;
for (F = src->field; tgt && E > F; ++F) {
RADD (tgt, F->tag, F->val, F->len, !0);
}
return tgt;
}
Rec *luti_wrap (Rec *env, Rec *rec, int tag) {
if (!rec) {
return env;
}
env = rAddI (env, tag, rec->len, !0);
return luti_append (env, rec);
}
int luti_ptrincr (
void *start, int *num, int incr, int siz, int maxn
) {
char **base = (char**)start;
char *arr;
int oldn = *num;
if (0 < maxn && oldn >= maxn) {
return -1;
}
arr = mAlloc ((oldn + incr) * siz);
if (!arr) {
return -1;
}
if (oldn) {
memcpy (arr, *base, oldn * siz);
mFree (*base);
}
memset (arr + oldn * siz, 0, incr * siz);
*num += incr;
*base = arr;
return oldn;
}
void luti_free (void **arr, int num) {
if (arr) {
while (0 <= --num) {
if (arr[num]) {
mFree (arr[num]);
}
}
mFree (arr);
}
}
int luti_true (const char *str, int len) {
if (! str || ! *str) {
return -1;
}
if (0 > len) {
len = strlen (str);
}
if (1 == len) {
if ('0' == *str ||
'f' == *str ||
'F' == *str ||
'n' == *str ||
'N' == *str
) {
return 0;
}
if ('1' == *str ||
'y' == *str ||
'Y' == *str ||
't' == *str ||
'T' == *str
) {
return 1;
}
return -1;
}
if (! strncmp ("false", str, len) ||
! strncmp ("no", str, len)) {
return 0;
}
if (! strncmp ("true", str, len) ||
! strncmp ("yes", str, len)) {
return 1;
}
return -1;
}
/* ------------------------------------------------------------------------
* unwrap by path
*/
Fdt* luti_fdt_from_rec (Rec *rec) {
Db *dbh;
if (rec &&
0 <= rec->dbid &&
(dbh = nDbById (rec->dbid))
) {
return dbh->fdt;
}
return 0;
}
const char* luti_parse_path (
const char *path, const Fdt *fdt, Fd **fd, int *tag, int *occ
) {
Fd *_f = 0;
int _t = -1;
int _o = -1;
int num, qsub;
if (fd) {
*fd = 0;
}
if (tag) {
*tag = -1;
}
if (occ) {
*occ = -1;
}
if (! path) {
return 0;
}
if ('.' == *path
|| ('-' == *path && (path[1]<'0' || '9'<path[1]))
) { /* path or option style */
++path;
}
num = a2il (path, -1, &_t);
if (num) {
if (fdt) {
_f = fById (fdt, _t, 0);
}
}
else {
char name[OPENISIS_FD_NAMELEN];
if (! fdt) {
return 0;
}
for (num = 0;
path[num] && '[' != path[num] && '(' != path[num];
++num)
;
if (num > OPENISIS_FD_NAMELEN - 1) {
return 0;
}
strncpy (name, path, num) [num] = 0;
_f = fByName ( fdt, name );
if (! _f) {
return 0;
}
_t = _f->id;
}
path += num;
if (tag) {
*tag = _t;
}
if (fd) {
*fd = _f;
}
if ('[' != *path && '(' != *path) {
return path;
}
qsub = *path;
++path;
num = a2il (path, -1, &_o);
if (! num || 0 > _o) {
return 0;
}
path += num;
if ((']' != *path && '[' == qsub) || (')' != *path && '(' == qsub)) {
return 0;
}
if (occ) {
*occ = _o;
}
return path + 1;
}
Rec* luti_getembed ( Rec *env, const char *path, const Fdt *fdt)
{
Rec *res;
int tag, occ, idx, pos;
if ( ! env || ! path || ! *path) {
return 0;
}
if (! fdt) {
fdt = luti_fdt_from_rec (env);
}
path = luti_parse_path (path, fdt, 0, &tag, &occ);
if (! path) {
return 0;
}
res = 0;
idx = pos = 0;
while (1) {
res = _unwrap (env, &pos, tag, -1, idx < occ);
if (! res) {
return 0;
}
if (idx >= occ) {
break;
}
++idx;
}
if (*path) {
Rec *r2 = luti_getembed (res, path, 0);
mFree (res);
return r2;
}
return res;
}
int lhash ( const char *str, int len )
{
int h = 0;
while ( len-- )
h = 31*h + *str++;
return 0<=h ? h : -h;
}
/* ************************************************************
public functions
*/
char *toHtml ( const char *str, int len )
{
int l;
char *p, *e, *ret;
if ( ! str )
return 0;
if ( 0 > len )
len = strlen(str);
l = len + 1;
for ( e = (p=(char*)str) + len; p<e; p++ )
switch ( *p ) {
case '<': case '>': l += 3; break;
case '&': l += 4; break;
case '"': l += 5; break;
/* do not replace ' w/ ' -- many clients don't grok it */
}
ret = mAlloc( l );
if ( ! ret )
return 0;
for ( p=ret; str<e; str++ )
switch ( *str ) {
case '<': *p='&'; p[1]='l'; p[2]='t'; p[3]=';'; p+=4; break;
case '>': *p='&'; p[1]='g'; p[2]='t'; p[3]=';'; p+=4; break;
case '&': *p='&'; p[1]='a'; p[2]='m'; p[3]='p'; p[4]=';'; p+=5; break;
case '"': *p='&';p[1]='q';p[2]='u';p[3]='o';p[4]='t';p[5]=';';p+=6;break;
default: *p++ = *str;
}
*p = 0;
return ret;
} /* toHtml */
int utf8Chk ( void *mem, int len, int *tof )
{
unsigned char *c = (unsigned char *)mem;
int f = 0; /* expected followers */
int l = len;
if ( tof )
f = *tof;
for ( ; l--; c++ ) {
unsigned char u = *c;
int tofollow = u < 128 ? 0 : u < 192 ? -1 : u < 224 ? 1 : 2;
if ( f ) {
if ( 0 > tofollow ) { f--; continue; }
break;
}
if ( 0 <= tofollow ) { f = tofollow; continue; }
goto croak;
}
if ( tof )
*tof = f;
if ( ! f )
return 0;
if ( 0 > l && tof ) /* exhausted buf, want follower */
return 0;
sMsg( LOG_ERROR, "expected follower at %d got %x", len-l-1, (int)*c );
return len-l;
croak:
sMsg( LOG_ERROR, "expected no follower at %d got %x", len-l-1, (int)*c );
return len-l;
} /* utf8Chk */