/*
	Copyright (C) 1994 Sean Luke

	COWSIPCLibrary.m
	Version 10
	Sean Luke
	
*/




#import "COWSIPCLibrary.h"
#import <stdio.h>
#import <remote/NXProxy.h>




// Timed Entry

DPSTimedEntryProc _ipc_timer
	(DPSTimedEntry teNum, double now, void* ipc_library)
	{
	COWSIPCLibrary* the_library=(COWSIPCLibrary*) ipc_library;
	[the_library _checkup];
	return (void*) NULL;
	}





@implementation COWSIPCLibrary


- init
	{
	char nm[COWSLARGESTPATHLENGTH];	
	id returnval=[super init];
	sprintf (nm,"%s(COWS)",[NXApp appName]);
	//printf ("%s(COWS)\n",[NXApp appName]);
	connection = [NXConnection registerRoot: self withName:nm];
	[connection runFromAppKit];
	arguments=[[COWSArgumentList alloc] init];
	arguments_state=COWSIPCLIBRARY_ARGSTATE_READY;
	result=[[COWSStringNode alloc] init];
	inited=YES;
	return returnval;
	}
	
- awake
	{
	if (!inited) return [self init];
	return self;
	}	
	
	
- free
	{
	if (connection!=NULL) [connection free];
	[arguments free];
	[result free];
	[NXConnection removeObject:self];
	return [super free];
	}


- loadLibrary:sender
	{
	id returnval=[super loadLibrary:sender];
	
	if (![sender conformsTo:@protocol(LibraryControl)])
		{
		printf ("StandardLibrary error:  Interpreter cannot accept Library Control protocol!\n");
		return NULL;
		}
	[sender addLibraryFunction:"send-out"
			selector:@selector(ipc_sendout:)
			target:self];
	[sender addLibraryFunction:"send-out-remote"
			selector:@selector(ipc_sendout_machine:)
			target:self];
	[sender addLibraryFunction:"send"
			selector:@selector(ipc_send:)
			target:self];
	[sender addLibraryFunction:"send-remote"
			selector:@selector(ipc_send_machine:)
			target:self];
	[sender addLibraryFunction:"launch"
			selector:@selector(ipc_launch:)
			target:self];
	[sender addLibraryFunction:"launched?"
			selector:@selector(ipc_launched:)
			target:self];
	[sender makeMeALibraryDelegate:self];
	interpreter=sender;	
	return returnval;
	}


- _checkup						// pings the recieving library to see if it's done
	{
	int answer;
	
	// this option is for background sending interpreters
	
	[connection setOutTimeout:250];	// library will wait for 1/4 second before
									// giving up on message 
	NX_DURING
	answer=[server state];
	NX_HANDLER
 	if (NXLocalHandler.code >= NX_REMOTE_EXCEPTION_BASE &&
     	NXLocalHandler.code < NX_REMOTE_EXCEPTION_BASE +1000) 
		{
		//printf ("waiting on error\n");
		return NULL;		// try again next time...
		}
	else NX_RERAISE();
	NX_ENDHANDLER
	if (answer==COWSIPCLIBRARY_ARGSTATE_READY)
		{
		id return_val=[[COWSStringNode alloc] init];
		if (teNum) DPSRemoveTimedEntry(teNum);
		teNum=0;
		[return_val setString:[server result]];
		[return_val setError:[server resultIsError]];
		[interpreter resumeInterpretingWithValue:return_val];
		return self;
		}
	//printf ("waiting...\n");
	return NULL;
	}



