/*	
**	mod_std.c	-  Standard w3-msql library module
**
**
** Copyright (c) 1995-96  Hughes Technologies Pty Ltd
**
** Permission to use, copy, and distribute for non-commercial purposes,
** is hereby granted without fee, providing that the above copyright
** notice appear in all copies and that both the copyright notice and this
** permission notice appear in supporting documentation.
**
** The software may be modified for your own purposes, but modified versions
** may not be distributed.
**
** This software is provided "as is" without any expressed or implied warranty.
**
*/


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <pwd.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/signal.h>
#include <fcntl.h>

#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/socket.h>

#include <common/portability.h>
#include "lite.h"
#include "regexp/regexp.h"

#include <time.h>
#ifdef HAVE_SYS_TIMEB_H
#  include <sys/timeb.h>
#endif

typedef struct	fdtab_s {
	int	fd,
		type,
		mark,
		len;
	char	buf[4096];
	FILE	*fp;
	struct	fdtab_s *next;
} fdtab_t;

#define	NORMAL	1
#define	PIPE	2
#define	FIFO	3


fdtab_t	*fdtab = NULL;
extern	sym_t	*externReturn;

#ifndef HAVE_SYS_ERRLIST
extern	char	*sys_errlist[];
#endif

char *sigmap[NSIG];
void	checkContentType();
static	char	errBuf[160];





/****************************************************************************
** 	_formatPrintf
**
**	Purpose	: The engine behind the *printf routines
**	Args	: usual printf args in a param list
**	Returns	: char * output
**	Notes	: uses static buf for output
*/

#define FMT_TYPE_ERR "Bad value type for field %d in printf"

static char *formatPrintf(params)
	plist_t	*params;
{
	plist_t	*cur;
	char	*fmt,
		*icp,
		*ocp,
		rJust,
		zeroPad,
		*tbp,
		tmpBuf[10 * 1024],
		*cp;
	static	char	buf[10 * 1024];
	int	fieldWidth,
		dataWidth,
		widthDiff,
		precision,
		fieldCount,
		foundPeriod = 0;
	

	if (!params)
		return(NULL);
	(void)bzero(buf,sizeof(buf));

	fmt = params->sym->val;
	if (!fmt)
		return(NULL);
	cur = params->next;
	icp = fmt;
	ocp = buf;

	while(*icp)
	{
		switch(*icp)
		{
			case '%':
				icp++;
				rJust = 1;
				if (*icp == '-')
				{
					rJust = 0;
					icp++;
				}
				zeroPad=0;
				if (*icp == '0')
				{
					zeroPad = 1;
					icp++;
				}
	
				fieldWidth = 0;
				dataWidth = 0;
				precision = -1;
				while(isdigit(*icp))
				{
					fieldWidth *= 10;
					fieldWidth += *icp - '0';
					icp++;
				}

				if (*icp == '.')
				{
					icp++;
					precision = 0;
					while(isdigit(*icp))
					{
						precision *= 10;
						precision += *icp - '0';
						icp++;
					}
				}

				bzero(tmpBuf,sizeof(tmpBuf));
				tbp = tmpBuf;
				switch(*icp)
				{
					char	*tcp;

					case 's':
						fieldCount++;
						if (!cur)
						{
							*tbp++ = 's';
							break;
						}
						if (cur->sym->type != TYPE_CHAR)
						{
							sprintf(errBuf,
								FMT_TYPE_ERR,
								fieldCount);
							runError(errBuf);
							exit(1);
						}
						tcp = symUnpackSymbol(cur->sym);
						strcpy(tbp,tcp);
						free(tcp);
						cur = cur->next;
						break;

					case 'd':
						fieldCount++;
						if (!cur)
						{
							*tbp++ = 'd';
							break;
						}
						if (cur->sym->type!=TYPE_INT&&
						    cur->sym->type!=TYPE_UINT)
						{
							sprintf(errBuf,
								FMT_TYPE_ERR,
								fieldCount);
							runError(errBuf);
							exit(1);
						}
						tcp = symUnpackSymbol(cur->sym);
						cp = (char *)index(tcp,'.');
						if (cp)
							*cp = 0;
						strcpy(tbp,tcp);
						free(tcp);
						cur = cur->next;
						break;

					case 'u':
						fieldCount++;
						if (!cur)
						{
							*tbp++ = 'u';
							break;
						}
						if (cur->sym->type!=TYPE_INT&&
						    cur->sym->type!=TYPE_UINT)
						{
							sprintf(errBuf,
								FMT_TYPE_ERR,
								fieldCount);
							runError(errBuf);
							exit(1);
						}
						sprintf(tbp,"%u",
						    (int)*(int*)cur->sym->val);
						cur = cur->next;
						break;

					case 'f':
						fieldCount++;
						if (precision < 0)
							precision = 6;
						if (!cur)
						{
							*tbp++ = 'f';
							break;
						}
						if (cur->sym->type != TYPE_REAL)
						{
							sprintf(errBuf,
								FMT_TYPE_ERR,
								fieldCount);
							runError(errBuf);
							exit(1);
						}
						tcp = symUnpackSymbol(cur->sym);
						cp = (char *)index(tcp,'.');
						if (cp)
						{
							cp++;
							foundPeriod=1;
							while (precision&&*cp)
							{
								cp++;
								precision--;
							}
							if (cp)
								*cp = 0;
						}
						strcpy(tbp,tcp);
						free(tcp);
						if (!foundPeriod)
						{
							strcat(tbp,".");
							if (precision <1)
								strcat(tbp,"0");
						}
						while (precision > 0)
						{
							strcat(tbp, "0");
							precision--;
						}
						cur=cur->next;
						break;

					default:
						*tbp++ = *icp;
				}
				
				dataWidth = strlen(tmpBuf);
				widthDiff = fieldWidth - dataWidth;
				if (widthDiff < 1)
				{
					strcat(ocp,tmpBuf);
					while(*ocp)
						ocp++;
					break;
				}

				if(rJust || zeroPad)
				{
					while(widthDiff)
					{
						if (zeroPad)
							*ocp++ = '0';
						else
							*ocp++ = ' ';
						widthDiff--;
					}
				}
				strcat(ocp,tmpBuf);
				while(*ocp)
					ocp++;
				if(!rJust)
				{
					while(widthDiff)
					{
						*ocp++ = ' ';
						widthDiff--;
					}
				}
				break;

/*
			case '\\':
				icp++;
				switch(*icp)
				{
					case 'n':
						*ocp = '\n';
						break;

					case 't':
						*ocp = '\t';
						break;

					case 'r':
						*ocp = '\r';
						break;

					default:
						*ocp = *icp;
				}
				ocp++;
				break;
*/

			default:
				*ocp++ = *icp;
		}
		icp++;

	}
	return(buf);
}





