-- File: EFTPSend.mesa, Last Edit:
-- MAS April 17, 1980 8:49 PM
-- HGM October 16, 1979 7:38 PM

-- Copyright Xerox Corporation 1979, 1980

DIRECTORY
InlineDefs: FROM "InlineDefs" USING [COPY],
CommUtilDefs: FROM "CommUtilDefs" USING [
SetTimeout, MsecToTicks, AllocateHeapString, FreeHeapString],
PupDefs: FROM "PupDefs" USING [
DataWordsPerPupBuffer, GetFreePupBuffer, ReturnFreePupBuffer,
GetPupContentsBytes, SetPupContentsBytes,
PupAddress, PupBuffer, PupSocketID,
PupSocket, PupSocketMake, PupSocketDestroy,
SecondsToTocks, UniqueLocalPupSocketID],
PupTypes: FROM "PupTypes" USING [
maxDataBytesPerGatewayPup, noProcessPupErrorCode],
EFTPDefs: FROM "EFTPDefs" USING [
EFTPAbortCode, eftpExternalSenderAbort, eftpOK, eftpRejected],
DriverDefs: FROM "DriverDefs" USING [Network],
BufferDefs: FROM "BufferDefs";

EFTPSend: MONITOR
IMPORTS InlineDefs, CommUtilDefs, PupDefs
EXPORTS EFTPDefs
SHARES BufferDefs =
BEGIN OPEN PupDefs, EFTPDefs;

EFTPTimeOut: PUBLIC SIGNAL = CODE;
EFTPAlreadySending: PUBLIC ERROR = CODE;
EFTPNotSending: PUBLIC ERROR = CODE;
EFTPTroubleSending: PUBLIC ERROR [e: EFTPAbortCode, s: STRING] = CODE;

send: CONDITION;
senderStop
: BOOLEAN;
senderFork: PROCESS;
sendSocket: PupSocket ← NIL;
sendHim: PupAddress;
sendSeqNumber: CARDINAL;
sendAbortCode: EFTPAbortCode;
sendState: {acked, waitingForAck};
sendErrorText: STRING ← NIL;
retransmissi
ons: CARDINAL;

da
taBytesPerPup: CARDINAL;

EFTPSetSendTimeout: PUBLIC PROCEDURE [ms, tries: CARDINAL] =
BEGIN
retransmissions ← tries;
CommUtilDefs.SetTimeout[@s
end,CommUtilDefs.MsecToTicks[ms]];
END;

EFTPOpenForSending: PUBLIC PROCEDURE [who: PupAddress, waitForAck: BOOLEAN] =
BEGIN
EFTPOpenCheck[who];
BEGIN ENABLE UNWIND => EFTPKillSending[];
IF waitF
orAck THEN EFTPOpenLocked[];
END;
END;

EFTPOpenCheck: ENTRY PROCEDURE [to: PupAddr
ess] =
BE
GIN ENABLE UNWIND => NULL;
me: PupSocketID ← UniqueLocalPupSocketID[];
IF sendSocket#NIL THEN ERROR EFTPAlreadySending;
dataBytesPerPup ← MIN[
2*DataWordsPerPupBuffer[],
PupTypes.maxDataBytesPerGatewayPup];
sendHim ← to;
sendSocket ← PupSocketMake[me,sendHim,SecondsToTocks[1]];
sendSeqNumber ← LAST[CARDINAL];
sendAbortCode ← eftpOK;
sendState ← acked;
senderStop ← FALSE;
send
erFork ← FORK EFTPReplyForSender[];
END;

EFTPOpenLocked: ENTRY PROCEDURE =
BEGIN ENABLE UNWIND => NULL;
b: PupBuffer;
sendState ← waitingForAck;
sendSeqNumber ← sendSeqNumber+1;
UNTIL sendState=acked DO
THROUGH [0..10) UNTIL sendState=acked DO
b ← GetFreePupBuffer[];
b.pupType ← eData;
b.pupID ← [0,sendSeqNumber];
SetPupContentsBytes[b,0];
sendSocket.put[b];
WAIT send; -- 10*1 sec
ENDLOOP;
IF sendState=acked THEN EXIT;
SIGNAL EFTPTimeOut;
ENDLOOP;
IF sendAbortCode#eftpOK THEN
BEGIN
ERROR EFTPTroubleSending[sen
dAbortCode,sendErrorText];
END;
END;

EFTPSendBlock: PUBLIC ENTRY PROCEDURE [p: POINTER, l: CARDINAL] =
BEGIN ENABLE UNWIND => NULL;
n: CARDINAL;
b: PupBuffer;
IF sendSocket=NIL THEN ERROR EFTPNotSending;
UNTIL l=0 DO
sendSeqNumber ← sendSeqNumber+1;
sendState ← waitingForAck;
n ← MIN[l,dataBytesPerPup];
DO
THROUGH [0..retransmissions) UNTIL sendState=acked DO
b ← GetFreePupBuffer[];
b.pupType ← eData;
b.pupID ← [0,sendSeqNumber];
InlineDefs.COPY[from:p, nwords:(n+1)/2, to:@b.pupBytes];
SetPupContentsBytes[b,n];
sendSocket.put[b];
WAIT send; -- 25*1 sec
ENDLOOP;
IF sendAbortCode#eftpOK THEN
BEGIN
ERROR EFTPTroubleSending[sendAbortCode,sendErrorText];
END;
IF sendState=acked THEN EXIT;
SIGNAL EFTPTimeOut;
ENDLOOP;
p ← p+
(n+1)/2;
l ←
l-n;
ENDLOOP;
END;

