-- InstallImpl.mesa - last edit: -- Masinter, add comments -- Lichtenberg Aug 1984, from Install -- Daniels 13-Jun-83 14:09:06 -- Loretta 20-Apr-83 12:12:48 -- Karlton 19-Aug-83 16:07:42 -- Bruce 2-Sep-81 15:25:10 -- Johnsson 16-Jan-84 9:12:07 DIRECTORY Environment: TYPE USING [bytesPerPage, PageCount, PageNumber, PageOffset], Exec: TYPE USING [ AddCommand, CheckForAbort, Confirm, EndOfCommandLine, ExecProc, FreeTokenString, GetNameandPassword, GetToken, Handle, Outcome, OutputProc], File: TYPE USING [ Create, Delete, File, MakePermanent, nullFile, PageCount, PageNumber, SetSize, GetSize, Type, Unknown], FileName: TYPE USING [AllocVFN, FreeVFN, NormalizeVFN, VFN], FileTransfer: TYPE USING [ ClientProc, Connection, Create, Destroy, Error, GetStreamInfo, MessageProc, ReadStream, SetPrimaryCredentials, SetProcs], FileTypes: TYPE USING [tUntypedFile], Format: TYPE USING [Line, StringProc], Heap: TYPE USING [systemZone], OthelloDefs: TYPE USING [LeaderPage, leaderPages, lpNoteLength, lpVersion], OthelloOps: TYPE USING [ GetVolumeBootFile, MakeBootable, MakeUnbootable, SetVolumeBootFile, SetPhysicalVolumeBootFile], PrincOps: TYPE USING [Port], Space: TYPE USING [CopyOut, Interval, Map, PageCount, PageOffset, Unmap], SpecialVolume: TYPE USING [OpenVolume], Stream: TYPE USING [Delete, GetBlock, Handle], String: TYPE USING [ AppendCharAndGrow, AppendString, AppendStringAndGrow, CopyToNewString, Empty, EqualString, EquivalentString, StringBoundsFault, Length], TemporaryBooting: TYPE USING [InvalidParameters, BootButton], Time: TYPE USING [Append, Unpack], Volume: TYPE USING [ Close, GetLabelString, GetNext, GetStatus, GetType, ID, InsufficientSpace, maxNameLength, NeedsScavenging, nullID, Status, systemID, Type, TypeSet, GetAttributes]; InstallLispImpl: MONITOR IMPORTS Exec, File, FileName, FileTransfer, Format, Heap, OthelloOps, Space, SpecialVolume, Stream, String, Time, TemporaryBooting, Volume = BEGIN volumeName: STRING = [Volume.maxNameLength]; volume: Volume.ID ← Volume.nullID; volumeOpened: BOOLEAN ← FALSE; originalVolumeStatus: Volume.Status ← unknown; write: Format.StringProc ← NIL; exec: Exec.Handle ← NIL; expandVolume: BOOLEAN ← FALSE; setDefault: BOOLEAN ← FALSE; startLisp: BOOLEAN ← FALSE; InstallFiles: ENTRY Exec.ExecProc = { ENABLE UNWIND => {volumeOpened ← FALSE}; token, switches: LONG STRING ← NIL; haveOne: BOOLEAN ← FALSE; FreeTokens: PROCEDURE = { [] ← Exec.FreeTokenString[token]; [] ← Exec.FreeTokenString[switches]}; exec ← h; write ← h.OutputProc[]; [] ← SetVolumeName["Extra"L]; expandVolume ← FALSE; setDefault ← FALSE; startLisp ← FALSE; UNTIL h.EndOfCommandLine[] DO -- parse command line ENABLE UNWIND => FreeTokens[]; [token, switches] ← h.GetToken[]; CheckSwitches[switches]; IF String.Empty[token] THEN { outcome ← IF haveOne THEN error ELSE InstallHelp[h]; EXIT}; IF ~SetVolumeName[token] THEN { write["Error: volume "L]; write[token]; write[" not found!"L]; outcome ← error; EXIT}; FreeTokens[]; [token, switches] ← h.GetToken[]; CheckSwitches[switches]; IF String.Empty[token] THEN IF ~DoNoFileOps[] THEN outcome ← abort; IF outcome = abort THEN EXIT; IF ~String.Empty[switches] OR ~String.EqualString[token, "←"L] THEN { write["Missing ""←""; OK to continue?"L]; IF ~h.Confirm[] THEN {outcome ← abort; EXIT}} ELSE {FreeTokens[]; [token, switches] ← h.GetToken[]}; CheckSwitches[switches]; IF h.CheckForAbort[] THEN {outcome ← abort; EXIT}; IF ~String.Empty[token] THEN IF startLisp THEN Format.Line[write,"Will boot Lisp when done."L]; IF expandVolume THEN Format.Line[write,"Will expand VMem size"L]; IF setDefault THEN Format.Line[write,"Will make this default lisp"L]; outcome ← InstallFileOnVolume[token ! UNWIND => CloseVolume[]]; haveOne ← TRUE; REPEAT FINISHED => IF ~haveOne THEN outcome ← InstallHelp[h] ENDLOOP; FreeTokens[]; IF startLisp THEN TemporaryBooting.BootButton[]; }; DoNoFileOps: PROC RETURNS [foo: BOOLEAN] = BEGIN OPEN OthelloOps; file: File.File; firstPage: File.PageNumber; IF ~(expandVolume OR setDefault OR startLisp) THEN RETURN[TRUE]; IF ~OpenVolume[] THEN RETURN[FALSE]; [file, firstPage] ← GetVolumeBootFile[volume,hardMicrocode]; IF file = File.nullFile THEN { Format.Line[write,"No sysout on that volume"L]; RETURN[FALSE] }; IF expandVolume THEN {MakeUnbootable[file,hardMicrocode,firstPage]; SetBootFileSize[file,volume]; MakeBootable[file,hardMicrocode,firstPage]; }; IF startLisp OR setDefault THEN SetPhysicalVolumeBootFile[file,hardMicrocode, firstPage]; CloseVolume[]; IF startLisp THEN TemporaryBooting.BootButton[]; END; CheckSwitches: PROC [switches: LONG STRING] = BEGIN i: CARDINAL; length: CARDINAL ← String.Length[switches]; IF ~String.Empty[switches] THEN FOR i IN [0..length) DO SELECT switches[i] FROM 'x,'X => expandVolume ← TRUE; 'd,'D => setDefault ← TRUE; 's,'S => startLisp ← TRUE; ENDCASE => {}; ENDLOOP; END; SetVolumeName: PROCEDURE [v: LONG STRING] RETURNS [ok: BOOLEAN ← TRUE] = { CloseVolume[]; volumeName.length ← 0; String.AppendString[ volumeName, v ! String.StringBoundsFault => { write["Volume name too long!"L]; ok ← FALSE; CONTINUE}]}; OpenVolume: PROCEDURE RETURNS [BOOLEAN] = { OPEN Volume; myType: Volume.Type = Volume.GetType[Volume.systemID]; all: TypeSet ← [ normal: TRUE, debugger: myType = debugger OR myType = debuggerDebugger, debuggerDebugger: myType = debuggerDebugger]; name: STRING = [maxNameLength]; IF ~volumeOpened THEN { FOR volume ← GetNext[nullID, all], GetNext[volume, all] UNTIL volume = nullID DO name.length ← 0; GetLabelString[volume, name]; IF String.EquivalentString[name, volumeName] THEN EXIT; ENDLOOP; SELECT volume FROM nullID => { write[volumeName]; Format.Line[write, " not found!"L]; RETURN[FALSE]}; Volume.systemID => { write[volumeName]; Format.Line[write, " is your system volume!"L]; RETURN[FALSE]}; ENDCASE; SELECT (originalVolumeStatus ← Volume.GetStatus[volume]) FROM openRead => Volume.Close[volume]; openReadWrite => RETURN[TRUE]; unknown, partiallyOnLine, closedAndInconsistent => RETURN[FALSE]; ENDCASE => NULL; SpecialVolume.OpenVolume[volume: volume, access: readWrite ! Volume.NeedsScavenging => GOTO YouLose]; volumeOpened ← TRUE}; RETURN[TRUE]; EXITS YouLose => { write[volumeName]; Format.Line[write, " needs scavenging."L]; RETURN[FALSE]}}; CloseVolume: PROC = { IF volumeOpened THEN { Volume.Close[volume]; IF originalVolumeStatus = openRead THEN SpecialVolume.OpenVolume[volume: volume, access: read]; volumeOpened ← FALSE}}; PortRep: TYPE = PrincOps.Port; InstallFileOnVolume: PROC [name : LONG STRING] RETURNS [outcome: Exec.Outcome ← normal] = { -- from OthelloFTP (sort of) OPEN OthelloOps; created: BOOLEAN ← FALSE; file: File.File; firstPage: File.PageNumber; -- clean the PORT up from last time LOOPHOLE[GetFile, PortRep].in ← 0; -- CONNECT GetFile.out TO Retrieve; LOOPHOLE[GetFile, PortRep].out ← LOOPHOLE[Retrieve]; write["Opening "]; write[volumeName]; write["... "L]; IF ~OpenVolume[] THEN RETURN[error] ELSE Format.Line[write, " open."L]; [file, firstPage] ← GetVolumeBootFile[volume, hardMicrocode]; IF NOT GetFile[name: name] THEN {CloseVolume[]; RETURN[error]}; IF (created ← file = File.nullFile) THEN file ← File.Create[volume, 1, FileTypes.tUntypedFile] ELSE MakeUnbootable[ file, hardMicrocode, firstPage ! File.Unknown => CONTINUE; TemporaryBooting.InvalidParameters => { outcome ← warning; write["Warning: trouble making unbootable"L]; CONTINUE}]; write[" Fetching... "L]; IF NOT GetFile[ file: file ! UNWIND => {IF created THEN file.Delete; CloseVolume[]}] THEN {IF created THEN file.Delete; CloseVolume[]; RETURN[error]}; write["Installing..."L]; SetVolumeBootFile[file, hardMicrocode, OthelloDefs.leaderPages]; file.MakePermanent; File.SetSize[file,File.GetSize[file]+1]; IF expandVolume THEN { write["Expanding..."L]; SetBootFileSize[file,volume]; write["OK..."L]; }; MakeBootable[ file, hardMicrocode, OthelloDefs.leaderPages ! TemporaryBooting.InvalidParameters => { outcome ← warning; write["Warning: trouble making bootable"L]; CONTINUE}]; IF setDefault THEN OthelloOps.SetPhysicalVolumeBootFile[file,hardMicrocode, OthelloDefs.leaderPages]; Format.Line[write, " installed."L]; CloseVolume[]}; --~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ SetBootFileSize: PRIVATE PROC[file: File.File, lvID: Volume.ID] = BEGIN oldSize: File.PageCount ← File.GetSize[file]; newSize: File.PageCount ← oldSize + Volume.GetAttributes[lvID].freePageCount; File.SetSize[file, newSize ! File.Unknown => {write["Warning: Trouble making file fill volume - File.Unknown"L]; CONTINUE}; Volume.InsufficientSpace => {write["."L]; newSize ← newSize - 100; IF newSize < oldSize THEN CONTINUE ELSE RETRY}]; END; --~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ GetFile: PORT [ file: File.File ← File.nullFile, name: LONG STRING ← NIL] RETURNS [BOOLEAN]; Retrieve: PROC [file: File.File, name: LONG STRING] RETURNS [gotIt: BOOLEAN ← TRUE] = { OPEN FileName, FileTransfer; ResumeSetup: PORT [BOOLEAN] RETURNS [file: File.File, name: LONG STRING]; Cleanup: PROC = { IF readStream # NIL THEN {readStream.Delete; readStream ← NIL}; IF buffer # NIL THEN buffer ← Space.Unmap[buffer]; IF vfn # NIL THEN {FreeVFN[vfn]; vfn ← NIL}; IF conn # NIL THEN {conn.Destroy; conn ← NIL}}; LoginUser: FileTransfer.ClientProc = { user: STRING = [64]; password: STRING = [64]; exec.GetNameandPassword[user, password]; conn.SetPrimaryCredentials[user: user, password: password]}; Message: MessageProc = {write[s1]; write[s2]; write[s3]; write[s4]}; bufferPages: Space.PageCount = 64; bufferBytes: CARDINAL = CARDINAL[bufferPages*Environment.bytesPerPage]; buffer: LONG POINTER ← NIL; conn: Connection ← FileTransfer.Create[]; vfn: VFN ← NIL; readStream: Stream.Handle ← NIL; fileSize: File.PageCount; -- CONNECT ResumeSetup.out TO GetFile LOOPHOLE[ResumeSetup, PortRep].out ← @GetFile; -- CONNECT GetFile.out TO ResumeSetup LOOPHOLE[GetFile, PortRep].out ← @ResumeSetup; conn.SetProcs[clientData: NIL, messages: Message, login: LoginUser]; vfn ← AllocVFN[name]; readStream ← conn.ReadStream[files: vfn ! Error => {Cleanup[]; GOTO noGood}]; file ← ResumeSetup[gotIt].file; fileSize ← (FileTransfer.GetStreamInfo[readStream].size + Environment.bytesPerPage - 1)/Environment.bytesPerPage + OthelloDefs.leaderPages; file.SetSize[ fileSize ! Volume.InsufficientSpace => { write["Not enough room for file!"L]; Cleanup[]; GOTO noGood}]; buffer ← Space.Map[ window: [file: File.nullFile, base: NULL, count: bufferPages], class: data, swapUnits: [uniform[4]]].pointer; SetLeaderPage[file, readStream, vfn]; FOR windowPage: Space.PageOffset ← OthelloDefs.leaderPages, windowPage + bufferPages WHILE windowPage < fileSize DO bytesTransferred: CARDINAL ← readStream.GetBlock[ [buffer, 0, bufferBytes]].bytesTransferred; [] ← Space.CopyOut[buffer, [file, windowPage, bufferPages]]; ENDLOOP; Cleanup[]; RETURN[TRUE]; EXITS noGood => RETURN[FALSE]}; SetLeaderPage: PROC [ file: File.File, stream: Stream.Handle, vfn: FileName.VFN] = { lp: LONG POINTER TO OthelloDefs.LeaderPage = Space.Map[ [file, 0, OthelloDefs.leaderPages]].pointer; note: LONG STRING ← Heap.systemZone.NEW[StringBody[60]]; -- STARTKLUDGE: work around the FileTransfer not giving the host name <<String.AppendStringAndGrow[ @note, FileTransfer.GetStreamName[stream], Heap.systemZone];>> vfn.NormalizeVFN; IF NOT String.Empty[vfn.host] THEN { String.AppendStringAndGrow[@note, "["L, Heap.systemZone]; String.AppendStringAndGrow[@note, vfn.host, Heap.systemZone]; String.AppendStringAndGrow[@note, "]"L, Heap.systemZone]}; IF NOT String.Empty[vfn.directory] THEN { String.AppendStringAndGrow[@note, vfn.directory, Heap.systemZone]; String.AppendStringAndGrow[@note, ">"L, Heap.systemZone]}; IF NOT String.Empty[vfn.name] THEN String.AppendStringAndGrow[@note, vfn.name, Heap.systemZone]; IF NOT String.Empty[vfn.version] THEN { String.AppendStringAndGrow[@note, "!"L, Heap.systemZone]; String.AppendStringAndGrow[@note, vfn.version, Heap.systemZone]}; -- ENDKLUDGE String.AppendStringAndGrow[@note, " ("L, Heap.systemZone]; -- STARTKLUDGE: Time.Append screws up on StringBoundsFault {oldLength: CARDINAL = note.length; Time.Append[ s: note, zone: TRUE, unpacked: Time.Unpack[FileTransfer.GetStreamInfo[stream].create] ! String.StringBoundsFault => { ns ← String.CopyToNewString[ s: note, z: Heap.systemZone, longer: s.maxlength - s.length + 20]; Heap.systemZone.FREE[@note]; note ← ns; note.length ← oldLength; RETRY}]; };-- ENDKLUDGE String.AppendCharAndGrow[@note, '), Heap.systemZone]; lp.version ← OthelloDefs.lpVersion; lp.length ← MIN[note.length, OthelloDefs.lpNoteLength]; FOR i: CARDINAL IN [0..lp.length) DO lp.note[i] ← note[i]; ENDLOOP; [] ← Space.Unmap[lp]; Heap.systemZone.FREE[@note]}; InstallHelp: Exec.ExecProc = { Format.Line[ h.OutputProc[], "Command format: InstallLisp.~ volume ← sysoutfile Switches: /x to expand volume file size /d to make this lisp the boot lisp /s to start lisp from this volume"L]}; {Exec.AddCommand["InstallLisp.~"L, InstallFiles, InstallHelp]}; END.