/****************************************************************************
** 	_doPrintf
**
**	Purpose	: printf front-end
**	Args	: printf args as param list (passed on)
**	Returns	: nothing
**	Notes	: 
*/

void doPrintf(params)
	plist_t	*params;
{
	char	*outbuf;

	checkContentType();
	outbuf = formatPrintf(params);
	(void)write(fileno(stdout),outbuf,strlen(outbuf));
}




/****************************************************************************
** 	_doFprintf
**
**	Purpose	: printf front-end for file IO
**	Args	: printf args as param list (passed on)
**	Returns	: nothing
**	Notes	: 
*/

void doFprintf(params)
	plist_t	*params;
{
	int	fd;
	char	*outbuf;
	fdtab_t	*cur;

	fd = (int) * (int *)params->sym->val;
	cur = fdtab;
	while(cur)
	{
		if(cur->fd == fd)
			break;
		cur = cur->next;
	}	
	if (!cur)
	{
		sprintf(errBuf,"Bad file descriptor (%d).",fd);
		runError(errBuf);
		exit(1);
	}
	outbuf = formatPrintf(params->next);
	(void)write(fd,outbuf,strlen(outbuf));
}


void doEcho(params)
	plist_t	*params;
{
	checkContentType();
	printf("%s",params->sym->val);
	fflush(stdout);
}



void initStandardModule()
{
	fdtab_t	*new;
	int	fd;

	for (fd = 0; fd < 3; fd++)
	{
		new = (fdtab_t *)malloc(sizeof(fdtab_t));
        	new->fd = fd;
        	new->mark = new->len = 0;
		new->type = NORMAL;
        	new->next = fdtab;
        	fdtab = new;
	}
	symCreateCharMacro("LITE_BRK",LITE_BRK);
}


/****************************************************************************
** 	_doOpen
**
**	Purpose	: Open the specified file/pipe/process
**	Args	: path,mode
**	Returns	: nothing
**	Notes	: Updates internal file descriptor table
*/

void doOpen(params)
	plist_t	*params;
{
	char	*path,
		*mode;
	int	fd;
	FILE	*pfile;
	fdtab_t	*new;

	path = (char *)params->sym->val;
	mode = params->next->sym->val;

	new = (fdtab_t *)malloc(sizeof(fdtab_t));
	switch(*mode)
	{
		case '>':
			switch(*(mode+1))
			{
				case 'P':	/* Write named pipe */
					fd = mknod(path,S_IFIFO | 0777, 0);
					if (fd < 0)
					{
						setError(sys_errlist[errno]);
						externReturn = 
							createIntSymbol(-1);
						return;
					}
					fd = open(path,O_WRONLY|O_CREAT, 0777);
					new->type = FIFO;
					break;

				case '\0':	/* Write mode */
					fd = open(path,O_WRONLY|O_CREAT|O_TRUNC,
						0777);
					if (fd < 0)
					{
						setError(sys_errlist[errno]);
						externReturn = 
							createIntSymbol(-1);
						return;
					}
					new->type = NORMAL;
					break;
				case '>': 	/* Append mode */
					fd = open(path,O_WRONLY | O_APPEND
						| O_CREAT, 0777);
					if (fd < 0)
					{
						setError(sys_errlist[errno]);
						externReturn = 
							createIntSymbol(-1);
						return;
					}
					new->type = NORMAL;
					break;
				case '<':	/* Read write mode */
					fd = open(path,O_RDWR | O_CREAT, 0777);
					if (fd < 0)
					{
						setError(sys_errlist[errno]);
						externReturn = 
							createIntSymbol(-1);
						return;
					}
					new->type = NORMAL;
					break;
				case '|':	/* Write pipe mode */ 
					pfile = popen(path,"w");
					if (pfile)
						fd = fileno(pfile);
					else
					{
						setError(sys_errlist[errno]);
						externReturn = 
							createIntSymbol(-1);
						return;
					}
					new->type = PIPE;
					new->fp = pfile;
					break;
				default:
					sprintf(errBuf,"Bad file open mode '%s'"
						, mode);
					runError(errBuf);
					exit(1);
					break;
			}
			break;
		case '<':
			switch(*(mode+1))
			{
				case 'P':	/* Write named pipe */
					fd = mknod(path,S_IFIFO, 0);
					if (fd < 0)
					{
						setError(sys_errlist[errno]);
						externReturn = 
							createIntSymbol(-1);
						return;
					}
					fd = open(path,O_RDONLY);
					new->type = FIFO;
					break;

				case '\0':	/* Read mode */
					fd = open(path,O_RDONLY);
					if (fd < 0)
					{
						setError(sys_errlist[errno]);
						externReturn = 
							createIntSymbol(-1);
						return;
					}
					new->type = NORMAL;
					break;
				case '|': 	/* Read pipe mode */
					pfile = popen(path,"r");
					if (pfile)
						fd = fileno(pfile);
					else
					{
						setError(sys_errlist[errno]);
						externReturn = 
							createIntSymbol(-1);
						return;
					}
					new->type = PIPE;
					new->fp = pfile;
					break;
				case '>':	/* Read write mode */
					fd = open(path,O_RDWR|O_CREAT,0777);
					if (fd < 0)
					{
						setError(sys_errlist[errno]);
						externReturn = 
							createIntSymbol(-1);
						return;
					}
					new->type = NORMAL;
					break;
				default:
					sprintf(errBuf,"Bad file open mode '%s'"
						, mode);
					runError(errBuf);
					exit(1);
					break;
			}
			break;
		default:
			sprintf(errBuf,"Bad file open mode '%s'", mode);
			runError(errBuf);
			exit(1);
			break;
	}
	new->fd = fd;
	new->mark = new->len = 0;
	new->next = fdtab;
	fdtab = new;
	externReturn = createIntSymbol(fd);
}




