/* runtime.c

 * This file contains the functions to interpret the TSL instructions and
 * interact with the remote host */

/* This file is part of TSL.
 *
 * TSL is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * TSL 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with TSL; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include "tsl.h"

void runtime( int sockfd, struct list_struct *sections )
{
	struct list_struct *lines, *fields, *vars, *rotary;
	int maxloops = 0;							/* Default max # of pattern matches */
	int current_loops;						/* The # of pattern matches so far */
	int waitfor=30;				        /* Default # of seconds to wait */
	FILE *caphandle=NULL;					/* Capture file handle */
	int capturing=0;							/* Flag to indicate if capturing is active */
	int verbose=0;								/* Flag to indicate if local printing is active */

	/* Initialize variables */
	vars=list_create();						/* The list of user specified variables */
	rotary=list_create();					/* The list of rotary variables */

	/* Begin by processing the first section */
	list_rewind( sections );
	alarm( waitfor );							/* Set the alarm */

	while( 1 )										/* Loop through each section */
	{
		int searching = 0;							/* We are not yet pattern matching in this
																			 section */
		int inon=0;											/* In an "on" clause */
		int onlyon=0;										/* Only process "ON" clauses */

		current_loops = 0;							/* We have found no matches so far */
		lines = list_get2( sections );	/* Get the lines for this section */
		list_rewind( lines );						/* Start at the first line */

		while( 1 )									/* Process all lines */
		{
			if ( timeout_flag )						/* If the alarm went off */
				return;											/*   stop all processing */

			fields = list_get2( lines );				/* Load the fields for the line */
			list_rewind( fields );							/* Start at the first field */

			/* VERSION */
			if ( strcmp( list_get1(fields), "version") == 0 && !onlyon )
			{
				/* Make sure that the file version isn't greater that what we
					 understand */
				int fileversion;
				list_next(fields);							/* Get the file version */
				fileversion = atoi(list_get1(fields));
				if ( fileversion > MAX_VERSION )
				{
					printf( "Error TSL file version is %d, maximum supported "
							"version is %d.", fileversion, MAX_VERSION );
					exit(1);
				}
			}

			/* SLEEP  */
			else if ( strcmp( list_get1(fields), "sleep") == 0 && !onlyon )
			{
				int sleeptime;

				list_next(fields);
				sleeptime = atoi( list_get1(fields) );
				if ( sleeptime > 0 )
				{
					alarm(0);							/* Disable alarm until we wake */
					sleep( sleeptime );
					alarm(waitfor);				/* Reinstate alarm */
				}
			}

			/* VERBOSE */
			else if ( strcmp( list_get1(fields), "verbose") == 0 && !onlyon )
			{
				list_next(fields);							/* Retrieve the maximum # */
				if ( strcasecmp( list_get1(fields), "on" ) == 0 )
					verbose = 1;
				else if ( strcasecmp( list_get1(fields), "off" ) == 0 )
					verbose = 0;
			}

			/* WAITFOR */
			else if ( strcmp( list_get1(fields), "waitfor") == 0 && !onlyon )
			{
				list_next(fields);							/* Retrieve the maximum # */
				waitfor = atoi(list_get1(fields));	/* Set the new default */
				alarm( waitfor );
			}

			/* SENDFILE */
			else if ( strcmp( list_get1(fields), "sendfile") == 0 && !onlyon )
			{
				FILE *sendfile;
				char *buffer;
				int octets;
				char *dummy;

				buffer = malloc( 1024 );
				assert( buffer );

				list_next(fields);
				dummy = get_expanded( list_get1(fields), vars, rotary, 0 );
				sendfile = fopen( dummy, "r" );
				if ( sendfile != NULL )
				{
					while( (octets=fread(buffer,1,1023, sendfile)) != 0)
					{
						buffer[octets]=0;
						send_string( sockfd, buffer );
					}
					fclose( sendfile );
				}
				free( buffer );
			}

			/* MAXLOOPS */
			else if ( strcmp( list_get1(fields), "maxloops") == 0 && !onlyon )
			{
				list_next(fields);							/* Retrieve the maximum # */
				maxloops = atoi(list_get1(fields));	/* Set the new default */
			}

			/* ++ */
			else if ( strcmp( list_get1(fields), "++" ) == 0 && !onlyon )
			{
				list_next(fields);
				if ( list_findfirst( vars, list_get1(fields)) == 1 )
				{
					char *newval;
					int i;

					/* Get the current value */
					i = atoi( list_get2(vars) );

					/* Set the new value */
					free( list_get2(vars) );
					newval = calloc( 1, 1024 );
					assert( newval );
					sprintf( newval, "%d", ++i );
					newval = realloc( newval, strlen(newval)+1 );
					list_set2(vars, newval );
				}
			}

			/* --  */
			else if ( strcmp( list_get1(fields), "--" ) == 0 && !onlyon )
			{
				list_next(fields);
				if ( list_findfirst( vars, list_get1(fields)) == 1 )
				{
					char *newval;
					int i;

					/* Get the current value */
					i = atoi( list_get2(vars) );

					/* Set the new value */
					free( list_get2(vars) );
					newval = calloc( 1, 1024 );
					assert( newval );
					sprintf( newval, "%d", --i );
					newval = realloc( newval, strlen(newval)+1 );
					list_set2(vars, newval );
				}
			}

			/* VAR */
			else if ( strcmp( list_get1(fields), "var" ) == 0 && !onlyon )
			{
				list_next(fields);								/* Point at the variable name */
				/* Already a variable? */
				if ( list_findfirst(vars,list_get1(fields)) == 1 )
				{
					list_next(fields);											/* If so, reset it */
					list_set2(vars,list_get1(fields));		
				}
				/* Otherwise, add it */
				else
				{
					char *varname;
					varname = list_get1(fields);					/* variable name */
					list_next(fields);										/* variable value */
					list_add( vars, varname, list_get1(fields) );
				}
			}

			/* ROTARY */
			else if ( strcmp( list_get1(fields), "rotary") == 0 && !onlyon )
			{
				struct list_struct *temp;					/* This will contain the rotary
																						 variables */
				list_next(fields);								/* Point to variable name */

				/* If we are modifying an old rotary variable, delete its previous
					 values first */
				if ( list_findfirst(rotary,list_get1(fields)) == 1 )
				{
					temp=list_get2(fields);					/* Get old list */
					list_free(temp);								/* Delete old entries */
				}
				/* If this is a new rotary variable then we need to create a new
					 list to hold its values */
				else
				{
					temp=list_create();							/* Create an empty list */
					list_add( rotary, list_get1(fields), NULL );	/* Populate */
					list_findfirst( rotary, (char *)list_get1(fields) ); /* Jump to it */
				}

				/* Add the specified values to the rotary variable */
				while( 1 )
				{
					list_next(fields);							/* Point to next value */
					list_add(temp, list_get1(fields), NULL );	/* Add it to the rotary */
					if ( list_atlast(fields) )
					{
						list_rewind(temp);						/* Make sure we start at the first one */
						break;
					}
				}

				/* Modify the rotary variable with the list of possible values */
				list_set2( rotary, temp );
			}

			/* DONE */
			else if ( strcmp( list_get1(fields), "done") == 0 && !onlyon )
			{
				if ( list_atlast(sections) )		/* If we are at the last section,  */
					return;												/*     then we're done! */
	 			list_next(sections);						/* We need to start processing the
																				 next section */
				alarm( waitfor );								/* Reset alarm on new section */
				break;
			}

			/* GOTO */
			else if ( strcmp( list_get1(fields), "goto") == 0 && !onlyon )
			{
				char *current_section;
				char *new_section;

				/* Get the name of the section to jump to */
				list_next(fields);
				new_section = get_expanded( list_get1(fields),
						vars, rotary, 0 );

				/* Save our place in case we can't find the jump point */
				current_section = list_get1(sections);

				if ( list_findfirst( sections, new_section ) == 1 )
					break;								/* Found it, resume processing there */
				
				/* We couldn't find the jump point.  Stay put. */
				fprintf( stderr, "Unable to find jump point \"%s\"", new_section );
				list_findfirst( sections, current_section );
			}

			/* SEARCH */
			else if ( strcmp( list_get1(fields), "search" ) == 0 && !onlyon )
			{
				int retrchar;
				searching = 1;
				retrchar = get_char(sockfd);
				if ( capturing )
					fprintf( caphandle, "%c", retrchar );
				if ( verbose )
					printf( "%c", retrchar );
			}

			/* SEND */
			else if ( strcmp( list_get1(fields), "send" ) == 0 && !onlyon )
			{
				char *dummy;
				while(1)
				{
					list_next(fields);							/* Get the next argument to send */
					dummy = get_expanded( list_get1(fields), vars, rotary, 0 );
					if ( strlen(dummy) > 0 )
						send_string( sockfd, dummy );
					if ( list_atlast(fields) )
						break;
				}
			}

			/* WRITE */
			else if ( strcmp( list_get1(fields), "write" ) == 0 && !onlyon )
			{
				char *dummy;
				while(1)
				{
					list_next(fields);							/* Get the next argument to send */
					dummy = get_expanded( list_get1(fields), vars, rotary, 0 );
					if ( strlen(dummy) > 0 )
					{
						if ( capturing )
							fprintf( caphandle, "%s", dummy );
						if ( verbose )
							printf( "%s", dummy );
					}
					if ( list_atlast(fields) )
						break;
				}
			}

			/* ON */
			else if ( strcmp( list_get1(fields), "on" ) == 0 )
			{
				if ( inon == 1 )								/* If already processing a previous ON */
				{
					while( ! list_atlast(lines) )   /*  Jump to the end of the section */
						list_next(lines);
				}
				else
				{
					list_next(fields); 						/* If not, see if we match this ON */
					if ( netmatch( get_expanded( list_get1(fields),
								 vars, rotary, 0)	) )
					{
						inon=1;											/* If so, process the statements */
						onlyon=0;
						++current_loops;						/* We are matching, note this */
						if ( current_loops > maxloops    /* Have we matched too many times? */
								&& maxloops != 0 )
						{
							fprintf( stderr, "Exceeded %d pattern matches in the same section, "
									"probably in an infinite loop.\n", maxloops );
							exit(1);
						}
					}
					else													/* Otherwise skip all statements except */
						onlyon=1;									  /* for other ON match clauses */	
				}
			}

			/* EXIT */
			else if ( strcmp( list_get1(fields), "exit" ) == 0 && !onlyon &&
				 !capturing	)
			{
				list_next(fields);
				exit( atoi(list_get1(fields)) );
			}
			
			/* SYSTEM */
			else if ( strcmp( list_get1(fields), "system" ) == 0 && !onlyon &&
				 !capturing	)
			{
				char *cmd, *dummy;

				cmd = calloc( 1, 1 );
				assert(cmd);
				while(1)
				{
					list_next(fields);
					dummy = get_expanded( list_get1(fields), vars, rotary, 0 ); 
					if ( strlen(dummy) > 0 )
					{
						cmd = realloc( cmd,
								strlen(cmd)+strlen(dummy)+2 );
						assert(cmd);
						strcat(cmd, dummy );
					}
					if ( list_atlast(fields) )
						break;
				}
				system( cmd );
				free( cmd );
			}


			/* CAPTURE */
			else if ( strcmp( list_get1(fields), "capture" ) == 0 && !onlyon &&
				 !capturing	)
			{
				char *cap_filename, *dummy;

				cap_filename = calloc(1,1);
				assert( cap_filename );
				while(1)
				{
					list_next(fields);
					dummy = get_expanded( list_get1(fields), vars, rotary, 0 );
					if ( strlen(dummy) > 0 )
					{
						cap_filename = realloc( cap_filename,
								strlen(cap_filename)+strlen(dummy)+1 );
						assert(cap_filename);
						strcat( cap_filename, dummy );
					}
					if ( list_atlast(fields) )
						break;
				}

				caphandle = fopen( cap_filename, "a" );
				if ( caphandle == NULL )
					perror( "fopen" );
				else
					capturing=1;

				free( cap_filename );
			}

			/* ENDCAP */
			else if ( strcmp( list_get1(fields), "endcap" ) == 0 && !onlyon &&
					capturing )
			{
				fclose( caphandle );
				capturing=0;
			}

			/* OTHER (UNKNOWN) */
			else
			{
			}

			/* The line has been processed above, we need to read the next line */
			if ( list_atlast(lines) )						/* If we are at the last line */
			{
				if ( ! searching )								/*   and we're not looping */
				{
					if ( list_atlast(sections) )		/*     and at the last section */
						return;												/*     then we're done! */
					list_next(sections);						/*     Otherwise, we process the */
					alarm( waitfor );								/*     next section (reset alarm too) */
					break;													
				}
				else															/* At the last line, but searching */
				{
					int debugchar;
					if ( (debugchar=get_char(sockfd)) == -1 ) /* Alarm or EOF */
					{
						/* If timeout_flag == 1 then we reached here due to timeout.  If
							 == 0 then we reached here due to remote close */
						exit(timeout_flag);
					}

					if ( verbose )
						printf( "%c", debugchar );
					if ( capturing )
						fprintf( caphandle, "%c", debugchar );

					list_rewind(lines);							/*   Jump to the SEARCH line */
					onlyon=0;
					inon=0;
					while(1)
					{
						fields=list_get2(lines);
						if ( strcmp( list_get1(fields), "search" ) == 0 )
							break;
						list_next(lines);
					}
					list_next(lines);								/* Start on the first statement
																						 AFTER search */
				}	
			}
			else																/* We are NOT at the last line */
				list_next(lines);									/*   so get the next line */
		}				/* End of LINES loop */

		/* We are done processing the above section.  The next section to process
			 has already been fetched and sections now points to it. */
	}			/* End of SECTIONS loop */
}

