<> <> <> <> <> <> <> <> <> <> <> <> <> <> DIRECTORY BasicTime USING [GMT], IO USING [CreateStream, CreateStreamProcs, EndOf, PutChar, STREAM, StreamProcs, UnsafeBlock, UnsafeGetBlock, UnsafePutBlock], PupStream USING [CloseReason, ConsumeMark, SendMark, StreamClosing], Rope USING [ROPE], STP USING [Access, CredentialsErrors, Error, FileErrors, Type], STPOps USING [CheckConnection, ErrorIfNextNotEOC, ErrorIfNextNotYes, GenerateProtocolError, GenerateStreamClosingError, GetHereIsAndPList, Handle, LookAtMark, MakeRemoteName, markEOC, markHereIsFile, markHereIsPList, markNewStore, markNo, markRetrieve, markYes, MyGetMark, NameToPList, Object, ResetPList, PList, PutCommand, PutPList, RemoteObject, RemoteStream, SelectError, SetByteSize, SetCreateTime, SetFileType, SmashClosed, UserStateToPList]; STPsC: CEDAR PROGRAM IMPORTS IO, PupStream, STP, STPOps EXPORTS STP = { OPEN PupStream, STPOps; Object: PUBLIC TYPE = STPOps.Object; <> remoteStreamProcs: REF IO.StreamProcs = IO.CreateStreamProcs[ variety: $inputOutput, class: $FTP, endOf: EndOf, unsafeGetBlock: UnsafeGetBlock, putChar: PutChar, unsafePutBlock: UnsafePutBlock, close: DeleteRemoteStream]; CreateRemoteStream: PUBLIC PROC [stp: STPOps.Handle, file: Rope.ROPE, access: STP.Access, fileType: STP.Type, creation: BasicTime.GMT] RETURNS [stream: IO.STREAM] = { rs: STPOps.RemoteStream = NEW[STPOps.RemoteObject _ [access: access, state: initial, stp: stp]]; CheckConnection[stp]; IF stp.remoteStream # NIL THEN ERROR STP.Error[stp, accessError, "Remote Stream Exists"]; 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 stp.plist[eolConversion] _ "CR"; stp.remoteStream _ rs; RETURN[IO.CreateStream[remoteStreamProcs, rs]] }; NextFileName: PUBLIC PROC [remoteStream: IO.STREAM] RETURNS [file: Rope.ROPE] = { rs: RemoteStream _ ConvertHandle[remoteStream]; reason: PupStream.CloseReason; IF rs.access # read THEN ERROR STP.Error[rs.stp, accessError, NIL]; { ENABLE PupStream.StreamClosing => {reason _ why; GOTO streamClosing}; SELECT rs.state FROM initial => RequestRetrieve[rs]; confirm => { PutCommand[rs.stp, markNo, 0C, "No Thanks"]; IF LookAtMark[rs.stp] = markEOC THEN {[] _ MyGetMark[rs.stp]; rs.state _ end} ELSE GetHereIsAndPList[rs.stp]; }; data => { ErrorIfNextNotYes[rs.stp]; IF LookAtMark[rs.stp] = markEOC THEN {[] _ MyGetMark[rs.stp]; rs.state _ end} ELSE {GetHereIsAndPList[rs.stp]; rs.state _ confirm}; }; 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]; }; RETURN[IF rs.state = end THEN NIL ELSE MakeRemoteName[rs.stp.plist, alto]] }; <> ConvertHandle: PROC [h: IO.STREAM] RETURNS [rs: RemoteStream] = INLINE { RETURN[NARROW[h.streamData]] }; UnsafeGetBlock: UNSAFE PROC[self: IO.STREAM, block: IO.UnsafeBlock] RETURNS[nBytesRead: INT] = UNCHECKED { rs: RemoteStream _ ConvertHandle[self]; reason: PupStream.CloseReason; { ENABLE PupStream.StreamClosing => {reason _ why; GOTO streamClosing}; IF rs.state # data THEN { StartRemote[rs, read]; IF rs.state = end THEN RETURN[0]; }; nBytesRead _ rs.stp.byteStream.UnsafeGetBlock[block]; IF rs.stp.byteStream.EndOf[] THEN { rs.stp.mark _ PupStream.ConsumeMark[rs.stp.byteStream]; rs.stp.gotMark _ TRUE; SetupForNextOrEnd[rs.stp]; }; EXITS streamClosing => {rs.state _ end; GenerateStreamClosingError[rs.stp, reason]}; }; }; EndOf: PROC[self: IO.STREAM] RETURNS[BOOL] = { rs: RemoteStream _ ConvertHandle[self]; RETURN[rs.state # data OR rs.stp.byteStream.EndOf[]] }; PutChar: PROC[self: IO.STREAM, char: CHAR] = { rs: RemoteStream _ ConvertHandle[self]; reason: PupStream.CloseReason; { ENABLE PupStream.StreamClosing => {reason _ why; GOTO streamClosing}; IF rs.state # data THEN StartRemote[rs, write]; rs.stp.byteStream.PutChar[char]; EXITS streamClosing => {rs.state _ end; GenerateStreamClosingError[rs.stp, reason]}; }; }; UnsafePutBlock: PROC[self: IO.STREAM, block: IO.UnsafeBlock] = { rs: RemoteStream _ ConvertHandle[self]; reason: PupStream.CloseReason; { ENABLE PupStream.StreamClosing => {reason _ why; GOTO streamClosing}; IF rs.state # data THEN StartRemote[rs, write]; rs.stp.byteStream.UnsafePutBlock[block]; EXITS streamClosing => {rs.state _ end; GenerateStreamClosingError[rs.stp, reason]}; }; }; DeleteRemoteStream: PROC[self: IO.STREAM, abort: BOOL _ FALSE] = { rs: RemoteStream _ ConvertHandle[self]; stp: STPOps.Handle = rs.stp; { ENABLE UNWIND => stp.remoteStream _ NIL; 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" ! 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;}; }; <> SetupForNextOrEnd: PROC [stp: STPOps.Handle] = { ErrorIfNextNotYes[stp]; SELECT LookAtMark[stp] FROM markEOC => {[] _ MyGetMark[stp]; stp.remoteStream.state _ end}; markHereIsPList => stp.remoteStream.state _ complete; ENDCASE => GenerateProtocolError[stp, badMark, MyGetMark[stp]]; }; RequestRetrieve: PROC [rs: RemoteStream] = { 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; }; StartRemote: PROC [rs: RemoteStream, access: STP.Access] = { reason: PupStream.CloseReason; IF rs.access # access THEN ERROR STP.Error[rs.stp, accessError, "Remote stream access Error"]; { ENABLE PupStream.StreamClosing => {reason _ why; GOTO streamClosing}; SELECT access FROM read => { 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 { mark: [0..256); PutCommand[rs.stp, markYes, 0C, "Yes, please"]; SELECT (mark _ MyGetMark[rs.stp]) FROM markHereIsFile => NULL; markNo => {rs.state _ complete; SelectError[rs.stp, "He says NO", mark]}; ENDCASE => SelectError[rs.stp, "HereIsFile Expected", mark]; }; }; write => { PutPList[rs.stp, markNewStore]; GetHereIsAndPList[rs.stp]; PupStream.SendMark[rs.stp.byteStream, markHereIsFile]; }; ENDCASE => ERROR; rs.state _ data; EXITS streamClosing => GenerateStreamClosingError[rs.stp, reason]; }; }; }.