/****************************************************************************
** 	_doClose
**
**	Purpose	: Close an fd and free it's internal struct
**	Args	: fd
**	Returns	: nothing
**	Notes	: 
*/

void doClose(params)
	plist_t	*params;
{
	fdtab_t	*cur,
		*prev = NULL;
	int	fd;

	fd = (int) * (int *)params->sym->val;
	cur = fdtab;
	while(cur)
	{
		if(cur->fd == fd)
			break;
		prev = cur;
		cur = cur->next;
	}	
	if (!cur)
	{
		sprintf(errBuf,"Bad file descriptor (%d).",fd);
		runError(errBuf);
		exit(1);
	}
	switch(cur->type)
	{
		case NORMAL:
		case FIFO:
			(void)close(cur->fd);
			break;
		case PIPE:
			(void)pclose(cur->fp);
			break;
	}
	if (!prev)
	{
		fdtab = fdtab->next;
	}
	else
	{
		prev->next = cur->next;
	}
	free(cur);
}







/****************************************************************************
** 	_bufRead
**
**	Purpose	: The file IO engine
**	Args	: Read buffered IO for the given fd
**	Returns	: a single character
**	Notes	: Buffer maintained
*/

char *bufRead(tab)
	fdtab_t	*tab;
{
	static	char	cb;

	if (tab->mark == tab->len)
	{
		tab->len = read(tab->fd,tab->buf,4096);
		if(tab->len < 1)
		{
			tab->mark = tab->len = 0;
			return(NULL);
		}
		tab->mark = 0;
	}
	cb = *(tab->buf + tab->mark++);
	return(&cb);
}







/****************************************************************************
** 	_tokRead
**
**	Purpose	: Toekn based file IO engine
**	Args	: fd, token
**	Returns	: read data
**	Notes	: 
*/

char *tokRead(fd,tok)
	int	fd;
	char	*tok;
{
	int	count,
		bail;
	static	char buf[4096];
	char	*newChar;
	register char	*cp,	
			*tp;
	fdtab_t	*cur;

	cur = fdtab;
	while(cur)
	{
		if(cur->fd == fd)
			break;
		cur = cur->next;
	}	
	if (!cur)
	{
		sprintf(errBuf,"Bad file descriptor (%d).",fd);
		runError(errBuf);
		exit(1);
	}

	(void)bzero(buf,sizeof(buf));
	cp = buf;
	count = 0;
	bail = 0;
	while(count < sizeof(buf))
	{
		newChar = bufRead(cur);
		if (newChar)
		{
			count++;
			tp = tok;
			while(*tp)
			{
				if (*newChar == *tp)
				{
					bail = 1;
					break;
				}
				tp++;
			}
			*cp++ = *newChar;
		}
		else
			bail = 1;
		if (bail)
			break;
	}
	if (!count)
	{
		return(NULL);
	}
	else
		return(buf);
}






/****************************************************************************
** 	_doReadLn
**
**	Purpose	: Read a line of text from the given fd
**	Args	: fd and buflen as param list
**	Returns	: nothing
**	Notes	: Uses tokread for the dirty work
*/

void doReadLn(params)
	plist_t *params;
{
	int	fd;
	char	*buf;

	fd = (int) * (int *)params->sym->val;

	buf = tokRead(fd,"\n");
	if (!buf)
	{
		externReturn = createCharSymbol("");
	}
	else
	{
		externReturn = createCharSymbol(buf);
	}
}
	





/****************************************************************************
** 	_doReadTok
**
**	Purpose	: Read from a file till a token is found
**	Args	: fd, token (as param list)
**	Returns	: nothing
**	Notes	: uses tokRead 
*/

void doReadTok(params)
	plist_t *params;
{
	int	fd;
	char	*tok,
		*buf;

	fd = (int) * (int *)params->sym->val;
	tok = params->next->sym->val;
	tok = tok;

	buf = tokRead(fd,tok);
	if (!buf)
	{
		 externReturn = createCharSymbol("");
	}
	else
	{
		 externReturn = createCharSymbol(buf);
	}
}
	




