/* -------------------------------------------------------------------- */
/* SMS Client, send messages to mobile phones and pagers		*/
/*									*/
/* gs_token.c								*/
/*									*/
/*  Copyright (C) 1997,1998 Angelo Masci				*/
/*									*/
/*  This library is free software; you can redistribute it and/or	*/
/*  modify it under the terms of the GNU Library General Public		*/
/*  License as published by the Free Software Foundation; either	*/
/*  version 2 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	*/
/*  Library General Public License for more details.			*/
/*									*/
/*  You should have received a copy of the GNU Library General Public	*/
/*  License along with this library; if not, write to the Free		*/
/*  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.	*/
/*									*/
/*  You can contact the author at this e-mail address:			*/
/*									*/
/*  angelo@styx.demon.co.uk						*/
/*									*/
/* -------------------------------------------------------------------- */
/* $Id$
   -------------------------------------------------------------------- */

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>

#include "common/common.h"
#include "logfile/logfile.h"
#include "gs_token.h"
#include "gs_list.h"
#include "gs_io.h"
#include "error.h"

/* -------------------------------------------------------------------- */

TOKEN_ID	
	null_tok,
	eof_tok,
	assignment_tok,
	lparen_tok,
	rparen_tok,
	lcurly_tok,
	rcurly_tok,
	comma_tok,
	dot_tok,
	semicolon_tok,
	true_tok,
	false_tok,
	yes_tok,
	no_tok;

struct init_token_struct
	{
		TOKEN_ID 	
			*var;

		char	*str;
		int	type;
		int	type_ptr;
		void	*ptr;

	} init_token_table[] =

		{	
			{ &null_tok, 		"<NULL>", 	T_SPECIAL, 0, NULL },
			{ &eof_tok, 		"<EOF>", 	T_SPECIAL, 0, NULL },
			{ &assignment_tok, 	"=", 		T_SPECIAL, 0, NULL },
			{ &lparen_tok, 		"(", 		T_SPECIAL, 0, NULL },
			{ &rparen_tok, 		")", 		T_SPECIAL, 0, NULL },
			{ &lcurly_tok, 		"{", 		T_SPECIAL, 0, NULL },
			{ &rcurly_tok, 		"}", 		T_SPECIAL, 0, NULL },
			{ &comma_tok, 		",", 		T_SPECIAL, 0, NULL },
			{ &dot_tok, 		".", 		T_SPECIAL, 0, NULL },
			{ &semicolon_tok, 	";", 		T_SPECIAL, 0, NULL },
			{ &true_tok, 		"TRUE", 	T_STRING, 0, NULL },
			{ &false_tok, 		"FALSE", 	T_STRING, 0, NULL },
			{ &yes_tok, 		"YES", 		T_STRING, 0, NULL },
			{ &no_tok, 		"NO", 		T_STRING, 0, NULL },
			{ NULL, 		NULL, 		T_SPECIAL, 0, NULL }
		};


static	TOKEN_ID
		token_pushed = NULL;

static TOKEN_HEAP
		builtin = { NULL };

/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */

static int is_valid_first_identifier_character(int character);
static int is_valid_identifier_character(int character);

/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
void push_back_token(TOKEN_ID token)
{	token_pushed = token;
}


/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
static int is_valid_first_identifier_character(int character)
{
	if ((isalpha(character)) || (character == '_'))
	{	return TRUE;
	}

	return FALSE;
}


/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
static int is_valid_identifier_character(int character)
{
	if ((isalnum(character)) || (character == '_'))
	{	return TRUE;
	}

	return FALSE;
}


/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
char *expand_environment_variables(char *str)
{
static	char 	data[1024],
		env_str[1024],
		*env_value,
		*env_ptr,
		*dst,
		*src;


	dst = data;
	src = str;

	while(*src != '\0')
	{
		if (*src == '$')
		{
			env_ptr = env_str;

			src++;
			if (*src == '{')
			{
				src++;
				while((*src != '}') && (*src != '\0'))
				{
					*env_ptr++ = *src++;
				}

				*env_ptr = '\0';


				fprintf(stderr, "FOUND VAR: '%s'\n", env_str);


				if (*src == '}')
				{
					src++;

					env_value = getenv(env_str);
					if (env_value != NULL)
					{
						strcpy(dst, env_value);

						dst += strlen(env_value);
					}
				}
				else
				{	return NULL;
				}
			}
			else
			{	return NULL;
			}
		}
		else
		{	*dst++ = *src++;
		}
	}

	*dst = '\0';
	return data;
}