- _send:arg_list:(BOOL) remote
	{
	char nm[COWSLARGESTPATHLENGTH];	
	id current;
	id name=[[COWSStringNode alloc] init];
	id function=[[COWSStringNode alloc] init];
	id machine=[[COWSStringNode alloc] init];
	int answer;
	
	
	if (remote)
		{
		if ([arg_list top]==NULL) 				// no args
			{
			id return_val=[[COWSStringNode alloc] init];
			if (remote) [return_val setString:"send-remote error:  no machine to connect to"];
			else [return_val setString:"send error:  no machine to connect to"];
			[return_val setError:YES];
			[name free];
			[machine free];
			[function free];
			return return_val;
			}
		else
			{
			current=[arg_list pop];
			[machine setString:[current string]];
			[current free];
			}
		}
		
	if ([arg_list top]==NULL) 				// no args
		{
		id return_val=[[COWSStringNode alloc] init];
		if (remote) [return_val setString:"send-remote error:  nothing to connect to"]; 
		else [return_val setString:"send error:  nothing to connect to"];
		[return_val setError:YES];
		[name free];
		[machine free];
		[function free];
		return return_val;
		}
	else
		{
		current=[arg_list pop];
		[name setString:[current string]];
		[current free];
		}
		
	if ([arg_list top]==NULL) 				// only one arg
		{
		id return_val=[[COWSStringNode alloc] init];
		if (remote) [return_val setString:"send-remote error:  no function to call"]; 
		else [return_val setString:"send error:  no function to call"];
		[return_val setError:YES];
		[name free];
		[machine free];
		[function free];
		return return_val;
		}
	else
		{
		current=[arg_list pop];
		[function setString:[current string]];
		[current free];
		}
	sprintf (nm,"%s(COWS)",[name string]);
	if (!remote) server = [NXConnection connectToName:nm];
	else server = [NXConnection connectToName:nm onHost:[machine string]];
	[name free];
	[machine free];
	[connection setOutTimeout:5000];// library will wait for 5 seconds before
									// giving up on message 
	if (server==nil)
		{
		id return_val=[[COWSStringNode alloc] init];
		if (remote) [return_val setString:"send-remote error:  cannot establish connection"]; 
		else [return_val setString:"send error:  cannot establish connection"];
		[return_val setError:YES];
		[function free];
		return return_val;
		}

	NX_DURING
	answer=[server conformsTo:@protocol(InterpreterIPC)];
	NX_HANDLER				
					// server is unavailable!
	
  		if (NXLocalHandler.code >= NX_REMOTE_EXCEPTION_BASE &&
   	    	NXLocalHandler.code < NX_REMOTE_EXCEPTION_BASE +1000) 
			{
			id return_val=[[COWSStringNode alloc] init];
   		    if (remote) [return_val setString:"send-remote error:  send timeout"]; 
			else [return_val setString:"send error:  send timeout"];
			[return_val setError:YES];
			[function free];
			return return_val;
			}
		else NX_RERAISE();
	NX_ENDHANDLER

	if (!answer)		// doesn't conform to protocol
			{
			id return_val=[[COWSStringNode alloc] init];
			if (remote) [return_val setString:"send-remote error:  remote object is not an interpreter"]; 
			else [return_val setString:"send error:  remote object is not an interpreter"];
			[return_val setError:YES];
			[function free];
			return return_val;
			}

	NX_DURING
	answer=[server state];
	NX_HANDLER				
					// server is unavailable!
	
  		if (NXLocalHandler.code >= NX_REMOTE_EXCEPTION_BASE &&
   	    	NXLocalHandler.code < NX_REMOTE_EXCEPTION_BASE +1000) 
			{
			id return_val=[[COWSStringNode alloc] init];
   		    if (remote) [return_val setString:"send-remote error:  send timeout"]; 
			else [return_val setString:"send error:  send timeout"];
			[return_val setError:YES];
			[function free];
			return return_val;
			}
		else NX_RERAISE();
	NX_ENDHANDLER


	if (answer!=COWSIPCLIBRARY_ARGSTATE_READY)			// interpreter is in a busy state
			{
			id return_val=[[COWSStringNode alloc] init];
			if (remote) [return_val setString:"send-remote error:  interpreter is busy"]; 
			else [return_val setString:"send error:  interpreter is busy"];
			[return_val setError:YES];
			[function free];
			return return_val;
			}



	while ([arg_list top]!=NULL)
		{
		id current=[arg_list pop];
		NX_DURING
		
		[server addArgument:(const char*)[current string]];
		
			// if server is working and background, this does nothing but print
			// a warning message to standard out on the server

		NX_HANDLER
		
			// if server is working and foreground, or app is actively engaged
			// in something important, this times out! 
		
  		if (NXLocalHandler.code >= NX_REMOTE_EXCEPTION_BASE &&
   		    NXLocalHandler.code < NX_REMOTE_EXCEPTION_BASE +1000) 
			{
			id return_val=[[COWSStringNode alloc] init];
   		    if (remote) [return_val setString:"send-remote error:  send timeout"]; 
			else [return_val setString: "send error:  send timeout"];
			[return_val setError:YES];
			[current free];
			[function free];
			return return_val;
			} 
		else NX_RERAISE();
		NX_ENDHANDLER
		
		[current free];
		}
	NX_DURING


	[server sendFunction:(const char*) [function string]];
	[function free];
	
	NX_HANDLER
  	if (NXLocalHandler.code >= NX_REMOTE_EXCEPTION_BASE &&
   	    NXLocalHandler.code < NX_REMOTE_EXCEPTION_BASE +1000) 
		{
		id return_val=[[COWSStringNode alloc] init];
		if (remote) [return_val setString:"send-remote error:  send timeout"]; 
		else [return_val setString:"send error:  send timeout"];
		[return_val setError:YES];
		return return_val;
		} 
	else NX_RERAISE();
	NX_ENDHANDLER

	// this option is good for foreground sending interpreters
	// but not background sending ones:
	
	if ([interpreter foreground]) 
		{
		id return_val=[[COWSStringNode alloc] init];
		while (1)
			{
			NX_DURING
			answer=[server state];
			NX_HANDLER
  			if (NXLocalHandler.code >= NX_REMOTE_EXCEPTION_BASE &&
   		    	NXLocalHandler.code < NX_REMOTE_EXCEPTION_BASE +1000) 
				{
				//printf ("waiting on error\n");
				usleep (500);					// pester twice a second
				continue;
				}
			else NX_RERAISE();
			NX_ENDHANDLER
			if (NXUserAborted())
				{
				[interpreter stopInterpreting];
				return return_val;
				}
			if (answer==COWSIPCLIBRARY_ARGSTATE_READY) break;
			//printf ("waiting\n");
			usleep (500);						// pester twice a second
			}
		//printf ("Got an answer\n");	
		[return_val setString:[server result]];
		[return_val setError:[server resultIsError]];
		return return_val;
		}
	else		// interpreter is background mode... set up timed entry
				// to do same thing
		{
		[interpreter pauseInterpreting];
		if (teNum) DPSRemoveTimedEntry(teNum);
		teNum=DPSAddTimedEntry(.5,					// pester twice a second 
			(DPSTimedEntryProc) _ipc_timer,
			(void*) self, (int) NX_RUNMODALTHRESHOLD);
		return NULL;
		}
	}

