Revision 238 (by dpavlin, 2004/03/08 17:46:16) tagging openisis 0.9.0
/*
	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/ &apos; -- 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 */