/****************************************************************************
** 	_doRead
**
**	Purpose	: Straight read from the fd
**	Args	: fd and buf length
**	Returns	: nothing
**	Notes	: Still uses internal buffering
*/

void doRead(params)
	plist_t *params;
{
	int	fd,
		bufLen,
		count;
	char	*buf,
		*newChar,
		*cp;
	fdtab_t	*cur;

	fd = (int) * (int *)params->sym->val;
	bufLen = (int) * (int *)params->next->sym->val;

	cur = fdtab;
	while(cur)
	{
		if(cur->fd == fd)
			break;
		cur = cur->next;
	}	
	if (!cur)
	{
		sprintf(errBuf,"Bad file descriptor (%d).",fd);
		runError(errBuf);
		exit(1);
	}

	buf = (char *)malloc(bufLen+1);
	(void)bzero(buf,bufLen+1);
	cp = buf;
	count = 0;
	while(count < bufLen)
	{
		newChar = bufRead(cur);
		if (newChar)
			*cp++ = *newChar;
		else
			break;
		count++;
	}

	if (!count)
		externReturn = createCharSymbol("");
	else
		externReturn = createCharSymbol(buf);
}







/****************************************************************************
** 	_doExit
**
**	Purpose	: Interface to exit(2)
**	Args	: exit code
**	Returns	: Nothing
**	Notes	: 
*/

void doExit(params)
	plist_t	*params;
{
	sendFooter();
	exit((int) *(int *)params->sym->val);
}





/****************************************************************************
** 	_doUnlink
**
**	Purpose	: Interface to unlink(2)
**	Args	: path
**	Returns	: nothing
**	Notes	: 
*/

void doUnlink(params)
	plist_t *params;
{
	int 	res;

	res = unlink(params->sym->val);
	if (res < 0)
		setError(sys_errlist[errno]);
	externReturn = createIntSymbol(res);
}






/****************************************************************************
** 	_doCrypt
**
**	Purpose	: Interface to crypt()
**	Args	: value and salt
**	Returns	: crypt value
**	Notes	: 
*/

char *fcrypt();

void doCrypt(params)
	plist_t *params;
{
	char 	*res,
		*pw;

	pw = params->sym->val;
	res = (char *)fcrypt(pw,params->next->sym->val);
	externReturn = createCharSymbol(res);
}





/****************************************************************************
** 	_doUmask
**
**	Purpose	: Interface to umask(2)
**	Args	: mask
**	Returns	: nothing
**	Notes	: 
*/

void doUmask(params)
	plist_t	*params;
{
	int	mask;

	mask = (int) * (int *)params->sym->val;
	mask = umask(mask);
        externReturn = createIntSymbol(mask);

}



/****************************************************************************
** 	_doChmod
**
**	Purpose	: Interface to chmod(2)
**	Args	: mode
**	Returns	: nothing
**	Notes	: 
*/

void doChmod(params)
	plist_t	*params;
{
	int	mode,
		result;
	char	*path;

	path = params->sym->val;
	mode = (int) * (int *)params->next->sym->val;
	result = chmod(path,mode);
        externReturn = createIntSymbol(result);
}







/****************************************************************************
** 	_doSleep
**
**	Purpose	: Interface to sleep(2)
**	Args	: sleep time
**	Returns	: Nothing
**	Notes	: 
*/

void doSleep(params)
	plist_t	*params;
{
	int	time;

	time = (int) * (int *)params->sym->val;
	sleep(time);
}




void doSystem(params)
	plist_t	*params;
{
	char 	*command;
	int	res;
	sym_t	*sym;

	command = params->sym->val;
	res = system(command);
	sym = createIntSymbol(res);
	externReturn = sym;
}


/****************************************************************************
** 	_doGetpid
**
**	Purpose	: Interface to getpid(2)
**	Args	: Nothing
**	Returns	: Nothing
**	Notes	: 
*/


void doGetpid(params)
	plist_t	*params;
{
	int	pid;

	pid = getpid();
	externReturn = createIntSymbol(pid);
}




/****************************************************************************
** 	_doGetppid
**
**	Purpose	: Interface to getppid(2)
**	Args	: Nothing
**	Returns	: Nothing
**	Notes	: 
*/

void doGetppid(params)
	plist_t	*params;
{
	int	pid;

	pid = getppid();
	externReturn = createIntSymbol(pid);
}





/****************************************************************************
** 	_doChdir
**
**	Purpose	: Interface to chdir()
**	Args	: 
**	Returns	: 
**	Notes	: 
*/


void doChdir(params)
	plist_t	*params;
{
	int	result;
	char	*path;

	path = params->sym->val;
	result = chdir(path);
	if (result < 0)
		setError(sys_errlist[errno]);
        externReturn = createIntSymbol(result);
}


/****************************************************************************
** 	_doMkdir
**
**	Purpose	: Interface to mkdir()
**	Args	: 
**	Returns	: 
**	Notes	: 
*/


void doMkdir(params)
	plist_t	*params;
{
	int	result;
	char	*path;

	path = params->sym->val;
	result = mkdir(path,0777);
	if (result < 0)
		setError(sys_errlist[errno]);
        externReturn = createIntSymbol(result);
}



/****************************************************************************
** 	_doMerge
**
**	Purpose	: Merge elements of an array into a string
**	Args	: text string and separating character
**	Returns	: 
**	Notes	: 
*/