EFTPAbortSending: PUBLIC PROCEDURE [s: STRING] =
BEGIN
IF sendSocket=NIL THEN RETURN;
EFTPAbortSen
dingLocked[s];
EFTPK
illSending[];
END;

EFTPAbortSendingLocked: ENTRY PROCEDURE [s: STRING] =
BEGIN
i, end: CARDINAL;
b: PupBuffer;
IF sendAbortCode=eftpOK THEN
BEGIN
b ← GetFreePupBuffer[];
b.pupType ← eAbort;
b.pupWords[0] ← eftpExternalSenderAbort;
end ← MIN[s.length+2,dataBytesPerPup];
FOR i ← 2,i+1 UNTIL i=end DO b.pupChars[i] ← s[i-2]; ENDLOOP;
SetPupContentsBytes[b,en
d];
sendSocke
t.put[b];
END;
END;

EFTPFinishSending: PUBLIC PROCEDURE =
BEGIN
EFTPFinishSe
ndingLocked[];
EFTPKi
llSending[];
END;

EFTPFinishSendingLocked: ENTRY PROCEDURE =
BEGIN ENABLE UNWIND => NULL;
b: PupBuffer;
IF sendSocket=NIL THEN ERROR EFTPNotSending;
sendSeqNumber ← sendSeqNumber+1;
sendState ← waitingForAck;
UNTIL sendState=acked DO
THROUGH [0..10) UNTIL sendState=acked DO
b ← GetFreePupBuffer[];
b.pupType ← eEnd;
b.pupID ← [0,sendSeqNumber];
SetPupContentsBytes[b,0];
sendSocket.put[b];
WAIT send; -- 10*1 sec
ENDLOOP;
IF sendAbortCode#eftpOK THEN
ERROR EFTPTroubleSending[sendAbortCode,sendErrorText];
IF sendState=acked THEN EXIT;
SIGNAL EFTPTimeOut;
ENDLOOP;
-- send the second END to keep the receiver from dallying any more
sendSeqNumber ← sendSeqNumber+1;
b ← GetFreePupBuffer[];
b.pupType ← eEnd;
b.pupID ← [0,sendSeqNumber];
SetPupContentsBytes[b,0];
sendSocket.put[b];
IF sendAbortCode#eftpOK THEN
ERROR EFTPTroubleSending[sendAbortCode,send
ErrorText];
END;



-- internal procedures
EFTPReplyForSender: PROCEDURE =
BEGIN
b: PupBuffer;
UNTIL senderStop DO
b ← sendSocket.get[];
IF b#NIL
AND (b.source=sendHim OR CheckSource[b]) THEN EFTPReplyForSenderLocked[b];
IF b#NIL THEN ReturnFreePupBuffer[b];
ENDLOOP;
END;

-- kl
udge to get around net0 troubles
CheckSource: PROCEDURE [b: PupBuffer] RETURNS [BOOLEAN] =
BEGIN
network: DriverDefs.Network ← b.network;
IF sendHim.net=0 THEN sendHim.net ← [network.netNumber];
RETURN[b.source=sendHi
m];
END;


-- extra pr
ocedure to get lock
EFTPReplyForSenderLocked: ENTRY PROCEDURE [b: PupBuffer] =
BEGIN
i, len: CARDINAL;
SELECT b.pupType FROM
eAck => IF b.pupID=[0,sendSeqNumber] THEN
BEGIN sendState ← acked; NOTIFY send; END;
eAbort =>
BEGIN
sendState ← acked;
NOTIFY send;
sendAbortCode ← LOOPHOLE[b.pupWords[0]];
IF sendErrorText=NIL THEN
BEGIN
len ← GetPupContentsBytes[b]-2;
len ← MIN[len,100];
sendErrorText ← CommUtilDefs.AllocateHeapString[len];
FOR i IN [0..len) DO sendErrorText[i] ← b.pupChars[i+2]; ENDLOOP;
sendErrorText.length ← len;
END;
END;
error =>
IF b.errorCode=PupTypes.noProcessPupErrorCode THEN
BEGIN
sendState ← acked;
NOTIFY send;
sendAbortCode ← eftpRejected;
IF sendErrorText=NIL THEN
BEGIN
len ← GetPupContentsBytes[b]-2*(10+1+1);
len ← MIN[len,100];
sendErrorText ← CommUtilDefs.AllocateHeapString[len];
FOR i IN [0..len) DO sendErrorText[i] ← b.errorText[i]; ENDLOOP;
sendErrorText.length ← len;
END;
END;
ENDCASE;
END;


-- Don’t call this if
you have the lock. EFTPReplyForSenderLocked may need it too.
EFTPKillSending: PROCEDURE =
BEGIN
senderStop ← TRUE;
JOIN senderFork;
IF sendSocket#NIL THEN PupSocketDestroy[sendSocket];
sendSocket ← NIL;
IF sendErrorText#NIL THEN
BEGIN
CommUtilDefs.FreeHeapString[sendErrorText];
sendErrorText ← NIL;
END;
END;

-- initialization
EFTPSetSendTimeout[1000,25];
END.