/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
TOKEN_ID get_next_token(TOKEN_HEAP *heap)
{
	char  	str[1024],
		*str_ptr,
		*env_str,
		*nstr;


	long	value;

	int 	character;

	TOKEN_ID
		token_id;

	
	if (token_pushed != NULL)
	{
		token_id = token_pushed;
		token_pushed = NULL;
		return token_id;
	}


	character = get_next_character();

	/* ---------------------------- */
	/* Throw away any whitespace 	*/
	/* ---------------------------- */


	while (isspace(character))
	{	character = get_next_character();
	}

	/* ---------------------------- */
	/* Identifier -			*/
	/* A String without any quotes	*/
	/* ---------------------------- */

	if (is_valid_first_identifier_character(character))
	{
		str_ptr = str;
		while (is_valid_identifier_character(character))
		{
			*str_ptr++ = character;
			 character = get_next_character();
		}

		*str_ptr = '\0';
		push_character(character);
			
		lprintf(LOG_VERYVERBOSE, "Found Identifier '%s'\n", str);


		/* This Identifier could be a special	*/
		/* reserved String.			*/
		/* If it has a matching builtin string	*/
		/* then return this.			*/
		/* Else add the token as an identifier	*/


		token_id = find_item(builtin.list, str, T_STRING);
		if (token_id != NULL)
		{	lprintf(LOG_VERYVERBOSE, "Overriding Identifier with Builtin String\n");
		}
		else
		{	token_id = add_token(heap, str, T_IDENTIFIER, TP_NULL, NULL);
		}
	}
	else
	if (character == '$') 
	{
		character = get_next_character();

		str_ptr = str;
		while ((!isspace(character)) && (character != EOF))
		{
			*str_ptr++ = character;
			character = get_next_character();
		}

		*str_ptr = '\0';

		lprintf(LOG_VERYVERBOSE, "Found Environment Variable '%s'\n", str);

		/* this is an environment variable 	*/
		/* expand it if it exists, else		*/
		/* set it to an empty string 		*/

		env_str = getenv(str);
		if (env_str == NULL)
		{
			strcpy(str, "");
		}
		else
		{	strcpy(str, env_str);
		}

		lprintf(LOG_VERYVERBOSE, "Shell variable, expanding Identifier to '%s'\n", str);
		token_id = add_token(heap, str, T_STRING, TP_NULL, NULL);
	}
	else
	if (character == '"') 
	{
		character = get_next_character();

		str_ptr = str;
		while ((character != '"') && (character != EOF))
		{
			if (character == '\\')
			{
				*str_ptr++ = escape_char();
			}
			else
			{	*str_ptr++ = character;
			}

			character = get_next_character();
		}

		*str_ptr = '\0';

		if (character == '"')
		{
			lprintf(LOG_VERYVERBOSE, "Found Identifier '%s'\n", str);

			nstr = expand_environment_variables(str);
			if (nstr != NULL)
			{
				token_id = add_token(heap, nstr, T_STRING, TP_NULL, NULL);
			}
			else
			{	token_id = null_tok;
			}
		}
		else
		{	token_id = null_tok;
		}			
	}
	else
	if (isdigit(character)) 
	{
		value = character - '0';

		str_ptr = str;
		*str_ptr++ = character;

		character = get_next_character();
		while (isdigit(character) && (character != EOF))
		{
			value *= 10;
			value += character - '0';

			*str_ptr++ = character;
			character  = get_next_character();
		}

		*str_ptr = '\0';

		token_id = add_token(heap, str, T_NUMERIC, TP_NULL, NULL);
	}
	else
	if (character == '\'') 
	{
		character = get_next_character();

		str_ptr = str;
		while ((character != '\'') && (character != EOF))
		{
			*str_ptr++ = character;
			 character = get_next_character();
		}

		*str_ptr = '\0';

		if (character == '\'')
		{
			lprintf(LOG_VERYVERBOSE, "Found Identifier '%s'\n", str);
			token_id = add_token(heap, str, T_STRING, TP_NULL, NULL);
		}
		else
		{	token_id = null_tok;
		}			
	}
	else
	if (character == '=')
	{	token_id = assignment_tok;
	}
	else
	if (character == '(')
	{	token_id = lparen_tok;
	}
	else
	if (character == ')')
	{	token_id = rparen_tok;
	}
	else
	if (character == '{')
	{	token_id = lcurly_tok;
	}
	else
	if (character == '}')
	{	token_id = rcurly_tok;
	}
	else
	if (character == ',')
	{	token_id = comma_tok;
	}
	else
	if (character == '.')
	{	token_id = dot_tok;
	}
	else
	if (character == ';')
	{	token_id = semicolon_tok;
	}
	else
	if (character == EOF)
	{	token_id = eof_tok;
	}
	else
	{	token_id = null_tok;
	}


	lprintf(LOG_VERYVERBOSE, "Token id = %p\n", (void *) token_id);
	return token_id;
}


