-- Copyright (C) 1981, 1982, 1983 by Xerox Corporation. All rights reserved.
-- file: [Igor]<Emerson>STP>Private>STPsC.mesa - Simple/Stream Transfer Protocol
-- Remote Stream stuff in here
-- Edited by:
-- SXW 16-Jul-81 17:24:59
-- JGS 17-Aug-81 11:32:55
-- PXK 9-Dec-81 18:36:06
-- SXS 11-Sep-81 16:15:34
-- BXM 20-Oct-81 11:24:17
-- RXJ 16-Dec-81 17:39:42
-- AXD 21-Sep-82 13:06:31
-- BJD 9-Sep-83 16:25:30
DIRECTORY
Heap USING [systemZone],
PupStream USING [CloseReason, StreamClosing],
STP USING [Access, CredentialsErrors, Error, FileErrors, Type],
STPOps USING [
CheckConnection, ErrorIfNextNotEOC, ErrorIfNextNotYes, GetCommand,
GenerateProtocolError, GenerateStreamClosingError, GetHereIsAndPList, Handle,
GetSSTNop, LookAtMark, MakeRemoteName, markEOC, markHereIsFile,
markHereIsPList, markNewStore, markNo, markRetrieve, markYes, MyGetMark,
NameToPList, Object, ResetPList, PList, PutCommand, PutPList, RemoteObject,
RemoteStream, SelectError, SetByteSize, SetCreateTime, SetFileType, SmashClosed,
UserStateToPList, SetSSTNop, WaitAttentionNop],
Stream USING [
Block, Byte, CompletionCode, defaultObject, DeleteProcedure, EndOfStream,
GetBlock, GetProcedure, GetSSTProcedure, GetTimeoutProcedure, Handle,
InputOptions, InvalidOperation, Object, PutBlock, PutByte, PutProcedure, SendNow,
SendNowProcedure, SendAttentionProcedure, SetSST, SetTimeoutProcedure, SSTChange,
SubSequenceType, Word],
String USING [Replace],
Time USING [Packed];
STPsC: PROGRAM
IMPORTS Heap, PupStream, String, STP, STPOps, Stream
EXPORTS STP, STPOps =
BEGIN OPEN PupStream, STPOps;
z: UNCOUNTED ZONE = Heap.systemZone;
Object: PUBLIC TYPE = STPOps.Object;
-- Procedures implementing STP interface
CreateRemoteStream: PUBLIC PROCEDURE [
stp: STPOps.Handle, file: LONG STRING, access: STP.Access, fileType: STP.Type,
options: Stream.InputOptions, creation: Time.Packed]
RETURNS [stream: Stream.Handle] =
BEGIN
rs: STPOps.RemoteStream;
CheckConnection[stp];
IF stp.remoteStream # NIL THEN ERROR STP.Error[stp, accessError, "Remote Stream Exists"L];
rs ← z.NEW[STPOps.RemoteObject];
rs↑ ← STPOps.RemoteObject[
access: access, state: initial, stp: stp,
stream: [
options: options, get: GetBlock, put: PutBlock, getByte: GetByte,
putByte: PutByte, getWord: GetWord, putWord: PutWord,
getSST: STPOps.GetSSTNop, setSST: STPOps.SetSSTNop,
sendAttention: SendAttentionAbort,
waitAttention: STPOps.WaitAttentionNop, sendNow: SendNow,
delete: DeleteRemoteStream, getPosition: Stream.defaultObject.getPosition,
setPosition: Stream.defaultObject.setPosition,
getTimeout: GetTimeout, setTimeout: SetTimeout,
clientData: NIL]];
ResetPList[stp.plist];
UserStateToPList[stp];
NameToPList[stp.plist, file, alto];
SetFileType[stp, fileType];
IF access = write THEN {
STPOps.SetCreateTime[stp, creation];
SetByteSize[stp, fileType]};
IF fileType = text THEN String.Replace[@stp.plist[eolConversion], "CR"L, z];
stp.remoteStream ← rs;
RETURN[@rs.stream]
END;
NextFileName: PUBLIC PROCEDURE [remoteStream: Stream.Handle]
RETURNS [file: LONG STRING] =
BEGIN
rs: RemoteStream ← ConvertHandle[remoteStream];
reason: PupStream.CloseReason;
IF rs.access # read THEN ERROR STP.Error[rs.stp, accessError, NIL];
BEGIN ENABLE PupStream.StreamClosing => {reason ← why; GOTO streamClosing};
SELECT rs.state FROM
initial => RequestRetrieve[rs];
confirm =>
BEGIN
PutCommand[rs.stp, markNo, 0C, "No Thanks"L];
IF LookAtMark[rs.stp] = markEOC THEN {[] ← MyGetMark[rs.stp]; rs.state ← end}
ELSE GetHereIsAndPList[rs.stp];
END;
data =>
BEGIN
ErrorIfNextNotYes[rs.stp];
IF LookAtMark[rs.stp] = markEOC THEN {[] ← MyGetMark[rs.stp]; rs.state ← end}
ELSE {GetHereIsAndPList[rs.stp]; rs.state ← confirm};
END;
complete =>
IF LookAtMark[rs.stp] = markEOC THEN {[] ← MyGetMark[rs.stp]; rs.state ← end}
ELSE {GetHereIsAndPList[rs.stp]; rs.state ← confirm};
end => NULL;
ENDCASE;
EXITS
streamClosing => GenerateStreamClosingError[rs.stp, reason];
END;
RETURN[IF rs.state = end THEN NIL ELSE MakeRemoteName[rs.stp.plist, alto]]
END;
-- Procedures for Remote streams
conversionFudge: LONG CARDINAL = LOOPHOLE[@LOOPHOLE[LONG[0], RemoteStream].stream];
ConvertHandle: PROCEDURE [h: Stream.Handle] RETURNS [rs: RemoteStream] = INLINE
BEGIN RETURN[LOOPHOLE[h - conversionFudge]] END;
DeleteRemoteStream: PUBLIC Stream.DeleteProcedure =
BEGIN
rs: RemoteStream ← ConvertHandle[sH];
stp: STPOps.Handle = rs.stp;
{ENABLE UNWIND => {stp.remoteStream ← NIL; z.FREE[@rs]};
IF rs.access = read THEN {IF rs.state # end THEN SmashClosed[stp]} -- in mid-transfer
ELSE SELECT rs.state FROM
initial => NULL;
data => {
PutCommand[rs.stp, markYes, 0C, "Transfer Completed"L
! PupStream.StreamClosing => CONTINUE]; -- OK, if closed in advance
ErrorIfNextNotYes[rs.stp];
ErrorIfNextNotEOC[rs.stp]};
confirm, complete, end => NULL; -- worry about later
ENDCASE;
stp.remoteStream ← NIL; z.FREE[@rs]};
END;
SendAttentionAbort: PUBLIC Stream.SendAttentionProcedure =
BEGIN
-- To be called by client when a store is aborted, followed by DeleteRemoteStream;
rs: RemoteStream ← ConvertHandle[sH];
stp: STPOps.Handle = rs.stp;
IF rs.access = write THEN {
ENABLE PupStream.StreamClosing => GOTO return; -- OK, if closed in advance
SELECT rs.state FROM
data => {
string: LONG STRING ← NIL;
mark: Stream.SubSequenceType;
code: CHAR ← 0C;
PutCommand[rs.stp, markNo, 106C, "Transfer Aborted"L];
[mark, code] ← STPOps.GetCommand[stp, @string];
z.FREE[@string];
IF mark # markNo THEN
STPOps.GenerateProtocolError[stp, badMark, mark, code];
ErrorIfNextNotEOC[rs.stp]};
initial, confirm, complete, end => NULL;
ENDCASE;
rs.state ← end
EXITS
return => NULL}
END;
GetByte: PUBLIC PROCEDURE [sH: Stream.Handle] RETURNS [byte: Stream.Byte] =
BEGIN
options: Stream.InputOptions = [FALSE, FALSE, FALSE, TRUE, TRUE, TRUE];
rs: RemoteStream ← ConvertHandle[sH];
bytesTransferred: CARDINAL;
charArray: PACKED ARRAY [0..1] OF Stream.Byte;
block: Stream.Block ← [@charArray, 0, 1];
[bytesTransferred,,] ← GetBlock[sH, block, options];
RETURN[IF bytesTransferred # 0 THEN charArray[0] ELSE 0]
END;
GetWord: PUBLIC PROCEDURE [sH: Stream.Handle] RETURNS [word: Stream.Word] =
BEGIN
t: UNSPECIFIED = CARDINAL[GetByte[sH]]*256;
RETURN[t + GetByte[sH]];
END;
GetBlock: PUBLIC Stream.GetProcedure =
BEGIN
rs: RemoteStream ← ConvertHandle[sH];
reason: PupStream.CloseReason;
BEGIN
ENABLE PupStream.StreamClosing => {reason ← why; GOTO streamClosing};
IF rs.state # data THEN
BEGIN
StartRemote[rs, read];
IF rs.state = end THEN GOTO done;
END;
[bytesTransferred, why, sst] ← Stream.GetBlock[ rs.stp.byteStream, block !
Stream.EndOfStream => {bytesTransferred ← nextIndex; GOTO done};
Stream.SSTChange => {
rs.stp.mark ← sst;
rs.stp.gotMark ← TRUE;
bytesTransferred ← nextIndex - block.startIndex;
CONTINUE}];
IF rs.stp.gotMark THEN {SetupForNextOrEnd[rs.stp]; GOTO done};
EXITS
streamClosing => {rs.state ← end; GenerateStreamClosingError[rs.stp, reason]};
done =>
BEGIN
why ← endOfStream;
IF options.signalEndOfStream THEN
Stream.EndOfStream[nextIndex: bytesTransferred];
END;
END;
END;
PutByte: PUBLIC PROCEDURE [sH: Stream.Handle, byte: Stream.Byte] =
BEGIN
rs: RemoteStream ← ConvertHandle[sH];
reason: PupStream.CloseReason;
BEGIN
ENABLE PupStream.StreamClosing => {reason ← why; GOTO streamClosing};
IF rs.state # data THEN StartRemote[rs, write];
Stream.PutByte[rs.stp.byteStream, byte];
EXITS streamClosing => {rs.state ← end; GenerateStreamClosingError[rs.stp, reason]};
END;
END;
PutWord: PUBLIC PROCEDURE [sH: Stream.Handle, word: Stream.Word] =
BEGIN
PutByte[sH, CARDINAL[word]/256];
PutByte[sH, CARDINAL[word] MOD 256];
END;
PutBlock: PUBLIC Stream.PutProcedure =
BEGIN
rs: RemoteStream ← ConvertHandle[sH];
reason: PupStream.CloseReason;
BEGIN
ENABLE PupStream.StreamClosing => {reason ← why; GOTO streamClosing};
IF rs.state # data THEN StartRemote[rs, write];
Stream.PutBlock[rs.stp.byteStream, block, FALSE];
EXITS streamClosing => {rs.state ← end; GenerateStreamClosingError[rs.stp, reason]};
END;
END;
SendNow: PUBLIC Stream.SendNowProcedure =
BEGIN
rs: RemoteStream ← ConvertHandle[sH];
reason: PupStream.CloseReason;
BEGIN
ENABLE PupStream.StreamClosing => {reason ← why; GOTO streamClosing};
IF rs.state # data THEN StartRemote[rs, write];
Stream.SendNow[rs.stp.byteStream, TRUE];
EXITS streamClosing => {rs.state ← end; GenerateStreamClosingError[rs.stp, reason]};
END;
END;
GetTimeout: PUBLIC Stream.GetTimeoutProcedure =
BEGIN
ERROR Stream.InvalidOperation
END;
SetTimeout: PUBLIC Stream.SetTimeoutProcedure =
BEGIN
ERROR Stream.InvalidOperation
END;
-- Utilities
SetupForNextOrEnd: PROCEDURE [stp: STPOps.Handle] =
BEGIN
ErrorIfNextNotYes[stp];
SELECT LookAtMark[stp] FROM
markEOC => {[] ← MyGetMark[stp]; stp.remoteStream.state ← end};
markHereIsPList => stp.remoteStream.state ← complete;
ENDCASE => GenerateProtocolError[stp, badMark, MyGetMark[stp]];
END;
RequestRetrieve: PROCEDURE [rs: RemoteStream] =
BEGIN
PutPList[rs.stp, markRetrieve];
GetHereIsAndPList[rs.stp ! STP.Error =>
SELECT code FROM
IN STP.CredentialsErrors, IN STP.FileErrors => rs.state ← end;
ENDCASE];
rs.state ← confirm;
END;
StartRemote: PROCEDURE [rs: RemoteStream, access: STP.Access] =
BEGIN
reason: PupStream.CloseReason;
IF rs.access # access THEN
ERROR STP.Error[rs.stp, accessError, "Remote stream access Error"L];
BEGIN
ENABLE PupStream.StreamClosing => {reason ← why; GOTO streamClosing};
SELECT access FROM
read =>
BEGIN
IF rs.state = initial THEN RequestRetrieve[rs];
IF rs.state = complete THEN {
IF LookAtMark[rs.stp] = markEOC THEN {rs.state ← end; RETURN}
ELSE GetHereIsAndPList[rs.stp]; rs.state ← confirm};
IF rs.state = confirm THEN
BEGIN
mark: Stream.SubSequenceType;
PutCommand[rs.stp, markYes, 0C, "Yes, please"L];
SELECT (mark ← MyGetMark[rs.stp]) FROM
markHereIsFile => NULL;
markNo => {rs.state ← complete; SelectError[rs.stp, "He says NO"L, mark]};
ENDCASE => SelectError[rs.stp, "HereIsFile Expected"L, mark];
END;
END;
write =>
BEGIN
PutPList[rs.stp, markNewStore];
GetHereIsAndPList[rs.stp];
Stream.SetSST[rs.stp.byteStream, markHereIsFile];
END;
ENDCASE => ERROR;
rs.state ← data;
EXITS streamClosing => GenerateStreamClosingError[rs.stp, reason];
END;
END;
END. of STPsC