- ipc_send:arg_list
	{
	return [self _send:arg_list:NO];
	}

- ipc_send_machine:arg_list
	{
	return [self _send:arg_list:YES];
	}


- _sendout:arg_list:(BOOL) remote					// calls a remote function in another
													// interpreter and ignores answer.

	{
	char nm[COWSLARGESTPATHLENGTH];	
	id current;
	id name=[[COWSStringNode alloc] init];
	id function=[[COWSStringNode alloc] init];
	id machine=[[COWSStringNode alloc] init];
	int answer;
	
	
	if (remote)
		{
		if ([arg_list top]==NULL) 				// no args
			{
			id return_val=[[COWSStringNode alloc] init];
			if (remote) [return_val setString:"send-out-remote error:  no machine to connect to"];
			else [return_val setString:"send-out error:  no machine to connect to"];
			[return_val setError:YES];
			[name free];
			[machine free];
			[function free];
			return return_val;
			}
		else
			{
			current=[arg_list pop];
			[machine setString:[current string]];
			[current free];
			}
		}
		
	if ([arg_list top]==NULL) 				// no args
		{
		id return_val=[[COWSStringNode alloc] init];
		if (remote) [return_val setString:"send-out-remote error:  nothing to connect to"]; 
		else [return_val setString:"send-out error:  nothing to connect to"];
		[return_val setError:YES];
		[name free];
		[machine free];
		[function free];
		return return_val;
		}
	else
		{
		current=[arg_list pop];
		[name setString:[current string]];
		[current free];
		}
		
	if ([arg_list top]==NULL) 				// only one arg
		{
		id return_val=[[COWSStringNode alloc] init];
		if (remote) [return_val setString:"send-out-remote error:  no function to call"]; 
		else [return_val setString:"send-out error:  no function to call"];
		[return_val setError:YES];
		[name free];
		[machine free];
		[function free];
		return return_val;
		}
	else
		{
		current=[arg_list pop];
		[function setString:[current string]];
		[current free];
		}
	sprintf (nm,"%s(COWS)",[name string]);
	if (!remote) server = [NXConnection connectToName:nm];
	else server = [NXConnection connectToName:nm onHost:[machine string]];
	[name free];
	[machine free];
	[connection setOutTimeout:5000];// library will wait for 5 seconds before
									// giving up on message 
	if (server==nil)
		{
		id return_val=[[COWSStringNode alloc] init];
		if (remote) [return_val setString:"send-out-remote error:  cannot establish connection"]; 
		else [return_val setString:"send-out error:  cannot establish connection"];
		[return_val setError:YES];
		[function free];
		return return_val;
		}

	NX_DURING
	answer=[server conformsTo:@protocol(InterpreterIPC)];
	NX_HANDLER				
					// server is unavailable!
	
  		if (NXLocalHandler.code >= NX_REMOTE_EXCEPTION_BASE &&
   	    	NXLocalHandler.code < NX_REMOTE_EXCEPTION_BASE +1000) 
			{
			id return_val=[[COWSStringNode alloc] init];
   		    if (remote) [return_val setString:"send-out-remote error:  send timeout"]; 
			else [return_val setString:"send-out error:  send timeout"];
			[return_val setError:YES];
			[function free];
			return return_val;
			}
		else NX_RERAISE();
	NX_ENDHANDLER

	if (!answer)		// doesn't conform to protocol
			{
			id return_val=[[COWSStringNode alloc] init];
			if (remote) [return_val setString:"send-out-remote error:  remote object is not an interpreter"]; 
			else [return_val setString:"send-out error:  remote object is not an interpreter"];
			[return_val setError:YES];
			[function free];
			return return_val;
			}

	NX_DURING
	answer=[server state];
	NX_HANDLER				
					// server is unavailable!
	
  		if (NXLocalHandler.code >= NX_REMOTE_EXCEPTION_BASE &&
   	    	NXLocalHandler.code < NX_REMOTE_EXCEPTION_BASE +1000) 
			{
			id return_val=[[COWSStringNode alloc] init];
   		    if (remote) [return_val setString:"send-out-remote error:  send timeout"]; 
			else [return_val setString:"send-out error:  send timeout"];
			[return_val setError:YES];
			[function free];
			return return_val;
			}
		else NX_RERAISE();
	NX_ENDHANDLER


	if (answer!=COWSIPCLIBRARY_ARGSTATE_READY)			// interpreter is in a busy state
			{
			id return_val=[[COWSStringNode alloc] init];
			if (remote) [return_val setString:"send-out-remote error:  interpreter is busy"]; 
			else [return_val setString:"send-out error:  interpreter is busy"];
			[return_val setError:YES];
			[function free];
			return return_val;
			}



	while ([arg_list top]!=NULL)
		{
		id current=[arg_list pop];
		NX_DURING
		
		[server addArgument:(const char*)[current string]];
		
			// if server is working and background, this does nothing but print
			// a warning message to standard out on the server

		NX_HANDLER
		
			// if server is working and foreground, or app is actively engaged
			// in something important, this times out! 
		
  		if (NXLocalHandler.code >= NX_REMOTE_EXCEPTION_BASE &&
   		    NXLocalHandler.code < NX_REMOTE_EXCEPTION_BASE +1000) 
			{
			id return_val=[[COWSStringNode alloc] init];
   		    if (remote) [return_val setString:"send-out-remote error:  send timeout"]; 
			else [return_val setString: "send-out error:  send timeout"];
			[return_val setError:YES];
			[current free];
			[function free];
			return return_val;
			} 
		else NX_RERAISE();
		NX_ENDHANDLER
		
		[current free];
		}
	
	if (1)											// otherwise...	
		{
		id return_val=[[COWSStringNode alloc] init];
		[return_val setBooleanVal:YES];
		
		NX_DURING
	
		[server sendOutFunction:(const char*) [function string]];
					
		NX_HANDLER
	  		if (NXLocalHandler.code >= NX_REMOTE_EXCEPTION_BASE &&
	   	    NXLocalHandler.code < NX_REMOTE_EXCEPTION_BASE +1000) 
			{
	   	    if (remote) [return_val setString:"send-out-remote error:  send timeout"];
			else [return_val setString:"send-out error:  send timeout"];
			[return_val setError:YES];
			} 
		else NX_RERAISE();
		NX_ENDHANDLER
		
		[function free];
		return return_val;
		}
	}
	

