-- Copyright (C) 1986 by Xerox Corporation. All rights reserved.
-- TajoCEnvUtil.mesa
-- NFS 29-May-86 11:57:28
-- MEW 14-Apr-86 16:37:19
-- Implementation of CEnvUtil for Tajo.
DIRECTORY
Ascii USING [BS, CR, LF, NUL, SP, TAB],
BucketAlloc USING [Destroy],
CEnvUtil USING [
AppendCmdProc, ArgSeq, Data, DataHandle, error, normal,
Outcome, ReadAndRun, StandardStream, StringSeq, ToolType,
zone],
CmFile USING [Error, FreeString, UserDotCmLine],
Context USING [
Create, Data, Destroy, Find, Type, UniqueType],
CRuntime USING [arrayZone, log, NoteAbortedProcess, z],
CString USING [CString, LongStringToCString],
CWindowLib,
Emulator USING [
Create, Destroy, EmuSWType, PutChar, StartLog, StopLog,
SetRefresh, table],
Exec USING [
AddCommand, CheckForAbort, ExecProc, FreeTokenString,
GetChar, GetToken, GetTTY, Handle, Object, OutputProc,
RemoveCommand],
FileSW USING [GetFile],
Format USING [LongOctal, StringProc],
FormSW USING [
AllocateItemDescriptor, BooleanItem, ClientItemsProcType,
CommandItem, DisplayItem, NotifyProcType, line0, line1,
ProcType, StringItem],
Heap USING [Delete],
Inline USING [BITAND, LowHalf],
MFile USING [
Acquire, AppendErrorMessage, EnumerateDirectory, EnumerateProc, Error,
Handle, maxNameLength, Object, Release, ValidFilename],
MLoader USING [Handle, Object],
MStream USING [
Error, GetLength, Log, PleaseReleaseProc, ReadOnly,
SetLogReadLength],
Pipe USING [Create, Delete, Handle],
Process USING [Abort, GetCurrent, Pause, SecondsToTicks],
Put USING [Text],
Runtime USING [GetBcdTime, IsBound],
StartState USING [
EnumerateHandles, EnumerateProc, Unload, UnloadError,
UnloadFromFile, zone],
Stream USING [
defaultObject, Delete, EndOfStream, Handle,
PutByteProcedure, PutChar, PutProcedure, SetSSTProcedure,
SubSequenceType],
String USING [
AppendChar, AppendCharAndGrow, AppendExtensionIfNeeded,
AppendNumber, AppendString, AppendStringAndGrow, StringBoundsFault,
CopyToNewString, Empty, Equivalent, FreeString,
InvalidNumber, StringToLongNumber],
Time USING [Append, Unpack],
Token USING [
Filtered, FilterProcType, FreeStringHandle,
FreeTokenString, Handle, Item, MaybeQuoted, Skip,
StringToHandle, Switches, UnterminatedQuote, WhiteSpace],
Tool USING [
Create, Destroy, MakeClientSW, MakeFormSW, MakeMsgSW,
MakeSWsProc, MakeTTYSW, UnusedLogName],
ToolWindow USING [
Handle, TransitionProcType, WindowForSubwindow],
TTY USING [
CharStatus, Create, Destroy, GetEditedString, Handle,
LineOverflow, PutChar, PutString, ResetUserAbort,
Rubout],
TTYConstants USING [
blinkDisplay, normal, removeChars, setBackingSize],
TTYSW USING [GetTTYHandle],
UserInput USING [
AttentionProcType, SetAttention, StringProcType],
UserTerminal USING [BlinkDisplay],
Version USING [Append],
Window USING [Handle, Object];
TajoCEnvUtil: PROGRAM
IMPORTS
BucketAlloc, CEnvUtil, CmFile, Context, CRuntime,
CString, Emulator, Exec, FileSW, Format,
FormSW, Heap, Inline, MFile, MStream, Pipe, Process, Put,
Runtime, StartState, Stream, String, Time, Token, Tool,
ToolWindow, TTY, TTYSW, UserInput, UserTerminal, Version
EXPORTS CEnvUtil =
{
OPEN CEnvUtil;
Data: PUBLIC TYPE = MACHINE DEPENDENT RECORD [
stdin(0): LONG STRING ← zone.NEW[
StringBody [MFile.maxNameLength]],
stdout(2): LONG STRING ← zone.NEW[
StringBody [MFile.maxNameLength]],
stderr(4): LONG STRING ← zone.NEW[
StringBody [MFile.maxNameLength]],
debug(6): BOOLEAN ← FALSE,
stopOnError(7): BOOLEAN ← TRUE,
endingToolInstance(8): BOOLEAN ← FALSE,
msgSW(9): Window.Handle ← NIL, -- remains nil for CExec
iomode(11): INTEGER ← 0,
cToolPart(12): SELECT tool(12): ToolType FROM
ctool => [
formSW(13): Window.Handle ← NIL,
ttySW(15): Window.Handle ← NIL,
p(17): PROCESS,
emuPart(18): SELECT tty(18): * FROM
emulation => [
pH(19): Pipe.Handle,
toBuffer(21), ttyStream(23): Stream.Handle,
modeVal(25): LONG CARDINAL ← 0,
mode(27): Stream.SubSequenceType ←
TTYConstants.normal,
logging(28): BOOLEAN ← FALSE],
regular => [logStream(19): Stream.Handle ← NIL],
ENDCASE],
cexec => [],
temp => [],
ENDCASE];
Handle: TYPE = LONG POINTER TO Data;
CTHandle: TYPE = LONG POINTER TO ctool Data;
RCTHandle: TYPE = LONG POINTER TO regular ctool Data;
ECTHandle: TYPE = LONG POINTER TO emulation ctool Data;
FileHandle: PUBLIC TYPE = MFile.Handle;
LoadHandle: PUBLIC TYPE = MLoader.Handle;
FileError: PUBLIC ERROR [message: LONG STRING] = CODE;
AcquireFile: PUBLIC PROCEDURE [name: LONG STRING]
RETURNS [fh: FileHandle] = {
fh ← MFile.Acquire[
name: name, access: readOnly, release: [] !
MFile.Error => {
msg: LONG STRING ← [100];
MFile.AppendErrorMessage[msg, code, file];
ERROR FileError[msg];
}];
};
ReleaseFile: PUBLIC PROCEDURE [fh: FileHandle] = {
MFile.Release[fh ! MFile.Error => CONTINUE]; };
UniqueFilePrefix: PUBLIC PROCEDURE [fileName: LONG STRING]
RETURNS [matches: CARDINAL] = {
Check: PROCEDURE [ext: LONG STRING] RETURNS [BOOLEAN] =
BEGIN
NoteMatch: MFile.EnumerateProc =
BEGIN
IF matches = 0 THEN {
String.AppendString[
to: matchName, from: name];
matches ← matches.SUCC}
ELSE
IF ~String.Equivalent[name, matchName] THEN {
matchName.length ← 0;
String.AppendString[to: matchName, from: name];
matches ← matches.SUCC;
};
RETURN[matches > 1]; -- stop if already not unique.
END; -- NoteMatch
extName: LONG STRING ← [MFile.maxNameLength];
matchName: LONG STRING ← [MFile.maxNameLength];
matchName.length ← 0;
IF ext # NIL THEN {
extName.length ← 0;
String.AppendString[
to: extName, from: fileName !
String.StringBoundsFault => GOTO TooLong];
String.AppendString[
to: extName, from: ext !
String.StringBoundsFault => GOTO TooLong];
}
ELSE extName ← fileName;
MFile.EnumerateDirectory[
extName, NoteMatch, filesOnly];
IF matches = 1 THEN {
fileName.length ← 0;
String.AppendString[to: fileName, from: matchName];};
RETURN[matches # 0]; -- TRUE if we have a match or an ambiguous name
EXITS TooLong => RETURN[matches # 0];
END; -- Check
matches ← 0;
IF ~MFile.ValidFilename[fileName] THEN RETURN;
IF Check[NIL] OR Check[".archivebcd"L] OR Check[".bcd"L]
OR Check["*.archivebcd"L] OR Check["*.bcd"L] THEN NULL;
};
GetStdStreams: PUBLIC PROCEDURE [
data: Handle,
defaultStdin, defaultStdout, defaultStderr:
Stream.Handle]
RETURNS [stdin, stdout, stderr: Stream.Handle] = {
stdin ←
IF String.Empty[LOOPHOLE[data, Handle].stdin] THEN
defaultStdin
ELSE MStream.ReadOnly[
data.stdin, [] !
MStream.Error => ERROR StdStreamError[stdin]];
stdout ←
IF String.Empty[data.stdout] THEN defaultStdout
ELSE MStream.Log[
data.stdout, [SetReadLengthAndRefuse] !
MStream.Error => {
Stream.Delete[stdin];
ERROR StdStreamError[stdout]}];
stderr ←
SELECT TRUE FROM
String.Empty[data.stderr] => defaultStderr,
String.Equivalent[
data.stdout, LOOPHOLE[data, Handle].stderr] =>
stdout
ENDCASE => MStream.Log[
data.stderr, [SetReadLengthAndRefuse] !
MStream.Error => {
Stream.Delete[stdin];
Stream.Delete[stdout];
ERROR StdStreamError[stderr]}];
};
SetReadLengthAndRefuse: MStream.PleaseReleaseProc = {
MStream.SetLogReadLength[
stream, MStream.GetLength[stream]];
RETURN[no]; -- no conflict if read access requested
};
StdStreamError: PUBLIC ERROR [
whichStream: StandardStream] = CODE;
GetFileInputStream: PUBLIC PROCEDURE [buffer: LONG STRING]
RETURNS [sH: Stream.Handle] = {
fileName: LONG STRING;
tH: Token.Handle ← Token.StringToHandle[buffer];
Token.Skip[tH, NIL, Token.WhiteSpace, TRUE];
fileName ← Token.Item[tH];
IF fileName = NIL THEN {
tH ← Token.FreeStringHandle[tH]; RETURN[NIL]; };
sH ← MStream.ReadOnly[
fileName, [] !
MStream.Error => {sH ← NIL; CONTINUE; }];
IF sH = NIL THEN { -- Try adding ".cscript" extension.
extensionLength: CARDINAL = 8;
extendedName: LONG STRING ← String.CopyToNewString[
fileName, zone, extensionLength];
IF String.AppendExtensionIfNeeded[
to: @extendedName, extension: ".cscript"L, z: zone]
THEN
sH ← MStream.ReadOnly[
extendedName, [] ! MStream.Error => {CONTINUE; }];
String.FreeString[zone, extendedName];
};
[] ← Token.FreeTokenString[fileName];
tH ← Token.FreeStringHandle[tH];
};
AppendLine: PUBLIC PROCEDURE [
line: LONG POINTER TO LONG STRING,
getChar: PROC RETURNS [c: CHAR]] = {
DO
c: CHAR ← getChar[
! MStream.Error, Stream.EndOfStream => EXIT];
String.AppendCharAndGrow[to: line, c: c, z: zone];
IF c = Ascii.CR THEN EXIT;
ENDLOOP;
};
contextType: Context.Type;
MakeCTool: PROCEDURE [emulation: BOOLEAN] = {
name: LONG STRING ← zone.NEW[StringBody [60]];
name.length ← 0;
String.AppendString[name, "CTool "L];
Version.Append[name];
String.AppendString[name, " of "L];
Time.Append[name, Time.Unpack[Runtime.GetBcdTime[]]];
[] ← Tool.Create[
makeSWsProc: IF emulation THEN MakeSWsEmu ELSE MakeSWs,
initialState: default,
initialBox: [
[0, 60], [512, IF emulation THEN 480 ELSE 465]],
clientTransition: ClientTransition, name: name,
tinyName1: "CTool"L, cmSection: "CTool"L];
zone.FREE[@name];
};
Another: FormSW.ProcType = {
h: CTHandle ← Context.Find[
contextType, ToolWindow.WindowForSubwindow[sw]];
WITH hh: h SELECT FROM
emulation => MakeCTool[TRUE];
regular => MakeCTool[FALSE];
ENDCASE;
};
DestroyInstance: FormSW.ProcType = {
Tool.Destroy[ToolWindow.WindowForSubwindow[sw]]; };
messageLines: CARDINAL = 3;
MakeSWs: Tool.MakeSWsProc = { -- MakeSWsProc without terminal emulation
h: RCTHandle;
logName: LONG STRING ← [12];
h ← zone.NEW[regular ctool Data];
Tool.UnusedLogName[unused: logName, root: "CTool.log"L];
Context.Create[contextType, h, FreeToolData, window];
h.msgSW ← Tool.MakeMsgSW[
window: window, lines: messageLines];
h.formSW ← Tool.MakeFormSW[
window: window, formProc: MakeForm];
h.ttySW ← Tool.MakeTTYSW[window: window, name: logName];
h.logStream ← FileSW.GetFile[h.ttySW].s;
UserInput.SetAttention[h.ttySW, AbortProgram];
h.p ← FORK CallReadAndRun[
LOOPHOLE[h], TTYSW.GetTTYHandle[h.ttySW]];
};
MakeSWsEmu: Tool.MakeSWsProc = { --MakeSWsProc with terminal emulation
h: ECTHandle;
tH: TTY.Handle;
h ← zone.NEW[emulation ctool Data];
Context.Create[contextType, h, FreeToolData, window];
h.msgSW ← Tool.MakeMsgSW[
window: window, lines: messageLines];
h.formSW ← Tool.MakeFormSW[
window: window, formProc: MakeFormEmu];
[h: h.pH, producer: h.toBuffer, consumer: h.ttyStream] ←
Pipe.Create[200];
h.ttyStream.put ← WriteToWindow;
h.ttyStream.putByte ← Stream.defaultObject.putByte;
-- Pipe.Create set putByte to a proc. that raises Stream.InvalidOperation.
h.ttyStream.setSST ← SetSST;
h.ttyStream.clientData ← h;
h.ttySW ← Tool.MakeClientSW[
window: window, clientProc: MakeEmulator,
clientData: NIL, swType: Emulator.EmuSWType];
UserInput.SetAttention[h.ttySW, AbortProgram];
tH ← TTY.Create[
name: NIL, backingStream: NIL, ttyImpl: h.ttyStream];
h.p ← FORK ReadAndRunThenCleanUp[h, tH];
};
CallReadAndRun: PROCEDURE [h: CTHandle, tH: TTY.Handle] = {
AppendCmd: AppendCmdProc = {
RETURN[CallGetEditedString[tH, buffer]]; };
[] ← ReadAndRun[h, tH, AppendCmd];
};
ReadAndRunThenCleanUp: PROCEDURE [
h: ECTHandle, tH: TTY.Handle] = {
AppendCmd: AppendCmdProc = {
RETURN[CallGetEditedString[tH, buffer]]; };
[] ← ReadAndRun[h, tH, AppendCmd];
TTY.Destroy[tH, TRUE];
Pipe.Delete[h.pH];
Emulator.Destroy[h.ttySW];
};
AbortProgram: UserInput.AttentionProcType = {
h: CTHandle = Context.Find[
contextType, ToolWindow.WindowForSubwindow[window]];
CRuntime.NoteAbortedProcess[h.p];
};
FreeToolData: PROCEDURE [
d: Context.Data, w: Window.Handle] = {
h: Handle = LOOPHOLE[d];
zone.FREE[@h.stdin];
zone.FREE[@h.stdout];
zone.FREE[@h.stderr];
zone.FREE[@d];
};
FreeData: PUBLIC PROCEDURE [h: Handle] = {
IF h = NIL THEN RETURN;
IF h.stdin # NIL THEN zone.FREE[@h.stdin];
IF h.stdout # NIL THEN zone.FREE[@h.stdout];
IF h.stderr # NIL THEN zone.FREE[@h.stderr];
};
ClientTransition: ToolWindow.TransitionProcType = {
IF new = inactive THEN {
h: CTHandle ← Context.Find[contextType, window];
h.endingToolInstance ← TRUE;
zone.FREE[@h.stdin];
zone.FREE[@h.stdout];
zone.FREE[@h.stderr];
Process.Abort[h.p];
JOIN h.p;
Context.Destroy[contextType, window];
};
};
FormItems: TYPE = {
another, destroy, debug, stopOnError, stdin, stdout,
stderr};
MakeForm: FormSW.ClientItemsProcType = {
OPEN FormSW;
nItems: CARDINAL = FormItems.LAST.ORD + 1;
h: Handle ← Context.Find[
contextType, ToolWindow.WindowForSubwindow[sw]];
items ← AllocateItemDescriptor[nItems];
items[FormItems.another.ORD] ← CommandItem[
tag: "Another"L, place: [1, line0], proc: Another];
items[FormItems.destroy.ORD] ← CommandItem[
tag: "Destroy"L, place: [150, line0],
proc: DestroyInstance];
items[FormItems.debug.ORD] ← BooleanItem[
tag: "Debug"L, place: [300, line0], switch: @h.debug];
items[FormItems.stopOnError.ORD] ← BooleanItem[
tag: "StopScriptOnError"L, place: [365, line0],
switch: @h.stopOnError];
items[FormItems.stdin.ORD] ← StringItem[
tag: "stdin"L, place: [1, line1], string: @h.stdin];
items[FormItems.stdout.ORD] ← StringItem[
tag: "stdout"L, place: [150, line1],
string: @h.stdout];
items[FormItems.stderr.ORD] ← StringItem[
tag: "stderr"L, place: [300, line1],
string: @h.stderr];
RETURN[items: items, freeDesc: TRUE];
};
FormItemsEmu: TYPE = {
another, destroy, debug, stopOnError, writeLog, stdin,
stdout, stderr};
MakeFormEmu: FormSW.ClientItemsProcType = {
OPEN FormSW;
nItems: CARDINAL = FormItemsEmu.LAST.ORD + 1;
h: ECTHandle ← Context.Find[
contextType, ToolWindow.WindowForSubwindow[sw]];
items ← AllocateItemDescriptor[nItems];
items[FormItemsEmu.another.ORD] ← CommandItem[
tag: "Another"L, place: [1, line0], proc: Another];
items[FormItemsEmu.destroy.ORD] ← CommandItem[
tag: "Destroy"L, place: [90, line0],
proc: DestroyInstance];
items[FormItemsEmu.debug.ORD] ← BooleanItem[
tag: "Debug"L, place: [180, line0], switch: @h.debug];
items[FormItemsEmu.stopOnError.ORD] ← BooleanItem[
tag: "StopScriptOnError"L, place: [245, line0],
switch: @h.stopOnError];
items[FormItemsEmu.writeLog.ORD] ← BooleanItem[
tag: "WriteLog"L, place: [370, line0],
proc: SetEmuLogging, switch: @h.logging];
items[FormItemsEmu.stdin.ORD] ← StringItem[
tag: "stdin"L, place: [1, line1], string: @h.stdin];
items[FormItemsEmu.stdout.ORD] ← StringItem[
tag: "stdout"L, place: [150, line1],
string: @h.stdout];
items[FormItemsEmu.stderr.ORD] ← StringItem[
tag: "stderr"L, place: [300, line1],
string: @h.stderr];
RETURN[items: items, freeDesc: TRUE];
};
-- Emulation window procs.
MakeEmulator: PROCEDURE [
sw: Window.Handle, clientData: LONG POINTER] = {
h: ECTHandle = Context.Find[
contextType, ToolWindow.WindowForSubwindow[sw]];
Emulator.Create[
sw: sw, data: Emulator.table[vt100],
typeIn: WriteToBuffer,
otherData: Emulator.table[xvt52], refresh: never,
logfile: "EmuCTool.log"L, writeToLog: h.logging];
Emulator.SetRefresh[sw, always];
};
WriteToBuffer: UserInput.StringProcType = {
h: ECTHandle ← Context.Find[
contextType, ToolWindow.WindowForSubwindow[window]];
FOR i: CARDINAL IN [0..string.length) DO
Stream.PutChar[h.toBuffer, string[i]]; ENDLOOP;
};
WriteToWindow: Stream.PutProcedure = {
h: ECTHandle = sH.clientData;
crMode: BOOLEAN ← Inline.BITAND[
h.iomode, CWindowLib.crMode] # 0;
SELECT h.mode FROM
TTYConstants.normal => {
FOR i: CARDINAL IN
[block.startIndex..block.stopIndexPlusOne) DO
Emulator.PutChar[
h.ttySW, LOOPHOLE[block.blockPointer[i]]];
IF ~crMode AND block.blockPointer[i] = Ascii.CR.ORD
THEN Emulator.PutChar[h.ttySW, Ascii.LF];
ENDLOOP;
};
TTYConstants.setBackingSize, TTYConstants.removeChars
=> {
FOR i: CARDINAL IN
[block.startIndex..block.stopIndexPlusOne) DO
h.modeVal ←
h.modeVal * 256 + block.blockPointer[i];
ENDLOOP;
};
ENDCASE;
};
SetSST: Stream.SetSSTProcedure = {
h: ECTHandle = sH.clientData;
SELECT h.mode FROM
TTYConstants.removeChars =>
FOR i: CARDINAL IN [0..Inline.LowHalf[h.modeVal]) DO
Emulator.PutChar[h.ttySW, Ascii.BS];
Emulator.PutChar[h.ttySW, Ascii.SP];
Emulator.PutChar[h.ttySW, Ascii.BS];
ENDLOOP;
TTYConstants.setBackingSize => {};
TTYConstants.blinkDisplay =>
UserTerminal.BlinkDisplay[];
ENDCASE;
h.mode ← sst;
h.modeVal ← 0;
};
SetEmuLogging: FormSW.NotifyProcType = {
h: ECTHandle = Context.Find[
contextType, ToolWindow.WindowForSubwindow[sw]];
IF h.logging THEN Emulator.StartLog[h.ttySW]
ELSE Emulator.StopLog[h.ttySW];
};
CToolCommand: Exec.ExecProc = {
token, switches: LONG STRING;
sense: BOOLEAN ← TRUE;
emulation: BOOLEAN ← FALSE;
[token: token, switches: switches] ← Exec.GetToken[h];
IF switches # NIL THEN
FOR i: CARDINAL IN [0..switches.length) DO
SELECT switches[i] FROM
'~, '- => sense ← ~sense;
'e, 'E => {emulation ← sense; EXIT};
ENDCASE => sense ← TRUE;
ENDLOOP;
[] ← Exec.FreeTokenString[token];
[] ← Exec.FreeTokenString[switches];
IF emulation
AND ~Runtime.IsBound[LOOPHOLE[Emulator.Create]] THEN
Exec.OutputProc[h]["Emu.bcd not loaded"L]
ELSE MakeCTool[emulation];
};
ErrorCToolCommand: Exec.ExecProc = {
Exec.OutputProc[h][
"Can not create CTool in this environment. Use CExec.~"L];
};
CExecCommand: Exec.ExecProc = {
AbortWatcher: PROCEDURE = {
ENABLE ABORTED => CONTINUE;
DO
Process.Pause[Process.SecondsToTicks[2]];
IF Exec.CheckForAbort[h] THEN {
TTY.ResetUserAbort[Exec.GetTTY[h]];
CRuntime.NoteAbortedProcess[cExecProcess];
};
ENDLOOP;
};
NormalAppendCmd: AppendCmdProc = {
RETURN[CallGetEditedString[tH, buffer]]; };
OneStringAppendCmd: AppendCmdProc = {
IF pos >= restOfCmdLine.length THEN
String.AppendString[to: buffer↑, from: "↑\N"L]
ELSE AppendLine[buffer, NextChar];
RETURN[FALSE];
};
NextChar: PROCEDURE RETURNS [c: CHAR] = {
IF pos >= restOfCmdLine.length THEN RETURN[Ascii.CR];
c ← restOfCmdLine[pos];
pos ← pos.SUCC;
};
data: Handle ← zone.NEW[cexec Data];
tH: TTY.Handle = Exec.GetTTY[h];
cExecProcess: PROCESS = Process.GetCurrent[];
abortWatchProcess: PROCESS;
restOfCmdLine: LONG STRING ← GetRestOfCmdLine[h];
pos: CARDINAL;
intOutcome: Outcome;
appendCmd: AppendCmdProc;
data↑ ← [cToolPart: cexec[]];
IF restOfCmdLine = NIL THEN {
name: LONG STRING ← zone.NEW[StringBody [60]];
write: Format.StringProc = Exec.OutputProc[h];
name.length ← 0;
String.AppendString[name, "CExec "L];
Version.Append[name];
String.AppendString[name, " of "L];
Time.Append[name, Time.Unpack[Runtime.GetBcdTime[]]];
write[name];
zone.FREE[@name];
write["\NCurrent status:\N"L];
PrintStatus[LOOPHOLE[data], tH];
appendCmd ← NormalAppendCmd;
}
ELSE {pos ← 0; appendCmd ← OneStringAppendCmd; };
{
ENABLE {
ABORTED => {
Process.Abort[abortWatchProcess];
JOIN abortWatchProcess;
CONTINUE;
};
UNWIND => {
Process.Abort[abortWatchProcess];
JOIN abortWatchProcess;
};
};
abortWatchProcess ← FORK AbortWatcher[];
intOutcome ← ReadAndRun[LOOPHOLE[data], tH, appendCmd];
Process.Abort[abortWatchProcess];
JOIN abortWatchProcess;
};
zone.FREE[@data.stdin];
zone.FREE[@data.stdout];
zone.FREE[@data.stderr];
zone.FREE[@data];
IF restOfCmdLine # NIL THEN {
zone.FREE[@restOfCmdLine];
IF intOutcome # normal THEN outcome ← error;};
};
GetRestOfCmdLine: PROCEDURE [h: Exec.Handle]
RETURNS [LONG STRING] = {
line: LONG STRING;
lineMarker: CHAR = '%;
c: CHAR ← Exec.GetChar[h];
WHILE c = Ascii.SP OR c = Ascii.TAB DO
c ← Exec.GetChar[h]; ENDLOOP;
IF c = Ascii.CR OR c = Ascii.NUL THEN RETURN[NIL];
line ← zone.NEW[StringBody [80]];
line.length ← 0;
UNTIL c = Ascii.NUL OR c = Ascii.CR DO
String.AppendCharAndGrow[
to: @line, c: IF c = lineMarker THEN Ascii.CR ELSE c,
z: zone];
c ← Exec.GetChar[h];
ENDLOOP;
String.AppendCharAndGrow[
to: @line, c: Ascii.CR, z: zone];
RETURN[line];
};
CallGetEditedString: PROCEDURE [
tH: TTY.Handle, buffer: LONG POINTER TO LONG STRING]
RETURNS [lineDeleted: BOOLEAN ← FALSE] = {
CommandLineRead: PROCEDURE [c: CHAR]
RETURNS [status: TTY.CharStatus] = {
IF c = '\N THEN {TTY.PutChar[tH, c]; RETURN[stop]; }
ELSE RETURN[ok];
};
[] ← TTY.GetEditedString[
h: tH, s: buffer↑, t: CommandLineRead !
TTY.LineOverflow => {
ns ← String.CopyToNewString[
s: s, z: zone, longer: s.maxlength / 2];
String.FreeString[z: zone, s: s];
buffer↑ ← ns;
RESUME [ns];
};
TTY.Rubout => {
TTY.PutString[tH, " XXX"L];
lineDeleted ← TRUE;
CONTINUE;
}];
};
CEnvironmentCommand: Exec.ExecProc = {
-- This prevents accidentally loading a new instance. --
};
RemoveCommands: Exec.ExecProc = {
Exec.RemoveCommand[h, "CTool.~"L];
Exec.RemoveCommand[h, "CExec.~"L];
Exec.RemoveCommand[h, "CEnvironment.~"L];
BucketAlloc.Destroy[];
Heap.Delete[StartState.zone];
Heap.Delete[CRuntime.z];
Heap.Delete[CRuntime.arrayZone];
Heap.Delete[zone];
IF CRuntime.log # NIL THEN Stream.Delete[CRuntime.log];
};
RegisterItems: PUBLIC PROCEDURE = {
-- Register the normal CTool.~ command only if it is running
-- in a tajo environment, rather than on an integration machine.
IF Runtime.IsBound[LOOPHOLE[Tool.Create]] THEN
Exec.AddCommand[name: "CTool.~"L, proc: CToolCommand]
ELSE
Exec.AddCommand[
name: "CTool.~"L, proc: ErrorCToolCommand];
Exec.AddCommand[name: "CExec.~"L, proc: CExecCommand];
Exec.AddCommand[
name: "CEnvironment.~"L, proc: CEnvironmentCommand,
unload: RemoveCommands];
};
GetPrompt: PUBLIC PROCEDURE RETURNS [prompt: LONG STRING] =
{
cmPrompt: LONG STRING ← NIL;
cmPrompt ← CmFile.UserDotCmLine[
"CTool"L, "Prompt"L !
CmFile.Error => {cmPrompt ← NIL; CONTINUE}];
IF cmPrompt = NIL THEN {
prompt ← zone.NEW[StringBody [4]];
prompt.length ← 0;
String.AppendString[to: prompt, from: "\N>>>"L];
}
ELSE {
prompt ← zone.NEW[StringBody [cmPrompt.length + 1]];
prompt.length ← 0;
String.AppendChar[s: prompt, c: Ascii.CR];
String.AppendString[to: prompt, from: cmPrompt];
[] ← CmFile.FreeString[cmPrompt];
};
};
Unload: PUBLIC PROCEDURE [
cmdLine: LONG STRING, tty: TTY.Handle]
RETURNS [outcome: Outcome ← normal] = {
th: Token.Handle ← NIL;
item: LONG STRING ← NIL;
fileName: LONG STRING ← zone.NEW[
StringBody [MFile.maxNameLength]];
{
ENABLE
UNWIND => {
IF item # NIL THEN [] ← Token.FreeTokenString[item];
IF th # NIL THEN [] ← Token.FreeStringHandle[th];
IF fileName # NIL THEN zone.FREE[@fileName];
};
th ← Token.StringToHandle[cmdLine];
item ← Token.Item[th]; -- Get past "Unload.~"
item ← Token.FreeTokenString[item];
DO
item ← Token.Item[th];
IF item = NIL THEN EXIT;
TTY.PutString[tty, "\NUnloading "L];
IF item[0] IN ['0..'9] THEN {
ENABLE {
String.InvalidNumber => {
Blink[];
TTY.PutString[tty, "Invalid parameter"L];
CONTINUE;
};
StartState.UnloadError => {
outcome ← error;
Blink[];
TTY.PutString[tty, message];
CONTINUE;
};
};
h: MLoader.Handle = String.StringToLongNumber[item];
TTY.PutString[tty, item];
TTY.PutString[tty, "..."L];
StartState.Unload[h];
TTY.PutString[tty, "done."L];
}
ELSE {
ENABLE
MFile.Error => {
msg: LONG STRING ← [75];
MFile.AppendErrorMessage[msg, code, file];
TTY.PutString[tty, msg];
outcome ← error;
CONTINUE;
};
num: LONG STRING ← [8];
file: MFile.Handle;
found: CARDINAL;
instancesUnloaded: CARDINAL;
fileName.length ← 0;
String.AppendString[to: fileName, from: item];
found ← UniqueFilePrefix[fileName];
IF found # 1 THEN {
Blink[];
TTY.PutString[tty, item];
TTY.PutString[
tty, IF found = 0 THEN ": not found"L ELSE ": ambiguous"L];
zone.FREE[@fileName];
th ← Token.FreeStringHandle[th];
RETURN[error];
};
TTY.PutString[tty, fileName];
TTY.PutString[tty, "..."L];
file ← MFile.Acquire[fileName, anchor, []];
instancesUnloaded ← StartState.UnloadFromFile[
file !
StartState.UnloadError => {
outcome ← error;
Blink[];
TTY.PutString[tty, message];
instancesUnloaded ← instancesAlreadyUnloaded;
CONTINUE;
}; UNWIND => MFile.Release[file]];
MFile.Release[file];
String.AppendChar[s: num, c: Ascii.CR];
String.AppendNumber[num, instancesUnloaded];
TTY.PutString[tty, num];
TTY.PutString[tty, " instance(s) unloaded."L];
};
item ← Token.FreeTokenString[item];
ENDLOOP;
th ← Token.FreeStringHandle[th];
zone.FREE[@fileName];
};
};
ShowHandles: PUBLIC PROCEDURE [
cmdLine: LONG STRING, tty: TTY.Handle]
RETURNS [outcome: Outcome ← normal] = {
PrintLoadHandle: StartState.EnumerateProc = {
Write: Format.StringProc = {TTY.PutString[tty, s]; };
Format.LongOctal[Write, mh];
TTY.PutChar[tty, ' ];
};
th: Token.Handle ← NIL;
item: LONG STRING ← NIL;
fileName: LONG STRING ← zone.NEW[
StringBody [MFile.maxNameLength]];
{
ENABLE
UNWIND => {
IF item # NIL THEN [] ← Token.FreeTokenString[item];
IF th # NIL THEN [] ← Token.FreeStringHandle[th];
IF fileName # NIL THEN zone.FREE[@fileName];
};
th ← Token.StringToHandle[cmdLine];
item ← Token.Item[th]; -- Get past "ShowHandles.~"
item ← Token.FreeTokenString[item];
DO
ENABLE
MFile.Error => {
msg: LONG STRING ← [75];
MFile.AppendErrorMessage[msg, code, file];
TTY.PutString[tty, msg];
outcome ← error;
CONTINUE;
};
file: MFile.Handle;
found: CARDINAL;
item ← Token.Item[th];
IF item = NIL THEN EXIT;
TTY.PutChar[tty, '\N];
fileName.length ← 0;
String.AppendString[from: item, to: fileName];
found ← UniqueFilePrefix[fileName];
IF found # 1 THEN {
Blink[];
TTY.PutString[tty, item];
TTY.PutString[
tty, IF found = 0 THEN ": not found"L ELSE ": ambiguous"L];
zone.FREE[@fileName];
th ← Token.FreeStringHandle[th];
RETURN[error];
};
TTY.PutString[tty, fileName];
TTY.PutString[tty, ": "L];
{ENABLE MFile.Error => {
TTY.PutString[tty, "Unable to acquire file."L];
CONTINUE;};
file ← MFile.Acquire[fileName, anchor, []];
StartState.EnumerateHandles[file, PrintLoadHandle];
MFile.Release[file];
};
item ← Token.FreeTokenString[item];
ENDLOOP;
zone.FREE[@fileName];
th ← Token.FreeStringHandle[th];
};
};
ChangeStandardStreams: PUBLIC PROCEDURE [
buffer: LONG STRING, data: Handle] = {
tH: Token.Handle = Token.StringToHandle[buffer];
tokenStdin, tokenStdout, tokenStderr: LONG STRING;
Token.Skip[tH, NIL, Token.WhiteSpace, TRUE];
tokenStdin ← Token.Filtered[tH, NIL, FilterFileName];
-- stdin
data.stdin.length ← 0;
IF tokenStdin # NIL THEN {
String.AppendStringAndGrow[
to: @(data.stdin), from: tokenStdin, z: zone];
[] ← Token.FreeTokenString[tokenStdin];
};
WITH hh: data SELECT FROM
ctool =>
FormSW.DisplayItem[
sw: hh.formSW,
index:
WITH hh SELECT FROM
regular => FormItems.stdin.ORD,
ENDCASE => FormItemsEmu.stdin.ORD];
ENDCASE;
-- stdout
tokenStdout ← Token.Filtered[tH, NIL, FilterFileName];
data.stdout.length ← 0;
IF tokenStdout # NIL THEN {
String.AppendStringAndGrow[
to: @(data.stdout), from: tokenStdout, z: zone];
[] ← Token.FreeTokenString[tokenStdout];
};
WITH hh: data SELECT FROM
ctool =>
FormSW.DisplayItem[
sw: hh.formSW,
index:
WITH hh SELECT FROM
regular => FormItems.stdout.ORD,
ENDCASE => FormItemsEmu.stdout.ORD];
ENDCASE;
-- stderr
tokenStderr ← Token.Filtered[tH, NIL, FilterFileName];
LOOPHOLE[data, Handle].stderr.length ← 0;
IF tokenStderr # NIL THEN {
String.AppendStringAndGrow[
to: @(LOOPHOLE[data, Handle].stderr),
from: tokenStderr, z: zone];
[] ← Token.FreeTokenString[tokenStderr];
};
WITH hh: data SELECT FROM
ctool =>
FormSW.DisplayItem[
sw: hh.formSW,
index:
WITH hh SELECT FROM
regular => FormItems.stderr.ORD,
ENDCASE => FormItemsEmu.stderr.ORD];
ENDCASE;
[] ← Token.FreeStringHandle[tH];
};
FilterFileName: Token.FilterProcType = {
inClass ← c # Ascii.SP AND c # ', AND c # Ascii.CR
AND c # Ascii.TAB;
};
ChangeSwitches: PUBLIC PROCEDURE [
buffer: LONG STRING, data: Handle] = {
tH: Token.Handle = Token.StringToHandle[buffer];
switches: LONG STRING;
sense: BOOLEAN ← TRUE;
Token.Skip[tH, NIL, Token.WhiteSpace, TRUE];
switches ← Token.Filtered[tH, NIL, Token.Switches];
IF switches # NIL THEN {
FOR i: CARDINAL IN [0..switches.length) DO
SELECT switches[i] FROM
'~, '- => sense ← ~sense;
'd => {
data.debug ← sense;
WITH hh: data SELECT FROM
ctool =>
FormSW.DisplayItem[
sw: hh.formSW,
index:
WITH hh SELECT FROM
regular => FormItems.debug.ORD,
ENDCASE => FormItemsEmu.debug.ORD];
ENDCASE;
sense ← TRUE;
};
's => {
data.stopOnError ← sense;
WITH hh: data SELECT FROM
ctool =>
FormSW.DisplayItem[
sw: hh.formSW,
index:
WITH hh SELECT FROM
regular => FormItems.stopOnError.ORD,
ENDCASE => FormItemsEmu.stopOnError.ORD];
ENDCASE;
sense ← TRUE;
};
ENDCASE;
ENDLOOP;
[] ← Token.FreeTokenString[switches];
};
[] ← Token.FreeStringHandle[tH];
};
PrintStatus: PUBLIC PROCEDURE [
data: Handle, tty: TTY.Handle] = {
TTY.PutString[tty, "stdin: "L];
TTY.PutString[
tty,
IF String.Empty[data.stdin] THEN "window"L
ELSE data.stdin];
TTY.PutString[tty, "\Nstdout: "L];
TTY.PutString[
tty,
IF String.Empty[data.stdout] THEN "window"L
ELSE data.stdout];
TTY.PutString[tty, "\Nstderr: "L];
TTY.PutString[
tty,
IF String.Empty[data.stderr] THEN "window"L
ELSE data.stderr];
TTY.PutString[tty, "\NDebug: "];
TTY.PutString[
tty, IF data.debug THEN "TRUE"L ELSE "FALSE"L];
TTY.PutString[tty, "\NStopScriptOnError: "L];
TTY.PutString[
tty, IF data.stopOnError THEN "TRUE"L ELSE "FALSE"L];
};
ParseLine: PUBLIC PROCEDURE [buffer: LONG STRING]
RETURNS [
name: LONG STRING ← NIL, argC: CARDINAL ← 1,
argV: ArgSeq ← NIL] = {
OPEN CString;
initArgSeqLength: CARDINAL = 8;
tH: Token.Handle ← NIL;
tempName: LONG STRING ← NIL;
{
ENABLE
UNWIND => {
IF tH # NIL THEN [] ← Token.FreeStringHandle[tH];
IF tempName # NIL THEN
[] ← Token.FreeTokenString[tempName];
};
IF buffer.length = 0 THEN RETURN;
tH ← Token.StringToHandle[buffer];
tempName ← Token.Item[tH];
IF tempName # NIL THEN {
name ← String.CopyToNewString[
tempName, zone,
MFile.maxNameLength - tempName.length];
tempName ← Token.FreeTokenString[tempName];
argV ← zone.NEW[StringSeq [initArgSeqLength]];
argV.length ← 1;
argV[0] ← LongStringToCString[name, zone];
};
DO
nextArg: LONG STRING ← Token.MaybeQuoted[
tH, NIL ! Token.UnterminatedQuote => RESUME ];
{
ENABLE
UNWIND =>
IF nextArg # NIL THEN
[] ← Token.FreeTokenString[nextArg];
IF String.Empty[nextArg] THEN EXIT;
argC ← argC + 1;
IF argV.length < argV.maxlength THEN {
argV[argV.length] ← LongStringToCString[
nextArg, zone];
argV.length ← argV.length + 1;
}
ELSE {
oldArgV: ArgSeq ← argV;
{
ENABLE
UNWIND => {
IF argV # NIL THEN zone.FREE[@argV];
IF oldArgV # NIL THEN zone.FREE[@oldArgV];
};
argV ← zone.NEW[StringSeq [oldArgV.maxlength * 2]];
FOR i: CARDINAL IN [0..oldArgV.length) DO
argV[i] ← oldArgV[i]; ENDLOOP;
argV.length ← oldArgV.length + 1;
argV[oldArgV.length] ← LongStringToCString[
nextArg, zone];
zone.FREE[@oldArgV];
};
};
nextArg ← Token.FreeTokenString[nextArg];
};
ENDLOOP;
tH ← Token.FreeStringHandle[tH];
};
};
GetFirstWord: PUBLIC PROCEDURE [buffer: LONG STRING]
RETURNS [firstWord: LONG STRING] = {
th: Token.Handle ← NIL;
ts: LONG STRING ← NIL;
{
ENABLE
UNWIND => {
IF ts # NIL THEN [] ← Token.FreeTokenString[ts];
IF th # NIL THEN [] ← Token.FreeStringHandle[th];
};
th ← Token.StringToHandle[buffer];
ts ← Token.Item[th];
IF ts = NIL THEN {
th ← Token.FreeStringHandle[th]; RETURN[NIL]; };
firstWord ← String.CopyToNewString[ts, zone];
ts ← Token.FreeTokenString[ts];
th ← Token.FreeStringHandle[th];
};
};
Blink: PUBLIC PROCEDURE = {UserTerminal.BlinkDisplay[]; };
Print: PUBLIC PROCEDURE [msg: LONG STRING, data: Handle] =
{Put.Text[data.msgSW, msg]; };
Debugging: PUBLIC PROCEDURE [data: Handle]
RETURNS [BOOLEAN] = {RETURN[data.debug]; };
StopOnError: PUBLIC PROCEDURE [data: Handle]
RETURNS [BOOLEAN] = {RETURN[data.stopOnError]; };
EndingToolInstance: PUBLIC PROCEDURE [data: Handle]
RETURNS [BOOLEAN] = {RETURN[data.endingToolInstance]; };
StopIfCExec: PUBLIC PROCEDURE [data: Handle] = {
IF data.tool = cexec THEN {
data.endingToolInstance ← TRUE;
ERROR ABORTED; -- quit
};
};
UpdateLogReadLength: PUBLIC PROCEDURE [data: Handle] = {
WITH hh: data SELECT FROM
ctool =>
WITH hhh: hh SELECT FROM
regular =>
MStream.SetLogReadLength[
stream: hhh.logStream,
position: MStream.GetLength[hhh.logStream]];
ENDCASE;
ENDCASE;
};
MakeDataForSystemCall: PUBLIC PROCEDURE
RETURNS [newData: Handle] = {
newData ← zone.NEW[temp Data];
newData.cToolPart ← temp[]};
SetOutputMode: PUBLIC PROCEDURE [
h: Handle, iomode: INTEGER] = {h.iomode ← iomode; };
IF Runtime.IsBound[LOOPHOLE[Tool.Create]] THEN
contextType ← Context.UniqueType[];
}.