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 => { 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] = { 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 _ 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)"]]; }; 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 _ 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; }; 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]; firstPage _ origin; IF firstPage >= altoStart AND firstPage <= altoStart + altoCount THEN firstPage _ [altoStart + altoCount]; pageCount _ diskCount-firstPage; IF firstPage < altoStart AND firstPage+pageCount > altoStart THEN pageCount _ altoStart-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 { 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 { 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[] }; END. >IagoOpsImpl.mesa - Iago user interface subroutines Copyright c 1984 by Xerox Corporation. All rights reserved. Birrell, December 7, 1983 10:51 am Levin, September 22, 1983 1:32 pm Russ Atkinson, January 2, 1985 5:02:30 pm PST Willie-Sue, August 13, 1984 10:30:29 am PDT ******** Input subroutines ******** text to be backed up is of the form ..., the and following are to be removed. ... registers the command with Iago so it can be found again. This allows external programs to register commands as well. Insert new command Replace old command May raise Rubout Defaults for "GetFile": Get to here if it's not in RDn format, or if that didn't find a volume First find a starting point outside reserved areas IF firstPage >= FormatDisk.hardUCodeStart AND firstPage <= FormatDisk.hardUCodeStart + FormatDisk.hardUCodeSize THEN firstPage _ [FormatDisk.hardUCodeStart + FormatDisk.hardUCodeSize]; Then find how many pages we can manage IF firstPage < FormatDisk.hardUCodeStart AND firstPage+pageCount > FormatDisk.hardUCodeStart THEN pageCount _ FormatDisk.hardUCodeStart-firstPage; Set the remote names from the file contents. 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. Κ˜šœ2™2Jšœ Οmœ1™žœ ˜lKšœžœ˜"—J˜šœ žœž˜KšžœSžœ)žœ4˜»KšžœΟc œ ˜!Kšœž˜K˜Kšžœžœžœ˜Kšžœžœžœžœ˜—˜J˜—Jšœ1™1˜Jšœžœžœžœ˜J˜šΟnœžœž˜Jš œ žœ žœžœžœžœžœ˜EJšžœžœžœ˜Jšžœ˜ Jšœ žœ žœ˜š œžœ˜šžœž˜ Jš žœžœž œžœžœžœ˜Q—Jšœžœ˜ J˜—š  œžœžœžœžœžœ˜.šžœ žœ˜šžœž˜ Jš œ žœ žœžœžœžœ˜@Jšžœ˜—Jšœ žœ˜Jšœ˜—Jš žœžœžœžœžœ˜#J˜—J˜Jšžœžœžœ˜&Jšœžœ ˜šžœ ž˜šžœž˜ Jšžœžœ˜šœ žœ˜Jšœžœ˜šžœ žœ˜J˜Jšžœžœ˜*Jšœ˜Jšœ˜—J˜—šœ˜Jšœ~™~Jšœžœžœ˜š žœžœž œžœž˜,Jšœžœ˜Jšžœžœžœ ž˜8Jšžœžœžœžœ˜6Jšžœžœ˜šž˜Jšžœ žœ˜—Jšžœ˜—J˜—Jšœ˜Jšžœ$žœžœžœ˜L—Jšœžœ ˜Jšžœ˜—Jšœ˜J˜—Jšœ#žœ˜'J˜š œžœžœ ˜Jšœ žœžœžœ˜4Jšœžœžœ ˜šžœ˜ Jšžœ_˜a——Jšžœ˜—Jšœ˜—J˜š œžœžœ žœ žœžœžœ˜Pšžœ ˜ JšœU˜UJšœ˜—Jšžœ˜Jšœ˜—J˜š  œžœž˜Jš œ žœ žœžœžœžœžœ˜IJšžœžœ ˜Jš œžœžœ˜'Jšœ žœ ˜.šž˜JšœT˜TJšœ ˜ š žœžœžœžœž˜4Jšœžœ˜šžœžœ ˜Jšžœ˜Jšžœžœ$žœ˜4—šžœžœ˜šžœ ˜Jšžœž˜ JšžœžœB˜I——Jšžœ˜—Jšžœ˜—Jšœ˜—J˜š œžœž˜Jšœ žœ žœžœžœžœžœžœ ˜Qšžœ ˜Jšœ˜JšœI˜I—Jšœ˜—J˜šœ™Jšœ žœžœ˜Jšœ žœžœ˜Jšœ žœžœ˜—J˜š$ œžœžœ žœ žœžœ žœžœ žœžœžœžœ žœžœ žœžœžœžœ˜Όš œžœ˜Kšžœ(˜*Kšžœ žœžœ˜1KšžœJ˜LKšœ˜—šœžœžœž˜Kšœ žœ ˜Kšœ˜Kšœ˜Kšžœ ˜—šž˜šœ˜K˜Kšœ,žœ žœžœ˜WKšœ˜—Kšžœžœ žœ˜+šžœžœ˜0Kšžœžœ˜@—šžœžœ˜šžœ˜Kšœ˜Kšžœžœ˜9Kšœ˜—Kšžœ˜—šžœžœ˜Kšœžœ#žœ žœ ž˜NšœQ˜QKšœžœžœ"žœ˜G—Kšžœžœ ˜Kšœ˜—Kšžœ˜Kšžœ˜—Kšœ˜—J˜š   œžœžœ žœžœ!˜RJšœžœžœžœ˜"šœ žœžœ˜8J˜1J˜6J˜-J˜1J˜[J˜PJ˜#J˜#J˜'J˜RJ˜GJ˜CJ˜UJ˜2J˜GJ˜'J˜:J˜8J˜4J˜šžœI˜KJšžœžœ˜ —Jšžœ˜—Jšžœ žœ˜Jšœ˜——JšœF™Fšžœ+žœžœž˜>Jšžœ3žœžœžœ˜IJšžœ˜—Jšžœ0˜2Jšžœ˜—Jšžœ˜Jšœ˜——Jšœ˜—J˜š  œžœžœ žœžœ˜DJšœžœ˜%Jšœ'žœ˜,šžœ žœžœž˜0šžœ˜JšžœT˜VJ˜ Jšœ˜—šžœ˜Jšœžœ˜ šž˜šœ˜J˜J˜ Jšœ˜J˜K—šžœ$žœžœž˜7Jšžœ%žœžœ˜4Jšžœ˜—Jšžœ/˜1Jšžœ˜—Jšžœžœ˜Jšœ˜——Jšœ˜—J˜š   œžœžœ žœžœžœ˜7šžœžœž˜Jšœ#˜#Jšœ8˜8Jšœ@˜@Jšœ-˜-Jšœ1˜1J˜+J˜6J˜Jšœ-˜-J˜@Jšžœ,˜3—Jšœ˜—J˜Jšœ$žœ˜(Jšœ žœžœŸ<˜VJšœ žœ˜J˜š  œžœžœ žœ!˜JšžœI˜KJšžœ ˜Jšžœžœ˜ —Jšœ˜Jšœ˜šœ˜JšœO˜O—šœ˜Jšœ ˜ šœžœ˜(Jšžœ˜ Jšžœh˜l—Jšœ0˜0—Jšœ ˜ Jšœ˜—J˜š  œžœžœ žœžœžœ˜AJšžœžœ žœ˜3Jšžœ˜Jšœ˜—J˜š œžœžœ žœ˜4Jšœ žœ˜Jšœ žœ˜šžœ˜!Jšžœ˜šž˜šœ˜J˜J˜ J˜ Jšœ˜šœ˜Jšœ˜Jšœ˜Jšœ'˜'—šœžœC˜KJšœK˜K—J˜———Jšœ žœ˜Jšœ˜—J˜š œžœž˜Jšœ*˜*Jšžœ7Ÿœ˜ZJšœE˜Ešœžœ˜>Jšžœ˜ Jšžœh˜l—Jšœ;˜;Jšžœžœžœ˜0Jšžœ%žœžœ˜NJšœ2™2Jšœ˜šžœ˜Jšžœ#˜&Jšžœ%˜)—šœ)™)JšœE™EJšœH™H—Jšœ&™&Jšœ ˜ šžœ˜Jšžœ ˜#Jšžœ!˜%—šœ(™(Jšœ3™3Jšœ5™5—Jšœ˜—J˜JšŸ œ žœžœ˜2J˜š œžœžœžœ˜.Jšœžœžœžœ˜BJšœ ˜ Jšœ ˜ Jšœ ˜ J˜,Jšœ˜—J˜š œžœžœžœ˜1šžœ˜!J˜J˜J˜J˜J˜—Jšœ˜—J˜š  œžœžœžœžœ˜8Jšœ žœ˜&šžœž˜%Jšœ+˜+šœžœžœž˜HJšœ"˜"—Jšœ/˜/Jšœ2˜2Jšžœ˜—JšžœA˜GJšœ˜—J˜š œžœž˜Jšœ/žœžœ˜Ašœ žœžœž˜5J˜J˜J˜J˜Jšžœ˜—Jšœžœ6˜?šžœžœž˜Jšœžœ˜Jšœ9˜9Jšœ+˜+Jšœ=˜=Jšžœžœ˜—Jšœ˜—J˜š  œžœžœžœžœ˜Išžœ ˜šžœž˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜J˜J˜Jšžœ ˜ Jšžœ ˜ J˜J˜—Jšžœžœ˜$—Jšœ˜—J˜š œžœžœ'žœžœ˜CJšœ˜šœ žœž˜0Jšœ˜Jšžœ ˜—Jšœ˜Jšœ˜J˜—š œžœž˜Jš œ žœžœžœžœ žœ˜Qšžœžœ ˜šžœT˜VJšžœžœ ˜J˜——Jšœ žœ8˜FJšœžœžœ˜1JšœH˜HJšžœžœžœ%˜=Jšžœ žœžœ!˜7JšžœA˜CšžœXžœžœž˜nJšžœB˜DJšžœ˜Jšžœ˜—Jšžœ˜Jšžœ;˜=Jšžœžœ˜Jšžœ žœžœ˜"J˜J˜—š  œžœžœ žœžœ žœ˜GKšœ žœ8˜FKš œžœžœžœ žœžœ ˜SKš œ žœ žœΟr œžœžœ ˜TKš œ žœ žœ‘ œžœžœ ˜Rš žœžœžœ žœžœ˜-Kšœ,™,KšœH˜HKšœ$˜$Kšœ ˜ šœ#˜#KšœQ˜Q—Kš œžœ!žœ‘ œžœ˜Dšž˜Kš œžœžœ!žœ‘ œžœ˜HKšœ\˜\Kšžœžœžœžœ˜Kšœ-˜-Kšžœ˜—šžœ(žœžœ˜4Kšœ‘™‘Kšœžœ˜#Kšœ žœ%˜3šœ(˜(šœ*˜*K˜!—Kšœ˜—K˜—K˜—Kšžœ˜Kšžœžœ˜Kšžœžœžœ˜#K˜K˜—š  œžœžœžœ žœžœ˜;Jš œžœžœ žœžœ˜JJ˜—J˜Jšžœ˜J˜—Jšžœ˜—…—U\wœ