- ipc_sendout:arg_list
	{
	return [self _sendout:arg_list:NO];
	}
	
- ipc_sendout_machine:arg_list
	{
	return [self _sendout:arg_list:YES];
	}	
	
	
- ipc_launch:arg_list
	{
	id current;
	id name=[[COWSStringNode alloc] init];
	id machine=[[COWSStringNode alloc] init];
	id return_val=[[COWSStringNode alloc] init];
	port_t response;
	
	if ([arg_list top]==NULL) 				// no args
		{
		[return_val setString:"launch error:  nothing to launch"];
		[return_val setError:YES];
		[name free];
		[machine free];
		return return_val;
		}
	else
		{
		current=[arg_list pop];
		[name setString:[current string]];
		[current free];
		}
		
	if ([arg_list top]==NULL) 				// only one arg
		{
		char host[MAXHOSTNAMELEN];
		gethostname(host,MAXHOSTNAMELEN);
		[machine setString:(const char*) host];
		}
	else
		{
		current=[arg_list pop];
		[machine setString:[current string]];
		[current free];
		}

	response=NXPortFromName([name string],[machine string]);
	[name free];
	[machine free];
	
	if (response==PORT_NULL)
		{
		[return_val setBooleanVal:NO];
		return return_val;
		}

	[return_val setBooleanVal:YES];
	return return_val;
	}



