-- file: OthelloFTP.mesa -- Edited by -- Lichtenberg 6-Jul-84 18:54:38 - Lisp stuff -- Johnsson, 15-Sep-83 15:40:26 -- Sandman, October 11, 1979 8:33 AM -- Gobbel, February 14, 1980 7:30 PM -- Forrest, 13-Jan-82 10:22:30 -- Bruce, 28-Dec-81 10:19:42 DIRECTORY Environment USING [bytesPerPage, bytesPerWord], Heap USING [systemZone], File USING [ Create, Delete, File, MakePermanent, nullFile, PageNumber, PageCount, GetSize, SetSize, Unknown], FileTypes USING [tUntypedFile], OthelloDefs, OthelloOps USING [ BootFileType, GetVolumeBootFile, MakeBootable, MakeUnbootable, SetPhysicalVolumeBootFile, SetVolumeBootFile], PhysicalVolume USING [ID], Process USING [Detach, Pause, SecondsToTicks], Space USING [Map, ScratchMap, Unmap], STP USING [ Close, Connect, Create, CreateRemoteStream, Enumerate, Error, ErrorCode, FileInfo, GetFileInfo, Handle, IsOpen, Login, NextFileName, NoteFileProcType, Open, SetDirectory, SetHost], Stream USING [EndOfStream, GetBlock, Handle], String USING [ AppendChar, AppendCharAndGrow, AppendExtensionIfNeeded, AppendStringAndGrow, CopyToNewString, Length], TemporaryBooting USING [InvalidParameters], Volume USING [Close, ID, InsufficientSpace, Open, GetAttributes]; OthelloFTP: PROGRAM IMPORTS File, Heap, OthelloDefs, OthelloOps, Process, Space, STP, Stream, String, TemporaryBooting, Volume EXPORTS OthelloDefs = BEGIN OPEN OthelloOps; host: LONG STRING ← NIL; userName: LONG STRING ← NIL; userPassword: LONG STRING ← NIL; connectName: LONG STRING ← NIL; connectPassword: LONG STRING ← NIL; directory: LONG STRING ← NIL; cmFile: LONG STRING ← NIL; fileName: LONG STRING ← NIL; --~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- String/Credentials Commands --~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Directory: PROC = { OthelloDefs.MyNameIs[ myNameIs: "Directory"L, myHelpIs: "Set Default FTP directory"L]; OthelloDefs.GetName["Directory: "L, @directory]}; GetConnectNamePassword: PROC = { OthelloDefs.MyNameIs[ myNameIs: "Connect"L, myHelpIs: "Set secondary Credentials"L]; OthelloDefs.GetName["Directory: "L, @connectName]; OthelloDefs.GetName["Password: "L, @connectPassword, stars]}; GetUserNamePassword: PROC = { OthelloDefs.MyNameIs[ myNameIs: "Login"L, myHelpIs: "Set user name-password"L]; OthelloDefs.GetName["User: "L, @userName]; OthelloDefs.GetName["Password: "L, @userPassword, stars]}; --~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- Basic Fetch Commands --~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ FetchBoot: PROC = { Fetch[pilot, "Boot file name: "L, "Fetch Boot File"L, "Fetch Boot File"L, "boot"L, FALSE]}; FetchGerm: PROC = { Fetch[germ, "Germ file name: "L, "Germ Fetch"L, "Fetch Germ"L, "germ"L, FALSE]}; FetchPilotMicrocode: PROC = { Fetch[ softMicrocode, "Pilot microcode file name: "L, "Pilot Microcode Fetch"L, "Fetch and Install Pilot Microcode"L, "db"L, FALSE]}; -- Fetch Lisp sysout command: Installs a Lisp sysout on the volume. FetchLispSysout: PROC = { Fetch[ hardMicrocode, "Lisp sysout file name: "L, "Lisp Sysout Fetch"L, "Fetch and Install Lisp Sysout"L, "sysout"L, TRUE]}; FetchDiagnosticMicrocode: PROC = { Fetch[ hardMicrocode, "Diagnostic microcode file name: "L, "Diagnostic Microcode Fetch"L, "Fetch and Install Diagnostic Microcode"L, "db"L, FALSE]}; Fetch: PROC [type: BootFileType, prompt, name, helpMsg, extension: STRING, lispSysout: BOOLEAN] = BEGIN created: BOOLEAN ← FALSE; file: File.File; firstPage: File.PageNumber; lvID: Volume.ID; OthelloDefs.MyNameIs[myNameIs: name, myHelpIs: helpMsg]; IF ~ConnectionOpen[] AND ~ReOpen[] THEN OthelloDefs.AbortingCommand["Please open a connection"L]; lvID ← OthelloDefs.GetLvIDFromUser[].lvID; OthelloDefs.GetName[prompt, @fileName]; [] ← String.AppendExtensionIfNeeded[@fileName, extension, Heap.systemZone]; Volume.Open[lvID]; [file, firstPage] ← GetVolumeBootFile[lvID, type]; IF (created ← file = File.nullFile) THEN file ← File.Create[lvID, 1, FileTypes.tUntypedFile] ELSE MakeUnbootable[file, type, firstPage ! File.Unknown => CONTINUE; TemporaryBooting.InvalidParameters => { OthelloDefs.WriteLine["Warning, trouble making unbootable"L]; CONTINUE}]; Retrieve[destination: [pilotFileSystemWrite[file]] ! UNWIND => {IF created THEN File.Delete[file]; Volume.Close[lvID]}]; OthelloDefs.WriteString["Installing..."L]; SetVolumeBootFile[file, type, OthelloDefs.leaderPages]; File.MakePermanent[file]; -- IF lispSysout THEN { -- OthelloDefs.WriteString["Expanding to fill volume..."L]; -- SetBootFileSize[file,lvID]; -- OthelloDefs.WriteString["OK..."L]; -- }; MakeBootable[file, type, OthelloDefs.leaderPages ! TemporaryBooting.InvalidParameters => { OthelloDefs.WriteLine["Warning, trouble making bootable"L]; CONTINUE}]; OthelloDefs.WriteLine["Done."L]; IF lispSysout THEN OthelloDefs.WriteLine["[Remember to use Expand VMem command if necessary]"L]; IF lispSysout THEN {IF OthelloDefs.Yes["Shall I also make this the default boot lisp? "L ! UNWIND => Volume.Close[lvID]] THEN SetPhysicalVolumeBootFile[file,type,OthelloDefs.leaderPages]; } ELSE IF type IN [hardMicrocode..germ] AND OthelloDefs.Yes["Shall I also use this for the Physical Volume? "L ! UNWIND => Volume.Close[lvID]] THEN SetPhysicalVolumeBootFile[file, type, OthelloDefs.leaderPages]; Volume.Close[lvID]; END; -- UGH.. 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 newSize: File.PageCount ← File.GetSize[file] + Volume.GetAttributes[lvID].freePageCount; 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; RETRY}]; END; --~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- Initial Ucode Fetch Command --~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ FetchInitialMicrocode: PUBLIC PROC [ InstallProc: PROC [getPage: PROC RETURNS [LONG POINTER]]] = { IF ~ConnectionOpen[] AND ~ReOpen[] THEN OthelloDefs.AbortingCommand["Please open a connection"L]; OthelloDefs.GetName["File name: "L, @fileName]; OthelloDefs.Confirm[]; Retrieve[destination: [rawWrite[InstallProc]]]}; --~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- Indirect command files --~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ AlternateGetCMFile: PUBLIC PROC [s: STRING] = { Heap.systemZone.FREE[@cmFile]; cmFile ← Heap.systemZone.NEW[StringBody[s.length]]; FOR i: CARDINAL IN [1..s.length) DO String.AppendChar[cmFile, s[i]] ENDLOOP; DoIndirect[]}; Indirect: PROC = { OthelloDefs.MyNameIs[ myNameIs: "@", myHelpIs: "Run command file from IFS"L]; OthelloDefs.GetName["Command file: "L, @cmFile ! OthelloDefs.Question => { OthelloDefs.WriteLine["[Host]<Dir>Filename"L]; RESUME}]; [] ← String.AppendExtensionIfNeeded[@cmFile, "hello"L, Heap.systemZone]; DoIndirect[]}; DoIndirect: PROC = BEGIN ParseCmFileName: PROC = { hostEnd: CARDINAL; IF cmFile.length = 0 THEN RETURN; FOR i: CARDINAL IN [0..cmFile.length) DO c: CHARACTER = cmFile[i]; SELECT c FROM '[ => LOOP; '] => {hostEnd ← i; EXIT}; ENDCASE => String.AppendCharAndGrow[@host, c, Heap.systemZone]; REPEAT FINISHED => {Heap.systemZone.FREE[@host]; RETURN} ENDLOOP; -- hostEnd points at '] FOR i: CARDINAL IN (hostEnd..cmFile.length) DO IF cmFile[i] = '< AND directory#NIL THEN directory.length ← 0; String.AppendCharAndGrow[@fileName, cmFile[i], Heap.systemZone]; IF cmFile[i] = '> THEN { FOR j: CARDINAL IN [0..fileName.length) DO String.AppendCharAndGrow[@directory, fileName[j], Heap.systemZone] ENDLOOP; fileName.length ← 0}; ENDLOOP}; s: LONG STRING ← NIL; GetString: PROC [c: LONG STRING] = {s ← String.CopyToNewString[c, Heap.systemZone]}; Heap.systemZone.FREE[@host]; Heap.systemZone.FREE[@directory]; Heap.systemZone.FREE[@fileName]; ParseCmFileName[]; CloseCmd[]; Open[]; Retrieve[destination: [string[GetString]] ! UNWIND => Heap.systemZone.FREE[@s]]; OthelloDefs.WriteLine["done"L]; OthelloDefs.SetCommandString[s]; Close[]; END; --~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- MISC Stuff/Commands --~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ userOpened: BOOLEAN ← FALSE; OpenCmd: PROC = { OthelloDefs.MyNameIs[myNameIs: "Open"L, myHelpIs: "STP Open"L]; CloseCmd[]; OthelloDefs.GetName["Open connection to "L, @host]; Open[]; userOpened ← TRUE}; ReOpen: PROC RETURNS [BOOLEAN] = { IF userOpened=FALSE THEN RETURN[FALSE]; Open[]; RETURN[TRUE]}; CloseCmd: PROC = { OthelloDefs.MyNameIs[myNameIs: "Close"L, myHelpIs: "STP Close"L]; userOpened ← FALSE; Close[]}; RemoteList: PROC = { OthelloDefs.MyNameIs[ myNameIs: "List Remote Files"L, myHelpIs: "List Remote Files"L]; IF ~ConnectionOpen[] AND ~ReOpen[] THEN OthelloDefs.AbortingCommand["Please open a connection"L]; OthelloDefs.GetName["Pattern: "L, @fileName]; IF String.Length[fileName] = 0 THEN String.AppendCharAndGrow[@fileName, '*, Heap.systemZone]; ListFiles[]}; --~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- Central commands --~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ commandProcessor: OthelloDefs.CommandProcessor ← [FtpCommands]; FtpCommands: PROC [index: CARDINAL] = { SELECT index FROM 0 => Indirect[]; 1 => GetConnectNamePassword[]; 2 => Directory[]; 3 => GetUserNamePassword[]; 4 => OpenCmd[]; 5 => CloseCmd[]; 6 => FetchBoot[]; 7 => FetchDiagnosticMicrocode[]; 8 => FetchGerm[]; 9 => FetchPilotMicrocode[]; 10 => RemoteList[]; 11 => FetchLispSysout[]; ENDCASE => OthelloDefs.IndexTooLarge}; --~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- STP Stuff/Commands --~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ stp: STP.Handle ← NIL; ConnectionOpen: PROC RETURNS [BOOLEAN] = { RETURN[stp # NIL AND STP.IsOpen[stp]]}; -- all callers close the connection first Open: PROC = { herald: LONG STRING ← NIL; IF stp = NIL THEN stp ← STP.Create[]; DO herald ← STP.Open[stp, host ! STP.Error => SELECT code FROM connectionTimedOut, connectionClosed, noRouteToNetwork, noNameLookupResponse, connectionRejected => { OthelloDefs.WriteLine[error]; CONTINUE}; ENDCASE => OthelloDefs.AbortingCommand[error]]; IF herald # NIL THEN EXIT; Process.Pause[Process.SecondsToTicks[10]]; OthelloDefs.CheckUserAbort[]; ENDLOOP; OthelloDefs.WriteLine[herald]; Heap.systemZone.FREE[@herald]; STP.SetHost[stp, host]}; CloseFetch: PUBLIC PROC = { Close[! OthelloDefs.AbortingCommand => { OthelloDefs.WriteString[reason]; OthelloDefs.WriteLine[reasonOne]; CONTINUE}]}; Close: PROC = { IF ~ConnectionOpen[] THEN RETURN; STP.Close[stp ! STP.Error => OthelloDefs.AbortingCommand[error]]; OthelloDefs.WriteLine["connection closed"L]}; -- could mess with directories. -- who cares ListFiles: PROC = { ListOne: STP.NoteFileProcType = { info: STP.FileInfo = STP.GetFileInfo[stp]; OthelloDefs.WriteString[file]; THROUGH [file.length..80-info.create.length) DO OthelloDefs.WriteChar[' ] ENDLOOP; OthelloDefs.WriteLine[info.create]; --OthelloDefs.WriteChar[' ]; --OthelloDefs.WriteLine[info.author]; --OthelloDefs.WriteChar[' ]; --OthelloDefs.WriteLongNumber[info.size]; --OthelloDefs.WriteLine[" bytes]"L]; OthelloDefs.CheckUserAbort[]; RETURN[yes]}; STP.Login[stp, userName, userPassword]; STP.Connect[stp, connectName, connectPassword]; STP.SetDirectory[stp, directory]; STP.Enumerate[stp, fileName, ListOne ! STP.Error => OthelloDefs.AbortingCommand[error]]}; Destination: TYPE = RECORD [ SELECT type: * FROM pilotFileSystemWrite => [localFile: File.File], string => [stringProc: PROC [LONG STRING]], rawWrite => [ linkProc: PROC [getPage: PROC RETURNS [LONG POINTER]]], ENDCASE]; StartFeedback: SIGNAL = CODE; Retrieve: PROC [destination: Destination] = { rs: Stream.Handle; rsSize: LONG CARDINAL; note: LONG STRING ← NIL; Cleanup: PROC = { rs.delete[rs]; OthelloDefs.SetCursor[pointer]; Heap.systemZone.FREE[@note]}; [rs, rsSize, note] ← GetReadStream[ ! STP.Error => OthelloDefs.AbortingCommand[error]]; IF rs=NIL THEN RETURN; GrabBits[rs, rsSize, destination, note ! STP.Error => OthelloDefs.AbortingCommand[error]; StartFeedback => { OthelloDefs.WriteString["Fetching..."L]; OthelloDefs.SetCursor[ftp]; RESUME}; UNWIND => Cleanup[]]; Cleanup[]}; HasWildCard: PROC [s: LONG STRING] RETURNS [BOOLEAN] = { IF s#NIL THEN FOR i: CARDINAL IN [0..s.length) DO IF s[i] = '* THEN RETURN[TRUE] ENDLOOP; RETURN[FALSE]}; PagesForBytes: PROC [bytes: LONG CARDINAL] RETURNS [LONG CARDINAL] = INLINE { OPEN Environment; RETURN[(bytes + bytesPerPage - 1)/bytesPerPage]}; GetReadStream: PROC RETURNS [rs: Stream.Handle, rsSize: LONG CARDINAL, note: LONG STRING ← NIL] = { wild: BOOLEAN = HasWildCard[directory] OR HasWildCard[fileName]; info: STP.FileInfo; STP.Login[stp, userName, userPassword]; STP.Connect[stp, connectName, connectPassword]; STP.SetDirectory[stp, directory]; rs ← STP.CreateRemoteStream[stp, fileName, read]; rs.options.signalEndOfStream ← TRUE; DO s: LONG STRING ← STP.NextFileName[rs ! UNWIND => rs.delete[rs]]; IF s = NIL THEN {rs.delete[rs]; RETURN[NIL, 0, NIL]}; info ← STP.GetFileInfo[stp ! UNWIND => rs.delete[rs]]; rsSize ← PagesForBytes[info.size]; OthelloDefs.WriteString[s]; Heap.systemZone.FREE[@s]; OthelloDefs.WriteChar['[]; OthelloDefs.WriteString[info.create]; OthelloDefs.WriteString[", pages = "]; OthelloDefs.WriteLongNumber[rsSize]; OthelloDefs.WriteChar[']]; IF ~wild THEN {OthelloDefs.NewLine[]; EXIT}; IF OthelloDefs.Yes[" [Confirm]: "L ! UNWIND => rs.delete[rs]] THEN EXIT; ENDLOOP; note ← Heap.systemZone.NEW[StringBody[60]]; String.AppendChar[note, '[]; String.AppendStringAndGrow[@note, host, Heap.systemZone]; String.AppendCharAndGrow[@note, '], Heap.systemZone]; IF String.Length[info.directory] # 0 THEN { String.AppendCharAndGrow[@note, '<, Heap.systemZone]; String.AppendStringAndGrow[@note, info.directory, Heap.systemZone]; String.AppendCharAndGrow[@note, '>, Heap.systemZone]}; String.AppendStringAndGrow[@note, info.body, Heap.systemZone]; String.AppendStringAndGrow[@note, " ("L, Heap.systemZone]; String.AppendStringAndGrow[@note, info.create, Heap.systemZone]; String.AppendCharAndGrow[@note, '), Heap.systemZone]; RETURN}; bufPages: CARDINAL = 8; GrabBits: PROC [ rs: Stream.Handle, rsSizePages: LONG CARDINAL, destination: 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; GetPage: PROC RETURNS [LONG POINTER] = { got: CARDINAL; index: CARDINAL ← 0; IF first THEN {SIGNAL StartFeedback; first ← FALSE}; WHILE ~done DO [bytesTransferred: got] ← rs.get[ rs, [blockPointer: buffer, startIndex: index, stopIndexPlusOne: Environment.bytesPerPage], rs.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]}; linkProc[GetPage ! UNWIND => [] ← Space.Unmap[buffer]]; WHILE ~done DO [] ← GetPage[! UNWIND => [] ← Space.Unmap[buffer]] ENDLOOP; [] ← Space.Unmap[buffer]}; ENDCASE => ERROR}; SetLeaderPage: 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; --~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- initialization --~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ StringInit: PROC = { OthelloDefs.SetCommandString[String.CopyToNewString["Online RD0"L, Heap.systemZone]]}; OthelloDefs.RegisterCommandProc[@commandProcessor]; StringInit[]; END..... -- November 13, 1979 10:16 AM By Forrest Changed to InitializeFTP once (and never Finalize), to use a bigger chunk in transfering (should use two buffers); increased size of directory string to 60 from 50; Add twiddle cursor hack since people were worried about ftp dying; export Server*loaded and UserMailLoaded to eliminate 3 modules from FTP -- January 12, 1980 4:46 PM By Forrest Changed to used new Othello FTP and support calls to writing Raw Data -- February 14, 1980 7:30 PM By Gobbel Notice if we didn't really retrieve any files -- July 26, 1980 7:04 PM By Forrest Change name to OthelloFTP, export OthelloDefs -- April 14, 1981 1:53 PM By Bruce Added string variant to dest; fixed AR4051 ; Add twiddle cursor hack since people were worried about ftp dying; export Server*loaded and UserMailLoaded to eliminate 3 modules from FTP -- January 12, 1980 4:46 PM By Forrest Changed to used new Othello FTP and support calls to writing Raw Data -- February 14, 1980 7:30 PM By Gobbel Notice if we didn't really retrieve any files -- July 26, 1980 7:04 PM By Forrest Change name to OthelloFTP, export OthelloDefs -- April 14, 1981 1:53 PM By Bruce Added string variant to dest; fixed AR4051 -- 11-Nov-81 16:54:09 By Forrest added missing catch phrase to Retrieve -- 28-Dec-81 10:21:01 By Bruce removed Guest Guest and set signalEndOfStream TRUE -- 13-Jan-82 10:23:20 By Forrest removed extra ! UNWIND => Space.Unmap[space] from Initial ucode fetch. -- 11-Dec-82 15:28:08 By Johnsson removed Storage; added default extensions. -- 13-Apr-83 9:38:39 By Johnsson Klamath conversion.