/*
 * Copyright 2001 Silicon Graphics, Inc. All rights reserved.
 */
#include <string.h>
#include <termio.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include "sial.h"

/* information necessary for a builtin function */
typedef struct builtin {

	var_t*v;		/* resulting variable declaration after parsing */
        bf_t *fp;		/* pointer to actual function */
	char *proto;		/* associated prototype_t*/
	struct builtin *next;	/* to chain them */

} builtin;

#define BT_EINVAL	1	/* Something is wrong and it's not ... */
value_t*
sial_exit(int v)
{
	/* Were we compiling ? */
	sial_parseback();

	/* we were running... exit () */
	sial_dojmp(J_EXIT, &v);

	/* NOT REACHED */
	return 0;
}

value_t*
sial_bexit(value_t *vv)
{
int v=sial_getval(vv);

	/* we're not going back to the he caller so free 
	   the input value_t */
	sial_freeval(vv);
	sial_exit(v);
	/* NOT REACHED */
	return 0;
}

#define MAXBYTES  4000
#define INCREMENT 16
value_t *
sial_getstr(value_t *vmadr)
{
ull madr=sial_getval(vmadr);
char *buf=sial_alloc(MAXBYTES+1);
char *p=buf;
value_t *v;

	/* sial as already verified that this is a V_REF */
	/* since this is reading from a unkown size pool
	   we have to do an exponential reduction on the number of bytes
	   read ... */
	buf[0]=0;
	while(1) {

		int i;

		if(!API_GETMEM(madr, p, INCREMENT)) break;

		/* have we found the '\0' yet ? */
		for(i=0;i<INCREMENT; i++) if(!p[i]) break;

		madr+=INCREMENT;
		p+=INCREMENT;
		if((p-buf) >= MAXBYTES) {
			buf[MAXBYTES]='\0';
			break;
		}

	}
	v=sial_setstrval(sial_newval(), buf);
	sial_free(buf);
	return v;
}

value_t *
sial_substr(value_t *vp, value_t *vi, value_t *vj)
{
char *p=sial_getptr(vp, char); 
ul i=sial_getval(vi); 
int l=strlen(p);
int j=(vj?sial_getval(vj):(l-i+1));
char *s;
value_t *v;

	if((i+j-1)>l || !i) {

		sial_error("Valid positions are [1..%d]\n", l);

	}

	s=sial_alloc(j+1);
	strncpy(s, p+i-1, j);
	s[j]='\0';
	v=sial_setstrval(sial_newval(), s);
	sial_free(s);
	return v;
}

value_t *
sial_getnstr(value_t* vmadr, value_t* vl)
{
ull madr=sial_getval(vmadr);
ul l=sial_getval(vl);
char *buf=sial_alloc(l+1);
value_t *v;

	if(!API_GETMEM(madr, buf, l)) buf[0]='\0';
	else buf[l]='\0';
	v=sial_setstrval(sial_newval(), buf);
	sial_free(buf);
	return v;
}

value_t *
sial_atoi(value_t *vs, value_t* vbase)
{
char *s=sial_getptr(vs, char);
int base=vbase ? sial_getval(vbase) : 0;

	strtoull(s, 0, (int) base);
	return sial_defbtypesize(sial_newval(), strtoull(s, 0, base), B_ULL);
}

value_t *
sial_itoa(value_t* vi)
{
ull i=sial_getval(vi);
char p[40];
	
	sprintf(p, "%llu", (unsigned long long)i);
	return sial_setstrval(sial_newval(), p);
}

value_t *
sial_strlen(value_t *vs)
{
char *s=sial_getptr(vs, char);
ull l;
	if(!s) l=0;
	else l=strlen(s);

	return sial_defbtype(sial_newval(), l);
}

value_t *
sial_getchar(void)
{
char c; 
struct termio tio, stio;
int in=fileno(stdin);

	if(ioctl(in, TCGETA, &tio)) c=255;
	else {
		stio=tio;
		tio.c_lflag &= ~(ICANON | ECHO);
		tio.c_iflag &= ~(ICRNL  | INLCR);
		tio.c_cc[VMIN] = 1;
		tio.c_cc[VTIME] = 0;
        	ioctl(in, TCSETA, &tio);
		c=getc(stdin);
		ioctl(in, TCSETA, &stio);
	}
	return sial_defbtype(sial_newval(), (ull)c);
}

value_t *
sial_gets(void)
{
char p[1024];
	
	if(!fgets(p, sizeof(p)-1, stdin)) p[0]='\0';
	else p[strlen(p)-1]='\0';
	return sial_setstrval(sial_newval(), p);
}

static builtin *bfuncs=0;

/*
	Check for the existance of a bt function
*/
void *
sial_chkbuiltin(char *name)
{
builtin *bf;

	for(bf=bfuncs; bf; bf=bf->next) {

		if(!strcmp(name, bf->v->name)) {

			return bf;
		}
	}
	return 0;
}

/*
	Remove a builtin.
	This is done when we 'unload' a *.so file.
*/
void
sial_rmbuiltin(var_t*v)
{
builtin *bf;
builtin *last=0;

	for(bf=bfuncs; bf; bf=bf->next) {

		if(!strcmp(v->name, bf->v->name)) {

			if(!last) bfuncs=bf->next;
			else {

				last->next=bf->next;
			}
			sial_free(bf->proto);
			sial_free(bf);
		}
		last=bf;
	}
}