void doMerge(params)
	plist_t	*params;
{
	char	*tok;
	sym_t	*array,
		*cur;
	int	num,
		count;
	char	buf[2048];

	array = params->sym;
	tok = params->next->sym->val;
	num = symGetNumArrayElements(array);
	count = 0;
	*buf = 0;
	while(count < num)
	{
		cur = symGetArrayElement(array,count);
		if (count > 0)
			strcat(buf,tok);
		if (cur->val)
			strcat(buf, cur->val);
		count++;
	}
	externReturn = createCharSymbol(buf);
}



/****************************************************************************
** 	_doSplit
**
**	Purpose	: Split a string on a given token into an array
**	Args	: text string and token
**	Returns	: Nothint
**	Notes	: 
*/

void doSplit(params)
	plist_t	*params;
{
	char	*data,
		sep,
		*cp,
		*cp1;
	sym_t	*array,
		*sym;
	int	index = 0;

	data = params->sym->val;
	sep = *(params->next->sym->val);

	array = createArray();	
	cp1 = cp = data;
	while(*cp)
	{
		if (*cp == sep)
		{
			*cp++ = 0;
			sym = createCharSymbol(cp1);
			symSetArrayElement(array,index,sym);
			cp1=cp;
			index++;
		}
		else
			cp++;
	}
	sym = createCharSymbol(cp1);
	symSetArrayElement(array,index,sym);
	externReturn = array;
}



/****************************************************************************
** 	_doTokSplit
**
**	Purpose	: Split a string on a given token into an array
**	Args	: text string and token
**	Returns	: Nothint
**	Notes	: 
*/

void doTokSplit(params)
	plist_t	*params;
{
	char	*data,
		sep,
		*cp,
		*cp1;
	sym_t	*array,
		*sym;
	int	index = 0;

	data = params->sym->val;
	sep = *(params->next->sym->val);

	array = createArray();	
	cp1 = cp = data;
	while(*cp)
	{
		if (*cp == sep)
		{
			*cp++ = 0;
			sym = createCharSymbol(cp1);
			symSetArrayElement(array,index,sym);
			cp1=cp;
			index++;
			while(*cp == sep)
				cp++;
		}
		else
			cp++;
	}
	sym = createCharSymbol(cp1);
	symSetArrayElement(array,index,sym);
	externReturn = array;
}


void doStrSeg(params)
	plist_t	*params;
{
	char	*str,
		*new;
	int	start,
		end,
		length;

	str = params->sym->val;
	start = (int) * (int *)params->next->sym->val;
	end = (int) * (int *)params->next->next->sym->val;
	if ( (start < 0) ||
	     (start > end) )
	{
		setError("Bad string index");
		return;
	}
	length = end - start + 1;
	new = (char *)malloc(length+1);
	(void)bzero(new, length+1);
	strncpy(new, str + start, length);
	externReturn = createCharSymbol(new);
	free(new);
}

	
void doChop(params)
	plist_t	*params;
{
	char	*str,
		*new;

	str = params->sym->val;
	new = (char *)strdup(str);
	new[strlen(new) - 1]=0;
	externReturn = createCharSymbol(new);
	free(new);
}



static char *trSpec(spec)
	char	*spec;
{
	char	res[4096];
	register char	*cp1,
			*cp2,
			c;
			

	bzero(res,4096);
	cp1 = spec;
	cp2 = res;
	while(*cp1)
	{
		if (*cp1 == '-')
		{
			if (cp1 == spec)
			{
				setError("Bad TR format");
				return(NULL);
			}
			if (*(cp1+1) < (*cp1-1))
			{
				setError("Bad TR format");
				return(NULL);
			}
			c = *(cp1-1);
			while(c < *(cp1+1))
			{
				c++;
				*cp2++ = c;
			}
			cp1+=2;
			continue;
		}
		*cp2++ = *cp1++;
	}
	return((char *)strdup(res));
}



void doSub(params)
	plist_t	*params;
{
	char	*spec1,
		*spec2,
		*str,
		*buf,
		*oldBuf;
	register char	*cpb,
			*cp;
	int	len,
		curLen,
		spec1Len,
		spec2Len;

	str = params->sym->val;
	spec1 = params->next->sym->val;
	spec2 = params->next->next->sym->val;
	curLen = strlen(str);
	spec1Len = strlen(spec1);
	spec2Len = strlen(spec2);
	len = 0;

	buf = (char *)malloc(curLen+1);
	(void)bzero(buf,curLen);
	cpb = buf;
	cp = str;
	while (*cp)
	{
		if (*cp != *spec1)
		{
			*cpb++ = *cp++;	
			len++;
			continue;
		}
		if (strncmp(cp,spec1,spec1Len) != 0)
		{
			*cpb++ = *cp++;	
			len++;
			continue;
		}
		len += spec2Len ;
		if (spec2Len > spec1Len)
		{
			oldBuf = buf;
			buf = (char *)realloc(buf, curLen + 
				(spec2Len-spec1Len)+1);
			cpb = buf + (cpb - oldBuf);
			curLen += (spec2Len-spec1Len)+1;
		}
		strcpy(cpb,spec2);
		cpb += spec2Len;
		cp += spec1Len;
	}
	*cpb = 0;
	externReturn = createCharSymbol(buf);
	free(buf);
}


void doTr(params)
	plist_t	*params;
{
	char	*trans1,
		*trans2,
		*spec1,
		*spec2,
		*str;
	register char	*cps,
			*cp;

	str = params->sym->val;
	spec1 = params->next->sym->val;
	spec2 = params->next->next->sym->val;

	if((trans1 = trSpec(spec1)) == NULL)
		return;
	if((trans2 = trSpec(spec2)) == NULL)
	{
		free(trans1);
		return;
	}
	cps = str;
	while(*cps)
	{
		cp = trans1;
		while(*cp)
		{
			if (*cp == *cps)
			{
				*cps = *(char *)(trans2 + (cp-trans1));
				break;
			}
			cp++;
		}
		cps++;
	}
	externReturn = createCharSymbol(str);
	free(trans1);
	free(trans2);
}




