//CHATCOMINIT.BCPL - Bob Sproull - Pup User Telnet - BCPL
// Copyright Xerox Corporation 1979, 1980
// modified: February 24, 1980 2:44 PM (E. Taft)
//Initialization of communication free storage and of communications.
get "Chat.d"
get "ChatBSP.d"
//outgoing procedure
external [
ChatComInit
ChatComOpen
]
//incoming procedures
external [
//CHAT
GetString
GetKey
CheckShiftSwat
DirectKeys
ChatHandlePup
InitAudio
//BSP
InitPupLevel1
OpenLevel1Socket
CloseLevel1Socket
SetAllocation
OpenRTPSocket
GetPartner
CreateBSPStream
GetPBI
AppendStringToPup
CompletePup
ReleasePBI
//ALTOTIME
SetTimer
TimerHasExpired
//OS
Allocate
InitializeZone
EraseBits
GetBitPos
MoveBlock
SetBlock
Ws
Wns
//CONTEXT
InitializeContext
CallContextList
Block
//QUEUE
Enqueue
Unqueue
Dequeue
//ETHERBOOT
EtherBoot
]
//incoming statics
external [
// CHAT
Parm //Chat paramters
ComZone //Chat free storage pool for communications
ComZoneLeft
TTYSoc; TTYStr //Socket, stream for TTY connection
DISSoc; DISStr //Socket, stream for DIS connectoin
ctxQ //Main context Q
makeBootFile
// SYSTEM
UserName
UserPassword
lvAbortFlag
dsp
]
static InitDone
let ChatComInit(zoneBottom, zoneLength) be
[CI
ComZone = InitializeZone(zoneBottom, zoneLength)
ctxQ=Allocate(ComZone, 2)
ctxQ!0=0
// Do this first so it gets the highest-priority interrupt channel:
InitAudio(ComZone, 30)
InitPupLevel1(ComZone, ctxQ, nTotalPBI, nBytesPerPup)
TTYSoc=Allocate(ComZone, lenBSPSoc)
DISSoc=Allocate(ComZone, lenBSPSoc)
//Now start communicating:
@lvAbortFlag = @lvAbortFlag +1
ChatComOpen()
Allocate(ComZone, #77777, lv ComZoneLeft)
]CI
and ChatComOpen() be
[
InitDone=false
let initCtx=vec 200
Enqueue(ctxQ, InitializeContext(initCtx, 200, ChatInitAux))
until InitDone do
[
CallContextList(ctxQ!0)
CheckShiftSwat()
]
Unqueue(ctxQ, initCtx)
]
and ChatInitAux() be
[CIX
// There are three numbers that determine allocation:
// nDisplayPBI -- those allocated to display connection
// nTtyPBIDis -- those allocated to tty connection if display on
// nTtyPBI -- those allocated to tty connection if no display
// Actually allocate one more to be under the receiver.
let disp=Parm>>PARM.DisplayProtocol
let ttyAllocn=(disp)? nTtyPBIDis,nTtyPBI
DISStr=0
DirectKeys(dsp)
test Parm>>PARM.Server then
[Server
Ws("*NListening for connection")
OpenLevel1Socket(TTYSoc, table [ 0; 0; socketTelnet ])
SetAllocation(TTYSoc, ttyAllocn, ttyAllocn-2, 4)
until OpenRTPSocket(TTYSoc, ctxQ, modeListenAndWait, 0,0,0, ComZone)
do loop
]Server
or
[User
//Parse the user's description of the intended partner, using the
// name lookup facilities if necessary.
let frnPort=vec lenPort
[
// Prompt user if coming from a boot file:
let cs=lv Parm>>PARM.ConnectString
if @cs eq 0 then GetPromptString("*nConnect to: ",cs,true," ")
if GetPartner(cs, dsp, frnPort, 0, 1) & frnPort>>Port.host ne 0 break
@cs = 0
] repeat
//Note: all hosts that respond to the "where is user" request to the
// Miscellaneous server are assumed to be AutoLogin servers.
// This includes Maxc and IFSs running version 1.18 or newer.
Login() // Would like not to do this if server doesn't
// require login, but have to send user name to find out!
let jobNumber=nil
let how=SetMaxcStrategy(frnPort, Parm>>PARM.MAXCForce, lv jobNumber)
OpenLevel1Socket(TTYSoc, 0, frnPort)
SetAllocation(TTYSoc, ttyAllocn, ttyAllocn-2, 4)
if OpenRTPSocket(TTYSoc, ctxQ, 0,0, ChatHandlePup,0, ComZone) then
[ if how gr 1 then InitialString(how, jobNumber); break ]
Ws("*nFailed to open connection.")
CloseLevel1Socket(TTYSoc)
@(lv Parm>>PARM.ConnectString) = 0
]User repeat
TTYStr=CreateBSPStream(TTYSoc)
Ws("*nConnected to: ")
PrintPort(lv TTYSoc>>PupSoc.frnPort)
InitDone=true
Block() repeat // kludge
]CIX
//Login()
// If necessary, prompt user for name,password,acct and save away
// in core.
and Login() be
[LI
if Parm>>PARM.MAXCForce eq 1 then return
if UserPassword>>STR.length gr 0 & UserPassword>>STR.length ls 20 then return
GetPromptString("*nName: ", UserName, true, " ")
GetPromptString("Password: ", UserPassword, false," (pst)")
]LI
and GetPromptString(msg,addr,echo,after) be
[
Ws(msg) //Prompt
let p = GetBitPos(dsp)
if echo then Ws(addr) //was there
let c=GetKey()
if c ne $*s & c ne $*n then
[
if echo then EraseBits(dsp, p-GetBitPos(dsp))
GetString(addr, echo, true, c)
]
Ws(after)
]
//SetMaxcStrategy()
// Sends inquiries to MAXC to discover where the user is, if at all.
// MAXCForce contains a code for what the user wants:
// 0=CHAT decides; 1=no prologue; 2=login; 3=attach or login
// Returns:
// 1 No prologue
// 2 Use a login sequence
// 3 Use an attach sequence, jobnumber= MaxcJobNumber
// 4 Attach, but ambiguous number of jobs
and SetMaxcStrategy(frnport, MAXCForce, lvJobNumber) = valof
[SMS
@lvJobNumber=0
//If user wants a forced login or if he wants nothing done, return:
if MAXCForce eq 1 % MAXCForce eq 2 then resultis MAXCForce
let soc=vec lenPupSoc
let infoPort=vec lenPort
MoveBlock(infoPort, frnport, lenPort)
infoPort>>Port.socket↑1=0
infoPort>>Port.socket↑2=socketMiscServices
OpenLevel1Socket(soc, 0, infoPort)
let answer=1 //Assume no prologue
for i=1 to 5 do //Try five times....
[SendWherePup
let p=GetPBI(soc)
AppendStringToPup(p, 1, UserName)
p>>PBI.pup.type = typeWhereUserRequest
CompletePup(p)
let wait=nil; SetTimer(lv wait, 200) // 2 sec
Block() repeatuntil TimerHasExpired(lv wait) %
soc>>PupSoc.iQ.head ne 0
p=Dequeue(lv soc>>PupSoc.iQ)
if p eq 0 then loop
let pup=lv p>>PBI.pup
switchon p>>PBI.pup.type into
[
case typeWhereUserReply:
[
answer = 2 // Assume login
let nb=pup>>Pup.length-22 //Number of data bytes
for i=1 to nb by 2 do
[
let job=pup>>Pup.bytes↑i
let term=pup>>Pup.bytes↑(i+1)
if term eq #377 then //Found detached
[
answer=3 //Assume an attach command
test @lvJobNumber then answer=4
or @lvJobNumber=job
]
]
ReleasePBI(p)
break
]
case typeError:
[
if pup!10 eq 2 then [ ReleasePBI(p); break ] // no such port
] // fall thru
default:
[
ReleasePBI(p)
]
] //Switchon
]SendWherePup
CloseLevel1Socket(soc)
resultis answer
]SMS
//InitialString(MAXC, jobNumber)
// Computes an initial string to send to the connection, governed
// by "MAXC" (see comments in SetMaxcStrategy, above)
and InitialString(MAXC, jobNumber) be
[
let AppendC(c) be
[
let str=(lv Parm>>PARM.InitialString)
let i=str>>STR.length+1
str>>STR.char↑i=c
str>>STR.length=i
]
and PlugString(s) be
for i=1 to s>>STR.length do AppendC(s>>STR.char↑i)
if MAXC le 1 then return
if MAXC eq 4 then
[
PlugString("Where "); PlugString(UserName); PlugString("*N")
]
PlugString(((MAXC eq 2)? "Login ","Attach "))
PlugString(UserName) //Plug in his name
PlugString(" ")
PlugString(UserPassword)
PlugString(" ")
test MAXC eq 2 then [ PlugString("1*N") ] or
if MAXC eq 3 then
[
let div=10
let going=false
for i=1 to 2 do
[
let digit=jobNumber/div
if going ne 0 % digit ne 0 then
[
going=true
AppendC(digit+$0)
]
jobNumber=jobNumber-div*digit
div=div/10
]
AppendC($*N)
]
]
//Print out a network address
and PrintPort(p) be
[
Ws("["); Wns(dsp, p>>Port.net, 1, 8)
Ws("#"); Wns(dsp, p>>Port.host, 1, 8)
Ws("#"); Wns(dsp, p>>Port.socket↑1, 1, 8)
Ws("|"); Wns(dsp, p>>Port.socket↑2, 1, 8)
Ws("]")
]