IagoOpsImpl.mesa - Iago user interface subroutines
Copyright © 1984, 1985 by Xerox Corporation. All rights reserved.
Birrell, December 7, 1983 10:51 am
Levin, September 22, 1983 1:32 pm
Willie-Sue, May 10, 1985 4:27:32 pm PDT
Russ Atkinson (RRA) May 2, 1986 6:01:37 pm PDT
DIRECTORY
Ascii USING [BS, ControlA, ControlQ, ControlW, ControlX, CR, DEL, Digit, Letter, Lower, SP],
BasicTime USING [Now, ToNSTime],
Booting USING [Switches, switches],
Convert USING [Error, IntFromRope, RopeFromInt],
DefaultRemoteNames USING [DefaultNames, Get],
Disk USING [Channel, DriveAttributes, NextChannel, PageCount, PageNumber],
File USING [Error, FindVolumeFromName, GetVolumeName, NextVolume, RC, SystemVolume, Volume, VolumeFile, VolumeID],
FileBackdoor USING [ReserveNoPages, ReservePages],
FormatDisk USING [altoRegionJargon, altoRegionsBottomUp, altoRegionsMax, altoRegionsSize, altoRegionsStart, hardUCodeSize, hardUCodeStart, Start],
FS USING [Error, FileInfo, StreamOpen],
FSPseudoServers USING [GetPseudoServers, InsertPseudoServer, Lookup, PseudoServerFromRope, PseudoServerList, RopeFromPseudoServer],
GermSwap USING [Switch],
IagoOps USING [CommandInfo, CommandList, VolumeID],
IO USING [Close, BreakProc, EndOfStream, EraseChar, GetChar, GetLineRope, GetTokenRope, PutChar, PutF, PutF1, PutFR, PutRope, STREAM],
PhysicalVolume USING [NextPhysical, Physical, PhysicalInfo, PhysicalRC],
ProcessorFace USING [processorID],
Rope USING [Cat, Compare, Concat, Equal, Fetch, Find, FromChar, Length, Match, ROPE, Replace, Run, SkipTo, Substr],
SystemVersion USING [machineType];
IagoOpsImpl: CEDAR PROGRAM
IMPORTS Ascii, BasicTime, Booting, Convert, DefaultRemoteNames, Disk, File, FileBackdoor, FormatDisk, FS, FSPseudoServers, IO, PhysicalVolume, ProcessorFace, Rope, SystemVersion
EXPORTS File--VolumeID--, IagoOps
= BEGIN
ROPE: TYPE = Rope.ROPE;
STREAM: TYPE = IO.STREAM;
******** Input subroutines ********
Rubout: PUBLIC ERROR = CODE;
GetID: PUBLIC PROC [in, out: STREAM, default: ROPE, init: ROPENIL, echo: BOOLTRUE] RETURNS [id: ROPE, c: CHAR] = {
OPEN Ascii;
firstTime: BOOL ← init = NIL;
EraseAll: PROC = {
IF echo THEN
FOR i: INT DECREASING IN [0..id.Length[]) DO out.EraseChar[id.Fetch[i]]; ENDLOOP;
id ← NIL;
};
Done: PROC [c: CHAR] RETURNS [BOOL] = INLINE {
IF firstTime THEN {
SELECT c FROM
ControlA, BS, ControlQ, ControlW, ControlX, CR, SP, DEL => NULL;
ENDCASE => EraseAll[];
firstTime ← FALSE;
};
RETURN [c = SP OR c = CR OR c = '?]
};
id ← Rope.Concat[init, default];
IF echo THEN IO.PutRope[out, default];
c ← IO.GetChar[in];
UNTIL Done[c] DO
SELECT c FROM
DEL => ERROR Rubout;
ControlA, BS => {
len: INT ← id.Length[];
IF len > 0 THEN {
len ← len - 1;
IF echo THEN out.EraseChar[id.Fetch[len]];
id ← id.Substr[len: len];
};
};
ControlW, ControlQ => {
text to be backed up is of the form ...<non-alpha><alpha><non-alpha>, the <alpha> and following <non-alpha> are to be removed.
alpha: BOOLFALSE;
FOR i: INT DECREASING IN [0..id.Length[]) DO
ch: CHAR = id.Fetch[i];
IF Ascii.Letter[ch] OR Ascii.Digit[ch] THEN alpha ← TRUE
ELSE IF alpha THEN {id ← id.Substr[len: i + 1]; EXIT};
IF echo THEN out.EraseChar[ch];
REPEAT
FINISHED => id ← NIL;
ENDLOOP;
};
ControlX => EraseAll[];
ENDCASE => {
id ← Rope.Concat[id, Rope.FromChar[c]];
IF echo THEN IO.PutChar[out, c];
};
c ← IO.GetChar[in];
ENDLOOP;
};
commandList: IagoOps.CommandList ← NIL;
RegisterCommand: PUBLIC PROC [info: IagoOps.CommandInfo] = {
... registers the command with Iago so it can be found again. This allows external programs to register commands as well.
new: IagoOps.CommandList = LIST[info];
lag: IagoOps.CommandList ← NIL;
FOR each: IagoOps.CommandList ← commandList, each.rest WHILE each # NIL DO
full: ROPE ← each.first.fullName;
SELECT Rope.Compare[info.fullName, full, FALSE] FROM
less => {
Insert new command
new.rest ← each;
EXIT;
};
equal => {
Replace old command
each.first ← info;
RETURN;
};
ENDCASE;
lag ← each;
ENDLOOP;
IF lag = NIL THEN commandList ← new ELSE lag.rest ← new;
};
GetCommandList: PUBLIC PROC RETURNS [IagoOps.CommandList] = {
RETURN [commandList];
};
isDiskReadable: BOOLTRUE;
GetCommand: PUBLIC PROC [in, out: STREAM, diskReadable: BOOL] RETURNS [found: IagoOps.CommandInfo] = {
May raise Rubout
prompt: ROPE = "\n\n> ";
BoolFalse: TYPE = BOOLFALSE;
prefix: ROPENIL;
isDiskReadable← diskReadable;
IO.PutRope[out, prompt];
DO
end: CHAR;
matches: { none, one, many } ← none;
uniqueLength: INT;
[id: prefix, c: end] ← GetID[in: in, out: out, default: NIL, init: prefix];
uniqueLength ← prefix.Length[];
IF end = '? THEN IO.PutChar[out, '?];
FOR each: IagoOps.CommandList ← commandList, each.rest WHILE each # NIL DO
c: IagoOps.CommandInfo = each.first;
fullName: ROPE = c.fullName;
okWhenNoDisk: BOOL = c.okWhenNoDisk;
IF (diskReadable OR okWhenNoDisk)
AND Rope.Run[s1: fullName, s2: prefix, case: FALSE] = prefix.Length[]
THEN {
IF matches = none
THEN {
matches ← one;
uniqueLength ← Rope.Length[fullName];
found ← c;
IF end = '? THEN IO.PutRope[out, " ... one of:\n"];
}
ELSE {
stillMatches: INT =
Rope.Run[s1: fullName, s2: found.fullName, case: FALSE];
uniqueLength ← MIN[uniqueLength, stillMatches];
matches ← many;
IF end = '? THEN IO.PutRope[out, ", "];
};
IF end = '? THEN IO.PutRope[out, fullName];
};
ENDLOOP;
IF end = '? AND matches # none
THEN { IO.PutRope[out, prompt]; IO.PutRope[out, prefix] }
ELSE {
IF uniqueLength = prefix.Length[]
THEN {
SELECT matches FROM
none => IO.PutRope[out, " ... command not found"];
one => EXIT;
many => IF uniqueLength # 0 THEN IO.PutRope[out, " ... ambiguous"];
ENDCASE => ERROR;
IO.PutRope[out, prompt]; IO.PutRope[out, prefix];
}
ELSE {
extra: ROPE = Rope.Substr[
found.fullName, prefix.Length[], uniqueLength-prefix.Length[]];
IO.PutRope[out, extra];
SELECT matches FROM
one => EXIT;
many => prefix ← Rope.Concat[prefix, extra];
ENDCASE => ERROR;
};
};
ENDLOOP;
};
GetArg: PUBLIC PROC [in, out: STREAM, prompt, default: ROPE, help: PROC] RETURNS [value: ROPENIL] = {
DO
end: CHAR;
IO.PutRope[out, prompt];
IO.PutRope[out, value];
[id: value, c: end] ← GetID[in: in, out: out, default: default, init: value];
default ← NIL;
IF end = '? THEN help[] ELSE EXIT;
ENDLOOP;
};
Confirm: PUBLIC PROC[in, out: STREAM] RETURNS [BOOL] = {
DO c: CHAR = IO.GetChar[in];
SELECT c FROM
'y, 'Y, Ascii.CR => { IO.PutRope[out, "yes"]; RETURN [TRUE] };
'n, 'N => { IO.PutRope[out, "no"]; RETURN [FALSE] };
Ascii.DEL => ERROR Rubout[];
ENDCASE =>
IO.PutRope[out, "?\nConfirm by typing \"Y\" or CR, deny by typing \"N\", abort by typing DEL: "];
ENDLOOP;
};
ConfirmDestruction: PUBLIC PROC[in, out: STREAM, which: ROPE] RETURNS [BOOL] = {
IO.PutF[out,
"\nThis operation will destroy all files on %g.\nAre you sure you want to do this? ",
[rope[which]] ];
RETURN [Confirm[in, out]]
};
GetNumber: PUBLIC PROC [in, out: STREAM, default: INT, max: INTLAST[INT], prompt, help: ROPE] RETURNS [size: INT ← 0] = {
Help: PROC = { IO.PutRope[out, help] };
sizeRope: ROPE ← Convert.RopeFromInt[default];
DO
ok: BOOLTRUE;
size ← 0;
sizeRope ← GetArg[ in: in, out: out, prompt: prompt, default: sizeRope, help: Help];
size ← Convert.IntFromRope[sizeRope ! Convert.Error => { ok ← FALSE; CONTINUE } ];
IF ~ok THEN { IO.PutRope[out, " ... not a number"]; LOOP };
IF size <= max
THEN RETURN
ELSE IO.PutF[out, " ... number should be less than %g", [integer[max]] ];
ENDLOOP;
};
GetSize: PUBLIC PROC [in, out: STREAM, default: INT, max: INTLAST[INT]] RETURNS [size: INT ← 0] = {
RETURN [GetNumber[
in, out, default, max,
"\nSize in pages: ", "? type the desired number of pages (in decimal)"]];
};
Defaults for "GetFile":
lastFile: ROPENIL;
lastPattern: ROPENIL;
lastDest: ROPENIL;
GetFile: PUBLIC PROC[in, out: STREAM, prompt: ROPENIL, extension: ROPENIL, default: ROPENIL, wDir: ROPENIL, check: BOOLFALSE, pattern: BOOLFALSE] RETURNS [name: ROPE] = {
Help: PROC = {
IO.PutRope[out, "? type an FS file name"];
IF pattern THEN IO.PutRope[out, " (or pattern)"];
IO.PutRope[out, " such as \"[]<volume>a.b\" or \"[server]<directory>c.d\""];
};
name ← SELECT TRUE FROM
default # NIL => default,
pattern => lastPattern,
check => lastFile,
ENDCASE => lastDest;
DO
name ← GetArg[
in: in, out: out,
prompt: Rope.Cat["\n", prompt, "file name", IF pattern THEN " or pattern: " ELSE ": "],
default: name, help: Help];
IF name.Length[] = 0 THEN { Help[]; LOOP };
IF name.Find["."] < 0 AND extension.Length[] > 0
THEN { name ← Rope.Concat[name, extension]; IO.PutRope[out, extension] };
IF check OR pattern
THEN {
lastPattern ← name;
IF lastPattern.Find["*"] < 0 THEN lastFile ← lastPattern;
}
ELSE lastDest ← name;
IF check THEN
name ← FS.FileInfo[name: name, wDir: wDir
! FS.Error => {
IO.PutF1[out, " ... %g", [rope[error.explanation]] ];
LOOP }].fullFName;
EXIT;
ENDLOOP;
};
GetSwitches: PUBLIC PROC [in, out: STREAM] RETURNS [switches: Booting.Switches] = {
RopeDefaultNil: TYPE = ROPENIL;
switchTable: ARRAY GermSwap.Switch OF RopeDefaultNil ← [
zero: "worry-call debugger as early as possible",
one: "call debugger after MesaRuntime initialization",
two: "call debugger after VM initialization",
three: "call debugger after File initialization",
four: "call debugger after finding VM file and swapping in non-initial parts of boot file",
five: "call debugger in LoaderDriver after loading but before starting loadees",
a: "don't start Ethernet1 drivers",
b: "don't start Ethernet2 drivers",
c: "don't start Communication package",
d: "development version: don't try the Cedar directory in LoaderDriver/Installer",
f: "do full booting sequence, even if defaults would imply a rollback",
h: "hang in CPU loop with MP code instead of swapping to debugger",
i: "enable control-swat as early as possible (instead of waiting until after login)",
l: "long (and interactive) installation dialogue",
n: "don't touch the disk during initialization (forces entry to Iago)",
p: "don't read user profile from disk",
q: "quick - don't read DF files",
r: "rollback if the system volume has a valid checkpoint",
s: "use software safe storage ops instead of microcode",
t: "force teledebugging instead of disk world-swap",
v: "perform VM copying for checkpoint (internal use only!)",
w: "for world-swap debugger, assume debuggee outload file is valid/interesting"
];
value: ROPENIL;
DO
end: CHAR;
errors: BOOLFALSE;
FromChar: PROC[c: CHAR] = {
SELECT TRUE FROM
Ascii.Letter[c] => switches[VAL[GermSwap.Switch.a.ORD+(Ascii.Lower[c]-'a)]] ← TRUE;
Ascii.Digit[c] => switches[VAL[GermSwap.Switch.zero.ORD+(c-'0)]] ← TRUE;
ENDCASE => {
IO.PutRope[out, IF errors THEN ", " ELSE "\nUnrecognized switches: "];
IO.PutChar[out, c];
errors ← TRUE;
};
};
ToChar: PROC [sw: GermSwap.Switch] RETURNS [CHAR] = {
IF sw IN [zero..nine]
THEN RETURN ['0+sw.ORD]
ELSE RETURN ['A + (sw.ORD - GermSwap.Switch.a.ORD)]
};
IO.PutRope[out, "\nSwitches: "];
IO.PutRope[out, value];
[id: value, c: end] ← GetID[in: in, out: out, default: NIL, init: value];
IF end = '? THEN IO.PutChar[out, end];
switches ← ALL[FALSE];
FOR i: INT IN [0..value.Length[]) DO FromChar[value.Fetch[i]] ENDLOOP;
IF end = '?
THEN {
all: BOOL = value.Length[] = 0;
IF all THEN IO.PutRope[out, " Type one or more boot switches."];
IO.PutRope[out, "\nThe boot switches have the following meanings:"];
FOR sw: GermSwap.Switch IN GermSwap.Switch DO
IF (all AND switchTable[sw] # NIL) OR switches[sw] THEN {
IO.PutRope[out, "\n "];
IO.PutChar[out, ToChar[sw]];
IO.PutRope[out, ": "];
IO.PutRope[out, IF switchTable[sw] = NIL
THEN "I don't know any meaning for this switch"
ELSE switchTable[sw]];
};
ENDLOOP;
}
ELSE { IF NOT errors THEN EXIT };
ENDLOOP;
};
clientVolName: PUBLIC ROPE
SELECT TRUE FROM
Booting.switches[n], File.SystemVolume[] = NIL => "Cedar",
ENDCASE => File.GetVolumeName[File.SystemVolume[]];
GetLogical: PUBLIC PROC [in, out: STREAM, direction: ROPENIL] RETURNS [v: File.Volume] = {
Help: PROC = {
first: BOOLTRUE;
IO.PutRope[out, "? one of:\n"];
FOR v: File.Volume ← File.NextVolume[NIL], File.NextVolume[v] UNTIL v = NIL DO
IF first THEN first ← FALSE ELSE IO.PutRope[out, ", "];
IO.PutRope[out, File.GetVolumeName[v ! File.Error => CONTINUE]];
ENDLOOP;
};
name: ROPE ← clientVolName;
DO
name ← GetArg[
in: in,
out: out,
prompt: Rope.Cat["\n", direction, "logical volume name: "],
default: name,
help: Help];
v ← File.FindVolumeFromName[name];
IF v # NIL THEN EXIT;
IO.PutRope[out, " ... that volume doesn't exist"];
ENDLOOP;
clientVolName ← name;
};
physVolName: ROPENIL;
GetPhysical: PUBLIC PROC[in, out: STREAM] RETURNS [p: PhysicalVolume.Physical] = {
prompt: ROPE = "\nOn physical volume: ";
first: PhysicalVolume.Physical = PhysicalVolume.NextPhysical[NIL];
name: ROPESELECT TRUE FROM
physVolName # NIL => physVolName,
first # NIL => PhysicalVolume.PhysicalInfo[first].name,
ENDCASE => NIL;
IF first # NIL AND PhysicalVolume.NextPhysical[first] = NIL
THEN {
IO.PutF[out, "%g%g", [rope[prompt]], [rope[PhysicalVolume.PhysicalInfo[first].name]] ];
p ← first;
}
ELSE {
Help: PROC = {
beginning: BOOLTRUE;
IO.PutRope[out, "? one of:\n"];
FOR p: PhysicalVolume.Physical ← first, PhysicalVolume.NextPhysical[p] UNTIL p = NIL DO
thisName: ROPE;
channel: Disk.Channel;
rootStatus: PhysicalVolume.PhysicalRC;
[channel: channel, rootStatus: rootStatus, name: thisName] ←
PhysicalVolume.PhysicalInfo[p];
IF beginning THEN beginning ← FALSE ELSE IO.PutRope[out, ", "];
IF rootStatus = ok
THEN IO.PutRope[out, thisName]
ELSE IO.PutF[out, "RD%g", [integer[Disk.DriveAttributes[channel].ordinal]] ];
ENDLOOP;
};
DO
name ← GetArg[
in: in,
out: out,
prompt: prompt,
default: name,
help: Help];
IF Rope.Match["rd*", name, FALSE] THEN {
n: INT ← 0;
FOR i: CARDINAL IN CARDINAL[2..name.Length[]) DO
c: CHAR = name.Fetch[i];
IF c IN ['0..'9]
THEN n ← n * 10 + (c-'0)
ELSE GOTO noNumb;
ENDLOOP;
FOR p ← first, PhysicalVolume.NextPhysical[p] UNTIL p = NIL DO
IF Disk.DriveAttributes[PhysicalVolume.PhysicalInfo[p].channel].ordinal = n
THEN GOTO ok;
ENDLOOP;
EXITS noNumb => NULL;
};
Get to here if it's not in RDn format, or if that didn't find a volume
FOR p ← first, PhysicalVolume.NextPhysical[p] UNTIL p = NIL DO
IF (PhysicalVolume.PhysicalInfo[p].name).Equal[name, FALSE] THEN GO TO ok;
ENDLOOP;
IO.PutRope[out, " ... that volume doesn't exist"];
ENDLOOP;
EXITS ok => physVolName ← name;
};
};
GetDrive: PUBLIC PROC[in, out: STREAM] RETURNS [d: Disk.Channel] = {
prompt: ROPE = "\nOn drive number: ";
first: Disk.Channel = Disk.NextChannel[NIL];
IF first # NIL AND Disk.NextChannel[first] = NIL
THEN {
IO.PutF[out, "%g%g", [rope[prompt]], [integer[Disk.DriveAttributes[first].ordinal]] ];
d ← first;
}
ELSE {
n: INT ← 0;
DO
n ← GetNumber[
in: in, out: out,
default: n,
prompt: prompt,
help: "? type the ordinal number of the physical disk drive (in decimal)"];
FOR d ← first, Disk.NextChannel[d] UNTIL d = NIL DO
IF Disk.DriveAttributes[d].ordinal = n THEN GOTO ok;
ENDLOOP;
IO.PutRope[out, " ... that drive doesn't exist"];
ENDLOOP;
EXITS ok => NULL;
};
};
FileError: PUBLIC PROC[why: File.RC] RETURNS [ROPE] = {
RETURN [SELECT why FROM
wentOffline => "volume is offline",
nonCedarVolume => "can't do that to a non-Cedar volume",
inconsistent => "the volume root page seems to be inconsistent",
software => "label-check: consult an expert",
hardware => "hard disk error: consult an expert",
unknownFile => "the file has been deleted",
unknownPage => "accessing beyond the end of the file",
volumeFull => "volume full",
fragmented => "the volume is too fragmented",
mixedDevices => "the file (or part of it) is on the wrong disk",
ENDCASE => "unknown File.Error: consult an expert"]
};
reserved: PhysicalVolume.Physical ← NIL;
interacted: BOOLFALSE; -- whether we've asked the user to pronounce on Alto regions
altoRegions: INT ← 0;
ReservePages: PUBLIC PROC[in, out: STREAM, p: PhysicalVolume.Physical] = {
IF Disk.DriveAttributes[PhysicalVolume.PhysicalInfo[p].channel].ordinal # 0
OR p = reserved
THEN RETURN;
[] ← CheckAltoRegions[in, out];
FileBackdoor.ReserveNoPages[p];
FileBackdoor.ReservePages[
physical: p, start: FormatDisk.hardUCodeStart, size: FormatDisk.hardUCodeSize];
FileBackdoor.ReservePages[
physical: p,
start: IF FormatDisk.altoRegionsBottomUp
THEN FormatDisk.altoRegionsStart
ELSE [FormatDisk.altoRegionsStart + FormatDisk.altoRegionsSize * (FormatDisk.altoRegionsMax - altoRegions)],
size: FormatDisk.altoRegionsSize * altoRegions];
reserved ← p;
};
CheckAltoRegions: PUBLIC PROC[in, out: STREAM] RETURNS [BOOL] = {
IF NOT interacted THEN ReserveAltoRegions[in, out];
RETURN [ altoRegions#0 ]
};
ReserveAltoRegions: PUBLIC PROC[in, out: STREAM] = {
reserved ← NIL;
interacted ← FALSE;
IF FormatDisk.altoRegionsMax <= 0
THEN altoRegions ← 0
ELSE
altoRegions ← GetNumber[
in: in,
out: out,
default: 0,
max: FormatDisk.altoRegionsMax,
prompt: Rope.Cat["
\nHow many Alto ",
FormatDisk.altoRegionJargon,
" do you want to have on drive RD0? "],
help: IO.PutFR["? type the desired number of %g, in the range [ 0 .. %g )",
[rope[FormatDisk.altoRegionJargon]], [integer[FormatDisk.altoRegionsMax]] ]
];
interacted ← TRUE;
};
NextRun: PUBLIC PROC [d: Disk.Channel, origin: Disk.PageNumber] RETURNS [firstPage: Disk.PageNumber, pageCount: Disk.PageCount-- -1 at end of disk --] = {
altoCount: Disk.PageCount = FormatDisk.altoRegionsSize * altoRegions;
altoStart: Disk.PageNumber = IF FormatDisk.altoRegionsBottomUp
THEN FormatDisk.altoRegionsStart
ELSE [FormatDisk.altoRegionsStart + FormatDisk.altoRegionsSize * (FormatDisk.altoRegionsMax - altoRegions)];
diskCount: Disk.PageCount = Disk.DriveAttributes[d].nPages;
IF origin >= diskCount THEN RETURN [origin, -1];
IF Disk.DriveAttributes[d].ordinal # 0 THEN RETURN [origin, diskCount-origin];
First find a starting point outside reserved areas
firstPage ← origin;
IF firstPage >= altoStart
AND firstPage <= altoStart + altoCount
THEN firstPage ← [altoStart + altoCount];
IF firstPage >= FormatDisk.hardUCodeStart
AND firstPage <= FormatDisk.hardUCodeStart + FormatDisk.hardUCodeSize
THEN firstPage ← [FormatDisk.hardUCodeStart + FormatDisk.hardUCodeSize];
Then find how many pages we can manage
pageCount ← diskCount-firstPage;
IF firstPage < altoStart
AND firstPage+pageCount > altoStart
THEN pageCount ← altoStart-firstPage;
IF firstPage < FormatDisk.hardUCodeStart
AND firstPage+pageCount > FormatDisk.hardUCodeStart
THEN pageCount ← FormatDisk.hardUCodeStart-firstPage;
};
--File.--VolumeID: PUBLIC TYPE = IagoOps.VolumeID;
NewID: PUBLIC PROC RETURNS [new: VolumeID] = {
id: RECORD[a,b,c: CARDINAL] = LOOPHOLE[ProcessorFace.processorID];
new.a ← id.a;
new.b ← id.b;
new.c ← id.c;
new.t ← BasicTime.ToNSTime[BasicTime.Now[]];
};
PutID: PUBLIC PROC [out: STREAM, id: VolumeID] = {
IO.PutF[out, "[%xH,%xH,%xH,%bB]",
[cardinal[id.a]],
[cardinal[id.b]],
[cardinal[id.c]],
[cardinal[id.t]]
];
};
InitialMicrocodeFileName: PUBLIC PROC RETURNS [ROPE] = {
shortName: ROPE ← "InitialUnknown.eb";
SELECT SystemVersion.machineType FROM
dolphin => shortName ← "InitialPilotD0.eb";
dorado => shortName ← IF isDiskReadable THEN "InitialDiskDorado.eb" ELSE
  "InitialEtherAltoMesaDorado.eb";
dandelion => shortName ← "InitialDiskDLion.db";
dicentra => shortName ← "InitialEtherDicentra.eb";
ENDCASE;
RETURN [Rope.Cat[DefaultRemoteNames.Get[].current, "Top>", shortName]];
};
RemoteRootFileName: PUBLIC PROC [which: File.VolumeFile[checkpoint..bootFile]] RETURNS [ROPE] = {
machine: ROPE = SELECT SystemVersion.machineType FROM
dolphin => "D0",
dorado => "Dorado",
dandelion => "DLion",
dicentra => "Dicentra",
ENDCASE => "Unknown";
dir: ROPE = Rope.Concat[DefaultRemoteNames.Get[].current, "Top>"];
RETURN [SELECT which FROM
checkpoint => NIL,
microcode => Rope.Cat[dir, "Cedar", machine, ext[which]],
germ => Rope.Cat[dir, machine, ext[which]],
bootFile => Rope.Cat[dir, "BasicCedar", machine, ext[which]],
ENDCASE => ERROR]
};
LocalRootFileName: PUBLIC PROC [which: File.VolumeFile] RETURNS [ROPE] = {
RETURN [Rope.Concat[
SELECT which FROM
checkpoint => "Checkpoint",
microcode => "Microcode",
germ => "Germ",
bootFile => "BootFile",
debugger => "Debugger",
debuggee => "Debuggee",
VM => "VM",
VAM => "VAM",
client => "FS",
alpine => "Alpine",
ENDCASE => ERROR,
".DontDeleteMe"] ]
};
ext: PUBLIC ARRAY File.VolumeFile[checkpoint..bootFile] OF ROPE ← [
checkpoint: ".outload",
microcode: SELECT SystemVersion.machineType FROM
dandelion, dicentra => ".db",
ENDCASE => ".eb",
germ: ".germ",
bootFile: ".boot"];
WriteRemoteNames: PUBLIC PROC [volume: ROPENIL, out: STREAMNIL] RETURNS [success: BOOL] = {
Writes the remote names on the given volume (NIL => systems volume), and write messages to the out stream (default: no messages).
IF NOT Booting.switches[n] THEN {
ENABLE FS.Error => {
IF out # NIL THEN
IO.PutF1[out, "\nNames and pseudo-servers set, but not saved to disk:\n %g\n", [rope[error.explanation]] ];
GO TO noWrite;
};
fileName: ROPE = Rope.Cat["[]<", volume, ">RemoteNames.DontDeleteMe"];
stream: STREAM = FS.StreamOpen[fileName, create];
remoteNames: DefaultRemoteNames.DefaultNames = DefaultRemoteNames.Get[];
IO.PutF1[stream, "System: %g\n", [rope[remoteNames.systemHost]] ];
IO.PutF1[stream, "User: %g\n", [rope[remoteNames.userHost]] ];
IO.PutF1[stream, "Registry: .%g\n", [rope[remoteNames.registry]] ];
FOR each: FSPseudoServers.PseudoServerList ← FSPseudoServers.GetPseudoServers[], each.rest WHILE each # NIL DO
IO.PutRope[stream, FSPseudoServers.RopeFromPseudoServer[each]];
IO.PutRope[stream, "\n"];
ENDLOOP;
IO.Close[stream];
IF out # NIL THEN IO.PutF1[out, "\nHost names saved to %g\n", [rope[fileName]]];
RETURN [TRUE];
EXITS noWrite => {};
};
RETURN [FALSE];
};
ReadRemoteNames: PUBLIC PROC [volume: ROPE] RETURNS [success: BOOLFALSE] = {
Reads the remote names from the given volume (default: system volume).
remoteNames: DefaultRemoteNames.DefaultNames = DefaultRemoteNames.Get[];
systemHost: ROPE ← remoteNames.systemHost;
userHost: ROPE ← remoteNames.userHost;
registry: ROPE ← remoteNames.registry;
IF NOT Booting.switches[n] THEN {
fileName: ROPE = Rope.Cat["[]<", volume, ">RemoteNames.DontDeleteMe"];
remoteNamesStream: STREAM = FS.StreamOpen[fileName ! FS.Error => GO TO useDefault];
{
ENABLE IO.EndOfStream => GO TO useDefault;
systemHost ← GetToken[remoteNamesStream];
IF NOT Rope.Equal[systemHost, "System:", FALSE] THEN {
Old style defaults, no tags, and no registry
userHost ← GetToken[remoteNamesStream];
GO TO oldStyle;
};
systemHost ← GetToken[remoteNamesStream];
IF NOT Rope.Equal[GetToken[remoteNamesStream], "User:", FALSE] THEN
GO TO useDefault;
userHost ← GetToken[remoteNamesStream];
IF NOT Rope.Equal[GetToken[remoteNamesStream], "Registry:", FALSE] THEN
GO TO useDefault;
registry ← GetToken[remoteNamesStream];
IF NOT Rope.Match[".*", registry] THEN GO TO useDefault;
remoteNames.systemHost ← systemHost;
remoteNames.userHost ← userHost;
remoteNames.registry ← Rope.Substr[registry, 1];
EXITS oldStyle => {};
};
Parse the pseudo-servers
remoteNames.current ← Rope.Replace[
remoteNames.current, 0, Rope.SkipTo[remoteNames.current, 0, "]" ]+1, systemHost];
[] ← IO.GetLineRope[remoteNamesStream ! IO.EndOfStream => CONTINUE];
DO
line: ROPEIO.GetLineRope[remoteNamesStream ! IO.EndOfStream => EXIT];
new: FSPseudoServers.PseudoServerList ← FSPseudoServers.PseudoServerFromRope[line].new;
IF new = NIL THEN EXIT;
FSPseudoServers.InsertPseudoServer[new];
ENDLOOP;
IO.Close[remoteNamesStream];
success ← TRUE;
EXITS useDefault => {}};
EnsureServer["Cedar", systemHost, "Cyan"];
EnsureServer["User", userHost, "Ivy"];
};
EnsureServer: PROC [server: ROPE, host: ROPE, local: ROPE] = {
IF FSPseudoServers.Lookup[server] = NIL THEN {
This is a standard name that we need to define via the host. If the host given is the same as the translation, then we fall back on more parochial definition.
stripped: ROPE ← StripBrackets[host];
IF Rope.Length[stripped] = 0 OR Rope.Equal[stripped, server, FALSE] THEN
The host name that currently exists is bogus or depends on the pseudo-server, so make the translation refer to the "local" host. This lets CSL'ers directly install Cedar6.0 on Cedar5.2 machines.
stripped ← local;
FSPseudoServers.InsertPseudoServer[
FSPseudoServers.PseudoServerFromRope[
Rope.Cat[server, " ", stripped, " ", stripped]
].new];
};
};
StripBrackets: PROC [name: ROPE] RETURNS [ROPE] = {
pos: INT ← 0;
IF Rope.Match["[*", name] THEN pos ← 1;
RETURN [Rope.Substr[name, pos, Rope.SkipTo[name, pos, "]"]-pos]];
};
GetToken: PROC [st: STREAM] RETURNS [token: ROPENIL] = {
MyBreak: IO.BreakProc = {
[char: CHAR] RETURNS [IO.CharClass]
SELECT char FROM
' , '\t, '\n, ',, '; => RETURN [sepr];
ENDCASE;
RETURN [other];
};
token ← IO.GetTokenRope[st, MyBreak ! IO.EndOfStream => CONTINUE].token;
};
TRUSTED { FormatDisk.Start[] };
END.