/****************************************************************************
** 	_doTest
**
**	Purpose	: Functionaility like the shell's test
**	Args	: test and filename
**	Returns	: nothing
**	Notes	: 
*/

void doTest(params)
	plist_t	*params;
{
	char	*filename,
		test;
	struct	stat buf;
	int 	result;

	test = *(params->sym->val);
	filename = params->next->sym->val;
	if (stat(filename,&buf) < 0)
		result = 0;
	else
	{
		switch(test)
		{
			case 'b':
				result = S_ISBLK(buf.st_mode);
				break;
			case 'c':
				result = S_ISCHR(buf.st_mode);
				break;
			case 'd':
				result = S_ISDIR(buf.st_mode);
				break;
			case 'f':
				result = S_ISREG(buf.st_mode);
				break;
			case 'p':
				result = S_ISFIFO(buf.st_mode);
				break;
			case 's':
				result = S_ISREG(buf.st_mode) &&
					 (buf.st_size > 0);
				break;
			case 'u':
				result = buf.st_mode & S_ISUID;
				break;
			case 'g':
				result = buf.st_mode & S_ISGID;
				break;
			default:
				runError("Bad test passed to test()");
		}
	}
	externReturn = createIntSymbol(result);
}





void doKill(params)
	plist_t	*params;
{
	int	sig,
		pid,
		res;

	pid = (int) * (int *)params->sym->val;
	sig = (int) * (int *)params->next->sym->val;
	if (sig < 0 || sig > NSIG-1)
	{
		externReturn = createIntSymbol(-1);
		setError("Bad signal number");
		return;
	}
	res = kill(pid,sig);
	if (res < 0)
	{
		externReturn = createIntSymbol(-1);
		setError(sys_errlist[errno]);
		return;
	}
	externReturn = createIntSymbol(0);
}



void doStat(params)
	plist_t	*params;
{
	struct	stat buf;
	char	*path;
	sym_t	*array,
		*sym;
	int	index;

	path = params->sym->val;
	if (stat(path,&buf) < 0)
	{
		setError(sys_errlist[errno]);
		return;
	}

	index = 0;
	array = createArray();	
	sym = createIntSymbol(buf.st_ino);
	symSetArrayElement(array,index++,sym);
	sym = createIntSymbol(buf.st_mode & 1023);
	symSetArrayElement(array,index++,sym);
	sym = createIntSymbol(buf.st_nlink);
	symSetArrayElement(array,index++,sym);
	sym = createIntSymbol(buf.st_uid);
	symSetArrayElement(array,index++,sym);
	sym = createIntSymbol(buf.st_gid);
	symSetArrayElement(array,index++,sym);
	sym = createIntSymbol(buf.st_size);
	symSetArrayElement(array,index++,sym);
	sym = createIntSymbol(buf.st_atime);
	symSetArrayElement(array,index++,sym);
	sym = createIntSymbol(buf.st_mtime);
	symSetArrayElement(array,index++,sym);
	sym = createIntSymbol(buf.st_ctime);
	symSetArrayElement(array,index++,sym);
	sym = createIntSymbol(buf.st_blksize);
	symSetArrayElement(array,index++,sym);
	sym = createIntSymbol(buf.st_blocks);
	symSetArrayElement(array,index++,sym);
	externReturn = array;
}

void doRmdir(params)
	plist_t	*params;
{
	char	*path;
	int	res;

	path = params->sym->val;
	res = rmdir(path);
	if (res < 0)
		setError(sys_errlist[errno]);
	externReturn = createIntSymbol(res);
}


void doRename(params)
	plist_t	*params;
{
	char	*old,
		*new;
	int	res;

	old = params->sym->val;
	new = params->next->sym->val;
	res = rename(old,new);
	if (res < 0)
		setError(sys_errlist[errno]);
	externReturn = createIntSymbol(res);
}


void doTruncate(params)
	plist_t	*params;
{
	char	*path;
	int	length,
		res;

	path = params->sym->val;
	length = (int) * (int *)params->next->sym->val;
	res = truncate(path,length);
	if (res < 0)
		setError(sys_errlist[errno]);
	externReturn = createIntSymbol(res);
}


void doLink(params)
	plist_t	*params;
{
	char	*path,
		*new;
	int	res;

	path = params->sym->val;
	new = params->next->sym->val;
	res = link(path,new);
	if (res < 0)
		setError(sys_errlist[errno]);
	externReturn = createIntSymbol(res);
}


void doSymlink(params)
	plist_t	*params;
{
	char	*path,
		*new;

	path = params->sym->val;
	new = params->next->sym->val;
#ifdef	HAVE_SYMLINK
	res = symlink(path,new);
	if (res < 0)
		setError(sys_errlist[errno]);
	externReturn = createIntSymbol(res);
#else
	setError("Symbolic links not available!");
	externReturn = createIntSymbol(-1);
#endif
	
}


void doSubStr(params)
	plist_t	*params;
{
	char	*reg,
		*source,
		*sub,
		buf[4096];
	regexp	*prog;

	source = params->sym->val;
	reg = params->next->sym->val;
	sub = params->next->next->sym->val;

	prog = regcomp(reg);
	regexec(prog,source);
	(void)bzero(buf,sizeof(buf));
	regsub(prog,sub,buf);
	free(prog);
	externReturn = createCharSymbol(buf);
}