- ipc_launched:arg_list
	{
	id current;
	id name=[[COWSStringNode alloc] init];
	id machine=[[COWSStringNode alloc] init];
	id return_val=[[COWSStringNode alloc] init];
	port_t response;
	
	if ([arg_list top]==NULL) 				// no args
		{
		[return_val setString:"launched? error:  nothing to check for"];
		[return_val setError:YES];
		[name free];
		[machine free];
		return return_val;
		}
	else
		{
		current=[arg_list pop];
		[name setString:[current string]];
		[current free];
		}
		
	if ([arg_list top]==NULL) 				// only one arg
		{
		char host[MAXHOSTNAMELEN];
		gethostname(host,MAXHOSTNAMELEN);
		[machine setString:(const char*) host];
		}
	else
		{
		current=[arg_list pop];
		[machine setString:[current string]];
		[current free];
		}

	response=NXPortNameLookup([name string],[machine string]);
	[name free];
	[machine free];
	
	if (response==PORT_NULL)
		{
		[return_val setBooleanVal:NO];
		return return_val;
		}

	[return_val setBooleanVal:YES];
	return return_val;
	}

	
	
// Delegate Messages


- finishedInterpreting:(const char*)returnValue:(int)thisMessage:sender
	// this message is sent to the delegate, if it can receive it, after
	// the interpreter has finished interpreting a function request.
	// it is defined here for IPC purposes (an interpreter can be its
	// own delegate in an IPC process.
	{
	[result setString:returnValue];
	[result setError:NO];
	started=NO;
	return self;
	}