/* 
	Install a new builtin function.
*/
var_t* 
sial_builtin(char *proto, bf_t* fp)
{
var_t*v;

	/* parse the prototype_t*/
	if((v=sial_parsexpr(proto))) {

		builtin *bt;
		int nargs=0;

		/* check name */
		if(!v->name || !v->name[0]) {

			sial_freevar(v);
			sial_msg("Syntax error: no function name specified [%s]\n", proto);
			return 0;
		}

		/* check for function with same name */
		if(sial_chkfname(v->name, 0)) {

			sial_freevar(v);
			sial_msg("Function already defined [%s]\n", proto);
			return 0;
		}

		if(v->dv->fargs) {

			var_t*vn=v->dv->fargs->next;

			while(vn!=v->dv->fargs) {

				nargs++;
				vn=vn->next;
			}
		}
		/* check number of args */
		if(nargs > BT_MAXARGS) {

			sial_freevar(v);
			sial_msg("Too many parameters to function (max=%d) [%s]\n", BT_MAXARGS, proto);
			return 0;
		}


		bt=sial_alloc(sizeof(builtin));
		bt->proto=sial_strdup(proto);
		bt->fp=fp;
		bt->v=v;
		bt->next=0;

		/* install it */
		if(!bfuncs) bfuncs=bt;
		else {
			builtin *btp;

			for(btp=bfuncs; ; btp=btp->next) if(!btp->next) break;
			btp->next=bt;
		}
		return v;
	}

	sial_msg("Builtin [%s] not loaded.", proto);

	return 0;
}

#define bcast(f) ((bf_t*)f)

static btspec_t sialbfuncs[] = {
	{ "unsigned long long atoi(string, ...)",bcast(sial_atoi)},
	{ "int exists(string)",			bcast(sial_exists)},
	{ "void exit(int)", 			bcast(sial_bexit)},
	{ "int getchar()",			bcast(sial_getchar)},
	{ "string gets()",			bcast(sial_gets)},
	{ "string getstr(char *)", 		bcast(sial_getstr)},
	{ "string getnstr(char *, int)", 	bcast(sial_getnstr)},
	{ "string itoa(int)",			bcast(sial_itoa)},
	{ "void printf(string, ...)",		bcast(sial_printf)},
	{ "void showtemp()",			bcast(sial_showtemp)},
	{ "void showaddr(char *)",		bcast(sial_showaddr)},
	{ "void memdebugon()",			bcast(sial_memdebugon)},
	{ "void memdebugoff()",			bcast(sial_memdebugoff)},
	{ "int sial_load(string)",		bcast(sial_bload)},
	{ "int sial_unload(string)",		bcast(sial_bunload)},
	{ "int depend(string)",			bcast(sial_bdepend)},
	{ "int strlen(string)",			bcast(sial_strlen)},
	{ "string sprintf(string, ...)",	bcast(sial_sprintf)},
	{ "string substr(string, int, ...)",	bcast(sial_substr)},
	{ "void prarr(string name, int i)",     bcast(sial_prarr)},
	{ "int member(void*, string name)",     bcast(sial_ismember)},
};


/*
	Install the sial builtins.
*/
void
sial_setbuiltins()
{
int i;

	for(i=0;i<sizeof(sialbfuncs)/sizeof(sialbfuncs[0]);i++) {

		(void)sial_builtin(sialbfuncs[i].proto, sialbfuncs[i].fp);
	}
}

value_t* 
sial_exebfunc(char *name, value_t **vals)
{
builtin *bf;
value_t *lvals[BT_MAXARGS*2]; /* use factor 2 for api where char * is 4 bytes */
value_t *v, *vr;


	if((bf=sial_chkbuiltin(name))) {

		int i=0, nargs=0;

		if(vals) for(i=0;vals[i];i++);

		memset(lvals, 0, sizeof(lvals));

		if(bf->v->dv->fargs) {

			var_t*vv=bf->v->dv->fargs->next;

			while(vv != bf->v->dv->fargs) {

				if(vv->name && !strcmp(vv->name, S_VARARG)) { 
					while(nargs<i) {

						lvals[nargs]=sial_cloneval(vals[nargs]);
						nargs++;
					}
					break; 
				}

				/* verify type compatibility and convert */
				if(vals[nargs]) {

					lvals[nargs]=sial_cloneval(vv->v);
					sial_chkandconvert(lvals[nargs], vals[nargs]);
				}
				nargs++;
				vv=vv->next;
			}
		}

		/* check parameters number */
		if(i<nargs) {

			sial_rerror(&bf->v->dv->pos, "Too few parameters to '%s'", bf->proto);

		} else if(i>nargs){

			sial_rerror(&bf->v->dv->pos, "Too many parameters to '%s'", bf->proto);

		}

		if(vals) {
			/* the actual call */
			v=(bf->fp) ( 
				lvals[0],  lvals[1],
				lvals[2],  lvals[3],
				lvals[4],  lvals[5],
				lvals[6],  lvals[7],
				lvals[8],  lvals[9],
				lvals[10], lvals[11],
				lvals[12], lvals[13],
				lvals[14], lvals[15],
				lvals[16], lvals[17],
				lvals[18], lvals[19]
				);
		} else {

			v=(bf->fp) ((value_t*)0);
		}

		while(i) {

			--i;
			sial_freeval(vals[i]);
			sial_freeval(lvals[i]);
		}

		/* make a copy of the return value_t info */
		vr=sial_cloneval(bf->v->v);
		sial_chkandconvert(vr, v);
		sial_freeval(v);

		return vr;
	}

	sial_error("Oops. sial_exebfunc()");
	return 0;
}
