-- Copyright (C) 1984 by Xerox Corporation. All rights reserved. -- OthelloFetchImpl.mesa -- Johnsson 26-Feb-84 15:24:23 -- Davirro 5-Nov-84 10:53:21 -- Masinter 16-Dec-84 15:54:49 DIRECTORY Auth, Environment USING [bytesPerPage, bytesPerWord], File USING [ Create, Delete, File, MakePermanent, nullFile, PageNumber, PageCount, GetSize, SetSize, Unknown], FileTypes USING [tUntypedFile], Heap USING [systemZone], NSName, NSString, OthelloDefs USING [ AbortingCommand, CommandProcessor, Confirm, FlipCursor, GetLvIDFromUser, GetName, IndexTooLarge, LeaderPage, leaderPages, lpNoteLength, lpVersion, MyNameIs, Question, RegisterCommandProc, WriteLine, WriteLongNumber, WriteString, Yes], OthelloFetch USING [Destination, Handle, Object], OthelloOps USING [ BootFileType, GetVolumeBootFile, GetPhysicalVolumeBootFile, MakeBootable, MakeUnbootable, SetPhysicalVolumeBootFile, SetVolumeBootFile, VoidVolumeBootFile, VoidPhysicalVolumeBootFile], PhysicalVolume USING [ID], Process USING [Detach], Profile , Space USING [Map, CopyIn, ScratchMap, Unmap, Window], Stream, String , TemporaryBooting USING [InvalidParameters], Volume USING [Close, GetAttributes, ID, InsufficientSpace, nullID, Open]; OthelloFetchImpl: PROGRAM IMPORTS Auth, File, Heap, NSString, OthelloDefs, OthelloOps, Process, Space, Stream, String, TemporaryBooting, Volume EXPORTS OthelloDefs, OthelloFetch, Profile = BEGIN Object: TYPE = OthelloFetch.Object; Handle: TYPE = OthelloFetch.Handle; list: Handle ← NIL; current: Handle ← NIL; cmFile: LONG STRING ← NIL; fileName: LONG STRING ← NIL; z: UNCOUNTED ZONE = Heap.systemZone; S: PROC [s: LONG STRING] RETURNS [NSString.String] = INLINE { RETURN[NSString.StringFromMesaString[s]]}; -- Fetcher registration Register: PUBLIC PROC [h: Handle] = BEGIN h.next ← list; list← h; END; Select: PUBLIC PROC [h: Handle] = BEGIN IF current # NIL THEN current.Close[]; current← h; END; --~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- String/Credentials Commands --~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ domain, org: LONG STRING ← NIL; GetDefaultDomain: --Profile--PUBLIC PROC [proc: PROC [LONG STRING]] = { proc[domain]}; GetDefaultOrganization: --Profile--PUBLIC PROC [proc: PROC [LONG STRING]] = { proc[org]}; GetID: --Profile--PUBLIC PROC [ flavor: Auth.Flavor, proc: PROC [id: Auth.IdentityHandle]] = BEGIN SELECT flavor FROM simple => proc[simple]; strong => proc[strong]; ENDCASE => ERROR; END; Qualify: --Profile--PUBLIC PROC [ token, newToken: LONG STRING, qualification: Profile.Qualification ← clearinghouse] = { namePart: CARDINAL ← String.Length[token]; currentQual: Profile.Qualification ← none; octalAddress: BOOLEAN ← TRUE; -- only '0..'7 and '# allowed ss: String.SubStringDescriptor; chChar: CHARACTER = ':; regChar: CHARACTER = '.; IF String.Length[token] = 0 THEN RETURN; FOR i: CARDINAL IN [0..token.length) DO SELECT token[i] FROM regChar => {namePart ← i; currentQual ← registry}; -- look for last dot chChar => {namePart ← i; currentQual ← clearinghouse; EXIT}; -- first : IN['0..'7], '# => NULL; ENDCASE => octalAddress ← FALSE; ENDLOOP; IF currentQual = qualification OR octalAddress THEN { String.AppendString[newToken, token]; RETURN}; ss ← [base: token, offset: 0, length: namePart]; String.AppendSubString[newToken, @ss]; SELECT qualification FROM none => NULL; <<registry => IF String.Length[defaultRegistry] > 0 THEN { String.AppendChar[newToken, regChar]; String.AppendString[newToken, defaultRegistry]};>> clearinghouse => IF String.Length[domain] > 0 OR String.Length[org] > 0 THEN { String.AppendChar[newToken, chChar]; String.AppendString[newToken, domain]; String.AppendChar[newToken, chChar]; String.AppendString[newToken, org]}; ENDCASE}; ClearinghouseCmd: PROC = { OthelloDefs.MyNameIs[ myNameIs: "Clearinghouse"L, myHelpIs: "Set defaults for Clearinghouse"L]; OthelloDefs.GetName["Domain: "L, @domain]; OthelloDefs.GetName["Organization: "L, @org]}; userName: PUBLIC LONG STRING ← NIL; userPassword: PUBLIC LONG STRING ← NIL; strong, simple: Auth.IdentityHandle ← NIL; LoginCmd: PROC = { OthelloDefs.MyNameIs[ myNameIs: "Login"L, myHelpIs: "Set user name & password"L]; OthelloDefs.GetName["User: "L, @userName]; OthelloDefs.GetName["Password: "L, @userPassword, stars]; IF simple # NIL THEN Auth.FreeIdentity[@simple, z]; IF strong # NIL THEN Auth.FreeIdentity[@strong, z]; simple ← MakeID[flavor: simple]; strong ← MakeID[flavor: strong]}; MakeID: PROC [flavor: Auth.Flavor] RETURNS [id: Auth.IdentityHandle] = { myName: NSName.NameRecord ← [ org: S[org], domain: S[domain], local: S[userName]]; id ← Auth.MakeIdentity[ myName: @myName, password: S[userPassword], z: z, style: flavor, dontCheck: TRUE]}; directory: PUBLIC LONG STRING ← NIL; Directory: PROC = { OthelloDefs.MyNameIs[ myNameIs: "Directory"L, myHelpIs: "Set Default FTP directory"L]; OthelloDefs.GetName["Directory: "L, @directory]}; -- Simple Fetches FetchBoot: PROC = { Fetch[pilot, "Boot file name: "L, "Fetch Boot File"L, "Fetch Boot File"L, "boot"L]}; FetchGerm: PROC = { Fetch[germ, "Germ file name: "L, "Germ Fetch"L, "Fetch Germ"L, "germ"L]}; FetchPilotMicrocode: PROC = { Fetch[ softMicrocode, "Pilot microcode file name: "L, "Pilot Microcode Fetch"L, "Fetch and Install Pilot Microcode"L, "db"L]}; FetchDiagnosticMicrocode: PROC = { Fetch[ hardMicrocode, "Diagnostic microcode file name: "L, "Diagnostic Microcode Fetch"L, "Fetch and Install Diagnostic Microcode"L, "db"L]}; FetchLisp: PROC = { Fetch[ hardMicrocode, "Lisp sysout file name: "L, "Lisp Sysout Fetch"L, "Fetch and Install Interlisp Sysout"L, "sysout"L]}; Fetch: PROC [ type: OthelloOps.BootFileType, prompt, name, helpMsg, extension: STRING] = { created: BOOLEAN; file: File.File; firstPage: File.PageNumber; lvID: Volume.ID; OthelloDefs.MyNameIs[myNameIs: name, myHelpIs: helpMsg]; CheckOpen[]; lvID ← OthelloDefs.GetLvIDFromUser[].lvID; OthelloDefs.GetName[prompt, @fileName]; Volume.Open[lvID]; [file, firstPage] ← OthelloOps.GetVolumeBootFile[lvID, type]; IF (created ← file = File.nullFile) THEN file ← File.Create[lvID, 1, FileTypes.tUntypedFile] ELSE OthelloOps.MakeUnbootable[file, type, firstPage ! File.Unknown => CONTINUE; TemporaryBooting.InvalidParameters => { OthelloDefs.WriteLine["Warning, trouble making unbootable"L]; CONTINUE}]; current.Retrieve[fileName, [pilotFileSystemWrite[file]] ! UNWIND => {IF created THEN File.Delete[file]; Volume.Close[lvID]}]; OthelloDefs.WriteString["Installing..."L]; OthelloOps.SetVolumeBootFile[file, type, OthelloDefs.leaderPages]; IF created THEN File.MakePermanent[file]; -- add for LISP IF String.Equal[extension, "sysout"L] THEN { OthelloDefs.WriteString["Expanding to fill volume..."L]; SetBootFileSize[file,lvID]; OthelloDefs.WriteString["OK..."L]; }; -- end OthelloOps.MakeBootable[file, type, OthelloDefs.leaderPages ! TemporaryBooting.InvalidParameters => { OthelloDefs.WriteLine["Warning, trouble making bootable"L]; CONTINUE}]; OthelloDefs.WriteLine["done"L]; IF type IN [hardMicrocode..germ] AND OthelloDefs.Yes["Shall I also use this for the Physical Volume? "L ! UNWIND => Volume.Close[lvID]] THEN OthelloOps.SetPhysicalVolumeBootFile[file, type, OthelloDefs.leaderPages]; Volume.Close[lvID]}; bufPages: CARDINAL = 8; StartFeedback: PUBLIC SIGNAL = CODE; GrabBitsFromStream: PUBLIC PROC [ rs: Stream.Handle, rsSizePages: LONG CARDINAL, destination: OthelloFetch.Destination, note: LONG STRING ← NIL] = { WITH destination SELECT FROM pilotFileSystemWrite => { buffer: LONG POINTER ← NIL; base: File.PageNumber ← 0; got: CARDINAL; File.SetSize[localFile, rsSizePages + OthelloDefs.leaderPages ! Volume.InsufficientSpace => OthelloDefs.AbortingCommand["Volume Full"L]]; SetLeaderPage[localFile, note]; SIGNAL StartFeedback; WHILE base < rsSizePages DO thisPages: CARDINAL = CARDINAL[MIN[rsSizePages-base, bufPages]]; size: CARDINAL = thisPages*Environment.bytesPerPage; start: CARDINAL ← 0; buffer ← Space.Map[ window:[localFile, base+OthelloDefs.leaderPages, thisPages], life: dead].pointer; DO [bytesTransferred: got] ← rs.GetBlock[[ blockPointer: buffer, startIndex: start, stopIndexPlusOne: size] ! Stream.EndOfStream => { got ← 0; start ← start + nextIndex; CONTINUE}; UNWIND => [] ← Space.Unmap[buffer]]; IF got = 0 THEN {[] ← Space.Unmap[buffer]; RETURN}; IF (start ← start + got) = size THEN EXIT; ENDLOOP; --buffer ← Space.Unmap[buffer, return]; Process.Detach[LOOPHOLE[FORK Space.Unmap[buffer]]]; buffer ← NIL; OthelloDefs.FlipCursor[]; base ← base + thisPages; ENDLOOP; buffer ← Space.ScratchMap[1]; -- check for any leftover stuff [bytesTransferred: got] ← rs.GetBlock[[ blockPointer: buffer, startIndex: 0, stopIndexPlusOne: Environment.bytesPerPage] ! Stream.EndOfStream => {got ← nextIndex; CONTINUE}; UNWIND => [] ← Space.Unmap[buffer]]; [] ← Space.Unmap[buffer]; IF got # 0 THEN OthelloDefs.AbortingCommand[ "File longer than advertised length"L]}; string => { SIGNAL StartFeedback; DO stringOverhead: CARDINAL = SIZE[StringBody]*Environment.bytesPerWord; string: LONG STRING = Space.ScratchMap[bufPages]; string↑ ← [ length: 0, maxlength: bufPages*Environment.bytesPerPage - stringOverhead, text: ]; WHILE string.length < string.maxlength DO got: CARDINAL; [bytesTransferred: got] ← rs.get[ rs, [blockPointer: LOOPHOLE[@string.text], startIndex: string.length, stopIndexPlusOne: string.maxlength], rs.options ! Stream.EndOfStream => { got ← 0; string.length ← string.length + nextIndex; CONTINUE}; UNWIND => [] ← Space.Unmap[string]]; IF got = 0 THEN { stringProc[string! UNWIND => [] ← Space.Unmap[string]]; [] ← Space.Unmap[string]; RETURN}; string.length ← string.length + got; ENDLOOP; [] ← Space.Unmap[string]; OthelloDefs.AbortingCommand["Command file too long!"L]; ENDLOOP}; rawWrite =>{ buffer: LONG POINTER = Space.ScratchMap[1]; done: BOOLEAN ← FALSE; first: BOOLEAN ← TRUE; options: Stream.InputOptions ← rs.options; GetPage: PROC RETURNS [LONG POINTER] = { got: CARDINAL; index: CARDINAL ← 0; IF first THEN {SIGNAL StartFeedback; first ← FALSE}; WHILE ~done DO [bytesTransferred: got] ← rs.get[ sH: rs, block: [blockPointer: buffer, startIndex: index, stopIndexPlusOne: Environment.bytesPerPage], options: options ! Stream.EndOfStream => {got ← nextIndex; done ← TRUE; CONTINUE}]; IF (index ← index + got) = Environment.bytesPerPage OR done THEN {OthelloDefs.FlipCursor[]; EXIT} ENDLOOP; RETURN[IF done AND index = 0 THEN NIL ELSE buffer]}; options.signalEndOfStream ← TRUE; linkProc[GetPage ! UNWIND => [] ← Space.Unmap[buffer]]; WHILE ~done DO [] ← GetPage[! UNWIND => [] ← Space.Unmap[buffer]] ENDLOOP; [] ← Space.Unmap[buffer]}; ENDCASE => ERROR}; -- Initial Ucode Fetch Command FetchInitialMicrocode: PUBLIC PROC [ InstallProc: PROC [getPage: PROC RETURNS [LONG POINTER]]] = { CheckOpen[]; OthelloDefs.GetName["File name: "L, @fileName]; OthelloDefs.Confirm[]; current.Retrieve[fileName, [rawWrite[InstallProc]]]}; -- Command Files AlternateGetCMFile: PUBLIC PROC [s: STRING] = { z.FREE[@cmFile]; cmFile ← z.NEW[StringBody[s.length+8]]; FOR i: CARDINAL IN [1..s.length) DO String.AppendChar[cmFile, s[i]] ENDLOOP; DoIndirect[]}; Indirect: PROC = { OthelloDefs.MyNameIs[ myNameIs: "@", myHelpIs: "Run command file"L]; OthelloDefs.GetName["Command file: "L, @cmFile ! OthelloDefs.Question => { OthelloDefs.WriteLine["[Host]<Dir>Filename"L]; RESUME}]; DoIndirect[]}; DoIndirect: PROC = { [] ← String.AppendExtensionIfNeeded[@cmFile, "othello"L, z]; FOR h: Handle ← list, h.next UNTIL h = NIL DO IF h.DoIndirect[cmFile] THEN RETURN; ENDLOOP; OthelloDefs.AbortingCommand["Unrecognizable command file name"L]}; -- Misc. commands CloseCmd: PROC = { OthelloDefs.MyNameIs[ myNameIs: "Close"L, myHelpIs: "Close currently open connection"L]; CloseFetch[]}; CloseFetch: PUBLIC PROC = { Select[NIL]}; ListCmd: PROC = { OthelloDefs.MyNameIs[ myNameIs: "List Files"L, myHelpIs: "Enumerate files matching pattern"L]; CheckOpen[]; OthelloDefs.GetName["Pattern: "L, @fileName ! OthelloDefs.Question => { OthelloDefs.WriteLine["pattern to match"L]; RESUME}]; current.List[fileName]}; CheckOpen: PROC = { IF current = NIL THEN OthelloDefs.AbortingCommand["You must execute an Open command first"L]}; SetLeaderPage: PUBLIC PROCEDURE [file: File.File, note: LONG STRING] = BEGIN lp: LONG POINTER TO OthelloDefs.LeaderPage ← Space.Map[[file, 0, OthelloDefs.leaderPages]].pointer; 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]; END; -- Commands for Interlisp-D CopyLisp: PROC = BEGIN fromVolume, toVolume: Volume.ID; fromFile, toFile: File.File; pvID: PhysicalVolume.ID; error: BOOLEAN ← FALSE; currentBase: File.PageNumber; nextCount: File.PageCount; fromWindow,toWindow: Space.Window; toPointer: LONG POINTER; fromPage1: LONG POINTER TO ARRAY[0..20] OF WORD; -- Lisp Interface Page fromFileSize: File.PageCount; fromFileActive: CARDINAL; tries: CARDINAL; toFileSize: File.PageCount; minFileSize: File.PageCount; OthelloDefs.MyNameIs[ myNameIs: "Copy Lisp From Another Volume"L, myHelpIs: "Copies lisp from one volume to another"L]; [pvID, fromVolume] ← OthelloDefs.GetLvIDFromUser[prompt:"Volume to copy from: "L]; IF fromVolume = Volume.nullID THEN OthelloDefs.AbortingCommand["Invalid volume."L]; toVolume ← OthelloDefs.GetLvIDFromUser[prompt:"Volume to copy to: "L].lvID; IF toVolume = Volume.nullID THEN OthelloDefs.AbortingCommand["Invalid volume."L]; -- Open up the volumes Volume.Open[fromVolume]; Volume.Open[toVolume]; -- Is there a source file? fromFile ← OthelloOps.GetVolumeBootFile[fromVolume,hardMicrocode].file; IF fromFile = File.nullFile THEN { Volume.Close[fromVolume]; Volume.Close[toVolume]; OthelloDefs.AbortingCommand["No Lisp sysout on source volume"L]; }; -- Is there a dest file? Delete it if yes. toFile ← OthelloOps.GetVolumeBootFile[toVolume,hardMicrocode].file; IF toFile # File.nullFile THEN { OthelloOps.VoidVolumeBootFile[toVolume,hardMicrocode]; File.Delete[toFile]; IF OthelloOps.GetPhysicalVolumeBootFile[pvID,hardMicrocode].file = toFile THEN OthelloOps.VoidPhysicalVolumeBootFile[pvID,hardMicrocode]; }; -- Create the dest file fromWindow ← [file: fromFile, base: 1 + OthelloDefs.leaderPages, count: 1]; fromPage1 ← Space.Map[fromWindow].pointer; fromFileSize ← File.GetSize[fromFile]; fromFileActive ← fromPage1[20]; fromPage1 ← Space.Unmap[fromPage1]; IF fromFileActive < 1000 THEN {Volume.Close[fromVolume]; Volume.Close[toVolume]; OthelloDefs.AbortingCommand["Source volume doesn't contain a Lisp system!"L];}; IF fromFileActive > fromFileSize THEN {Volume.Close[fromVolume]; Volume.Close[toVolume]; OthelloDefs.AbortingCommand["Can't copy a VMEM that has been already expanded by Lisp!"L];}; toFileSize ← fromFileActive; tries ← 1; toFile ← File.Create[toVolume, toFileSize, FileTypes.tUntypedFile ! Volume.InsufficientSpace => { tries ← tries - 1; toFileSize ← toFileSize - 100; OthelloDefs.WriteString["."L]; IF tries = 0 OR toFileSize < 0 THEN {error ← TRUE; CONTINUE} ELSE RETRY;};]; IF error THEN {Volume.Close[fromVolume]; Volume.Close[toVolume]; OthelloDefs.AbortingCommand["Destination volume does not contain sufficient space!"L];}; File.MakePermanent[toFile]; -- Create windows onto the file OthelloDefs.WriteString["Copying "L]; OthelloDefs.WriteLongNumber[toFileSize]; OthelloDefs.WriteString["pages... "L]; currentBase ← 0; nextCount ← 100; DO -- until file has been copied nextCount ← MIN[100,fromFileActive-currentBase]; IF nextCount=0 THEN EXIT; fromWindow ← [file: fromFile, base: currentBase, count: nextCount]; toWindow ← [file: toFile, base: currentBase, count: nextCount]; toPointer ← Space.Map[toWindow].pointer; nextCount ← Space.CopyIn[toPointer,fromWindow]; toPointer ← Space.Unmap[toPointer]; currentBase ← currentBase + nextCount; OthelloDefs.WriteString["."L]; IF currentBase >= minFileSize THEN EXIT; ENDLOOP; OthelloDefs.WriteString["Installing..."L]; OthelloOps.MakeBootable[toFile, hardMicrocode, OthelloDefs.leaderPages ! TemporaryBooting.InvalidParameters => { OthelloDefs.WriteLine["Warning, trouble making bootable"L]; CONTINUE}]; OthelloOps.SetVolumeBootFile[toFile,hardMicrocode,OthelloDefs.leaderPages]; OthelloDefs.WriteLine["done"L]; Volume.Close[fromVolume]; Volume.Close[toVolume]; END; -- This kludge sets the file's size to be as big as Pilot will let it be, on 100 page increments. SetBootFileSize: PRIVATE PROC[file: File.File, lvID: Volume.ID] = BEGIN tries: CARDINAL; oldSize: File.PageCount ← File.GetSize[file]; newSize: File.PageCount ← oldSize + Volume.GetAttributes[lvID].freePageCount; tries ← 20; OthelloOps.MakeUnbootable[file, hardMicrocode, OthelloDefs.leaderPages]; File.SetSize[file, newSize ! File.Unknown => {OthelloDefs.WriteLine["Warning: Trouble making file fill volume - File.Unknown"L]; CONTINUE}; Volume.InsufficientSpace => {OthelloDefs.WriteString["."L]; newSize ← newSize - 100; tries ← tries - 1; IF tries = 0 OR newSize < oldSize THEN {OthelloDefs.WriteLine["Warning: Expanding volume failed!"]; CONTINUE;} ELSE RETRY};]; OthelloOps.MakeBootable[file,hardMicrocode, OthelloDefs.leaderPages]; END; ExpandLisp: PROC = BEGIN pvID: PhysicalVolume.ID; exVolume: Volume.ID; exFile: File.File; OthelloDefs.MyNameIs[ myNameIs: "Expand Lisp Virtual Memory File"L, myHelpIs: "Expand the lisp virtual memory file on a volume"L]; [pvID, exVolume] ← OthelloDefs.GetLvIDFromUser[prompt:"Volume to expand: "L]; IF exVolume = Volume.nullID THEN OthelloDefs.AbortingCommand["Invalid volume."L]; Volume.Open[exVolume]; exFile ← OthelloOps.GetVolumeBootFile[exVolume, hardMicrocode].file; IF exFile = File.nullFile THEN OthelloDefs.AbortingCommand["No sysout on volume."L]; OthelloDefs.WriteString["Expanding file..."L]; SetBootFileSize[exFile,exVolume]; OthelloDefs.WriteLine["Done."L]; Volume.Close[exVolume]; END; -- command processor commandProcessor: OthelloDefs.CommandProcessor ← [FetchCommands]; FetchCommands: PROC [index: CARDINAL] = { SELECT index FROM 0 => Indirect[]; 1 => ClearinghouseCmd[]; 2 => CloseCmd[]; 3 => Directory[]; 4 => FetchBoot[]; 5 => FetchDiagnosticMicrocode[]; 6 => FetchGerm[]; 7 => FetchPilotMicrocode[]; 8 => ListCmd[]; 9 => LoginCmd[]; 10 => FetchLisp[]; 11 => CopyLisp[]; 12 => ExpandLisp[]; ENDCASE => OthelloDefs.IndexTooLarge}; -- init OthelloDefs.RegisterCommandProc[@commandProcessor]; END.