DIRECTORY
Ascii USING [BS, ControlA, ControlQ, ControlW, ControlX, CR, DEL, Digit, Letter, Lower, SP, Upper],
BasicTime USING [Now, ToNSTime],
Booting USING [Switches],
Convert USING [RopeFromInt],
DefaultRemoteNames USING [DefaultNames, Get],
Disk USING [Channel, DriveAttributes, NextChannel, PageCount, PageNumber],
File USING [Error, FindVolumeFromName, GetVolumeName, NextVolume, RC, SystemVolume, Volume, VolumeFile, VolumeID],
FileExtra USING [ReserveNoPages, ReservePages],
FormatDisk USING [altoRegionJargon, altoRegionsBottomUp, altoRegionsMax, altoRegionsSize, altoRegionsStart, hardUCodeSize, hardUCodeStart, Start],
FS USING [Error, FileInfo, StreamOpen],
FSPseudoServers USING [GetPseudoServers, PseudoServerList],
FSPseudoServersExtra USING [InsertPseudoServer, Lookup, PseudoServerFromRope, RopeFromPseudoServer],
GermSwap USING [Switch],
IagoOps USING [CommandInfo, CommandList, VolumeID],
IO USING [Close, EndOfStream, EraseChar, GetChar, GetLineRope, GetTokenRope, IDProc, PutChar, PutF, PutFR, PutRope, STREAM],
PhysicalVolume USING [NextPhysical, Physical, PhysicalInfo, PhysicalRC],
ProcessorFace USING [processorID],
Rope USING [Cat, Compare, Concat, Equal, Fetch, Find, FromChar, Length, ROPE, Replace, Run, SkipTo, Substr],
SystemVersion USING [machineType];
IagoOpsImpl:
CEDAR
PROGRAM
IMPORTS Ascii, BasicTime, Convert, DefaultRemoteNames, Disk, File, FileExtra, FormatDisk, FS, FSPseudoServers, FSPseudoServersExtra, IO, PhysicalVolume, ProcessorFace, Rope, SystemVersion
EXPORTS File--VolumeID--, IagoOps
= BEGIN
ROPE: TYPE = Rope.ROPE;
STREAM: TYPE = IO.STREAM;
Rubout: PUBLIC ERROR = CODE;
GetID:
PUBLIC
PROC
[in, out: STREAM, default: ROPE, init: ROPE ← NIL, echo: BOOL ← TRUE]
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.Cat[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: BOOL ← FALSE;
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 ← id.Cat[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: BOOL ← TRUE;
GetCommand:
PUBLIC
PROC
[in, out: STREAM, diskReadable: BOOL] RETURNS [found: IagoOps.CommandInfo] = {
May raise Rubout
prompt: ROPE = "\n\n> ";
BoolFalse: TYPE = BOOL ← FALSE;
prefix: ROPE ← NIL;
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 ← prefix.Cat[extra];
ENDCASE => ERROR;
};
};
ENDLOOP;
};
GetArg:
PUBLIC
PROC
[in, out: STREAM, prompt, default: ROPE, help: PROC] RETURNS [value: ROPE ← NIL] = {
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: INT ← LAST[INT], prompt, help: ROPE]
RETURNS [size: INT ← 0] = {
Help: PROC = { IO.PutRope[out, help] };
sizeRope: ROPE ← Convert.RopeFromInt[default];
DO
sizeRope ← GetArg[ in: in, out: out, prompt: prompt, default: sizeRope, help: Help];
size ← 0;
FOR i:
CARDINAL
IN
CARDINAL[0..sizeRope.Length[])
DO
c: CHAR = sizeRope.Fetch[i];
IF c
IN ['0..'9]
THEN size ← size * 10 + (c-'0)
ELSE { IO.PutRope[out, " ... not a number"]; EXIT };
REPEAT
FINISHED =>
IF size <= max
THEN RETURN
ELSE IO.PutF[out, " ... number should be less than %g", [integer[max]] ];
ENDLOOP;
ENDLOOP;
};
GetSize:
PUBLIC
PROC
[in, out: STREAM, default: INT, max: INT ← LAST[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: ROPE ← NIL;
lastPattern: ROPE ← NIL;
lastDest: ROPE ← NIL;
GetFile:
PUBLIC
PROC[in, out:
STREAM, prompt:
ROPE ←
NIL, extension:
ROPE ←
NIL, default:
ROPE ←
NIL, wDir:
ROPE ←
NIL, check:
BOOL ←
FALSE, pattern:
BOOL ←
FALSE]
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 ← name.Cat[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 => SELECT error.code FROM
$unknownServer, $unknownVolume, $unknownFile, $illegalName, $patternNotAllowed =>
{ IO.PutRope[out, " ... "]; IO.PutRope[out, error.explanation]; LOOP };
ENDCASE => NULL ].fullFName;
};
EXIT;
ENDLOOP;
};
GetSwitches:
PUBLIC
PROC[in, out:
STREAM]
RETURNS [switches: Booting.Switches] = {
RopeDefaultNil: TYPE = ROPE ← NIL;
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",
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: ROPE ← NIL;
DO end:
CHAR;
errors: BOOL ← FALSE;
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 ←
IF File.SystemVolume[] = NIL THEN "Cedar" ELSE File.GetVolumeName[File.SystemVolume[]];
GetLogical:
PUBLIC
PROC
[in, out: STREAM, direction: ROPE ← NIL] RETURNS [v: File.Volume] = {
Help:
PROC = {
first: BOOL ← TRUE;
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: ROPE ← NIL;
GetPhysical:
PUBLIC
PROC[in, out:
STREAM]
RETURNS [p: PhysicalVolume.Physical] = {
prompt: ROPE = "\nOn physical volume: ";
first: PhysicalVolume.Physical = PhysicalVolume.NextPhysical[NIL];
name:
ROPE ←
SELECT
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: BOOL ← TRUE;
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 name.Length[] >= 3
-- RD0
AND Ascii.Upper[name.Fetch[0]] = 'R
AND Ascii.Upper[name.Fetch[1]] = 'D
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 GOTO 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[first]
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: BOOL ← FALSE; -- 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];
FileExtra.ReserveNoPages[p];
FileExtra.ReservePages[
physical: p, start: FormatDisk.hardUCodeStart, size: FormatDisk.hardUCodeSize];
FileExtra.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.Cat[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.Cat[
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: ROPE, systemHost,userHost: ROPE, out: STREAM] RETURNS [success: BOOL]= {
ENABLE
FS.Error => {
IO.PutF[out, "\nSorry, default names not saved:\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[];
IF systemHost = NIL THEN systemHost ← remoteNames.systemHost;
IF userHost = NIL THEN userHost ← remoteNames.userHost;
IO.PutF[stream, "%g\n%g\n", [rope[systemHost]], [rope[userHost]] ];
FOR each: FSPseudoServers.PseudoServerList ← FSPseudoServers.GetPseudoServers[], each.rest
WHILE each #
NIL
DO
IO.PutRope[stream, FSPseudoServersExtra.RopeFromPseudoServer[each]];
IO.PutRope[stream, "\n"];
ENDLOOP;
IO.Close[stream];
IO.PutF[out, "\nHost names saved to %g\n", [rope[fileName]]];
RETURN [TRUE];
EXITS noWrite => {RETURN [FALSE]};
};
ReadRemoteNames:
PUBLIC
PROC [volume:
ROPE]
RETURNS [success:
BOOL] = {
fileName: ROPE = Rope.Cat["[]<", volume, ">RemoteNames.DontDeleteMe"];
remoteNamesStream: STREAM = FS.StreamOpen[fileName ! FS.Error => GO TO useDefault];
systemHost: ROPE = GetToken[remoteNamesStream ! IO.EndOfStream => GO TO useDefault];
userHost: ROPE = GetToken[remoteNamesStream ! IO.EndOfStream => GO TO useDefault];
IF systemHost #
NIL
AND userHost #
NIL
THEN {
Set the remote names from the file contents.
remoteNames: DefaultRemoteNames.DefaultNames = DefaultRemoteNames.Get[];
remoteNames.systemHost ← systemHost;
remoteNames.userHost ← userHost;
remoteNames.current ← Rope.Replace[
remoteNames.current, 0, Rope.SkipTo[remoteNames.current, 0, "]" ]+1, systemHost];
[] ← IO.GetLineRope[remoteNamesStream ! IO.EndOfStream => CONTINUE];
DO
line: ROPE ← IO.GetLineRope[remoteNamesStream ! IO.EndOfStream => EXIT];
new: FSPseudoServers.PseudoServerList ← FSPseudoServersExtra.PseudoServerFromRope[line].new;
IF new = NIL THEN EXIT;
FSPseudoServersExtra.InsertPseudoServer[new];
ENDLOOP;
IF FSPseudoServersExtra.Lookup["Cedar"] =
NIL
THEN {
This is a standard name that we need to define via the default system host. We define it to be a simple readonly translation to the system host.
len: INT ← Rope.Length[systemHost];
stripped: ROPE ← Rope.Substr[systemHost, 1, len-2];
FSPseudoServersExtra.InsertPseudoServer[
FSPseudoServersExtra.PseudoServerFromRope[
Rope.Concat["Cedar $ ", stripped]
].new];
};
};
IO.Close[remoteNamesStream];
RETURN [TRUE];
EXITS useDefault => RETURN [FALSE];
};
GetToken:
PROC [st:
STREAM]
RETURNS [token:
ROPE ←
NIL] = {
token ← IO.GetTokenRope[st, IO.IDProc ! IO.EndOfStream => CONTINUE].token;
};
TRUSTED{ FormatDisk.Start[] };