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; 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 => { 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] = { 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]; 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.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] = { 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 = { SELECT char FROM ' , '\t, '\n, ',, '; => RETURN [sepr]; ENDCASE; RETURN [other]; }; token _ IO.GetTokenRope[st, MyBreak ! IO.EndOfStream => CONTINUE].token; }; TRUSTED { FormatDisk.Start[] }; END. IagoOpsImpl.mesa - Iago user interface subroutines Copyright c 1984, 1985 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 ******** 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; Writes the remote names on the given volume (NIL => systems volume), and write messages to the out stream (default: no messages). Reads the remote names from the given volume (default: system volume). Old style defaults, no tags, and no registry Parse the pseudo-servers 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. 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. [char: CHAR] RETURNS [IO.CharClass] Κ˜codešœ2™2Kšœ Οmœ7™BKšœ"™"K™!K™'K™.K™—šΟk ˜ Kš œžœžœ*žœžœžœ˜\Kšœ žœ˜ Kšœžœ˜#Kšœžœ#˜0Kšœžœ˜-Kšœžœ@˜JKšœžœ8žœ.˜rKšœ žœ ˜2Kšœ žœ‚˜’Kšžœžœ˜'Kšœžœn˜ƒKšœ žœ ˜Kšœžœ&˜3Kšžœžœvžœ˜†Kšœžœ4˜HKšœžœ˜"KšœžœEžœ ˜sKšœžœ˜"—K˜šœ žœž˜Kšžœ_žœžœ4˜±KšžœΟc œ ˜!Kšœž˜K˜Kšžœžœžœ˜Kšžœžœžœžœ˜—˜K˜—Kšœ1™1˜Kšœžœžœžœ˜K˜šΟnœžœžœ žœ žœžœžœžœžœžœžœžœ˜xKšžœ˜ Kšœ žœ žœ˜š œžœ˜šžœž˜ Kš žœžœž œžœžœžœ˜Q—Kšœžœ˜ K˜—š  œžœžœžœžœžœ˜.šžœ žœ˜šžœž˜ Kš œ žœ žœžœžœžœ˜@Kšžœ˜—Kšœ žœ˜Kšœ˜—Kš žœžœžœžœžœ˜#K˜—K˜ Kšžœžœžœ˜&Kšœžœ ˜šžœ ž˜šžœž˜ Kšžœžœ˜šœ žœ˜Kšœžœ˜šžœ žœ˜K˜Kšžœžœ˜*Kšœ˜Kšœ˜—K˜—šœ˜Kšœ~™~Kšœžœžœ˜š žœžœž œžœž˜,Kšœžœ˜Kšžœžœžœ ž˜8Kšžœžœžœžœ˜6Kšžœžœ˜šž˜Kšžœ žœ˜—Kšžœ˜—K˜—Kšœ˜šžœ˜ Kšœ'˜'Kšžœžœžœ˜ Kšœ˜——Kšœžœ ˜Kšžœ˜—Kšœ˜K˜—Kšœ#žœ˜'K˜š œžœžœ ˜Kšœ žœžœžœ˜4Kšœžœžœ ˜šžœ˜ Kšžœ_˜a——Kšžœ˜—Kšœ˜—K˜š œžœžœ žœ žœžœžœ˜Pšžœ ˜ KšœU˜UKšœ˜—Kšžœ˜Kšœ˜—K˜š  œžœžœ žœ žœžœžœžœžœžœžœ ˜|Kš œžœžœ˜'Kšœ žœ ˜.šž˜Kšœžœžœ˜Kšœ ˜ KšœT˜TKšœ>žœžœ˜RKšžœžœžœ$žœ˜;šžœ ˜Kšžœž˜ KšžœžœB˜I—Kšžœ˜—Kšœ˜—K˜š œžœžœ žœ žœžœžœžœžœžœ ˜fšžœ ˜Kšœ˜KšœI˜I—Kšœ˜—K˜šœ™Kšœ žœžœ˜Kšœ žœžœ˜Kšœ žœžœ˜—K˜š$ œžœžœ žœ žœžœ žœžœ žœžœžœžœ žœžœ žœžœžœžœ˜Όš œžœ˜Kšžœ(˜*Kšžœ žœžœ˜1KšžœJ˜LKšœ˜—šœžœžœž˜Kšœ žœ ˜Kšœ˜Kšœ˜Kšžœ ˜—šž˜šœ˜K˜Kšœ,žœ žœžœ˜WKšœ˜—Kšžœžœ žœ˜+šžœžœ˜0Kšžœ(žœ˜I—šžœžœ˜šžœ˜Kšœ˜Kšžœžœ˜9Kšœ˜—Kšžœ˜—šžœž˜ šœžœ ˜)šœžœ ˜Kšžœ3˜5Kšžœ˜———Kšžœ˜Kšžœ˜—Kšœ˜—K˜š   œžœžœ žœžœ!˜SKšœžœžœžœ˜"šœ žœžœ˜8K˜1K˜6K˜-K˜1K˜[K˜PK˜#K˜#K˜'K˜RK˜GK˜CK˜UK˜2K˜GK˜'K˜!K˜:K˜8K˜4K˜šžœI˜KKšžœžœ˜ —Kšžœ˜—Kšžœ žœ˜Kšœ˜—KšœF™Fšžœ+žœžœž˜>Kšžœ3žœžœžœ˜JKšžœ˜—Kšžœ0˜2Kšžœ˜—Kšžœ˜Kšœ˜——Kšœ˜—K˜š  œžœžœ žœžœ˜DKšœžœ˜%Kšœ'žœ˜,šžœ žœžœž˜0šžœ˜KšžœT˜VK˜ Kšœ˜—šžœ˜Kšœžœ˜ šž˜šœ˜K˜K˜ Kšœ˜K˜K—šžœ žœžœž˜3Kšžœ%žœžœ˜4Kšžœ˜—Kšžœ/˜1Kšžœ˜—Kšžœžœ˜Kšœ˜——Kšœ˜—K˜š   œžœžœ žœžœžœ˜7šžœžœž˜Kšœ#˜#Kšœ8˜8Kšœ@˜@Kšœ-˜-Kšœ1˜1K˜+K˜6K˜Kšœ-˜-K˜@Kšžœ,˜3—Kšœ˜—K˜Kšœ$žœ˜(Kšœ žœžœŸ<˜VKšœ žœ˜K˜š  œžœžœ žœ!˜JšžœI˜KKšžœ ˜Kšžœžœ˜ —Kšœ˜Kšœ˜šœ˜KšœO˜O—šœ˜Kšœ ˜ šœžœ˜(Kšžœ˜ Kšžœh˜l—Kšœ0˜0—Kšœ ˜ Kšœ˜—K˜š  œžœžœ žœžœžœ˜AKšžœžœ žœ˜3Kšžœ˜Kšœ˜—K˜š œžœžœ žœ˜4Kšœ žœ˜Kšœ žœ˜šžœ˜!Kšžœ˜šž˜šœ˜K˜K˜ K˜ Kšœ˜šœ˜Kšœ˜Kšœ˜Kšœ'˜'—šœžœC˜KKšœK˜K—K˜———Kšœ žœ˜Kšœ˜—K˜š  œžœžœ+žœ7Ÿœ˜šKšœE˜Ešœžœ˜>Kšžœ˜ Kšžœh˜l—Kšœ;˜;Kšžœžœžœ˜0Kšžœ%žœžœ˜NKšœ2™2Kšœ˜šžœ˜Kšžœ#˜&Kšžœ%˜)—šœ)™)KšœE™EKšœH™H—Kšœ&™&Kšœ ˜ šžœ˜Kšžœ ˜#Kšžœ!˜%—šœ(™(Kšœ3™3Kšœ5™5—Kšœ˜—K˜KšŸ œ žœžœ˜2K˜š œžœžœžœ˜.Kšœžœžœžœ˜BKšœ ˜ Kšœ ˜ Kšœ ˜ K˜,Kšœ˜—K˜š œžœžœžœ˜2šžœ˜!K˜K˜K˜K˜K˜—Kšœ˜—K˜š  œžœžœžœžœ˜8Kšœ žœ˜&šžœž˜%Kšœ+˜+šœžœžœž˜HKšœ"˜"—Kšœ/˜/Kšœ2˜2Kšžœ˜—KšžœA˜GKšœ˜—K˜š  œžœžœ0žœžœ˜ašœ žœžœž˜5K˜K˜K˜K˜Kšžœ˜—Kšœžœ9˜Bšžœžœž˜Kšœžœ˜Kšœ9˜9Kšœ+˜+Kšœ=˜=Kšžœžœ˜—Kšœ˜—K˜š  œžœžœžœžœ˜Jšžœ˜šžœž˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜K˜K˜Kšžœ ˜ Kšžœ ˜ K˜K˜Kšžœžœ˜—Kšœ˜—Kšœ˜—K˜š œžœžœ'žœžœ˜CKšœ˜šœ žœž˜0Kšœ˜Kšžœ ˜—Kšœ˜Kšœ˜K˜—š œžœžœ žœžœžœžœžœ žœ˜aKšœ-žœQ™šžœžœžœ˜!šžœžœ ˜šžœžœž˜Kšžœj˜l—Kšžœžœ ˜K˜—Kšœ žœ8˜FKšœžœžœ˜1KšœH˜HKšžœ@˜BKšžœ<˜>KšžœA˜CšžœXžœžœž˜nKšžœ=˜?Kšžœ˜Kšžœ˜—Kšžœ˜Kšžœžœžœžœ<˜PKšžœžœ˜Kšžœ˜K˜—Kšžœžœ˜K˜K˜—š œžœžœ žœžœ žœžœ˜OKšœF™FKšœH˜HKšœ žœ˜*Kšœ žœ˜&Kšœ žœ˜&šžœžœžœ˜!Kšœ žœ8˜FKš œžœžœžœ žœžœ ˜SK˜˜Kš žœžœΟr œžœžœ ˜*Kšœ)˜)šžœžœ#žœžœ˜6Kšœ,™,Kšœ'˜'Kšžœžœ ˜K˜—Kšœ)˜)šžœžœ2žœž˜CKšžœžœ ˜—Kšœ'˜'šžœžœ6žœž˜GKšžœžœ ˜—Kšœ'˜'Kš žœžœžœžœžœ ˜8Kšœ$˜$Kšœ ˜ Kšœ0˜0Kšžœ˜K˜K˜—K™šœ#˜#KšœQ˜Q—Kš œžœ!žœ‘ œžœ˜Dšž˜Kš œžœžœ!žœ‘ œžœ˜HKšœW˜WKšžœžœžœžœ˜Kšœ(˜(Kšžœ˜—Kšžœ˜Kšœ žœ˜Kšžœ˜—Kšœ*˜*Kšœ&˜&K˜K˜—š   œžœ žœžœ žœ˜>šžœ"žœžœ˜.KšœŸ™ŸKšœ žœ˜%šžœžœžœž˜HKšœΓ™ΓKšœ˜—šœ#˜#šœ%˜%Kšœ.˜.Kšœ˜——K˜—K˜K˜—š   œžœžœžœžœ˜3Kšœžœ˜ Kšžœžœ ˜'Kšžœ;˜AK˜K˜—š  œžœžœžœ žœžœ˜;•StartOfExpansion' -- [char: CHAR] RETURNS [IO.CharClass]šœ žœ˜KšΠck#™#šžœž˜Kšœžœ˜&Kšžœ˜—Kšžœ ˜K˜—Kšœžœžœžœ˜HK˜—K˜Kšžœ˜K˜—Kšžœ˜—…—Y<~Χ