NSExecImpl.mesa
Copyright (C) 1984, 1985, 1986 by Xerox Corporation. All rights reserved.
Tim Diebert: December 11, 1986 1:22:02 pm PST
DIRECTORY
CHNameP2V0 USING [Name],
IO USING [STREAM],
NSString USING [AppendString, CopyString, EquivalentStrings, FreeString, LogicalLength, MakeString, nullString, ScanForCharacter, String, StringFromMesaString],
NSExec,
NSTTY USING [PutLine, SetPagination],
Process USING [Detach],
Rope USING [FromRefText, ROPE],
TTY USING [Handle, NewLine, PutCR, PutLine, PutString, UserAbort],
TTYExtras USING [],
XFormat USING [Handle],
XNS USING [Address, unknownAddress],
XNSAuth USING [Identity];
NSExecImpl:
CEDAR
MONITOR
IMPORTS NSExec, NSString, NSTTY, Rope, TTY, TTYExtras
EXPORTS NSExec
= BEGIN
OPEN CHName: CHNameP2V0;
-- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- --
Copied Types
-- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- --
ROPE: TYPE ~ Rope.ROPE;
STREAM: TYPE ~ IO.STREAM;
-- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- --
Types
-- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- --
Handle: PUBLIC TYPE = RECORD [session: CARDINAL];
Client: TYPE = REF ClientData;
ClientData:
TYPE =
RECORD [
name: NSString.String ← NSString.nullString, -- generic name of this client
command: NSExec.Command, -- the "primary" command registered to identify the client in the NSExec and executed by the user to switch to the client's context.
acronym: NSString.String ← NSString.nullString,
id: NSExec.ClientID,
procs: NSExec.ClientProcs,
initialized: BOOLEAN ← FALSE,
next: Client ← NIL]; -- pointer to next node in list
ExecData: TYPE = REF RECORD [data: SEQUENCE length: CARDINAL OF ExecObject];
ExecObject:
TYPE =
RECORD [
tty: TTY.Handle,
output: XFormat.Handle,
currentClient: Client ← NIL,
okForAsyncMessage: BOOLEAN ← FALSE, -- between commands
qTop: CARDINAL ← 0, -- index of first free queue slot in asyncQ
asyncQList: AsyncQList,
hostName: NSString.String ← NSString.nullString, -- of connected m/c
user:
SELECT loggedOnStatus: *
FROM
loggedOn => [
userName: CHName.Name ← [NIL, NIL, NIL],
userIdentity: XNSAuth.Identity ← NIL,
userFilingSession: NSFile.Session ← NSFile.nullSession,
userAServerSA, userEnabled: BOOLEAN ← FALSE],
notLoggedOn => NULL,
ENDCASE ← notLoggedOn[]];
AsyncQList: TYPE = RECORD[q: QArray];
QArray: TYPE = ARRAY [0..qSize) OF QEntry;
QEntry: TYPE = RECORD [time: CARD ← 0, message: NSString.String ← NIL];
qSize: CARDINAL = 3;
ActiveServices: TYPE = REF ActiveStatus;
ActiveStatus: TYPE = RECORD [name: NSString.String, next: ActiveServices];
-- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- --
Constants :
-- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- --
-- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- --
Variables :
-- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- --
localExec: PUBLIC READONLY Handle; -- handle for the local TTY
totalClients: CARDINAL ← 0;
firstClient: PUBLIC Client ← NIL;
execData: ExecData ← NIL;
serverName: CHName.Name ← [NIL, NIL, NIL];
serverDesc: NSString.String ← NSString.nullString;
serverIdentity: XNSAuth.Identity;
-- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- --
Errors:
-- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- --
Error: PUBLIC ERROR [type: NSExec.Errortype] = CODE;
NotFound: ERROR = CODE;
-- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- --
Creating and Manipulating Exec Clients:
-- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- --
AssignClientID:
PUBLIC
ENTRY
PROCEDURE
RETURNS [NSExec.ClientID] = {
ENABLE UNWIND => NULL; RETURN[[totalClients ← totalClients + 1]]};
CreateClient:
PUBLIC
ENTRY
PROCEDURE [id: NSExec.ClientID, command: NSExec.Command,
procs: NSExec.ClientProcs, acronym: NSString.String] =
BEGIN
ENABLE UNWIND => NULL;
IF NOT ValidID[id] THEN RETURN WITH ERROR NSExec.Error[invalidID];
IF command.name.length = 0
OR acronym.length = 0
THEN
RETURN WITH ERROR NSExec.Error[invalidString];
BEGIN
clientName: NSString.String ← NSString.CopyString[command.name];
firstClient ← NEW[ClientData ← [name: clientName,
command: [clientName, command.proc, command.activityCheck, command.helpProc],
acronym: NSString.CopyString[acronym], id: id, procs: procs,
next: firstClient]];
END;
END; -- CreateClient
DestroyClient:
PUBLIC
ENTRY
PROCEDURE [id: NSExec.ClientID] =
BEGIN
ENABLE UNWIND => NULL;
client, predecessor: Client;
[client, predecessor] ← FindClient[id ! NotFound => ERROR NSExec.Error[invalidID]];
RemoveClientCommands[id];
IF predecessor = NIL THEN firstClient ← client.next ELSE predecessor.next ← client.next;
END; -- DestroyClient
EnumerateClients:
PUBLIC
PROCEDURE [enumProc: NSExec.EnumerateClientProc] =
BEGIN
continue: BOOLEAN ← TRUE;
FOR client: Client ← firstClient, client.next
UNTIL client =
NIL
OR
NOT continue
DO
IF ~client.initialized THEN LOOP;
continue ←
enumProc[client.id, client.command, Rope.FromRefText[client.acronym], client.procs]
ENDLOOP;
END; -- EnumerateClients
ClientState:
PUBLIC
PROCEDURE [id: NSExec.ClientID]
RETURNS [activated, initialized:
BOOLEAN] =
BEGIN
client: Client ← FindClient[id ! NotFound => ERROR NSExec.Error[invalidID]].client;
activated ← Activated[client.name];
initialized ← client.initialized;
END; -- ClientState
AddClientCommands:
PUBLIC
PROCEDURE [id: NSExec.ClientID, commands: NSExec.Commands] =
BEGIN
ENABLE UNWIND => NULL;
client: Client ← FindClient[id ! NotFound => ERROR NSExec.Error[invalidID]].client;
FOR i:
CARDINAL
IN [0..commands.length)
DO
AddCommand[commands[i], client] ENDLOOP
END; -- AddClientCommands
-- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- --
Creating and Manipulating Execs:
-- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- --
CreateExec:
PUBLIC
PROCEDURE [tty:
TTY.Handle, host:
XNS.Address] =
BEGIN
ENABLE ABORTED => CONTINUE;
<< ASSERT: 1) All calls to this proc are for OTHER than the local exec. 2) The server profile has been established. >>
TTYExtras.SetDataFile[tty, LOOPHOLE[UserProfile.GetEnumeratedValue[[XM[keyServer], XM[keyRemoteTerminalType]]]]];
DisplayHerald[tty: tty, displayTime: FALSE]; -- may raise ABORTED
ExecProcess[CreateExecInternal[tty, host].exec];
END; -- CreateExec
EnumerateExecs:
PUBLIC
PROCEDURE [enumProc: NSExec.EnumerateExecProc] = {};
Aborted: SIGNAL = CODE;
EnumerateProc: NSSession.EnumerateProc = {
proc: PROCEDURE = {IF ~enumProc[LOOPHOLE[session]] THEN SIGNAL Aborted};
EnterSessionAndExecute[session, proc ! NSSession.Error => CONTINUE]};
NSSession.Enumerate[sessionId, EnumerateProc ! Aborted => CONTINUE]};
AddExecCommand:
PUBLIC
PROCEDURE [command: NSExec.Command,
unload:
PROCEDURE ←
NIL] =
BEGIN AddCommand[command: command, client: NIL, unload: unload]; END; -- AddExecCommand
RemoveExecCommand: PROCEDURE [name: NSString.String];
removes name as an exec command, having no effect if name is not a command.
GetTTY:
PUBLIC
PROCEDURE [exec: NSExec.Handle]
RETURNS [tty:
TTY.Handle] = {
RETURN[execData.data[exec.session].tty]};
CheckForAbort:
PUBLIC
PROCEDURE [exec: NSExec.Handle]
RETURNS [abort:
BOOLEAN] =
{RETURN[TTY.UserAbort[NSExec.GetTTY[exec]]]};
SetContext:
PUBLIC
PROCEDURE [exec: Handle, id: NSExec.ClientID, context: NSExec.Context,
terminationHandler: NSExec.TerminationHandler] =
BEGIN
noContext: BOOLEAN ← FALSE;
newContext: LONG POINTER TO ClientContext;
IF ~NSExecInternal.ValidID[id] THEN NSExec.Error[invalidID];
newContext ← NSSession.GetContext[exec, sessionId, id ! NSSession.Error => {noContext ← TRUE; CONTINUE}];
IF noContext THEN
newContext ← NEW[
ClientContext ← [
clientData: context, clientTerminateProc: terminationHandler]]
ELSE
newContext^ ← [clientData: context, clientTerminateProc: terminationHandler];
NSSession.SetContext[exec, sessionId, newContext, TerminationHandler, id];
END; -- SetContext
GetContext:
PUBLIC
PROCEDURE [exec: Handle, id: NSExec.ClientID]
RETURNS [context:
LONG
UNSPECIFIED] =
BEGIN
clientContext: LONG POINTER TO ClientContext;
IF ~NSExecInternal.ValidID[id] THEN NSExec.Error[invalidID];
clientContext ← NSSession.GetContext[exec.session, sessionId, LOOPHOLE[id]
! NSSession.Error => IF type = notSet THEN NSExec.Error[noContextSet]];
RETURN[clientContext.clientData];
RETURN[LONG[0]];
END; -- of GetContext
DestroyContext:
PUBLIC
PROCEDURE [exec: Handle, id: NSExec.ClientID] =
BEGIN
IF ~NSExecInternal.ValidID[id] THEN NSExec.Error[invalidID];
NSSession.DestroyContext[exec, sessionId, id ! NSSession.Error => GOTO NoContext];
EXITS NoContext => ERROR NSExec.Error[noContextSet];
END; -- of DestroyContext
-- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- --
String Output:
-- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- --
BroadcastAsyncMessage:
PUBLIC
PROCEDURE [string: NSString.String, id: NSExec.ClientID] = {
broadcastMessage: NSExec.EnumerateExecProc = {PutAsyncMessage[exec, string, id]};
NSExec.EnumerateExecs[broadcastMessage]}; -- of BroadcastAsyncMessage
PutAsyncMessage:
PUBLIC
PROCEDURE [exec: NSExec.Handle, string: NSString.String,
id: NSExec.ClientID] =
BEGIN
client: Client ← FindClient[id ! NotFound => ERROR NSExec.Error[invalidID]].client;
msg: NSString.String ← NSString.MakeString[client.acronym.length + 2 + string.length];
tty: TTY.Handle; -- should be initialized within the catch phase for NSSession.Error
BEGIN
ENABLE
--NSSession.Error, --
ABORTED => {NSString.FreeString[msg];
CONTINUE};
msg ← NSString.AppendString[msg, client.acronym];
msg ← NSString.AppendString[msg, NSString.StringFromMesaString[": "]];
msg ← NSString.AppendString[msg, string];
IF
TTY.NewLine[tty ← NSExec.GetTTY[exec]]
OR OkForAsyncMsg[exec]
THEN {
oldPageLength: CARDINAL ← NSTTY.SetPagination[h: tty, pageLength: 0]; -- eliminates (more)
PutMessage[exec, msg];
[] ← NSTTY.SetPagination[h: tty, pageLength: oldPageLength];
NSString.FreeString[msg]}
ELSE TRUSTED {Process.Detach[FORK PutAsyncMessageEntry[exec, msg]]};
END; -- ENABLE
END; -- PutAsyncMessage
PutErrorMessage,
PutIndentedMessage:
PUBLIC
PROCEDURE [exec: NSExec.Handle,
string: NSString.String] =
BEGIN
tty: TTY.Handle ← NSExec.GetTTY[exec];
IF ~TTY.NewLine[tty] THEN TTY.PutCR[tty];
TTY.PutString[tty, " "];
NSTTY.PutLine[tty, string];
END; -- PutErrorMessage, PutIndentedMessage
PutMessage:
PUBLIC
PROCEDURE [exec: NSExec.Handle, string: NSString.String] =
BEGIN
tty: TTY.Handle ← NSExec.GetTTY[exec];
IF ~TTY.NewLine[tty] THEN TTY.PutCR[tty];
NSTTY.PutLine[tty, string];
END; -- PutMessage
OutputHandle:
PUBLIC
PROCEDURE [exec: NSExec.Handle]
RETURNS [output: XFormat.Handle] = {RETURN[(execData[exec.session].output)]};
-- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- --
User Data:
-- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- --
UserName: PUBLIC PROCEDURE [exec: NSExec.Handle] RETURNS [name: CHName.Name ← [NIL, NIL, NIL]] = {RETURN[LoggedOnData[exec].userName]};
UserIdentity: PUBLIC PROCEDURE [exec: NSExec.Handle]
RETURNS [identity: XNSAuth.Identity] = {RETURN[LoggedOnData[exec].userIdentity]};
UserFilingSession: PROCEDURE [exec: Handle] RETURNS [session: NSFile.Session];
UserState:
PUBLIC
PROCEDURE [exec: NSExec.Handle]
RETURNS [loggedOn, serverSA, enabled:
BOOLEAN ←
FALSE] =
TRUSTED {
data: ExecObject = execData[exec.session];
WITH d: data
SELECT
FROM
loggedOn => {loggedOn ← TRUE; serverSA ← d.userAServerSA; enabled ← d.userEnabled};
ENDCASE => NULL;
};
UserAServerSA: PUBLIC NSExec.Predicate = {RETURN[UserState[exec].serverSA]};
UserEnabled: PUBLIC NSExec.Predicate = {RETURN[UserState[exec].enabled]};
UserLoggedOn: PUBLIC NSExec.Predicate = {RETURN[UserState[exec].loggedOn]};
-- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- --
Genesis:
-- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- --
These predicates are used when commands should be available during Genesis.
Genesis: PUBLIC NSExec.Predicate = {RETURN[FALSE]};
GenesisOrEnabledSA: PUBLIC NSExec.Predicate = {RETURN[TRUE]};
GenesisOrSA: PUBLIC NSExec.Predicate = {RETURN[TRUE]};
-- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- --
Server and System Data:
-- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- --
ServerName:
PUBLIC
PROCEDURE
RETURNS [name: CHName.Name] = {
RETURN[serverName]};
ServerIdentity:
PUBLIC
PROCEDURE
RETURNS [identity: XNSAuth.Identity] = {
RETURN[serverIdentity]};
SystemFilingSession: PROCEDURE RETURNS [session: NSFile.Session];
SystemDirectory:
PUBLIC
PROCEDURE
RETURNS [directory: Rope.
ROPE] = {
RETURN["///System/"]};
WorkingDirectory:
PUBLIC
PROCEDURE
RETURNS [directory: Rope.
ROPE] = {
RETURN["///Working/"]};
-- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- --
Running Additional Software:
-- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- --
Run: PROCEDURE [name: ROPE, exec: Handle, codeLinks: BOOLEAN ← FALSE] = {};
-- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- --
Local Procedures
-- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- --
Activated:
PUBLIC
--NSExecInternal--
PROCEDURE [name: NSString.String]
RETURNS [
BOOLEAN ←
FALSE] =
BEGIN
FOR s: ActiveServices ← activeServices, s.next
UNTIL s =
NIL DO
IF NSString.EquivalentStrings[name, s.name] THEN RETURN[TRUE];
ENDLOOP;
END; -- Activated
AddCommand:
ENTRY
PROCEDURE [command: NSExec.Command,
client: Client, clientName:
BOOLEAN ←
FALSE,
unload:
PROCEDURE ←
NIL] =
BEGIN
ENABLE UNWIND => NULL;
length: CARDINAL ← NSString.LogicalLength[command.name];
pos, offset: CARDINAL ← 0;
list: Entry ← topEntry;
commandWord: NSString.SubStringDescriptor;
IF length = 0 THEN ERROR NSExec.Error[invalidString];
DO
pos ← NSString.ScanForCharacter[c: [0, LOOPHOLE[Ascii.SP]], s: command.name, start: offset];
SELECT pos
FROM
offset => {offset ← offset + 1; LOOP}; -- skip spaces
LAST[
CARDINAL] => {
commandWord ← [command.name, offset, length - offset];
list ← InsertWordInList[list, commandWord];
EXIT};
ENDCASE => {
commandWord ← [command.name, offset, pos - offset];
list ← InsertWordInList[list, commandWord];
offset ← pos + 1};
ENDLOOP;
BEGIN -- begin exits DuplicateCommand
IF clientName
THEN {
IF list.commandProcs #
NIL
THEN GOTO DuplicateCommand
ELSE {list.commandProcs ← NEW[clientName ProcData ← [
execute: command.proc, activityProc: command.activityCheck,
helpProc: command.helpProc, commandType: clientName[client]]]}}
ELSE BEGIN
p: NSExecInternal.Procs;
FOR p ← list.commandProcs, p.next
UNTIL p =
NIL
DO
WITH commandProc: p
SELECT
FROM
exec, clientName => GOTO DuplicateCommand;
client => IF commandProc.client = client THEN GOTO DuplicateCommand;
ENDCASE
ENDLOOP;
IF client =
NIL
THEN
-- exec command
p ← NEW[exec ProcData ← [execute: command.proc,
activityProc: command.activityCheck, helpProc: command.helpProc, commandType: exec[unload]]]
ELSE
-- client command
p ← NEW[client ProcData ← [next: list.commandProcs, execute: command.proc,
activityProc: command.activityCheck, helpProc: command.helpProc,
commandType: client[client]]];
list.commandProcs ← p;
END;
EXITS
DuplicateCommand => {
s: LONG STRING ← [128];
nss: NSString.String ← NSString.StringFromMesaString[s];
o: XFormat.Object ← XFormat.NSStringObject[@nss];
SCSCommon.FormatExpandedMsg[@o, M[keyDuplicateCommand], command.name];
NSExec.BroadcastAsyncMessage[nss, NSExec.nullClientID]};
END; -- of exits DuplicateCommand
END; -- AddCommand
CreateExecInternal:
PUBLIC
PROCEDURE [tty:
TTY.Handle,
hostName:
XNS.Address ←
XNS.unknownAddress]
RETURNS [exec: Handle, execData: ExecData] =
BEGIN
nss: NSString.String ← NSString.MakeString[100];
object: XFormat.Handle ← XFormat.NSStringObject[nss];
asyncQ: AsyncQList;
XFormat.NetworkAddress[object, hostName, productSoftware];
execData ←
NEW[ExecObject ← [tty: tty, asyncQList: asyncQ, output: XFormat.TTYObject[tty],
hostName: NSString.CopyString[nss]]];
exec.session ← NSSession.Create[id: sessionId, name: NIL];
NSSession.SetContext[exec.session, sessionId, execData];
END; -- of CreateExecInternal
DisplayHerald:
PUBLIC
-- NSExecInternal --
PROCEDURE [tty:
TTY.Handle, displayTime:
BOOLEAN ←
TRUE] =
BEGIN
o: XFormat.Handle ← XFormat.TTYObject[tty];
PutHerald: PROCEDURE [s: NSString.String] = {NSTTY.PutString[tty, s]};
PutBcdTime:
PROCEDURE =
BEGIN
XFormat.Date[h: o, time: Loader.BCDBuildTime[DisplayHerald]];
END; -- PutBcdTime
beginning of DisplayHerald
TTY.PutCR[tty];
NSHerald.GetExecHerald[PutHerald];
NSTTY.PutString[tty, " of "];
PutBcdTime[];
IF displayTime THEN XFormat.Date[o, BasicTime.Now[], dateAndTime];
TTY.PutCR[tty];
NSHerald.GetCopyrightMsg[PutHerald];
END; -- DisplayHerald
ExecProcess:
PUBLIC
PROCEDURE [exec: Handle] =
BEGIN
ENABLE UNWIND => {}; -- FreeExecData[exec];
[] ← NSTTY.SetPagination[NSExec.GetTTY[exec], 23];
IF NSExec.ServerName[] #
NIL
AND exec #
LOOPHOLE[NSExec.localExec]
THEN
DisplayServerName[exec]; -- may raise ABORTED
ProcessCommands[exec];
FreeExecData[exec];
END; -- ExecProcess
FindClient:
PUBLIC
--NSExecInternal--
PROCEDURE [id:
CARDINAL]
RETURNS [client, predecessor: Client ←
NIL] =
BEGIN
FOR client ← firstClient, client.next
UNTIL client =
NIL
DO
IF client.id = id THEN RETURN; predecessor ← client; ENDLOOP;
ERROR NotFound;
END; -- FindClient
LoggedOnData:
PROCEDURE [exec: Handle]
RETURNS [
REF loggedOn ExecObject] = {
data: ExecObject = execData[exec];
WITH d: data
SELECT
FROM
loggedOn => {data: REF loggedOn ExecObject ← d; RETURN[data]};
ENDCASE => NSExec.Error[noUser]};
OkForAsyncMsg:
PROC [exec: Handle]
RETURNS [okForAsyncMessage:
BOOLEAN ←
FALSE] = {
proc: ExecDataProc = {okForAsyncMessage ← execData.okForAsyncMessage};
Execute[exec, proc];
};
PutAsyncMessageEntry:
ENTRY
PROCEDURE [exec: Handle, s: NSString.String] =
BEGIN
ENABLE UNWIND => NULL;
ENABLE {UNWIND => NULL; NSSession.Error => CONTINUE};
MustDisplayQ: PROCEDURE RETURNS [BOOLEAN ← FALSE] = BEGIN
IF qTop = NSExecInternal.qSize -- queue is full
OR PulsesToMilliSec[System.GetClockPulses[]] - PulsesToMilliSec[
asyncQ[qTop - 1].time] >= longAsyncDelayInMilliSec -- queue is old
THEN RETURN[TRUE];
END; -- of MustDisplayQ
tty: TTY.Handle = NSExec.GetTTY[exec]; -- raises NSSession.Error if the exec has already been destroyed.
qTop: CARDINAL ¬ GetQTop[exec];
qEntry: NSExecInternal.QEntry ¬ [
time: System.GetClockPulses[], message: s];
asyncQ: NSExecInternal.AsyncQList;
IF TTYAborted[tty] THEN RETURN;
SetQTop[exec, qTop ← qTop + 1];
SetQEntry[exec, qEntry];
asyncQ ¬ GetAsyncQ[exec];
BEGIN ENABLE ABORTED => CONTINUE; -- can be raised by ANY call to TTY
output msgs if we are between commands, between output lines, or the queue is full
IF OkForAsyncMsg[exec] OR TTY.NewLine[tty] OR MustDisplayQ[] THEN
DisplayQ[exec, qTop, asyncQ];
END; -- ENABLE
END; -- PutAsyncMessageEntry
ValidID:
PUBLIC
--NSExecInternal--
PROCEDURE [id: NSExec.ClientID]
RETURNS [BOOLEAN] = {RETURN[id IN [1..totalClients]]};
END.
LOG [Time - Person - Action]
14-Nov-83 8:54:49 - McManis - Created file.
3-Jan-84 15:16:44 - McManis - Added nullHandle, nullClientID, Context and TerminationHandler. Changed SetContext and GetContext to use Context and TerminationHandler. Renamed OutputObject to OutputHandle and changed it to return XFormat.Handle. Changed CreateExec to take "host: System.NetworkAddress" rather than credentials.
29-Apr-85 15:52:51 - McManis - Added cannotExpunge and cannotInitialize to ErrorType. Removed notInitialized from ClientStatus. Added predicates for Genesis.