char *get_expanded( char *word, struct list_struct *vars, 
		struct list_struct *rotary, int recurse_factor )
{
	int wordlen;
	static char *return_name = NULL;
	char *strippedname;

	/* Sanity check */
	assert( word && vars && rotary );
	if ( recurse_factor == 10 )
		return word;								/* Return original if recursing too far */

	/* Set up buffer */
	if ( return_name != NULL )
	{
		free( return_name );
		return_name = NULL;
	}

	wordlen = strlen(word);
	/* If less than 3 chars then we are definately normal text */
	if ( wordlen<3 )
	{
		return_name = strdup(word);
		assert(return_name);
		return return_name;
	}
	 
	/* Check to see if we have a variable */	
	if ( word[0] == '_' && word[wordlen-1] == '_' ) 
	{
		strippedname = strdup( word+1 );
		assert( strippedname );
		strippedname[wordlen-2] = 0;
		if ( list_findfirst(vars, strippedname) == 1 )   /* Look for variable */
		{
			free( strippedname );
			return get_expanded( list_get2(vars), vars, rotary, recurse_factor+1 );
		}
		else
		{
			free( strippedname );
			return "";                                     /* Return blank on not found */
		}
	}

	/* Check to see if we have a rotary variable */
	if ( word[0] == '@' && word[wordlen-1] == '@' )
	{
		strippedname = strdup( word+1 );
		assert( strippedname );
		strippedname[wordlen-2] = 0;
		if ( list_findfirst( rotary, strippedname ) == 1 )  /* Look for rotary */
		{
			struct list_struct *rtry_fields;
			free( strippedname );
			rtry_fields = list_get2(rotary);     	/* Point to the rotary values */ 
			strippedname = list_get1( rtry_fields );  /* Get the current value */
			if ( list_atlast(rtry_fields) )				/* Point to the next value */
				list_rewind(rtry_fields);
			else
				list_next(rtry_fields);
			return get_expanded( strippedname, vars, rotary, recurse_factor+1 );
		}
		else
		{
			free( strippedname );
			return "";                                     /* Return blank on not found */
		}
	}

	/* Check to see if an environment variable */
	if ( word[0] == '$' && word[wordlen-1] == '$' )
	{
		strippedname = strdup( word+1 );
		assert( strippedname );
		strippedname[wordlen-2] = 0;
		strippedname = getenv( strippedname );
		if ( strippedname != NULL )
			return get_expanded( strippedname, vars, rotary, recurse_factor+1 );
		else
			return "";
	}

	/* It seems to be just plain-old text */
	return word;
}