/* -------------------------------------------------------------------- */
/* Add token searches the token heap for 'str'				*/
/* -------------------------------------------------------------------- */
TOKEN_ID add_token(TOKEN_HEAP *heap, char *str, int type, int ptr_type, void *ptr)
{
	TOKEN_ID
		token;

	lprintf(LOG_VERYVERBOSE, "Entering add_token()\n");

	heap->list = add_token_list_item(heap->list, &token, str, type, ptr_type, ptr);

	lprintf(LOG_VERYVERBOSE, "returning %p\n", (void *) token);

	return token;
}

/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
char *get_token_strvalue(TOKEN_HEAP *heap, TOKEN_ID token_id)
{	return token_id->str;
}

/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
long get_token_numvalue(TOKEN_HEAP *heap, TOKEN_ID token_id)
{	return token_id->value;
}


/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
char *get_token_strtype(TOKEN_HEAP *heap, TOKEN_ID token_id)
{	return "UNKNOWN";
}


/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
int get_token_type(TOKEN_HEAP *heap, TOKEN_ID token_id)
{	return token_id->type;
}


/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
int get_token_indirect_type(TOKEN_HEAP *heap, TOKEN_ID token_id)
{	return token_id->ptr_type;
}


/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
char *get_token_indirect_strvalue(TOKEN_HEAP *heap, TOKEN_ID token_id)
{	return token_id->ptr;
}

/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
long get_token_indirect_numvalue(TOKEN_HEAP *heap, TOKEN_ID token_id)
{	return token_id->value;
}


/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
TOKEN_HEAP *get_token_indirect_dictionary(TOKEN_HEAP *heap, TOKEN_ID token_id)
{	return (TOKEN_HEAP *)token_id->ptr;
}


/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
TOKEN_HEAP *get_token_indirect_list(TOKEN_HEAP *heap, TOKEN_ID token_id)
{	return (TOKEN_HEAP *)token_id->ptr;
}


/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
void token_assign_strvalue(TOKEN_HEAP *heap, TOKEN_ID token_id, char *string)
{
	token_id->ptr_type = TP_STRING;
	token_id->ptr      = string;
}


/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
void token_assign_numvalue(TOKEN_HEAP *heap, TOKEN_ID token_id, long value)
{
	token_id->ptr_type = TP_NUMERIC;
	token_id->value    = value;
}


/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
void token_assign_dictionary(TOKEN_HEAP *heap, TOKEN_ID token_id, TOKEN_HEAP *dictionary)
{
	token_id->ptr_type = TP_DICTIONARY;
	token_id->ptr      = dictionary;
}


/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
void token_assign_list(TOKEN_HEAP *heap, TOKEN_ID token_id, TOKEN_HEAP *list)
{
	token_id->ptr_type = TP_LIST;
	token_id->ptr      = list;
}


/* -------------------------------------------------------------------- */
/* Add our reserved tokens to the reserved builtin heap.		*/
/* if builtin heap has been filled by an earlier call just		*/
/* return from this function.						*/
/* -------------------------------------------------------------------- */
void init_builtin_heap(void)
{
	int 	i;

	if (builtin.list == NULL)
	{
		for (i=0; init_token_table[i].var != NULL; i++)
		{
			*(init_token_table[i].var) = add_token(&builtin, 
			                                       init_token_table[i].str,
			                                       init_token_table[i].type,
			                                       TP_NULL,
			                                       init_token_table[i].ptr);
		}
	}
}


/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
TOKEN_HEAP *init_heap(TOKEN_HEAP *heap)
{
	heap->list = NULL;
	return heap;
}


/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
TOKEN_HEAP *generate_new_heap(void)
{
	TOKEN_HEAP
		*heap;

	heap = (TOKEN_HEAP *)malloc(sizeof(TOKEN_HEAP));
	if (heap == NULL)
	{
		lprintf(LOG_ERROR, "malloc() failed\n");
		exit(EMALLOC);
	}

	return init_heap(heap);
}


/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
void dump_heap(TOKEN_HEAP *heap, char *name)
{
	dump_heaptofile(heap, name, stdout);
}


