This is COWSIPCLibrary.m in view mode; [Download] [Up]
/*
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
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.