-- Copyright (C) 1986 by Xerox Corporation. All rights reserved.
-- CWindow.mesa
-- DWR 20-Mar-86 16:15:30
-- NFS 29-May-86 15:01:38
-- MEW 9-May-86 16:53:09
-- BGY 27-Aug-86 12:19:41
-- Parts of CTool and CExec that do not depend on Tajo or BWS.
-- All dependencies on these environments are absorbed by CEnvUtil.
DIRECTORY
Ascii USING [ControlC],
Beta USING [GetVersion],
CEnvUtil USING [AcquireFile, AppendCmdProc, AppendLine, ArgSeq, Blink,
ChangeStandardStreams, ChangeSwitches, DataHandle,
Debugging, EndingToolInstance, error, FileError,
FileHandle, FreeData, GetFileInputStream, GetFirstWord,
GetPrompt, GetStdStreams,
MakeDataForSystemCall, normal, Outcome,
ParseLine, Print, PrintStatus, RegisterItems,
ReleaseFile, SetOutputMode, ShowHandles, StandardStream,
StdinHandle, StdinObject,
StdStreamError, StopIfCExec, StopOnError,
StringSeq, UniqueFilePrefix, Unload, UpdateLogReadLength],
CRuntime USING [CanDelete, GetStdin, GetStdout, GetStderr,
ProcessNotRegistered, ProgramExited,
StopIfUserAborted, TooMuchGlobalArraySpace],
CString USING [CString, ToWordPointer],
CWindowLibExtras USING [],
Environment USING [Byte],
FBasics USING [LibraryProblem],
Format USING [Decimal, LongOctal, StringProc],
Heap USING [Create],
Inline USING [BITAND, LongCOPY],
CWindowLib USING[cBreakMode, echoMode, normalMode, rawMode],
Runtime USING [CallDebugger, IsBound],
StartState USING [GetHandle, GetLoadHandle, Handle, Load,
LoadError,normalOutcome, Object, Restart, Start, UnloadError,
UnloadUnstartedProgram, VersionMismatch],
Stream USING [defaultObject, Delete, DeleteProcedure,
EndOfStream, GetByteProcedure, GetChar, Handle,
InvalidOperation, Object, PutByteProcedure, SendNowProcedure],
String USING [AppendCharAndGrow, AppendString, CopyToNewString, Empty,
FreeString],
System,
Time,
TTY USING [CharStatus, GetChar, GetEditedString, Handle,
LineOverflow, PutChar, PutString, Rubout, SetEcho];
CWindow: MONITOR
IMPORTS
Beta, CEnvUtil, CRuntime, CString, FBasics, Format, Heap, Inline,
Runtime, StartState, Stream,
String, TTY, Time
EXPORTS CEnvUtil, CWindowLibExtras = {
OPEN CEnvUtil, CWindowLib;
zone: PUBLIC UNCOUNTED ZONE ← Heap.Create[initial:1];
printLoadMsg:BOOLEAN ← TRUE;
NullFileHandle:FileHandle = LOOPHOLE[LONG[0]];
hash: System.GreenwichMeanTime = [24204357100B];
SystemCall:PUBLIC SIGNAL[string:LONG STRING] RETURNS[exitStatus:INTEGER] = CODE;
ProcessLineProc:TYPE = PROCEDURE[buffer:LONG STRING,data:DataHandle,
defaultStdin,defaultStdout,defaultStderr:Stream.Handle]
RETURNS[outcome:Outcome ← normal];
ReadAndRun:PUBLIC PROCEDURE[
data:DataHandle,tH:TTY.Handle,appendCmd:AppendCmdProc]
RETURNS[outcome: Outcome ← normal] = {
inStream,outStream,stdin,stdout,stderr:Stream.Handle;
argV,copyArgV:ArgSeq ← NIL;
buffer:LONG STRING ← zone.NEW[StringBody[80]];
bufferPos:CARDINAL;
prompt: LONG STRING ← GetPrompt[];
Write:Format.StringProc = {Print[s,data];};
MakeStandardStreams:PROCEDURE = {
inStream ← LOOPHOLE[
zone.NEW[StdinObject ← [Stream.defaultObject,normalMode]]];
outStream ← LOOPHOLE[
zone.NEW[StdinObject ← [Stream.defaultObject,normalMode]]];
inStream.getByte ← ReadFromWindow;
inStream.putByte ← ErrorPutByte;
outStream.getByte ← ErrorGetByte;
outStream.putByte ← WriteToWindow;
inStream.delete ← outStream.delete ← NopDelete;
inStream.sendNow ← outStream.sendNow ← NopSendNow;
inStream.clientData ← outStream.clientData ← data;
};
DeleteStdStreams:PROCEDURE = {
IF CRuntime.CanDelete[stdin] THEN Stream.Delete[stdin];
IF CRuntime.CanDelete[stdout] THEN Stream.Delete[stdout];
IF CRuntime.CanDelete[stderr] THEN Stream.Delete[stderr];
};
ReadFromWindow:Stream.GetByteProcedure = {
controlD:Environment.Byte = 4;
InStreamRead:PROCEDURE[c:CHAR] RETURNS[status:TTY.CharStatus] = {
SELECT c FROM
'\N => {
TTY.PutChar[tH,c];
lastChar ← c;
RETURN[stop];};
LOOPHOLE[controlD] => {
lastChar ← c;
RETURN[stop];};
Ascii.ControlC => ERROR CRuntime.ProgramExited[1];
ENDCASE => RETURN[ok];
};
lastChar:CHAR;
iS:StdinHandle = LOOPHOLE[inStream];
IF IsRawMode[iS.mode] OR IsCBreakMode[iS.mode] THEN {
-- flush buffer in case of return to buffered input.
bufferPos ← buffer.length;
byte ← LOOPHOLE[TTY.GetChar[tH]];
IF LOOPHOLE[byte,CHAR] = Ascii.ControlC AND ~IsRawMode[iS.mode] THEN
ERROR CRuntime.ProgramExited[1];
IF IsEchoMode[iS.mode] THEN
TTY.PutChar[tH,LOOPHOLE[byte]];
RETURN[byte];
};
IF bufferPos = buffer.length THEN {
[] ← TTY.SetEcho[tH,
IF IsEchoMode[iS.mode] THEN plain ELSE none];
bufferPos ← 0;
DO
lineDeleted:BOOLEAN ← FALSE;
buffer.length ← 0;
[] ← TTY.GetEditedString[h:tH,s:buffer,t:InStreamRead
!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\N"L];
lineDeleted ← TRUE;
CONTINUE;}];
IF NOT lineDeleted THEN EXIT;
ENDLOOP;
String.AppendCharAndGrow[to:@buffer,c:lastChar,z:zone];
};
byte ← LOOPHOLE[buffer.text[bufferPos]];
IF byte = controlD THEN SIGNAL Stream.EndOfStream[0];
bufferPos ← bufferPos + 1;
};
WriteToWindow:Stream.PutByteProcedure = {
TTY.PutChar[tH,LOOPHOLE[byte]];};
ProcessLine:PROCEDURE[buffer:LONG STRING,data:DataHandle,
defaultStdin,defaultStdout,defaultStderr:Stream.Handle]
RETURNS[outcome:Outcome ← normal] = {
firstWord:LONG STRING ← GetFirstWord[buffer];
IF firstWord = NIL THEN RETURN;
{ENABLE UNWIND => zone.FREE[@firstWord];
SELECT firstWord[0] FROM
'@ => outcome ← ProcessFile[
buffer,data,defaultStdin,defaultStdout,defaultStderr];
'! => ChangeStandardStreams[buffer,data];
'/ => ChangeSwitches[buffer,data];
'? => PrintStatus[data,tH];
'- => NULL;
'↑ => StopIfCExec[data]
ENDCASE => IF (Runtime.IsBound[LOOPHOLE[Beta.GetVersion]]
AND Time.Current[] > hash) THEN {
Write["\nThe C Environment will not run after March 1, 1987."L];
TTY.PutString[
tH, "The C Environment will not run after March 1, 1987.\n"L];
}
ELSE outcome ←
SELECT TRUE FROM
ValidPrefix[firstWord,"unload.~"L] => Unload[buffer,tH],
ValidPrefix[firstWord,"show.~"L] => ShowHandles[buffer,tH],
ENDCASE => Run[buffer,data,defaultStdin,defaultStdout,defaultStderr];
};
zone.FREE[@firstWord];
UpdateLogReadLength[data];
CRuntime.StopIfUserAborted[];
};
ValidPrefix: PROCEDURE [prefix, string: LONG STRING]
RETURNS [BOOLEAN] = {
FOR i: CARDINAL IN [0..MIN[prefix.length, string.length]) DO
IF ToLower[prefix[i]] # string[i] THEN RETURN [FALSE];
ENDLOOP;
IF prefix.length # 0 THEN {
buf: LONG STRING ← [50];
String.AppendString[to: buf, from: prefix];
RETURN [CEnvUtil.UniqueFilePrefix[buf] = 0]; -- doesn't conflict with a file name
};
RETURN [FALSE];
};
ToLower: PROCEDURE [c: CHARACTER]
RETURNS [CHARACTER] = {
IF c IN ['A..'Z] THEN RETURN [VAL[ORD[c] - ORD['A] + ORD['a]]];
RETURN [c];
};
ProcessFile:PROCEDURE[
buffer:LONG STRING,data:DataHandle,
defaultStdin,defaultStdout,defaultStderr:Stream.Handle]
RETURNS[outcome:Outcome ← normal] = {
nextChar:PROCEDURE RETURNS[c:CHAR] = {
c ← Stream.GetChar[in];}; -- AppendLine catches signals from read.
in:Stream.Handle = GetFileInputStream[buffer];
line:LONG STRING ← zone.NEW[StringBody[80]];
IF in = NIL THEN {
Blink[];
Write["\nUnable to open script file"L];
RETURN[error];};
DO
line.length ← 0;
AppendLine[@line,nextChar];
IF String.Empty[line] THEN EXIT;
TTY.PutString[tH,prompt];
TTY.PutString[tH,line];
IF ProcessLine[line,data,defaultStdin,defaultStdout,defaultStderr] # normal
AND StopOnError[data] THEN {
outcome ← error; EXIT;};
ENDLOOP;
Stream.Delete[in];
zone.FREE[@line];
};
Run:PROCEDURE[
cmdLine:LONG STRING,data:DataHandle,
defaultStdin,defaultStdout,defaultStderr:Stream.Handle]
RETURNS[outcome:Outcome ← normal] = {
argC:CARDINAL;
fh:FileHandle ← NullFileHandle;
fileName:LONG STRING ← NIL;
ssh:StartState.Handle;
badVersion:BOOLEAN ← FALSE;
found: CARDINAL;
LOOPHOLE[inStream,StdinHandle].mode ← CWindowLib.normalMode;
[fileName,argC,argV] ← ParseLine[cmdLine];
IF String.Empty[fileName] THEN RETURN;
copyArgV ← zone.NEW[StringSeq[argV.maxlength]];
Inline.LongCOPY[
from:argV,
nwords:(2 * CARDINAL.SIZE) + (argV.maxlength * CString.CString.SIZE),
to: copyArgV];
cmdLine.length ← 0;
found ← CEnvUtil.UniqueFilePrefix[fileName]; -- appends rest of file name
IF found # 1 THEN { -- not a unique name
Blink[];
Write["\N"L];
Write[fileName];
Write[
IF found = 0 THEN ": No such file or directory."L ELSE ": Ambiguous."L];
zone.FREE[@fileName];
RETURN[error];
};
{ENABLE {
UNWIND => IF fileName # NIL THEN zone.FREE[@fileName];
FileError => {
Blink[];
Write["\N"L];
Write[message];
outcome ← error;
CONTINUE;};
FBasics.LibraryProblem => {
Blink[];
Write["\N"L];
Write[s];
outcome ← error;
CONTINUE;};
StartState.LoadError => {
Blink[];
Write[message];
ReleaseFile[fh];
outcome ← error;
DeleteStdStreams[];
CONTINUE;};
StartState.VersionMismatch => {
Blink[];
badVersion ← TRUE;
Write["\NVersion mismatch of "L];
Write[module];
Write["..."L];
RESUME;
};
CRuntime.TooMuchGlobalArraySpace => {
Write["\nToo much global array space required"L];
outcome ← error;
DeleteStdStreams[];
CONTINUE;};
StdStreamError => {
Blink[];
Write["\NError opening "L];
Write[SELECT whichStream FROM
stdin => "stdin"L,
stdout => "stdout"L,
stderr => "stderr"L,
ENDCASE => "?"L];
Write[" stream."L];
outcome ← error;
CONTINUE;};
CRuntime.ProcessNotRegistered => {
Write["\nProcess not registered"L];
outcome ← error;
CONTINUE;};};
started:BOOLEAN;
fh ← AcquireFile[name:fileName];
[stdin,stdout,stderr] ← GetStdStreams[
data,defaultStdin,defaultStdout,defaultStderr];
[ssh,started] ← StartState.GetHandle[LOOPHOLE[fh]];
IF ~started THEN {
IF printLoadMsg THEN {
Write["\nLoading "L];
Write[fileName];
Write["..."L];};
StartState.Load[ssh,LOOPHOLE[fh]];
IF printLoadMsg THEN
Format.LongOctal[Write,LOOPHOLE[StartState.GetLoadHandle[ssh]]];
IF badVersion THEN {
outcome ← error;
Write["\NUnloading..."L];
StartState.UnloadUnstartedProgram[ssh
!StartState.UnloadError => {
Blink[]; Write[message]; CONTINUE;}];
Write["...done."L];
DeleteStdStreams[];}
ELSE {
IF Debugging[data] THEN Runtime.CallDebugger[fileName];
IF printLoadMsg THEN Write["...Starting..."];
buffer.length ← bufferPos ← 0;
outcome ← StartState.Start[
ssh,argC,@(copyArgV[0]),stdin,stdout,stderr].outcome;
IF outcome # StartState.normalOutcome THEN {
Write["\NExit code: "L];
Format.Decimal[Write,outcome];};
};
}
ELSE { -- already loaded
ReleaseFile[fh];
IF printLoadMsg THEN {
Write["\nRestarting "L];
Write[fileName];
Write[" ("L];
Format.LongOctal[Write,LOOPHOLE[StartState.GetLoadHandle[ssh]]];
Write[")..."L];};
IF Debugging[data] THEN Runtime.CallDebugger[fileName];
buffer.length ← bufferPos ← 0;
outcome ← StartState.Restart[
ssh,argC,@(copyArgV[0]),stdin,stdout,stderr].outcome;
IF outcome # StartState.normalOutcome THEN {
Write["\NExit code: "L];
Format.Decimal[Write,outcome];};
};
};
CEnvUtil.SetOutputMode[data, CWindowLib.normalMode];
CleanUp[@fileName,@argV,@copyArgV];
};
-- ReadAndRun starts here --
{ENABLE ABORTED => {
String.FreeString[zone,buffer];
CleanUp[NIL,@argV,@copyArgV];
zone.FREE[@inStream]; zone.FREE[@outStream];
zone.FREE[@prompt];
CONTINUE;};
MakeStandardStreams[];
DO
ENABLE ABORTED =>
IF EndingToolInstance[data] THEN REJECT ELSE {
TTY.PutString[h:tH,s:"\NAborted"L];CONTINUE;};
TTY.PutString[h:tH,s:prompt];
buffer.length ← 0;
IF appendCmd[@buffer].lineDeleted THEN LOOP;
outcome ← ProcessLine[buffer,data,inStream,outStream,outStream
!SystemCall =>
RESUME[HandleSystemCall[string,inStream,ProcessLine]]];
ENDLOOP;
};
};
HandleSystemCall:PROCEDURE[
string:LONG STRING, inStream: Stream.Handle, processLine:ProcessLineProc]
RETURNS[outcome:INTEGER] = {
pos:CARDINAL ← 0;
nextChar:PROCEDURE RETURNS[c:CHAR] = {
IF pos >= string.length THEN SIGNAL Stream.EndOfStream[0];
c ← string[pos];
pos ← pos.SUCC;
};
inheritedStdin:Stream.Handle = CRuntime.GetStdin[]↑;
inheritedStdout:Stream.Handle = CRuntime.GetStdout[]↑;
inheritedStderr:Stream.Handle = CRuntime.GetStderr[]↑;
newData:DataHandle ← NIL;
line:LONG STRING ← NIL;
{ENABLE UNWIND => {
IF line # NIL THEN zone.FREE[@line];
IF newData # NIL THEN zone.FREE[@newData];};
saveMode:INTEGER = LOOPHOLE[inStream,StdinHandle].mode;
newData ← MakeDataForSystemCall[];
line ← zone.NEW[StringBody[80]];
DO
line.length ← 0;
AppendLine[@line,nextChar];
IF String.Empty[line] THEN EXIT;
outcome ← processLine[
line,newData,inheritedStdin,inheritedStdout,inheritedStderr
!SystemCall =>
RESUME[HandleSystemCall[string,inStream,processLine]]];
ENDLOOP;
LOOPHOLE[inStream, StdinHandle].mode ← saveMode;
zone.FREE[@line];
FreeData[newData];
zone.FREE[@newData];
};
};
CleanUp:PROCEDURE[
fileName: POINTER TO LONG STRING, argV,copyArgV:POINTER TO ArgSeq] = {
IF fileName # NIL AND fileName↑ # NIL THEN zone.FREE[fileName];
IF argV↑ # NIL THEN {
FOR i:CARDINAL IN [0..argV.length) DO
wptr:LONG POINTER ← CString.ToWordPointer[argV[i]];
zone.FREE[@wptr];
ENDLOOP;
zone.FREE[argV];
};
IF copyArgV # NIL THEN zone.FREE[copyArgV];
};
ErrorGetByte:Stream.GetByteProcedure = {ERROR Stream.InvalidOperation}; ErrorPutByte:PUBLIC Stream.PutByteProcedure = {ERROR Stream.InvalidOperation};
NopDelete: Stream.DeleteProcedure = {};
NopSendNow:Stream.SendNowProcedure = {};
IsItDefaultStdin:PUBLIC PROCEDURE[sH:Stream.Handle] RETURNS[BOOLEAN] = {
RETURN[sH.sendNow = NopSendNow];};
IsRawMode:PROCEDURE[mode:INTEGER] RETURNS[BOOLEAN] = INLINE {
RETURN[Inline.BITAND[rawMode,mode] # 0];};
IsCBreakMode:PROCEDURE[mode:INTEGER] RETURNS[BOOLEAN] = INLINE {
RETURN[Inline.BITAND[cBreakMode,mode] # 0];};
IsEchoMode:PROCEDURE[mode:INTEGER] RETURNS[BOOLEAN] = INLINE {
RETURN[Inline.BITAND[echoMode,mode] # 0];};
SetPrintLoadMessage:PUBLIC ENTRY PROCEDURE[
printMsg:BOOLEAN] RETURNS[oldPrintMsg:BOOLEAN] = {
oldPrintMsg ← printLoadMsg;
printLoadMsg ← printMsg;};
RegisterItems[];
}.