void doGetHostByName(params)
	plist_t	*params;
{
	char	*hostname;
	struct	hostent *hp;
	sym_t  	*array,
		*sym;
	int	index;


	hostname = params->sym->val;
	hp = gethostbyname(hostname);
	if (!hp)
	{
		setError("Host unknown");
		return;
	}
	index = 0;
	array = createArray();	
	sym = createCharSymbol((char *)hp->h_name);
	symSetArrayElement(array,index++,sym);
	sym = createCharSymbol(inet_ntoa(*(struct in_addr*)*(hp->h_addr_list)));
	symSetArrayElement(array,index++,sym);
	externReturn = array;
}


void doGetHostByAddr(params)
	plist_t	*params;
{
	char	*hostAddr;
	struct	hostent *hp;
	struct	sockaddr_in addr;
	sym_t  	*array,
		*sym;
	int	index;
	u_long	laddr;


	hostAddr = params->sym->val;
	(void)bzero(&addr, sizeof(addr));
	addr.sin_family = AF_INET;
	laddr = inet_addr(hostAddr);
	if (laddr < 0)
	{
		setError("Bad IP Address Format");
		return;
	}
	addr.sin_addr.s_addr = laddr;
	hp = gethostbyaddr((char *)&(addr.sin_addr),sizeof(addr.sin_addr),
		AF_INET);
	if (!hp)
	{
		setError("Host unknown");
		return;
	}
	index = 0;
	array = createArray();	
	sym = createCharSymbol((char *)hp->h_name);
	symSetArrayElement(array,index++,sym);
	sym = createCharSymbol(inet_ntoa(*(struct in_addr*)*(hp->h_addr_list)));
	symSetArrayElement(array,index++,sym);
	externReturn = array;
}



void doTime(params)
	plist_t	*params;
{
	time_t	res;

	time(&res);
	if (res < 0)
		setError(sys_errlist[errno]);
	externReturn = createIntSymbol(res);
}


void doGetTzOffset(params)
	plist_t	*params;
{
	/*
	** Note : ftime etc return the GMT offset in minutes _west_ of GMT.
	** In reality, global time is shown in minutes _east_ of GMT.
	** This like a very US centric way of handling offsets.  We
	** correct for this and return it as minutes east
	*/
	time_t	day1,
		val;

	day1 = 60*60*24 ;
	val = day1 - mktime(gmtime(&day1));
	val /= 60;
	externReturn = createIntSymbol(val);
}


void doSrandom(params)
	plist_t	*params;
{
	u_int	seed;

        seed = (u_int) * (u_int *)params->sym->val;
	srandom(seed);
}




void doRandom(params)
	plist_t	*params;
{
	int	res;

	res = random();
	externReturn = createIntSymbol(res);
}




void doCTime(params)
	plist_t	*params;
{
	time_t	time;
	char	*res;

        time = (time_t) * (int *)params->sym->val;
	res = (char *)ctime(&time);
	if (res == NULL)
	{
		setError(sys_errlist[errno]);
	}
	else
	{
		externReturn = createCharSymbol(res);
	}
}


char *contentType = NULL;


void doSetContentType(params)
	plist_t	*params;
{
	contentType = (char *)strdup(params->sym->val);
}


void doIncludeFile(params)
	plist_t	*params;
{
	char	*filename,
		buf[4096];
	int	fd,
		numBytes;

	checkContentType();
	filename = params->sym->val;
	fd = open(filename,O_RDONLY,0);
	if (fd < 0)
	{
		externReturn = createIntSymbol(-1);
		setError(sys_errlist[errno]);
		return;
	}
	while ((numBytes = read(fd,buf,4096)) > 0)
	{
		write(fileno(stdout),buf,numBytes);
	}
	close(fd);
	externReturn = createIntSymbol(0);
}



/*
** Extended time/date functions
*/

void doTime2UnixTime(params)
	plist_t	*params;
{
	int	sec, min, hour,
		day, mon, year;
	struct	tm timeVal;
	time_t	res;

        sec = (int) * (int *)params->sym->val;
        min = (int) * (int *)params->next->sym->val;
        hour = (int) * (int *)params->next->next->sym->val;
        day = (int) * (int *)params->next->next->next->sym->val;
        mon = (int) * (int *)params->next->next->next->next->sym->val;
        year = (int) * (int *)params->next->next->next->next->next->sym->val;

	bzero(&timeVal, sizeof(timeVal));
	timeVal.tm_sec = sec;
	timeVal.tm_min = min;
	timeVal.tm_hour = hour;
	timeVal.tm_mday = day;
	timeVal.tm_mon = mon - 1;
	timeVal.tm_year = year - 1900;
	res = mktime(&timeVal);
	externReturn = createUintSymbol(res);
}


void doUnixTime2Year(params)
	plist_t	*params;
{
	int	year;
	char	buf[10];
	struct	tm *locTime;
	time_t 	time;

        time = (time_t) * (int *)params->sym->val;
	locTime = localtime(&time);
	strftime(buf,sizeof(buf),"%Y",locTime);
	year = atoi(buf);
	externReturn = createUintSymbol(year);
}



void doUnixTime2Month(params)
	plist_t	*params;
{
	int	month;
	char	buf[10];
	struct	tm *locTime;
	time_t 	time;

        time = (time_t) * (int *)params->sym->val;
	locTime = localtime(&time);
	strftime(buf,sizeof(buf),"%m",locTime);
	month = atoi(buf);
	externReturn = createUintSymbol(month);
}


