// TridentEFTP.bcpl // last modified on October 24, 1980 11:18 AM by Butterfield // - EFTP, add T global switch to go to Trident drive 0 - 10/23 // - EFTPCli, don't use GP to open the files - 10/23 // - derived from: // EFTP.bcpl -- top level routine and user interface for // the EFTP subsystem // June 17, 1976 3:44 PM ap,bak,jfs // last modified on June 3, 1979 11:52 AM by Taft // Copyright Xerox Corporation 1979 // - 10/23/80 get "PupEFTP.decl" //that will also get pup-related stuff get "Streams.d" external [ //outgoing procedures Msg //incoming procedures InitEFTPPackage; EFTPSendFile; EFTPReceiveFile FixedLeft; GetFixed; InitializeZone; Allocate InitializeContext; CallContextList; Block InitPupLevel1; InitializeTimer; Enqueue; GetPartner OpenFile; Closes; Endofs; Gets; Puts Ws; Wl; Wo; Wns; CallSwat SetupReadParam; ReadParam; EvalParam LoadRam; SysErr; TFSInit; TFSClose; TFSSilentBoot; //outgoing statics EFTPZone; EFTPCtxq EFTPFrnPort; EFTPLclPort; EFTPSocket //incoming statics keys;dsp DiskRamImage; sysDisk; ] compileif nova then [ external [ InitNovaAlto ] ] static [ eftpDisk; EFTPZone;EFTPCtxq EFTPFrnPort;EFTPLclPort;EFTPSocket tempfile; tempstring DebugSwitch = false DebugConfirmSwitch = false cliBody; cliSwitches ] manifest [ cliStackSize = 3000 kbdStackSize = 3000 noHintFp = 0; // so readers won't have to figure out what the argument is ] //----------------------------------------------------------------- let EFTP() be //----------------------------------------------------------------- [ let versionText = "EFTP of October 23, 1980" //For the time being, allocate everything here let frnPort = vec lenPort; EFTPFrnPort = frnPort let lclPort = vec lenPort; EFTPLclPort = lclPort let socket = vec lenEFTPSoc; EFTPSocket = socket let tempString = vec 128; tempstring = tempString compileif alto then [ Msg("Initialize zones") ] let zoneSize = FixedLeft()-1500 //Set up a zone, for later use if zoneSize ls 0 then zoneSize = #77777 //biggest Alloc can handle EFTPZone = GetFixed(zoneSize) InitializeZone(EFTPZone, zoneSize) compileif nova then [ InitNovaAlto(EFTPZone) ] Wl(versionText) // Ws("["); Wns(dsp, zoneSize, 0, 10); Ws(" - (") // Wns(dsp, zoneSize, 0, 10); Ws("+1) = ") // Wns(dsp, FixedLeft(), 0, 10) // Wl("]") // Check for any global flags... We will set it up here, others // can read more of it later let cli = false let body = vec 256; cliBody = body let switches = vec 128; cliSwitches = switches // Read from the default, file COM.CM SetupReadParam(cliBody, cliSwitches, 0, 0) // Set global switch defaults eftpDisk = sysDisk; // Scan the global switches for i = 1 to cliSwitches!0 switchon cliSwitches!i into [GlobalSwitchBlock case $d: case $D: [ DebugSwitch = true endcase ] case $c: case $C: [ DebugConfirmSwitch = true DebugSwitch = true endcase ] case $s: case $S: [ CallSwat("Scanning global switches") endcase ] case $t: case $T: [ if LoadRam(DiskRamImage, true) ls 0 then [ Wl("Cannot load ram"); finish; ] eftpDisk = TFSInit(EFTPZone, true); if eftpDisk eq 0 then [ Wl("Cannot initialize Trident"); finish; ] endcase ] default: [ Ws("Unknown global switch: ") Puts(dsp, cliSwitches!i) Wl("") ] ]GlobalSwitchBlock // See if there is anything else left in COM.CM, get unpacked string ReadParam(0, 0, 0, 0, true) //puts file name into cliBody if cliBody!0 ne 0 then cli = true // Major task here is to initialize storage and context list, // start level1 running, decide what other processes (contexts) // to enable, and then let them all run Msg("Initialize contexts") EFTPCtxq = Allocate(EFTPZone, 2) //Main context queue EFTPCtxq!0 = 0 Msg("Initialize pup stuff") //Initializations InitPupLevel1(EFTPZone, EFTPCtxq, 10) //up to 10 buffers InitEFTPPackage() // Decide if we should schedule the interactive input handler, // or the command line interpreter test cli ifso InitEFTPCli() ifnot InitEFTPKbd() Msg("Start context mechanism") //Now pass off control, and never return here CallContextList(EFTPCtxq!0) repeat //loop forever ] // These next two routines run in the context (and stack) of // EFTP(), but merely establish and enqueue a context in which // the real routines can run. Actual routines can live elsewhere. //----------------------------------------------------------------- and InitEFTPCli() be //----------------------------------------------------------------- Enqueue(EFTPCtxq, InitializeContext(Allocate(EFTPZone, cliStackSize), cliStackSize, EFTPCli)) //----------------------------------------------------------------- and InitEFTPKbd() be //----------------------------------------------------------------- Enqueue(EFTPCtxq, InitializeContext(Allocate(EFTPZone, kbdStackSize), kbdStackSize, EFTPKbd)) //----------------------------------------------------------------- and Msg(str) be //----------------------------------------------------------------- [ unless DebugSwitch return Ws(str) test DebugConfirmSwitch ifso [ Wl("[OK??]"); Gets(keys) ] ifnot Wl("") ] // One of these routines will have been placed on the context list, // and will therefore be running //----------------------------------------------------------------- and EFTPCli() be //----------------------------------------------------------------- [ // Pick off the relevent information from the file COM.CM // format is: eftp to/from // If we get here, the file name is already in cliBody ReadParam(0, 0, tempstring) //get the direction switchon tempstring!1 into [CliCmd case $t: case $T: [ //tempfile = // EvalParam(cliBody, "I", "File does not exist, try again:") let body = cliBody; [ // beginning of a repeat EvalParam(body, "P", "File does not exist, try again: ", tempstring); tempfile = OpenFile(tempstring, ksTypeReadOnly, charItem, verLatest, noHintFp, SysErr, EFTPZone, nil, eftpDisk); let nullBody = vec 256; nullBody!0 = 0; body = nullBody; ] repeatuntil tempfile unless GetPartner(ReadParam("P"), dsp, EFTPFrnPort, 0, socketEFTPReceive) do [ CliError() endcase ] ReportResult(EFTPSendFile(tempfile, EFTPFrnPort)) Closes(tempfile) endcase ] case $f: case $F: [ //tempfile= EvalParam(cliBody, "O") let body = cliBody; [ // beginning of a repeat EvalParam(body, "P", "Can't open file, try again: ", tempstring); tempfile = OpenFile(tempstring, ksTypeWriteOnly, charItem, verNew, noHintFp, SysErr, EFTPZone, nil, eftpDisk); let nullBody = vec 256; nullBody!0 = 0; body = nullBody; ] repeatuntil tempfile unless GetPartner(ReadParam("P"), dsp, EFTPFrnPort) do [ CliError() endcase ] ReportResult(EFTPReceiveFile(tempfile, EFTPFrnPort)) Closes(tempfile) endcase ] default: [ CliError() endcase ] ]CliCmd if eftpDisk ne sysDisk then [ TFSClose(eftpDisk); TFSSilentBoot(); ] finish // we do not hang around..... ] //----------------------------------------------------------------- and CliError() be //----------------------------------------------------------------- [ Wl("Take care, the syntax for command line should be:") Ws("eftp to/from ") ] //----------------------------------------------------------------- and EFTPKbd() be //----------------------------------------------------------------- [ [CmdLoop let abortCmd = false Msg("Get command from kbd") Ws("->") let cmd = GetKeys() switchon cmd into [CommandBlock case $S: case $s: [ Ws("Send file -- name or number of remote host: ") [ unless GetString(tempstring) then [ abortCmd = true break ] if tempstring>>String.length eq 0 then loop if GetPartner(tempstring, dsp, EFTPFrnPort, 0, socketEFTPReceive) then break Ws("...try again: ") ] repeat if abortCmd then loop Ws("local file name: ") [ unless GetString(tempstring) then [ abortCmd = true break ] tempfile = OpenFile(tempstring, ksTypeReadOnly, charItem, verLatest, noHintFp, SysErr, EFTPZone, nil, eftpDisk); if tempfile ne 0 then break Ws("File "); Ws(tempstring) Ws(" does not exist, try again: ") ] repeat if abortCmd then loop ReportResult(EFTPSendFile(tempfile, EFTPFrnPort)) Closes(tempfile) endcase ] case $R: case $r: [ Ws("Receive a file -- name or number of remote host: ") [ unless GetString(tempstring) then [ abortCmd = true break ] if tempstring>>String.length eq 0 then loop if GetPartner(tempstring, dsp, EFTPFrnPort) then break Ws("...try again: ") ] repeat if abortCmd then loop [ Ws("local file name: ") unless GetString(tempstring) then [ abortCmd = true break ] tempfile = OpenFile(tempstring, ksTypeReadOnly, charItem, verLatest, noHintFp, SysErr, EFTPZone, nil, eftpDisk); if tempfile ne 0 then [ //File opened, it already exists Closes(tempfile) unless Confirm("File exists, OK to Overwrite?") loop ] //Now open a new file, for writing tempfile = OpenFile(tempstring, ksTypeWriteOnly, charItem, verNew, noHintFp, SysErr, EFTPZone, nil, eftpDisk); break ] repeat if abortCmd then loop ReportResult(EFTPReceiveFile(tempfile, EFTPFrnPort)) Closes(tempfile) endcase ] case #15: //CR is a no-op [ Wl("") endcase ] case $Q: case $q: [ Ws("Quit...") if Confirm("Confirm?") then [ if eftpDisk ne sysDisk then [ TFSClose(eftpDisk); TFSSilentBoot(); ] finish; ] endcase //program terminates here!!!! ] case $?: [ Wl("Send, Receive, Quit...") endcase ] default: [ Puts(dsp,cmd) Wl(" ??? Send, Receive, Quit...") endcase ] ]CommandBlock Block() ]CmdLoop repeat //will run until a quit ] //----------------------------------------------------------------- and ReportResult(boolean) be //----------------------------------------------------------------- [ test boolean ifso Wl("...all done") ifnot Wl("...file transfer failed") ] //----------------------------------------------------------------- and GetKeys() = valof //----------------------------------------------------------------- [ while Endofs(keys) do Block() resultis Gets(keys) ] //----------------------------------------------------------------- and GetString(str) = valof //----------------------------------------------------------------- [ // Caller must provide the place to put the string // Returns false if DEL is hit. Uses default streams dsp and keys let deletingFlag, char = false, 0 str>>String.length = 0 [charLoop if str>>String.length eq 255 then resultis true char = GetKeys() //this will block for a bit switchon char into [CharBlock case #15: //CR [ Wl("") resultis true ] case #21: //^Q, reset the line [ Wl(" xxx") deletingFlag=false char=0 str>>String.length=0 endcase ] case #10: case #1: //BS or ^A [ if str>>String.length eq 0 then endcase unless deletingFlag then [ deletingFlag=true Ws("[") ] Puts(dsp,str>>String.char^(str>>String.length)) str>>String.length = str>>String.length - 1 endcase ] case #22: //^R, retype the line [ Wl(""); Ws(str); deletingFlag=false endcase ] case #177: //DEL,delete the line and return false [ Wl(" XXX") resultis false ] default: [ if deletingFlag then [ deletingFlag=false Ws("]") ] str>>String.length = str>>String.length + 1 str>>String.char^(str>>String.length) = char Puts(dsp, char) ] ]CharBlock ]charLoop repeat ] //----------------------------------------------------------------- and Confirm(confirmString) = valof //----------------------------------------------------------------- [ // A general guy, for confirming some action Puts(dsp,$[); Ws(confirmString); Puts(dsp,$]) switchon GetKeys() into [ case $y: case $Y: case $*N: [ Wl(" Yes."); resultis true ] case $n: case $N: case $*177: [ Wl(" No."); resultis false ] default: Wl(" ?") ] repeat ]