/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
void dump_heaptofile_internal(TOKEN_HEAP *heap, char *name, FILE *fp, int level, int islist)
{
	int	i;
	TOKEN_LIST
		*item;
	TOKEN_ID
		token;


	
	if (heap == NULL)
	{	return;
	}

	item = heap->list;
	while(item != NULL)
	{
		token = item->token;

		if (get_token_type(heap, token) == T_IDENTIFIER)
		{
			if (get_token_indirect_type(heap, token) == TP_STRING)
			{

				for(i=0; i<level; i++)
				{	fputc('\t', fp);
				}

				if (islist)
				{
					fprintf(fp, "\"%s\"", 
					        get_token_indirect_strvalue(heap, token));
				}
				else
				{
					fprintf(fp, "%s = \"%s\"", 
					        get_token_strvalue(heap, token),
					        get_token_indirect_strvalue(heap, token));
				}

				fprintf(fp, "%s\n", ((islist && (item->next != NULL))?",":""));
			}
			else
			if (get_token_indirect_type(heap, token) == TP_NUMERIC)
			{

				for(i=0; i<level; i++)
				{	fputc('\t', fp);
				}

				if (islist)
				{
					fprintf(fp, "%s",
					        get_token_indirect_strvalue(heap, token));
				}
				else
				{
					fprintf(fp, "%s = %s", 
					        get_token_strvalue(heap, token),
					        get_token_indirect_strvalue(heap, token));
				}

				fprintf(fp, "%s\n", ((islist && (item->next != NULL))?",":""));
			}
			else
			if (get_token_indirect_type(heap, token) == TP_DICTIONARY)
			{

				for(i=0; i<level; i++)
				{	fputc('\t', fp);
				}

				fprintf(fp, "%s =\n", get_token_strvalue(heap, token));

				for(i=0; i<level; i++)
				{	fputc('\t', fp);
				}

				fprintf(fp, "{ \n");


				dump_heaptofile_internal(get_token_indirect_dictionary(heap, token), 
			 	          get_token_strvalue(heap, token), fp, level+1, FALSE);


				for(i=0; i<level; i++)
				{	fputc('\t', fp);
				}

				fprintf(fp, "}%s\n", ((islist && (item->next != NULL))?",":""));

			}
			else
			if (get_token_indirect_type(heap, token) == TP_LIST)
			{

				for(i=0; i<level; i++)
				{	fputc('\t', fp);
				}

				fprintf(fp, "%s =\n", get_token_strvalue(heap, token));


				for(i=0; i<level; i++)
				{	fputc('\t', fp);
				}


				fprintf(fp, "(\n");

				dump_heaptofile_internal(get_token_indirect_list(heap, token), 
			 	          get_token_strvalue(heap, token), fp, level+1, TRUE);


				for(i=0; i<level; i++)
				{	fputc('\t', fp);
				}


				fprintf(fp, ")%s\n", ((islist && (item->next != NULL))?",":""));
			}
		}

		item = item->next;
	}
}


/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
void dump_heaptofile(TOKEN_HEAP *heap, char *name, FILE *fp)
{
	fprintf(fp, "{\n");

	dump_heaptofile_internal(heap, name, fp, 1, FALSE);

	fprintf(fp, "}\n");
}


/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
TOKEN_HEAP *dup_heap(TOKEN_HEAP *heap)
{
	TOKEN_HEAP
		*new_heap,
		*list;

	TOKEN_LIST
		*item;
		
	TOKEN_ID
		token,
		new_token, 
		identifier;
	char	*str;



	if (heap == NULL)
	{	return NULL;
	}

	new_heap = generate_new_heap();

	item = heap->list;
	while(item != NULL)
	{
		token = item->token;

		if (get_token_type(heap, token) == T_IDENTIFIER)
		{
			identifier = add_token(new_heap, token->str, token->type, TP_NULL, NULL);


			if (get_token_indirect_type(heap, token) == TP_STRING)
			{
				str = get_token_indirect_strvalue(heap, token);
				new_token = add_token(new_heap, str, T_STRING, TP_NULL, NULL);
				str = get_token_strvalue(new_heap, new_token);

		                token_assign_strvalue(new_heap, identifier, str);
			}
			else
			if ((get_token_indirect_type(heap, token) == TP_DICTIONARY) ||
			    (get_token_indirect_type(heap, token) == TP_LIST))
			{
				list = dup_heap(get_token_indirect_dictionary(heap, token));

		                token_assign_list(new_heap, identifier, list);
			}
		}


		item = item->next;
	}

	return new_heap;
}

/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
void free_heap(TOKEN_HEAP *heap)
{
	TOKEN_LIST
		*item,
		*prev_item;

		
	TOKEN_ID
		token;


	item = heap->list;
	while(item != NULL)
	{
		token = item->token;
		if (get_token_type(heap, token) == T_IDENTIFIER)
		{
			if (get_token_indirect_type(heap, token) == TP_DICTIONARY)
			{
				free_heap(get_token_indirect_dictionary(heap, token));
			}
			else
			if (get_token_indirect_type(heap, token) == TP_LIST)
			{
				free_heap(get_token_indirect_list(heap, token));
			}
		}

		prev_item = item;
		item = item->next;

		free(prev_item->token->str);
		free(prev_item->token);
		free(prev_item);
	}
}

