/*
**      lib.c - Lite library handling routines
**
**
** Copyright (c) 1996,97  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.
**
** 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 <fcntl.h>
#include <ctype.h>
#include "lite.h"

#define	LIB_MAG	0x1234
#define	LIB_VER	3

#define	INF_FUNC	1
#define INF_PARAM	2
#define	INF_CODE	3
#define	INF_LIT		4
#define	INF_LIT_ARRAY	5


typedef	struct	_lhdr {
	int	magic,
		version;
} lhdr_t;

typedef struct _inf {
	char	type;
	u_short	len;
} inf_t;


extern	funct_t	*functions;
extern	sym_t	*litHead[];
extern	int	curLitIdx,
		curLabel;
extern	code_t  *codeHead, *codeTail;
extern	sym_t   *paramHead, *paramTail;

int	litBase,
	labelBase;

static 	char	buf[10 * 1024],
		tmpBuf[128];

char	*libNames[20];
int	curLib = 0;


static funct_t	*curFunct;




#define HD_LEN	(1 + sizeof(cur->line))
static int setupCode(cur)
	code_t	*cur;
{
	char	*cp;

	*buf = cur->op;
	cp = buf+1;
	bcopy(&cur->line,cp,sizeof(cur->line));
	cp += sizeof(cur->line);
	switch(cur->op)
	{
		case OP_PUSH:
		case OP_STORE:
		case OP_A_STORE:
		case OP_DEREF:
		case OP_ECHO:
		case OP_CALL:
			if (cur->arg)
			{
				strcpy(cp,cur->arg);
				*(cp + strlen(cur->arg) + 1)  = 0;
				return(strlen(cur->arg) + 1 + HD_LEN);
			}
			else
			{
				return(HD_LEN);
			}

		case OP_CAST:
		case OP_ALU:
		case OP_CMP:
			bcopy(&cur->type,cp,sizeof(cur->type));
			return(HD_LEN+sizeof(cur->type));
	
		case OP_LABEL:
		case OP_JMP:
		case OP_JMP_BACK:
		case OP_JMP_FALSE:
			bcopy(&cur->label,cp,sizeof(cur->label));
			return(HD_LEN+sizeof(cur->label));
	}
	return(HD_LEN);
}


extern char *opCodes[];

static code_t *decodeCode(buf)
	char	*buf;
{
	code_t	*new;
	char	*cp;

	new = (code_t *)malloc(sizeof(code_t));
	new->file = curLib;
	new->op = *buf;
	bcopy(buf+1, &new->line, sizeof(new->line));
	cp = buf + 1 + sizeof(new->line);

#ifdef DEBUG
	printf("\tcode '%s'\t",opCodes[new->op]);
#endif
	switch(new->op)
	{
		case OP_PUSH:
		case OP_STORE:
		case OP_A_STORE:
		case OP_DEREF:
		case OP_ECHO:
			if (isdigit(*(cp)))
			{
				sprintf(tmpBuf,"%d",atoi(cp)+litBase);
				new->arg = (char *)strdup(tmpBuf);
			}
			else
				new->arg = (char *)strdup(cp);
#ifdef DEBUG
			printf("%s",new->arg);
#endif
			break;

		case OP_CALL:
			new->arg = (char *)strdup(cp);
#ifdef DEBUG
			printf("%s",new->arg);
#endif
			break;


		case OP_CAST:
		case OP_ALU:
		case OP_CMP:
			bcopy(cp, &new->type,sizeof(new->type));
			break;
	
		case OP_LABEL:
		case OP_JMP:
		case OP_JMP_BACK:
		case OP_JMP_FALSE:
			bcopy(cp,&new->label,sizeof(new->label));
			new->label += labelBase;
			curLabel++;
			break;
	}
#ifdef DEBUG
	printf("\n");
#endif
	return(new);
}


static void writeArray(fd,sym)
	int	fd;
	sym_t	*sym;
{
	array_t	*array;
	sym_t	*curSym;
	inf_t	inf;

	array = (array_t *)sym->val;
	while(array)
	{
		curSym = array->sym;
		inf.type = INF_LIT_ARRAY;
		inf.len = curSym->length + sizeof(curSym->type);
		write(fd,&inf,sizeof(inf));
		write(fd,&curSym->type,sizeof(curSym->type));
		write(fd,curSym->val, curSym->length);
		array = array->next;
	}
}


int libWriteLibrary(libfile)
	char	*libfile;
{
	lhdr_t	hdr;
	int	fd,
		count;
	inf_t	inf;
	funct_t	*curFunct;
	sym_t	*curSym;
	code_t	*curCode;


	/*
	** A library cannot contain a main function so scan the
	** function list and bail if it's there
	*/
	curFunct = functions;
	while(curFunct)
	{
		if (strcmp(curFunct->name,"main") == 0)
		{
			if (curFunct->code)
			{
				runError(
				   "Library files can't include 'main' code!");
				return(-1);
			}
			break;
		}
		curFunct = curFunct->next;
	}

	/*
	** Create the library file
	*/
	fd = open(libfile,O_RDWR|O_CREAT|O_TRUNC, 0744);
	if (fd < 0)
		return(-1);
	hdr.magic = LIB_MAG;
	hdr.version = LIB_VER;
	write(fd,&hdr,sizeof(hdr));



	/*
	** Write the literal values
	*/
	count = 0;
	while(count < SYM_HASH_SIZE)
	{
		curSym = litHead[count];
		while(curSym)
		{
			inf.type = INF_LIT;
			inf.len = strlen(curSym->name) + curSym->length +
				sizeof(curSym->type) + 
				sizeof(curSym->array) + 1;
			write(fd,&inf,sizeof(inf));
			write(fd,curSym->name, strlen(curSym->name)+1);
			write(fd,&curSym->type,sizeof(curSym->type));
			write(fd,&curSym->array,sizeof(curSym->array));
			write(fd,curSym->val, curSym->length);

			if (curSym->array == ARRAY)
				writeArray(fd, curSym);
			curSym = curSym->next;
		}
		count++;
	}

	/*
	** Write the function info
	*/
	curFunct = functions;
	while(curFunct)
	{
		/*
		** Write the function header
		*/
		if (strcmp(curFunct->name,"main") == 0)
		{
			curFunct = curFunct->next;
			continue;
		}
		inf.type = INF_FUNC;
		inf.len = strlen(curFunct->name) + 1;
		write(fd,&inf,sizeof(inf));
		write(fd,curFunct->name,inf.len);

		/*
		** Write the params
		*/
		curSym = curFunct->params;
		while(curSym)
		{
			inf.type = INF_PARAM;
			inf.len = strlen(curSym->name)+1+sizeof(curSym->type);
			write(fd,&inf,sizeof(inf));
			write(fd,curSym->name,strlen(curSym->name)+1);
			write(fd,&curSym->type,sizeof(curSym->type));
			curSym = curSym->next;
		}


		/*
		** Write the code
		*/
		curCode = curFunct->code;
		while(curCode)
		{
			inf.type = INF_CODE;
			inf.len = setupCode(curCode);
			write(fd,&inf,sizeof(inf));
			write(fd,buf,inf.len);
			curCode = curCode->next;
		}

		curFunct = curFunct->next;
	}
	close(fd);
	return(0);
}


