<> <> <> <> <> <> <<>> 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: 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 => { <, the and following 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 => { <> new.rest _ each; EXIT; }; equal => { <> 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] = { <> 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)"]]; }; <> 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 \"[]a.b\" or \"[server]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; }; <> 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]; <> firstPage _ origin; IF firstPage >= altoStart AND firstPage <= altoStart + altoCount THEN firstPage _ [altoStart + altoCount]; <= FormatDisk.hardUCodeStart>> <> <> <> pageCount _ diskCount-firstPage; IF firstPage < altoStart AND firstPage+pageCount > altoStart THEN pageCount _ altoStart-firstPage; <> < FormatDisk.hardUCodeStart>> <> }; --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] = { < 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: BOOL _ FALSE] = { <> 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 { <> 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 => {}; }; <> 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"]; }; EnsureServer: PROC [server: ROPE, host: ROPE, local: ROPE] = { IF FSPseudoServers.Lookup[server] = NIL THEN { <> stripped: ROPE _ StripBrackets[host]; IF Rope.Length[stripped] = 0 OR Rope.Equal[stripped, server, FALSE] THEN <> 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[] }; END.