IagoOpsImpl.mesa - Iago user interface subroutines
Copyright © 1984, 1985, 1986 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
Doug Wyatt, June 10, 1986 6:31:14 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],
PupStreamBackdoor USING [maxOldGatewayBytes, SetMaxBufferSize],
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, PupStreamBackdoor, 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).
remoteNames: DefaultRemoteNames.DefaultNames = DefaultRemoteNames.Get[];
SetPupBufferSize[remoteNames.registry];
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];
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"];
SetPupBufferSize[remoteNames.registry];
};
SetPupBufferSize: PROC [registry: ROPE] ~ {
DKW: We assume for now that communication with sites outside the pa registry involves gateways that cannot yet cope with large buffers. When large buffers are accepted throughout the internet, this crock can go away. Presumably you will notice this when the PupStreamBackdoor interface disappears.
IF NOT Rope.Equal["pa", registry, FALSE] THEN
PupStreamBackdoor.SetMaxBufferSize[PupStreamBackdoor.maxOldGatewayBytes];
};
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.