static void libSetupFunction(name)
	char	*name;
{
	funct_t	*cur;

	cur = (funct_t *)malloc(sizeof(funct_t));
	cur->name = (char *)strdup(name);
	cur->next = functions;
	functions = cur;
	curFunct = cur;
	codeHead = codeTail = NULL;
	paramHead = paramTail = NULL;
}




int libLoadLibrary(libfile)
	char	*libfile;
{
	lhdr_t	hdr;
	int	fd,
		hash,
		numBytes;
	inf_t	inf;
	sym_t	*curSym,
		*newSym;
	code_t	*curCode;
	array_t	*prevArray,
		*newArray;
	char	*cp,
		*filename;

	if (*libfile == '"')
	{
		filename = libfile+1;
		libfile[strlen(libfile)-1]=0;
	}
	else
		filename = libfile;
#ifdef DEBUG
	printf("Loading library %s\n",filename);
#endif
	curLib++;
	libNames[curLib] = (char *)filename;

	fd = open(filename,O_RDONLY,0);
	if (fd < 0)
	{
		return(-1);
	}
	numBytes = read(fd,&hdr,sizeof(hdr));
	if (hdr.magic != LIB_MAG)
	{
		parseError("Bad library format!");
		return(-2);
	}
	if (hdr.version != LIB_VER)
	{
		parseError("Bad library version!\nPlease recompile the library with the current version of Lite");
		return(-2);
	}

	litBase = curLitIdx;
	labelBase = curLabel;
	numBytes = read(fd,&inf,sizeof(inf));
	while(numBytes)
	{
		switch(inf.type)
		{
			case INF_LIT:
				read(fd,buf,inf.len);
				sprintf(tmpBuf,"%d",atoi(buf)+litBase);
				curLitIdx++;
				curSym = (sym_t*)malloc(sizeof(sym_t));
				strcpy(curSym->name,tmpBuf);
				cp = (char *)index(buf,0);
				bcopy(cp+1,&(curSym->type),
					sizeof(curSym->type));
				bcopy(cp+1+sizeof(curSym->type),
					&(curSym->array),
					sizeof(curSym->array));
				curSym->length = inf.len - 1 - 
					sizeof(curSym->type) -
					sizeof(curSym->array);
				curSym->val = (char *)malloc(curSym->length);
				bcopy(cp+1+sizeof(curSym->type) + 
					sizeof(curSym->array),curSym->val,
					curSym->length);
#ifdef DEBUG
				printf("Literal value '%s' = '%s'\n",
					curSym->name, symUnpackSymbol(curSym));
#endif
				hash=calculateHash(curSym->name,SYM_HASH_SIZE);
				curSym->next = litHead[hash];
				litHead[hash] = curSym;
				if (curSym->array == ARRAY)
				{
					curSym->val = NULL;
					prevArray = NULL;
				}
				break;

			case INF_LIT_ARRAY:
				read(fd,buf,inf.len);
				newArray = (array_t*)malloc(sizeof(array_t));
				newSym = (sym_t*)malloc(sizeof(sym_t));
				newArray->sym = newSym;
				newArray->next = NULL;
				if (prevArray)
					prevArray->next = newArray;
				else
					curSym->val = (char *)newArray;
				bcopy(buf,&(newSym->type),
					sizeof(newSym->type));
				newSym->length = inf.len - 
					sizeof(newSym->type);
				newSym->val = (char *)malloc(newSym->length);
				bcopy(buf+sizeof(newSym->type), newSym->val,
					newSym->length);
				newSym->array = SCALAR;
				prevArray = newArray;
				break;
			

			case INF_FUNC:
				read(fd,buf,inf.len);
#ifdef DEBUG
				printf("Lib function '%s'\n",buf);
#endif
				libSetupFunction(buf);
				break;

			case INF_PARAM:
				read(fd,buf,inf.len);
#ifdef DEBUG
				printf("\tparam '%s'\n",buf);
#endif
				curSym = (sym_t *)malloc(sizeof(sym_t));
				strcpy(curSym->name,buf);
				cp = (char *)index(buf,0);
				bcopy(cp+1,&curSym->type,sizeof(curSym->type));
				curSym->length = 0;
				if (!paramHead)
				{
					curFunct->params = paramHead = 	
						paramTail = curSym;
					curSym->next = NULL;
				}
				else
				{
					paramTail->next = curSym;
					paramTail = curSym;
					paramTail->next = NULL;
				}
				break;

			case INF_CODE:
				read(fd,buf,inf.len);
				curCode = decodeCode(buf);
				if (!codeHead)
				{
					curFunct->code = codeHead = 	
						codeTail = curCode;
					curCode->next = NULL;
				}
				else
				{
					curCode->prev = codeTail;
					codeTail->next = curCode;
					codeTail = curCode;
					codeTail->next = NULL;
				}
				break;
		}
		numBytes = read(fd,&inf,sizeof(inf));
	}
#ifdef DEBUG
	printf("\n");
#endif
	close(fd);
	return(0);
}