void doUnixTime2Day(params)
	plist_t	*params;
{
	int	day;
	char	buf[10];
	struct	tm *locTime;
	time_t 	time;

        time = (time_t) * (int *)params->sym->val;
	locTime = localtime(&time);
	strftime(buf,sizeof(buf),"%d",locTime);
	day = atoi(buf);
	externReturn = createUintSymbol(day);
}


void doUnixTime2Hour(params)
	plist_t	*params;
{
	int	hour;
	char	buf[10];
	struct	tm *locTime;
	time_t 	time;

        time = (time_t) * (int *)params->sym->val;
	locTime = localtime(&time);
	strftime(buf,sizeof(buf),"%H",locTime);
	hour = atoi(buf);
	externReturn = createUintSymbol(hour);
}


void doUnixTime2Min(params)
	plist_t	*params;
{
	int	min;
	char	buf[10];
	struct	tm *locTime;
	time_t 	time;

        time = (time_t) * (int *)params->sym->val;
	locTime = localtime(&time);
	strftime(buf,sizeof(buf),"%M",locTime);
	min = atoi(buf);
	externReturn = createUintSymbol(min);
}


void doUnixTime2Sec(params)
	plist_t	*params;
{
	int	sec;
	char	buf[10];
	struct	tm *locTime;
	time_t 	time;

        time = (time_t) * (int *)params->sym->val;
	locTime = localtime(&time);
	strftime(buf,sizeof(buf),"%S",locTime);
	sec = atoi(buf);
	externReturn = createUintSymbol(sec);
}


void doStrftime(params)
	plist_t	*params;
{
	time_t	time;
	char	*fmt;
	static	char buf[4096];

        fmt = (char *)params->sym->val;
        time = (int) * (int *)params->next->sym->val;

	strftime(buf, 4096, fmt, localtime(&time));
	externReturn = createCharSymbol(buf);
}



void doGetUid(params)
	plist_t	*params;
{
	int	uid;

	uid = getuid();
	externReturn = createIntSymbol(uid);
}


void doGetGid(params)
	plist_t	*params;
{
	int	gid;

	gid = getgid();
	externReturn = createIntSymbol(gid);
}


void doGetEuid(params)
	plist_t	*params;
{
	int	uid;

	uid = geteuid();
	externReturn = createIntSymbol(uid);
}


void doGetPwNam(params)
	plist_t	*params;
{
	struct 	passwd *pwe;
	sym_t	*array,
		*sym;
	int	index;

	pwe = getpwnam(params->sym->val);
	if (!pwe)
	{
		externReturn = createCharSymbol("");
		return;
	}
	array = createArray();	
	index=0;
	sym = createCharSymbol(pwe->pw_name);
	symSetArrayElement(array,index++,sym);
	sym = createCharSymbol(pwe->pw_passwd);
	symSetArrayElement(array,index++,sym);
	sym = createIntSymbol(pwe->pw_uid);
	symSetArrayElement(array,index++,sym);
	sym = createIntSymbol(pwe->pw_gid);
	symSetArrayElement(array,index++,sym);
	sym = createCharSymbol(pwe->pw_gecos);
	symSetArrayElement(array,index++,sym);
	sym = createCharSymbol(pwe->pw_dir);
	symSetArrayElement(array,index++,sym);
	sym = createCharSymbol(pwe->pw_shell);
	symSetArrayElement(array,index++,sym);

	externReturn = array;
}

	
void doGetPwUid(params)
	plist_t	*params;
{
	struct 	passwd *pwe;
	sym_t	*array,
		*sym;
	int	index,
		uid;

	uid = (int) * (int *)params->sym->val;
	pwe = getpwuid(uid);
	if (!pwe)
	{
		externReturn = createCharSymbol("");
		return;
	}
	array = createArray();	
	index=0;
	sym = createCharSymbol(pwe->pw_name);
	symSetArrayElement(array,index++,sym);
	sym = createCharSymbol(pwe->pw_passwd);
	symSetArrayElement(array,index++,sym);
	sym = createIntSymbol(pwe->pw_uid);
	symSetArrayElement(array,index++,sym);
	sym = createIntSymbol(pwe->pw_gid);
	symSetArrayElement(array,index++,sym);
	sym = createCharSymbol(pwe->pw_gecos);
	symSetArrayElement(array,index++,sym);
	sym = createCharSymbol(pwe->pw_dir);
	symSetArrayElement(array,index++,sym);
	sym = createCharSymbol(pwe->pw_shell);
	symSetArrayElement(array,index++,sym);

	externReturn = array;
}


void doTypeOf(params)
	plist_t	*params;
{

	if (!params)
	{
		runError("Too few parameters to typeof()");
	}
	if (params->next)
	{
		runError("Too many parameters to typeof()");
	}
	if (params->sym->array == ARRAY)
	{
		externReturn = createCharSymbol("array");
	}
	else
	{
		switch(params->sym->type)
		{
			case TYPE_CHAR:
				externReturn = createCharSymbol("char");
				break;
			case TYPE_INT:
				externReturn = createCharSymbol("int");
				break;
			case TYPE_REAL:
				externReturn = createCharSymbol("real");
				break;
			default:
				externReturn = createCharSymbol("unknown");
				break;
		}
	}
	return;
}

void doExpandText(params)
      plist_t *params;
{
      char    *old,
              *new;

      old = params->sym->val;
      new = expandText(old);
      externReturn = createCharSymbol(new);
}