- errorInterpreting:(int) thisError:(const char*)thisFunction:
	(int)thisPosition:(const char*)thisString:sender
	// this message is sent to the delegate, if it can receive it, after
	// the interpreter has encountered an error while interpreting.
	{
	char anerror[COWSIPCLIBRARY_MAXERRSTRINGLENGTH];
	char msg[COWSIPCLIBRARY_MAXERRSTRINGLENGTH];
	
	switch (thisError)
		{
		case COWSLIBRARYFUNCTIONERROR : 
			strcpy(msg,thisString); break;
		case COWSERRORNOSUCHFUNCTION : 
			strcpy(msg,"No Such Function"); break;
		case COWSERRORNOTENOUGHARGUMENTS : 
			strcpy(msg,"Not Enough Arguments"); break;
		case COWSERRORTOOMANYARGUMENTS : 
			strcpy(msg,"Too Many Arguments"); break;
		case COWSERRORNOSUCHVARIABLE : 
			strcpy(msg,"No Such Variable"); break;
		case COWSERRORNOMORETOKENS : 
			strcpy(msg,"No More Tokens"); break;
		case COWSERRORNOFUNCTIONNAME : 
			strcpy(msg,"No Function Name"); break;
		case COWSERRORSETNOTVARIABLE : 
			strcpy(msg,"Set: Not a Variable"); break;
		case COWSERRORSETTOOMANYVALUES : 
			strcpy(msg,"Set: Too Many Values"); break;
		case COWSERRORSETNOVALUE : 
			strcpy(msg,"Set: No Value to Set"); break;
		case COWSERRORSETNOSUCHVARIABLE : 
			strcpy(msg,"Set: No Such Variable"); break;
		case COWSERRORIFNOTHENCLAUSE : 
			strcpy(msg,"If: No Then Clause"); break;
		case COWSERRORIFTOOMANYVALUES : 
			strcpy(msg,"If: Too Many Values"); break;
		case COWSERRORIFNOTENOUGHVALUES : 
			strcpy(msg,"If: Not Enough Values"); break;
		case COWSERRORWHILETOOMANYVALUES : 
			strcpy(msg,"While: Too Many Values"); break;
		case COWSERRORWHILENOTENOUGHVALUES : 
			strcpy(msg,"While: Not Enough Values"); break;
		case COWSERRORFORNOTAVARIABLE : 
			strcpy(msg,"For: Not a Variable"); break;
		case COWSERRORFORSTARTNOTNUMBER : 
			strcpy(msg,"For: Start Value Not a Number"); break;
		case COWSERRORFORSTOPNOTNUMBER : 
			strcpy(msg,"For: Stop Value Not a Number"); break;
		case COWSERRORFORSTEPNOTNUMBER : 
			strcpy(msg,"For: Step Value Not a Number"); break;
		case COWSERRORFORNOSUCHVARIABLE : 
			strcpy(msg,"For: No Such Variable"); break;
		case COWSERRORFORTOOMANYVALUES : 
			strcpy(msg,"For: Too Many Values"); break;
		case COWSERRORFORNOTENOUGHVALUES : 
			strcpy(msg,"For: Not Enough Values"); break;
		case COWSINTERNALERROR : 
			strcpy(msg,"Interpreter Internal Error"); break;
		default : 
			strcpy(msg,"Unknown Error"); break;
		}
	
	if (thisError==COWSLIBRARYFUNCTIONERROR)
		{
		sprintf (anerror,"Error from %s... %s",[NXApp appName], msg);
		}
	else
		{
		sprintf(anerror,"Error from %s... %s (%s, %d)",[NXApp appName], msg,thisString,thisPosition);
		}
	[result setString:anerror];
	[result setError:YES];
	started=NO;
	return self;
	}
	

