-- Copyright (C) 1981, 1982, 1983 by Xerox Corporation. All rights reserved. -- file: [Igor]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