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;
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.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: 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 ← 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: 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 ← Rope.Concat[prefix, 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
ok: BOOL ← TRUE;
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:
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 ← 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 = 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",
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: 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 ←
SELECT
TRUE
FROM
Booting.switches[n], File.SystemVolume[] = NIL => "Cedar",
ENDCASE => 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 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: 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];
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:
ROPE ←
NIL, out:
STREAM ←
NIL]
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:
BOOL ←
FALSE] = {
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: ROPE ← IO.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:
ROPE ←
NIL] = {
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[] };