- interpreterStopped:sender
	{
	char anerror[COWSIPCLIBRARY_MAXERRSTRINGLENGTH];
	if (sender==interpreter)
		{
		if (![result error]&&started) 			// interpreter prematurely stopped
			{
			sprintf (anerror,"Error from %s... User prematurely stopped interpreter",[NXApp appName]);
			[result setString:anerror];
			[result setError:YES];
			}
		started=NO;
		arguments_state=COWSIPCLIBRARY_ARGSTATE_READY;	// to remove problems receiving
		}
	return self;
	}

- interpreterDidStop:sender
	{
	if (teNum) DPSRemoveTimedEntry(teNum);			// to remove problems sending
	teNum=0;
	return self;
	}

- interpreterStarted:sender
	{
	started=YES;
	return self;
	}

- interpreterDidStart:sender
	{
	return self;
	}





// IPC Messages

- (void) addArgument:(const char*)this_argument
	{
	id new_string;
	//printf ("Adding Argument: %s\n",this_argument);
	if (arguments_state==COWSIPCLIBRARY_ARGSTATE_WORKING)
		{
		printf ("COWS IPC Error:  trying to begin new function before old one has finished\n");
		}
	
	new_string=[[COWSStringNode alloc] init];
	[new_string setString:this_argument];
	[arguments push:new_string];
	//printf ("Added Argument:  %s\n",[new_string string]);
	}


- (oneway void) sendOutFunction:(const char*) this_name
	{
	id arg_list=[[COWSArgumentList alloc] init];

	if (arguments_state==COWSIPCLIBRARY_ARGSTATE_WORKING)
		{
		printf ("COWS IPC Error:  calling new function before old one has finished\n");
		}
	else
		{
		arguments_state=COWSIPCLIBRARY_ARGSTATE_WORKING;
		
		// reverse the argument list	
				
		while([arguments top]!=NULL)
			{
			[arg_list push:[arguments pop]];
			}
		
		if (![interpreter locked]&&![interpreter working])
			// i.e., interpreter is able to respond to messages
			{
			// note no delegate is assigned.
			[interpreter setTempDelegate:NULL];
			[interpreter interpretFunction:this_name arguments:arg_list];
			}
		
		[arguments clear];
		arguments_state=COWSIPCLIBRARY_ARGSTATE_READY;
		}
	[arg_list free];
	}



- (oneway void) sendFunction:(const char*) this_name
	{
	id arg_list=[[COWSArgumentList alloc] init];

	if (arguments_state==COWSIPCLIBRARY_ARGSTATE_WORKING)
		{
		char junk[COWSLARGESTPATHLENGTH+40];
		sprintf (junk,"remote send error (%s):  calling new function before old one has finished.", [NXApp appName]);		
		[result setString:junk];
		[result setError:YES];	
		}
	else
		{
		arguments_state=COWSIPCLIBRARY_ARGSTATE_WORKING;
		
		// clean out result in preparation for delegate messages
		
		[result setString:""];
		[result setError:NO];
		
		// reverse the argument list	
				
		while([arguments top]!=NULL)
			{
			[arg_list push:[arguments pop]];
			}
		
		if (![interpreter locked]&&![interpreter working])
			{
			[interpreter setTempDelegate:self];
			[interpreter interpretFunction:this_name arguments:arg_list];
			}
		else
			{
			char junk[COWSLARGESTPATHLENGTH+40];
			sprintf (junk,"error from (%s):  application is busy.", [NXApp appName]);
			[result setString: junk];
			[result setError:YES];
			arguments_state=COWSIPCLIBRARY_ARGSTATE_READY;
			}
		}
	[arg_list free];
	}

- (BOOL) isForeground
	{
	if (interpreter==NULL) 
		{
		printf ("Yikes!  No Interpreter\n");
		return NO;
		}
	return (const) [interpreter foreground];
	}

- (const char*) result
	{
	return (const char*) [result string];
	}

- (BOOL) resultIsError
	{
	return (const) [result error];
	}
	
- (int) state
	{
	return (const) arguments_state;
	}



@end
