DIRECTORY BasicTime USING[ GMT, MonthOfYear, Unpack, Unpacked ], Convert USING[ IntFromRope ], IO USING[ EndOf, EndOfStream, GetBlock, GetChar, STREAM, PutChar, PutF, PutRope, ROS, RopeFromROS ], PupStream USING[ConsumeMark, SendMark, StreamClosing, TimeOut], Rope USING [Cat, Equal, Fetch, Find, FromRefText, Length, ROPE, Substr], STP USING [DesiredProperties, Error, ErrorCode, FileInfo, Open, Type], STPOps USING [ DestroyPList, ErrorCodeToSTPErrorCode, FileProperties, FilenameType, GenerateErrorString, GenerateProtocolError, Handle, markAbort, markComment, markEOC, markHereIsPList, markIAmVersion, markNo, markYes, markYouAreUser, Object, PList, PListArray, ServerType, SmashClosed, UserProperties, ValidProperties]; STPsD: CEDAR PROGRAM IMPORTS BasicTime, Convert, IO, PupStream, Rope, STP, STPOps EXPORTS STP, STPOps = BEGIN OPEN STPOps; Object: PUBLIC TYPE = STPOps.Object; MarkEncountered: PUBLIC ERROR = CODE; BadProperty: PUBLIC ERROR = CODE; propertyStrings: PListArray = [ userName: "User-Name", userPassword: "User-Password", connectName: "Connect-Name", connectPassword: "Connect-Password", byteSize: "Byte-Size", type: "Type", size: "Size", directory: "Directory", nameBody: "Name-Body", version: "Version", createDate: "Creation-Date", readDate: "Read-Date", writeDate: "Write-Date", author: "Author", eolConversion: "End-of-Line-Convention", account: "Account", userAccount: "User-Account", device: "Device", serverName: "Server-Filename"]; desiredPropString: Rope.ROPE = "Desired-property"; Connect: PUBLIC PROCEDURE [stp: STPOps.Handle, name, password: Rope.ROPE] = BEGIN stp.userState[connectName] _ name; stp.userState[connectPassword] _ password; END; IsOpen: PUBLIC PROCEDURE [stp: STPOps.Handle] RETURNS [yes: BOOLEAN] = { yes _ stp.byteStream # NIL}; GetProperty: PUBLIC PROCEDURE [stp: STPOps.Handle, prop: ValidProperties] RETURNS [Rope.ROPE] = { RETURN[stp.plist[prop]]}; Login: PUBLIC PROCEDURE [stp: STPOps.Handle, name, password: Rope.ROPE] = BEGIN stp.userState[userName] _ name; stp.userState[userPassword] _ password; END; SetHost: PUBLIC PROCEDURE [stp: STPOps.Handle, host: Rope.ROPE] = { stp.host _ host }; SetDirectory: PUBLIC PROCEDURE [stp: STPOps.Handle, directory: Rope.ROPE] = { stp.userState[directory] _ directory }; GetCommand: PUBLIC PROCEDURE [stp: STPOps.Handle] RETURNS [mark: [0..256), code: CHARACTER, ps: Rope.ROPE] = BEGIN code _ 0C; CheckConnection[stp]; mark _ MyGetMark[stp]; SELECT mark FROM markAbort, markComment, markYouAreUser => ps _ CollectString[stp]; markIAmVersion, markNo, markYes => {code _ CollectCode[stp]; ps _ CollectString[stp]}; ENDCASE => GenerateProtocolError[stp, badMark, mark, code]; END; GetHereIsAndPList: PUBLIC PROCEDURE [stp: STPOps.Handle, gobbleEOC: BOOLEAN _ TRUE] = BEGIN mark: [0..256); DO SELECT (mark _ MyGetMark[stp]) FROM markComment => stp.remoteString _ CollectString[stp]; markHereIsPList => {GetPList[stp, gobbleEOC, FALSE]; EXIT}; markNo => BEGIN code: CHARACTER = CollectCode[stp]; errorCode: STP.ErrorCode = ErrorCodeToSTPErrorCode[requestRefused, code]; stp.remoteString _ CollectString[stp]; ErrorIfNextNotEOC[stp]; GenerateErrorString[stp, errorCode, stp.remoteString, code]; END; ENDCASE => GenerateProtocolError[stp, badMark, mark, 0C]; ENDLOOP; END; GetPList: PUBLIC PROCEDURE [stp: STPOps.Handle, gobbleEOC: BOOLEAN, propertiesOk: BOOL] = BEGIN buffer: REF TEXT = NEW[TEXT[100]]; -- arbitrary choice of length Record: PROC = BEGIN IF buffer.length # 0 THEN BEGIN fragment: Rope.ROPE = Rope.FromRefText[buffer]; buffer.length _ 0; IF which = property THEN property _ property.Cat[fragment] ELSE value _ value.Cat[fragment]; END; END; Append: PROC[c: CHAR] = BEGIN IF buffer.length = buffer.maxLength THEN Record[]; buffer[buffer.length] _ c; buffer.length _ buffer.length+1; END; property: Rope.ROPE; value: Rope.ROPE; parens: INT _ 0; char: CHARACTER; which: {property, value} _ property; CheckConnection[stp]; stp.plist[nameBody] _ NIL; DO char _ MyGetChar[stp ! MarkEncountered => IF NOT propertiesOk THEN GOTO BadPList]; SELECT char FROM '' => Append[MyGetChar[stp ! MarkEncountered => GOTO BadPList]]; '( => BEGIN parens _ parens + 1; Record[]; which _ property; property _ NIL; END; ' => IF which = property THEN BEGIN Record[]; which _ value; value _ NIL; END ELSE Append[char]; ') => BEGIN Record[]; IF property.Length[] # 0 AND value.Length[] # 0 THEN BEGIN SetPListItem[ stp.plist, property, value ! BadProperty => CONTINUE]; property _ value _ NIL; END; IF (parens _ parens - 1) = 0 THEN EXIT; END; ENDCASE => Append[char]; ENDLOOP; IF (property.Length[] # 0 AND value.Length[] # 0) THEN GOTO BadPList; IF gobbleEOC AND MyGetMark[stp] # markEOC THEN GOTO BadPList; EXITS BadPList => {ResetPList[stp.plist]; GenerateProtocolError[stp, badPList, stp.mark]}; END; PutCommand: PUBLIC PROCEDURE [ stp: STPOps.Handle, mark: [0..256), code: CHARACTER, string: Rope.ROPE, sendEOC: BOOLEAN _ TRUE] = BEGIN CheckConnection[stp]; PupStream.SendMark[stp.byteStream, mark]; stp.byteStream.PutChar[code]; IF string.Length[] # 0 THEN MyPutString[stp.byteStream, string]; IF sendEOC THEN PupStream.SendMark[stp.byteStream, markEOC]; END; PutPList: PUBLIC PROCEDURE [ stp: STPOps.Handle, mark: [0..256), sendEOC: BOOLEAN _ TRUE] = BEGIN state: {okay, tryOpen, triedOpen} _ okay; DoIt: PROCEDURE = BEGIN CheckConnection[stp]; IF mark # 0B THEN PupStream.SendMark[stp.byteStream, mark]; stp.byteStream.PutChar['(]; FOR i: ValidProperties IN ValidProperties DO IF stp.plist[i].Length[] # 0 THEN PutPListItem[stp.byteStream, i, stp.plist[i]]; ENDLOOP; IF stp.desiredProps # ALL[TRUE] THEN PutDesiredProps[stp]; stp.byteStream.PutChar[')]; IF sendEOC THEN PupStream.SendMark[stp.byteStream, markEOC]; END; DO DoIt[! PupStream.StreamClosing, PupStream.TimeOut => IF state = okay AND stp.host.Length[] # 0 THEN {state _ tryOpen; CONTINUE}]; IF state # tryOpen THEN EXIT; BEGIN savedPList: PList _ stp.plist; stp.plist _ NIL; SmashClosed[stp]; -- Call outside catch so Pup monitor unlocked [] _ STP.Open[stp, stp.host]; stp.plist _ DestroyPList[stp.plist]; stp.plist _ savedPList; state _ triedOpen; END; ENDLOOP; END; CollectString: PUBLIC PROCEDURE [stp: STPOps.Handle] RETURNS[ r: Rope.ROPE _ NIL] = BEGIN buffer: REF TEXT = NEW[TEXT[100]]; -- arbitrary choice of length DO buffer.length _ stp.byteStream.GetBlock[buffer, 0, buffer.maxLength]; r _ r.Cat[Rope.FromRefText[buffer]]; IF stp.byteStream.EndOf[] THEN EXIT; ENDLOOP; stp.mark _ PupStream.ConsumeMark[stp.byteStream]; stp.gotMark _ TRUE; SELECT stp.mark FROM markHereIsPList, markEOC => NULL; ENDCASE => GenerateProtocolError[stp, eocExpected, stp.mark]; END; CollectCode: PUBLIC PROCEDURE [stp: STPOps.Handle] RETURNS [code: CHARACTER] = BEGIN code _ 0C; CheckConnection[stp]; code _ MyGetChar[stp ! MarkEncountered => GenerateProtocolError[stp, noCode, MyGetMark[stp]]]; END; CheckConnection: PUBLIC PROCEDURE [stp: STPOps.Handle] = BEGIN WHILE stp.byteStream = NIL DO SIGNAL STP.Error[stp, noConnection, "Please open a connection!", 0C] ENDLOOP END; ErrorIfNextNotYes: PUBLIC PROCEDURE [stp: STPOps.Handle] = BEGIN code: CHARACTER _ 0C; mark: [0..256); [mark, code, stp.remoteString] _ GetCommand[stp]; IF mark # markYes THEN GenerateErrorString[stp, requestRefused, stp.remoteString, code]; END; ErrorIfNextNotEOC: PUBLIC PROCEDURE [stp: STPOps.Handle] = BEGIN mark: [0..256) _ MyGetMark[stp]; IF mark # markEOC THEN GenerateProtocolError[stp, eocExpected, mark]; END; GetServerType: PUBLIC PROCEDURE [server: Rope.ROPE] RETURNS [serverType: ServerType] = BEGIN RETURN[SELECT TRUE FROM Rope.Equal[server, "MAXC", FALSE], Rope.Equal[server, "MAXC2", FALSE] => tenex, ENDCASE => ifs]; END; LookAtMark: PUBLIC PROCEDURE [stp: STPOps.Handle] RETURNS [[0..256)] = BEGIN IF ~stp.gotMark THEN DO [] _ MyGetChar[stp ! MarkEncountered => CONTINUE]; IF stp.gotMark THEN EXIT; ENDLOOP; RETURN[stp.mark] END; MyGetChar: PUBLIC PROCEDURE [stp: STPOps.Handle] RETURNS [char: CHARACTER] = BEGIN char _ stp.byteStream.GetChar[! IO.EndOfStream => GOTO end]; EXITS end => BEGIN stp.mark _ PupStream.ConsumeMark[stp.byteStream]; stp.gotMark _ TRUE; ERROR MarkEncountered; END END; MyGetMark: PUBLIC PROCEDURE [stp: STPOps.Handle] RETURNS [mark: [0..256)] = BEGIN mark _ LookAtMark[stp]; stp.gotMark _ FALSE; RETURN[mark] END; MyPutString: PUBLIC PROCEDURE [byteStream: IO.STREAM, string: Rope.ROPE] = BEGIN byteStream.PutRope[string]; END; MyPutStringVal: PUBLIC PROCEDURE [byteStream: IO.STREAM, string: Rope.ROPE] = BEGIN FOR i: INT IN [0..string.Length[]) DO char: CHAR = string.Fetch[i]; SELECT char FROM '(, '), '' => byteStream.PutChar[''] ENDCASE => NULL; byteStream.PutChar[char]; ENDLOOP; END; PropertyString: PUBLIC PROCEDURE [prop: STPOps.ValidProperties] RETURNS [string: Rope.ROPE] = BEGIN RETURN[propertyStrings[prop]]; END; SetCreateTime: PUBLIC PROCEDURE [stp: STPOps.Handle, creation: BasicTime.GMT] = BEGIN month: ARRAY BasicTime.MonthOfYear OF Rope.ROPE = [ January: "Jan", February: "Feb", March: "Mar", April: "Apr", May: "May", June: "Jun", July: "Jul", August: "Aug", September: "Sep", October: "Oct", November: "Nov", December: "Dec" ]; zoneIndex: TYPE = [4 .. 10]; zoneChars: ARRAY zoneIndex OF CHAR = ['A, 'E, 'C, 'M, 'P, 'Y, 'H]; unpack: BasicTime.Unpacked = BasicTime.Unpack[creation]; absZone: INT _ ABS[IF unpack.dst = yes THEN unpack.zone - 60 ELSE unpack.zone]; str: IO.STREAM = IO.ROS[]; str.PutF["%2d-%g-%02d %2d", [integer[unpack.day]], [rope[month[unpack.month]]], [integer[unpack.year MOD 100]], [integer[unpack.hour]] ]; str.PutF[":%02d:%02d ", [integer[unpack.minute]], [integer[unpack.second]] ]; IF (unpack.zone / 60 IN zoneIndex) AND absZone MOD 60 = 0 THEN BEGIN str.PutChar[zoneChars[unpack.zone / 60]]; str.PutChar[IF unpack.dst = yes THEN 'D ELSE 'S]; str.PutChar['T]; END ELSE str.PutF["%g%g:%02d", [character[IF unpack.zone < 0 THEN '- ELSE '+]], [integer[absZone/60]], [integer[absZone MOD 60]] ]; stp.plist[createDate] _ str.RopeFromROS[]; END; SetFileType: PUBLIC PROCEDURE [ stp: STPOps.Handle, fileType: STP.Type] = BEGIN stp.plist[type] _ SELECT fileType FROM text => "Text", binary => "Binary", ENDCASE => NIL; END; SetByteSize: PUBLIC PROCEDURE [stp: STPOps.Handle, fileType: STP.Type] = BEGIN stp.plist[byteSize] _ SELECT stp.serverType FROM ifs, unknown, tenex => IF fileType = text THEN NIL ELSE "8", ENDCASE => ERROR STP.Error[stp, undefinedError, NIL]; END; StringToFileType: PUBLIC PROCEDURE [string: Rope.ROPE] RETURNS [type: STP.Type] = BEGIN type _ SELECT TRUE FROM Rope.Equal["Text", string, FALSE] => text, Rope.Equal["Binary", string, FALSE] => binary, ENDCASE => unknown; END; NameToPList: PUBLIC PROCEDURE [plist: PList, name: Rope.ROPE, type: FilenameType] = BEGIN pos: INT _ 0; length: INT = name.Length[]; IF pos >= length THEN RETURN; IF name.Fetch[pos] = '[ THEN BEGIN end: INT = name.Find["]"]; IF end > pos THEN { plist[device] _ name.Substr[pos+1, end-pos-1]; pos _ end+1 }; IF pos >= length THEN RETURN; END; IF name.Fetch[pos] = '< THEN BEGIN end: INT = name.Find[">"]; IF end > pos THEN { plist[directory] _ name.Substr[pos+1, end-pos-1]; pos _ end+1 }; IF pos >= length THEN RETURN; END; BEGIN end: INT _ name.Find["!"]; IF end >= 0 THEN plist[version] _ name.Substr[end+1, length-end-1] ELSE end _ length; plist[nameBody] _ name.Substr[pos, end-pos]; END; END; PListToName: PUBLIC PROCEDURE [plist: PList, type: FilenameType] RETURNS [name: Rope.ROPE _ NIL] = BEGIN dir: Rope.ROPE = plist[directory]; dirLength: INT = dir.Length[]; IF dirLength # 0 THEN BEGIN SELECT dir.Fetch[0] FROM '> => name _ name.Cat[dir.Substr[1, dirLength-1]]; '< => name _ name.Cat[dir]; ENDCASE => name _ name.Cat["<", dir]; IF dir.Fetch[dirLength - 1] # '> THEN name _ name.Cat[">"]; END; IF plist[nameBody].Length[] # 0 THEN name _ name.Cat[plist[nameBody]]; IF plist[version].Length[] # 0 THEN name _ name.Cat[IF type = alto THEN "!" ELSE ";", plist[version]]; END; MakeRemoteName: PUBLIC PROCEDURE [plist: PList, type: FilenameType] RETURNS [Rope.ROPE] = { RETURN[IF plist[serverName].Length[] = 0 THEN PListToName[plist, type] ELSE plist[serverName] ]}; PutPListItem: PUBLIC PROCEDURE [ byteStream: IO.STREAM, property: STPOps.ValidProperties, value: Rope.ROPE] = BEGIN byteStream.PutChar['(]; MyPutString[byteStream, PropertyString[property]]; byteStream.PutChar[' ]; MyPutStringVal[byteStream, value]; byteStream.PutChar[')]; END; ResetPList: PUBLIC PROCEDURE [plist: PList] = { IF plist # NIL THEN plist^ _ ALL[NIL] }; GetFileInfo: PUBLIC PROCEDURE [stp: STPOps.Handle] RETURNS [STP.FileInfo] = BEGIN FOR i: STPOps.FileProperties IN STPOps.FileProperties DO SELECT i FROM directory => stp.info.directory _ stp.plist[i]; nameBody => stp.info.body _ stp.plist[i]; version => stp.info.version _ stp.plist[i]; author => stp.info.author _ stp.plist[i]; createDate => stp.info.create _ stp.plist[i]; readDate => stp.info.read _ stp.plist[i]; writeDate => stp.info.write _ stp.plist[i]; size => stp.info.size _ IF stp.plist[i].Length[] = 0 THEN 0 ELSE Convert.IntFromRope[stp.plist[i]]; type => stp.info.type _ StringToFileType[stp.plist[i]]; ENDCASE; ENDLOOP; RETURN[stp.info]; END; SetPListItem: PUBLIC PROCEDURE [plist: PList, property, value: Rope.ROPE] = BEGIN FOR i: STPOps.ValidProperties IN STPOps.ValidProperties DO IF property.Equal[PropertyString[i], FALSE] THEN { plist[i] _ value; RETURN}; ENDLOOP; ERROR BadProperty END; UserStateToPList: PUBLIC PROCEDURE [stp: STPOps.Handle] = BEGIN OPEN stp; FOR i: STPOps.UserProperties IN STPOps.UserProperties DO plist[i] _ userState[i] ENDLOOP; END; SetDesiredProperties: PUBLIC PROC[stp: STPOps.Handle, props: STP.DesiredProperties] = BEGIN stp.desiredProps _ props END; GetDesiredProperties: PUBLIC PROC [stp: STPOps.Handle] RETURNS [props: STP.DesiredProperties] = BEGIN RETURN[stp.desiredProps] END; PutDesiredProps: PUBLIC PROCEDURE [stp: STPOps.Handle] = BEGIN FOR i: STPOps.FileProperties IN STPOps.FileProperties DO IF stp.desiredProps[i] THEN PutDesiredPropsItem[stp.byteStream, PropertyString[i]] ENDLOOP; END; PutDesiredPropsItem: PUBLIC PROCEDURE [ byteStream: IO.STREAM, value: Rope.ROPE] = BEGIN byteStream.PutChar['(]; MyPutString[byteStream, desiredPropString]; byteStream.PutChar[' ]; MyPutString[byteStream, value]; byteStream.PutChar[')]; END; END. -- of STPsD  file: STPsD.mesa - Simple/Stream Transfer Protocol Common and protocol stuff in here Edited by: Mark, Feb 12, 1981 11:39 PM Smokey, 17-Jul-81 7:20:16 Karlton, 17-Jul-81 17:45:12 JGS, 14-Aug-81 10:35:26 Loretta 27-Oct-82 10:45:58 Daniels 21-Sep-82 13:07:12 Davirro 15-Dec-82 15:32:52 Andrew Birrell, June 1, 1983 5:39 pm Last Edited by: Levin, August 9, 1983 9:59 am Last Edited by: Schroeder, August 10, 1983 5:33 pm, but I only changed one character! Last Edited by: MBrown, September 17, 1983 8:14 pm Last Edited by: HGM, February 20, 1984 10:29:03 pm PST Exported Types Global Data Commonly used stuff Procedures for doing FTP protocol operations Utility routines Desired syntax is: "dd-mmm-yy hh:mm:ss zzz" PList and FileInfo Utilities Desired Property stuff ÊE˜Jšœ3™3Jšœ!™!Jšœ ™ Jšœ™Jšœ™Jšœ™Jšœ™Jšœ™Jšœ™Jšœ™J™$J™-J™UJ™2J™6šÏk ˜ Jšœ œœ"˜6Jšœœ˜Jšœœ)œ-˜dJšœ œ0˜?Jšœœ0œ˜HJšœœ=˜Fšœœ˜J˜DJšœô˜ô—J˜—šœœ˜Jšœœœ˜Jš˜J˜)šžœ œ˜Jš˜J˜Jšœ œ*˜;J˜šœœ˜,šœ˜!J˜.—Jšœ˜—Jšœœœœ˜:J˜Jšœ œ-˜