-- VolumeInitCommandImpl modified November 10, 1982 5:51 pm by Taft
-- This file is the command Processor/"Higher level" stuff.
-- It is machine- and device-independent.
DIRECTORY
File USING [Error, ErrorType, Unknown],
Inline USING [BITAND, BITOR, LowHalf],
OthelloDevice USING [CallCommandProc],
OthelloDefs,
PhysicalVolume USING [CanNotScavenge, Error, ErrorType],
Runtime USING [GetBuildTime],
RuntimeInternal USING [SendMsgSignal],
Scavenger USING [Error, ErrorType],
Storage USING [FreeString],
String USING [AppendLongNumber, StringBoundsFault],
System USING [LocalTimeParameters, LocalTimeParametersUnknown,
SetLocalTimeParameters],
TTY USING [Create, SetEcho, GetChar, Handle, PutChar],
Time USING [Append, Unpack],
UserTerminal USING [BlinkDisplay, SetCursorPattern],
Volume USING [InsufficientSpace, NeedsScavenging, NotOpen, Unknown];
VolumeInitCommandImpl: PROGRAM
IMPORTS
File, Inline, OthelloDevice, PhysicalVolume, Runtime, RuntimeInternal, Scavenger,
Storage, String, System, Time, TTY, UserTerminal, Volume
EXPORTS OthelloDefs =
BEGIN
Question: PUBLIC SIGNAL = CODE;
TryAgain: PUBLIC SIGNAL = CODE;
BS: CHARACTER = 10C;
ControlA: CHARACTER = 'A - 100B;
ControlP: CHARACTER = 'P - 100B;
ControlW: CHARACTER = 'W - 100B;
CR: CHARACTER = 15C;
DEL: CHARACTER = 177C;
ESC: CHARACTER = 33C;
SP: CHARACTER = ' ;
NUL: CHARACTER = 0C;
ttyHandle: TTY.Handle = TTY.Create["Othello"];
Cannon: PROC [c: CHARACTER] RETURNS [CHARACTER] =
{RETURN[IF (c ← UnMakeUserChar[c]) IN ['A..'Z] THEN c+('a-'A) ELSE c]};
CollectCommand: PROC [
p: DESCRIPTOR FOR ARRAY OF OthelloDefs.CommandTableRecord]
RETURNS [CARDINAL] =
BEGIN
userString: STRING ← [100];
ans: FindAnswer;
c: CHARACTER;
WriteString["> "L];
userString.length ← 0;
DO SELECT (c ← ReadChar[]) FROM
DEL => {WriteLine[" XXX"L]; ERROR TryAgain};
BS, ControlA => EraseBack[userString];
'? => ExplainOptions[userString, p];
CR, SP =>
BEGIN
IF c=CR AND userString.length=0 THEN {WriteString["\r> "L]; LOOP};
ans ← FindPossibles[userString, p];
WITH ans SELECT FROM
none, many => IF userString[userString.length-1]#SP THEN
{AbortCommandFileIfAny[]; UserTerminal.BlinkDisplay[]};
one => RETURN[index];
ENDCASE => ERROR;
END;
ENDCASE => {WriteChar[c]; StuffChar[userString, MakeUserChar[c]]};
ENDLOOP;
END;
Confirm: PUBLIC PROC [how: OthelloDefs.ConfirmType←once] =
BEGIN
Nap: PROC = {FOR i: CARDINAL IN [0..LAST[CARDINAL]) DO ENDLOOP};
IF CommandFileActive[] THEN RETURN;
WriteString["Are you "L];
IF how=thrice THEN WriteString["still "L];
WriteString["sure? [y or n]: "L];
DO
c: CHARACTER←ReadChar[];
SELECT c FROM
'y, 'Y => {WriteLine["Yes"L]; EXIT};
'n, 'N, DEL => {WriteLine["No"L]; ERROR TryAgain};
ENDCASE => UserTerminal.BlinkDisplay[];
ENDLOOP;
IF how=twice THEN {Nap[]; Confirm[thrice]};
END;
EraseBack: PROC [userString: STRING] =
BEGIN
c: CHARACTER;
WHILE userString.length#0 DO
userString.length ← userString.length - 1;
c ← userString[userString.length];
EraseTTYChar[UnMakeUserChar[c]];
IF IsUserChar[c] THEN EXIT;
ENDLOOP;
END;
EraseTTYChar: PROC [c: CHARACTER] =
BEGIN
SELECT c FROM IN [' ..'~] => NULL; CR => RETURN; ENDCASE => EraseTTYChar[' ];
TTY.PutChar[ttyHandle, BS];
TTY.PutChar[ttyHandle, ' ];
TTY.PutChar[ttyHandle, BS];
END;
ExplainOptions: PROC [
userString: STRING,
commandTable: DESCRIPTOR FOR ARRAY OF OthelloDefs.CommandTableRecord] =
BEGIN
first: BOOLEAN ← TRUE;
i: CARDINAL;
WriteChar['?];
IF userString.length#0 THEN FOR i IN [0..LENGTH[commandTable]) DO
IF HeadMatch[userString, commandTable[i].name, userString.length] THEN
BEGIN
WriteString[IF first THEN "\rCurrent Options Are: "L ELSE ", "L];
first ← FALSE;
WriteString[commandTable[i].name];
END;
ENDLOOP;
IF first THEN -- Didn't match... tell all
BEGIN
WriteString["\rValid Commands Are: "L];
FOR i IN [0..LENGTH[commandTable]) DO
WriteString[commandTable[i].name];
IF i+1#LENGTH[commandTable] THEN WriteString[", "L];
ENDLOOP;
END;
WriteString["\r> "L];
WriteString[userString];
END;
FindAnswer: TYPE = RECORD[SELECT how:* FROM
none=> NULL, many => NULL, one => [index: CARDINAL], ENDCASE];
FindPossibles: PROC [
userString: STRING,
commandTable: DESCRIPTOR FOR ARRAY OF OthelloDefs.CommandTableRecord]
RETURNS [ans: FindAnswer] =
BEGIN
cmd: CARDINAL;
head: CARDINAL ← userString.length;
matchString: STRING;
StickTail: PROC =
BEGIN
WHILE userString.length#commandTable[cmd].name.length DO
StuffChar[userString, matchString[userString.length]];
ENDLOOP;
END;
ReduceTail: PROC =
BEGIN
userString.length ← head;
WHILE Cannon[userString[userString.length]]=Cannon[matchString[userString.length]]
DO userString.length ← userString.length +1 ENDLOOP;
END;
ans ← [none[]];
IF head=0 THEN RETURN;
FOR cmd IN [0..LENGTH[commandTable]) DO
matchString ← commandTable[cmd].name;
IF HeadMatch[userString, matchString, head] THEN
WITH ans SELECT FROM
none => {ans ← [one[cmd]]; StickTail[]};
ENDCASE => {ans ← [many[]]; ReduceTail[]};
ENDLOOP;
WHILE head#userString.length DO WriteChar[userString[head]]; head ← head+1 ENDLOOP;
END;
GetName: PUBLIC PROC [
prompt, dest: STRING, how: OthelloDefs.EchoNoEcho←echo,
signalQuestion: BOOLEAN ← FALSE, escString: STRING ← NIL] =
BEGIN
c: CHARACTER;
first: BOOLEAN ← TRUE;
EraseChar: PROC =
BEGIN
IF dest.length=0 THEN RETURN;
dest.length ← dest.length - 1;
IF how=echo THEN EraseTTYChar[dest[dest.length]];
END;
CWriteC: PROC [c: CHARACTER] = {IF how=echo THEN WriteChar[c]};
CWriteString: PROC[s: STRING] = {IF how=echo THEN WriteString[s]};
WriteString[prompt];
CWriteString[dest];
DO
c ← ReadChar[];
SELECT TRUE FROM
c=BS, c=ControlA => EraseChar[];
c=SP, c=CR => {NewLine[]; RETURN};
c=DEL => {WriteLine[" XXX"L]; ERROR TryAgain};
c=ControlW => WHILE dest.length#0 DO EraseChar[]; ENDLOOP;
c=ESC AND escString#NIL AND dest.length=0 =>
BEGIN
i: CARDINAL;
dest.length ← escString.length;
FOR i IN [0..escString.length) DO
dest[i] ← escString[i]; CWriteC[dest[i]]; ENDLOOP;
END;
c='? AND signalQuestion =>
{SIGNAL Question; WriteString[prompt]; CWriteString[dest]};
dest.length >= dest.maxlength =>
BEGIN
WriteLine[" ... String Too Long"L];
WriteString[prompt];
dest.length ← 0;
END;
c >= SP =>
BEGIN
IF first THEN WHILE dest.length#0 DO EraseChar[]; ENDLOOP;
CWriteC[c];
dest[dest.length] ← c;
dest.length ← dest.length + 1;
END;
ENDCASE => UserTerminal.BlinkDisplay[];
first ← FALSE;
ENDLOOP;
END;
HeadMatch: PROC [userString, matchString: STRING, head: CARDINAL] RETURNS [BOOLEAN] =
BEGIN
i: CARDINAL;
IF head>matchString.length THEN RETURN[FALSE];
FOR i IN [0..head) DO
IF Cannon[userString[i]]#Cannon[matchString[i]] THEN RETURN[FALSE];
ENDLOOP;
RETURN[TRUE];
END;
IsUserChar: PROC [c: UNSPECIFIED] RETURNS [BOOLEAN] = {RETURN[Inline.BITAND[0200B, c]#0]};
MakeUserChar: PROC [c: UNSPECIFIED] RETURNS [UNSPECIFIED] =
{RETURN[Inline.BITOR[0200B, c]]};
NewLine: PUBLIC PROC = {WriteChar[CR]};
PrintBcdTime: PUBLIC PROC =
BEGIN
calParams: System.LocalTimeParameters = [west, 8, 0, 121, 305];
-- California time
string: STRING ← [40];
string.length ← 0;
BEGIN
Time.Append[string, Time.Unpack[Runtime.GetBuildTime[] !
System.LocalTimeParametersUnknown => GOTO TimeParamsUnknown]];
EXITS TimeParamsUnknown =>
BEGIN
System.SetLocalTimeParameters[calParams];
Time.Append[string, Time.Unpack[Runtime.GetBuildTime[]]];
END;
END;
WriteString[" of "L];
WriteLine[string];
END;
command: STRING ← NIL;
index: CARDINAL ← 0;
SetCommandString: PUBLIC PROC [s: STRING, i: CARDINAL ← 0] = {
IF command # NIL THEN Storage.FreeString[command];
command ← s; index ← i};
GetCommandString: PUBLIC PROC RETURNS [s: STRING, i: CARDINAL] = {
s ← command; i ← index;
command ← NIL; index ← 0};
CommandFileActive: PUBLIC PROC RETURNS [BOOLEAN] = {RETURN [command#NIL]};
AbortCommandFileIfAny: PUBLIC PROC =
{IF CommandFileActive[] THEN
{WriteLine["[Command file aborted]"L]; SetCommandString[NIL]}};
ReadChar: PUBLIC PROC RETURNS [c: CHARACTER] = {
IF command # NIL THEN
BEGIN
IF index < command.length THEN
{c ← command[index]; index ← index + 1; RETURN};
SetCommandString[NIL];
WriteLine["[End of command file]"L];
END;
RETURN[TTY.GetChar[ttyHandle]]};
ReadNumber: PUBLIC PROC [prompt: STRING, min, max, default: LONG CARDINAL]
RETURNS [ans: LONG CARDINAL] =
BEGIN
i: CARDINAL;
s: STRING ← [30];
DO
s.length ← 0;
IF default#LAST[LONG CARDINAL] THEN String.AppendLongNumber[s, default, 10];
WriteString[prompt];
WriteChar['[];
WriteLongNumber[min];
WriteString[".."L];
WriteLongNumber[max];
WriteString["]: "L];
GetName[""L, s];
ans ← 0;
FOR i IN [0..s.length) DO
IF s[i] NOT IN ['0..'9] THEN EXIT;
ans ← 10*ans + s[i]-'0;
REPEAT FINISHED => IF ans IN [min..max] THEN RETURN;
ENDLOOP;
ReportError["Bad Number !"L];
ENDLOOP;
END;
-- Should be more careful about truncation
ReadShortNumber: PUBLIC PROC [prompt: STRING, min, max, default: LONG CARDINAL]
RETURNS [CARDINAL] =
{RETURN[Inline.LowHalf[ReadNumber[prompt, min, max, default]]]};
DebugAsk: PUBLIC PROC =
BEGIN
AbortCommandFileIfAny[];
WriteString["\rType ControlP to muddle on........"L];
WHILE ReadChar[]#ControlP DO ENDLOOP;
NewLine[];
END;
ReportError: PUBLIC PROC [message: STRING] = {WriteLine[message]; AbortCommandFileIfAny[]};
RunCommand: PUBLIC PROC [p: DESCRIPTOR FOR ARRAY OF OthelloDefs.CommandTableRecord] =
BEGIN
i: CARDINAL;
SetCursor: PROC =
BEGIN
UserTerminal.SetCursorPattern[
[100000B, 140000B, 160000B, 170000B, 174000B, 176000B,
177000B, 170000B, 154000B, 114000B, 6000B, 6000B, 3000B,
3000B, 1400B, 1400B]];
END;
[] ← TTY.SetEcho[ttyHandle, FALSE];
SetCursor[];
i ← CollectCommand[p ! TryAgain => RETRY];
NewLine[];
OthelloDevice.CallCommandProc[p[i].proc
-- CallCommandProc catches machine- and device-dependent SIGNALs.
-- Only device-independent SIGNALs should be mentioned here.
! PhysicalVolume.Error =>
BEGIN
PrintNames: PROC [x: PhysicalVolume.ErrorType] =
BEGIN
e: ARRAY PhysicalVolume.ErrorType OF STRING = [
"badDisk"L, "badSpotTableFull"L, "containsOpenVolumes"L, "diskReadError"L,
"hardwareError"L, "hasPilotVolume"L, "alreadyAsserted"L,
"insufficientSpace"L, "invalidHandle"L, "nameRequired"L, "notReady"L,
"noSuchDrive"L, "noSuchLogicalVolume"L, "pageCountTooSmallForVolume"L,
"physicalVolumeUnknown"L, "subvolumeHasTooManyBadPages"L,
"tooManySubvolumes"L, "writeProtected"L, "wrongFormat"L];
WriteString[e[x]];
END;
WriteString["\rPhysicalVolume.Error["L];
PrintNames[error];
WriteChar[']];
DebugAsk[];
CONTINUE;
END;
Scavenger.Error =>
BEGIN
PrintNames: PROC [x: Scavenger.ErrorType] =
BEGIN
e: ARRAY Scavenger.ErrorType OF STRING = [
"cannotWriteLog"L, "noSuchPage"L, "orphanNotFound"L, "volumeOpen"L,
"diskHardwareError"L, "diskNotReady"L];
WriteString[e[x]];
END;
WriteString["\rScavenger.Error["L];
PrintNames[error];
WriteChar[']];
DebugAsk[];
CONTINUE;
END;
File.Error =>
BEGIN
PrintNames: PROC [x: File.ErrorType] =
BEGIN
e: ARRAY File.ErrorType OF STRING = [
"insufficientPermissions"L, "immutable"L, "nonuniqueID"L,
"notImmutable"L, "reservedType"L];
WriteString[e[x]];
END;
WriteString["\rFile.Error["L];
PrintNames[type];
WriteChar[']];
DebugAsk[];
CONTINUE;
END;
PhysicalVolume.CanNotScavenge =>
{WriteString["\rPhysicalVolume.CanNotScavenge"L]; DebugAsk[]; CONTINUE};
Volume.InsufficientSpace =>
{WriteString["\rVolume.InsufficientSpace"L]; DebugAsk[]; CONTINUE};
Volume.NotOpen => {WriteString["\rVolume.NotOpen"L]; DebugAsk[]; CONTINUE};
Volume.NeedsScavenging =>
{WriteLine["\rPlease Scavenge the volume first"L]; AbortCommandFileIfAny[]; CONTINUE};
Volume.Unknown => {WriteString["\rVolume.Unknown"L]; DebugAsk[]; CONTINUE};
File.Unknown => {WriteString["\rFile.Unknown"L]; DebugAsk[]; CONTINUE};
String.StringBoundsFault =>
{WriteString["\rString.StringBoundsFault"L]; DebugAsk[]; CONTINUE};
TryAgain => {AbortCommandFileIfAny[]; CONTINUE};
ANY =>
BEGIN
a, b: UNSPECIFIED;
WriteString["\rUncaught Signal = "L];
[b, a] ← SIGNAL RuntimeInternal.SendMsgSignal;
WriteOctal[a];
WriteString[", msg = "L];
WriteOctal[b];
DebugAsk[];
CONTINUE;
END];
END;
StuffChar: PROC [userString: STRING, char: CHARACTER] =
BEGIN
userString.length ← userString.length + 1;
IF userString.length=userString.maxlength THEN
{WriteLine[" Command too long!"L]; ERROR TryAgain};
userString[userString.length-1] ← char;
END;
UnMakeUserChar: PROC [c: CHARACTER] RETURNS [CHARACTER] =
{RETURN[Inline.BITAND[177B, c]]};
UpperCase: PUBLIC PROC [c: CHARACTER] RETURNS [CHARACTER] =
{IF c IN ['a..'z] THEN c ← c + ('A-'a); RETURN[c]};
WriteChar: PUBLIC PROC [c: CHARACTER] = {TTY.PutChar[ttyHandle, UnMakeUserChar[c]]};
WriteFixedWidthNumber: PUBLIC PROC [
x: LONG CARDINAL, count: CARDINAL, base: CARDINAL ← 10] =
BEGIN
WFD: PROC [x: LONG CARDINAL, c: CARDINAL] =
BEGIN
IF c=count THEN RETURN;
WFD[x/base, c+1];
WriteChar[IF c=0 OR x#0 THEN Inline.LowHalf[x MOD base]+'0 ELSE ' ];
END;
WFD[x, 0];
END;
WriteLine: PUBLIC PROC [s: STRING] = {WriteString[s]; NewLine[]};
WriteLongNumber: PUBLIC PROC [num: LONG CARDINAL] =
BEGIN
s: STRING ← [40];
s.length ← 0;
String.AppendLongNumber[s, num, 10];
WriteString[s];
END;
WriteOctal: PUBLIC PROC [num: CARDINAL] =
{IF num#0 THEN WriteOctal[num/8]; WriteChar[(num MOD 8)+'0]};
WriteString: PUBLIC PROC [s: STRING] =
BEGIN
i: CARDINAL;
IF s#NIL THEN FOR i IN [0..s.length) DO WriteChar[s[i]]; ENDLOOP;
END;
Yes: PUBLIC PROC [s: STRING] RETURNS [BOOLEAN] =
BEGIN
DO
WriteString[s];
SELECT ReadChar[] FROM
'Y, 'y => {WriteLine["yes"L]; RETURN[TRUE]};
'N, 'n => {WriteLine["no"L]; RETURN[FALSE]};
ENDCASE => ReportError["<y or n>"L];
ENDLOOP;
END;
END..
LOG
Time: June 1, 1980 2:31 AM By: Forrest
Action: upgraded for new formatSA4000
Time: August 29, 1980 10:21 AM By: BLyon
Action: added EquivalentSubString, StringToNumber, and UpperCase.
Time: September 3, 1980 10:58 PM By: Forrest
Action: Added new physical volume error types.
Time: October 2, 1980 3:39 PM By: Jose
Action: Added catch phrases for Floppy formatter signals.
Time: October 2, 1980 3:39 PM By: Forrest
Action: Kill Storage&String impls, import String.
Time: February 6, 1981 7:45 PM By: Luniewski
Action: New Scavenger.ErrorType's.
Time: February 11, 1981 12:10 PM By: Gobbel
Action: Catch System.LocalTimeParametersUnknown.
Time: April 14, 1981 11:33 AM By: Bruce
Action: add SetCommandString.
11-Jun-81 10:14:29 Taft catch SIGNALs from FormatTrident.
19-Feb-82 16:53:04 Taft remove WriteString \r interpretation since compiler does this now.
4-Jun-82 8:39:32 Taft Add GetCommandString
September 9, 1982 5:18 pm Taft Mods for new Cedar Login
September 12, 1982 1:08 pm Taft Add CommandFileActive, AbortCommandFileIfAny; Confirm just returns if command file is active; misc command file cleanup