-- 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[""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