-- file: STPsD.mesa - Simple/Stream Transfer Protocol -- Common and protocol stuff in here -- Edited by: -- Mark on: Feb 12, 1981 11:39 PM -- Smokey on: 11-Mar-81 14:51:17 -- Karlton on: Oct 10, 1980 5:24 PM -- Evans on: Nov 13, 1980 2:10 PM -- Levin on: 8-Mar-82 15:59:49 DIRECTORY Ascii USING [SP], HeapString USING [AppendChar, AppendString, Replace], PupStream USING[StreamClosing], Storage USING [ CopyString, EmptyString, Free, FreeString, FreeStringNil, String], STP USING [Error, ErrorCode, FileInfo, FileType, Open], STPOps USING [ DestroyPList, ErrorCodeToSTPErrorCode, FileProperties, FilenameType, GenerateErrorString, GenerateProtocolError, Handle, markAbort, markComment, markEOC, markHereIsPList, markIAmVersion, markNo, markYes, markYouAreUser, maxStringLength, Object, PList, PListArray, ServerType, SmashClosed, UserProperties, ValidProperties], Stream USING [ Block, GetChar, Handle, Object, PutBlock, PutChar, SetSST, SSTChange, SubSequenceType, TimeOut], String USING [EquivalentString, StringToLongNumber], Time USING [Append, Packed, Unpack]; STPsD: MONITOR IMPORTS HeapString, PupStream, Storage, STP, STPOps, Stream, String, Time EXPORTS STP, STPOps = BEGIN OPEN STPOps; -- Exported Types Object: PUBLIC TYPE = STPOps.Object; -- Global Data 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"]; -- Commonly used stuff Connect: PUBLIC PROCEDURE [stp: STPOps.Handle, name, password: STRING] = BEGIN HeapString.Replace[@stp.userState[connectName], name]; HeapString.Replace[@stp.userState[connectPassword], password]; END; Login: PUBLIC PROCEDURE [stp: STPOps.Handle, name, password: STRING] = BEGIN HeapString.Replace[@stp.userState[userName], name]; HeapString.Replace[@stp.userState[userPassword], password]; END; SetHost: PUBLIC PROCEDURE [stp: STPOps.Handle, host: STRING] = BEGIN HeapString.Replace[@stp.host, host]; END; SetDirectory: PUBLIC PROCEDURE [stp: STPOps.Handle, directory: STRING] = BEGIN HeapString.Replace[@stp.userState[directory], directory]; END; -- Procedures for doing FTP protocol operations GetCommand: PUBLIC PROCEDURE [stp: STPOps.Handle, ps: POINTER TO STRING] RETURNS [mark: Stream.SubSequenceType, code: CHARACTER] = BEGIN code ← 0C; CheckConnection[stp]; mark ← MyGetMark[stp]; SELECT mark FROM markAbort, markComment, markYouAreUser => CollectString[stp, ps]; markIAmVersion, markNo, markYes => {code ← CollectCode[stp]; CollectString[stp, ps]}; ENDCASE => GenerateProtocolError[badMark, mark, code]; RETURN[mark, code] END; GetHereIsAndPList: PUBLIC PROCEDURE [stp: STPOps.Handle, gobbleEOC: BOOLEAN ← TRUE] = BEGIN mark: Stream.SubSequenceType; DO SELECT (mark ← MyGetMark[stp]) FROM markComment => CollectString[stp, @stp.remoteString]; markHereIsPList => {GetPList[stp, gobbleEOC]; EXIT}; markNo => BEGIN code: CHARACTER = CollectCode[stp]; errorCode: STP.ErrorCode = ErrorCodeToSTPErrorCode[requestRefused, code]; CollectString[stp, @stp.remoteString]; ErrorIfNextNotEOC[stp]; GenerateErrorString[errorCode, stp.remoteString, code]; END; ENDCASE => GenerateProtocolError[badMark, mark, 0C]; ENDLOOP; END; GetPList: PUBLIC PROCEDURE [stp: STPOps.Handle, gobbleEOC: BOOLEAN ← TRUE] = BEGIN property: STRING ← Storage.String[maxStringLength]; value: STRING ← Storage.String[maxStringLength]; mark: Stream.SubSequenceType; CheckConnection[stp]; BEGIN ENABLE UNWIND => {Storage.FreeString[property]; Storage.FreeString[value]}; parens: INTEGER ← 0; char: CHARACTER; string: STRING ← property; DO char ← MyGetChar[stp ! MarkEncountered => GOTO BadPList]; SELECT char FROM '( => BEGIN parens ← parens + 1; string ← property; string.length ← 0; END; Ascii.SP => IF string = property THEN BEGIN string ← value; string.length ← 0; END ELSE HeapString.AppendChar[@string, char]; ') => BEGIN IF property.length # 0 AND value.length # 0 THEN BEGIN SetPListItem[ stp.plist, property, value ! BadProperty => GOTO BadPList]; property.length ← value.length ← 0; END; IF (parens ← parens - 1) = 0 THEN EXIT; END; ENDCASE => HeapString.AppendChar[@string, 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[badPList, mark]}; END; Storage.FreeString[property]; Storage.FreeString[value]; END; PutCommand: PUBLIC PROCEDURE [ stp: STPOps.Handle, mark: Stream.SubSequenceType, code: CHARACTER, string: STRING, sendEOC: BOOLEAN ← TRUE] = BEGIN CheckConnection[stp]; Stream.SetSST[stp.byteStream, mark]; Stream.PutChar[stp.byteStream, code]; IF ~Storage.EmptyString[string] THEN MyPutString[stp.byteStream, string]; IF sendEOC THEN Stream.SetSST[stp.byteStream, markEOC]; END; PutPList: PUBLIC PROCEDURE [ stp: STPOps.Handle, mark: Stream.SubSequenceType, sendEOC: BOOLEAN ← TRUE] = BEGIN state: {okay, tryOpen, triedOpen} ← okay; DoIt: PROCEDURE = BEGIN CheckConnection[stp]; Stream.SetSST[stp.byteStream, mark]; Stream.PutChar[stp.byteStream, '(]; FOR i: ValidProperties IN ValidProperties DO IF ~Storage.EmptyString[stp.plist[i]] THEN PutPListItem[stp.byteStream, i, stp.plist[i]]; ENDLOOP; Stream.PutChar[stp.byteStream, ')]; IF sendEOC THEN Stream.SetSST[stp.byteStream, markEOC]; END; DO DoIt[! PupStream.StreamClosing, Stream.TimeOut => IF state = okay AND ~Storage.EmptyString[stp.host] 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 Storage.FreeString[STP.Open[stp, stp.host]]; stp.plist ← DestroyPList[stp.plist]; stp.plist ← savedPList; state ← triedOpen; END; ENDLOOP; END; -- Utility routines CollectString: PUBLIC PROCEDURE [stp: STPOps.Handle, ps: POINTER TO STRING] = BEGIN char: CHARACTER; IF ps↑ # NIL THEN ps↑.length ← 0 ELSE ps↑ ← Storage.String[15]; DO char ← MyGetChar[stp ! MarkEncountered => { mark: Stream.SubSequenceType ← LookAtMark[stp]; SELECT mark FROM markHereIsPList, markEOC => EXIT; ENDCASE => GenerateProtocolError[eocExpected, mark]}]; HeapString.AppendChar[ps, char]; ENDLOOP; END; CollectCode: PUBLIC PROCEDURE [stp: STPOps.Handle] RETURNS [code: CHARACTER] = BEGIN code ← 0C; CheckConnection[stp]; code ← MyGetChar[stp ! MarkEncountered => GenerateProtocolError[noCode, MyGetMark[stp]]]; END; CheckConnection: PUBLIC PROCEDURE [stp: STPOps.Handle] = BEGIN WHILE stp.byteStream = NIL DO SIGNAL STP.Error[noConnection, "Please open a connection!"L, 0C] ENDLOOP END; ErrorIfNextNotYes: PUBLIC PROCEDURE [stp: STPOps.Handle] = BEGIN code: CHARACTER ← 0C; mark: Stream.SubSequenceType; [mark, code] ← GetCommand[stp, @stp.remoteString]; IF mark # markYes THEN GenerateErrorString[requestRefused, stp.remoteString, code]; END; ErrorIfNextNotEOC: PUBLIC PROCEDURE [stp: STPOps.Handle] = BEGIN mark: Stream.SubSequenceType ← MyGetMark[stp]; IF mark # markEOC THEN GenerateProtocolError[eocExpected, mark]; END; GetServerType: PUBLIC PROCEDURE [server: STRING] RETURNS [serverType: ServerType] = BEGIN OPEN String; RETURN[SELECT TRUE FROM EquivalentString[server, "MAXC"L], EquivalentString[server, "MAXC1"L], EquivalentString[server, "MAXC2"L] => tenex, ENDCASE => ifs]; END; LookAtMark: PUBLIC PROCEDURE [stp: STPOps.Handle] RETURNS [Stream.SubSequenceType] = 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 ← Stream.GetChar[ stp.byteStream ! Stream.SSTChange => BEGIN stp.mark ← sst; stp.gotMark ← TRUE; ERROR MarkEncountered; END]; RETURN[char]; END; MyGetMark: PUBLIC PROCEDURE [stp: STPOps.Handle] RETURNS [mark: Stream.SubSequenceType] = BEGIN mark ← LookAtMark[stp]; stp.gotMark ← FALSE; RETURN[mark] END; MyPutString: PUBLIC PROCEDURE [byteStream: Stream.Handle, string: STRING] = BEGIN block: Stream.Block ← [@string.text, 0, string.length]; Stream.PutBlock[byteStream, block, FALSE]; END; PropertyString: PUBLIC PROCEDURE [prop: STPOps.ValidProperties] RETURNS [string: STRING] = BEGIN RETURN[propertyStrings[prop]]; END; SetCreateTime: PUBLIC PROCEDURE [stp: STPOps.Handle, creation: Time.Packed] = BEGIN cDate: STRING ← [24]; Time.Append[cDate, Time.Unpack[creation]]; HeapString.Replace[@stp.plist[createDate], cDate]; END; SetFileType: PUBLIC PROCEDURE [ stp: STPOps.Handle, fileType: STP.FileType] = BEGIN HeapString.Replace[@stp.plist[type], SELECT fileType FROM text => "Text"L, binary => "Binary"L, ENDCASE => NIL]; END; SetByteSize: PUBLIC PROCEDURE [stp: STPOps.Handle, fileType: STP.FileType] = BEGIN HeapString.Replace[@stp.plist[byteSize], SELECT stp.serverType FROM ifs, unknown, tenex => IF fileType = text THEN NIL ELSE "8", ENDCASE => ERROR]; END; StringToFileType: PUBLIC PROCEDURE [string: STRING] RETURNS [type: STP.FileType] = BEGIN type ← SELECT TRUE FROM string = NIL => unknown, String.EquivalentString["Text"L, string] => text, String.EquivalentString["Binary"L, string] => binary, ENDCASE => unknown; END; -- PList and FileInfo Utilities NameToPList: PUBLIC PROCEDURE [plist: PList, name: STRING, type: FilenameType] = BEGIN i: CARDINAL; versionSeen: BOOLEAN ← FALSE; temp: STRING; IF Storage.EmptyString[name] THEN RETURN; temp ← Storage.String[100]; FOR i IN [0..name.length) DO SELECT name[i] FROM '[ => IF i # 0 THEN HeapString.AppendChar[@temp, name[i]]; '], ': => BEGIN HeapString.Replace[@plist[device], temp]; temp.length ← 0; END; '< => BEGIN IF temp.length # 0 THEN HeapString.Replace[@plist[device], temp]; temp.length ← 0; HeapString.AppendChar[@temp, name[i]]; IF plist[directory] # NIL THEN plist[directory].length ← 0; END; '> => BEGIN IF (Storage.EmptyString[plist[directory]] OR plist[directory][plist[directory].length-1] # '>) AND (temp.length = 0 OR temp[0] # '<) THEN HeapString.AppendChar[@plist[directory], name[i]]; HeapString.AppendString[@plist[directory], temp]; temp.length ← 0; END; '!, '; => BEGIN IF temp.length # 0 THEN HeapString.Replace[@plist[nameBody], temp]; versionSeen ← TRUE; temp.length ← 0; END; ENDCASE => HeapString.AppendChar[@temp, name[i]]; ENDLOOP; IF temp.length # 0 THEN { IF versionSeen THEN HeapString.Replace[@plist[version], temp] ELSE HeapString.Replace[@plist[nameBody], temp]}; Storage.FreeString[temp]; END; PListToName: PUBLIC PROCEDURE [plist: PList, type: FilenameType] RETURNS [name: STRING] = BEGIN name ← Storage.String[40]; IF ~Storage.EmptyString[plist[directory]] THEN BEGIN IF plist[directory][0] # '< THEN HeapString.AppendChar[@name, '<]; HeapString.AppendString[@name, plist[directory]]; IF plist[directory][0] # '< THEN HeapString.AppendChar[@name, '>]; END; IF ~Storage.EmptyString[plist[nameBody]] THEN HeapString.AppendString[@name, plist[nameBody]]; IF ~Storage.EmptyString[plist[version]] THEN BEGIN IF type = alto THEN HeapString.AppendChar[@name, '!] ELSE HeapString.AppendChar[@name, ';]; HeapString.AppendString[@name, plist[version]]; END; END; MakeRemoteName: PUBLIC PROCEDURE [plist: PList, type: FilenameType] RETURNS [STRING] = { RETURN[IF Storage.EmptyString[plist[serverName]] THEN PListToName[plist, type] ELSE Storage.CopyString[plist[serverName]]]}; PutPListItem: PUBLIC PROCEDURE [ byteStream: Stream.Handle, property: STPOps.ValidProperties, value: STRING] = BEGIN Stream.PutChar[byteStream, '(]; MyPutString[byteStream, PropertyString[property]]; Stream.PutChar[byteStream, Ascii.SP]; MyPutString[byteStream, value]; Stream.PutChar[byteStream, ')]; END; ResetPList: PUBLIC PROCEDURE [plist: PList] = BEGIN i: STPOps.ValidProperties; IF plist = NIL THEN RETURN; FOR i IN STPOps.ValidProperties DO IF plist[i] # NIL THEN plist[i] ← Storage.FreeStringNil[plist[i]]; ENDLOOP; END; 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 Storage.EmptyString[stp.plist[i]] THEN 0 ELSE String.StringToLongNumber[stp.plist[i], 10]; type => stp.info.type ← StringToFileType[stp.plist[i]]; ENDCASE; ENDLOOP; RETURN[stp.info]; END; SetPListItem: PUBLIC PROCEDURE [plist: PList, property, value: STRING] = BEGIN FOR i: STPOps.ValidProperties IN STPOps.ValidProperties DO IF String.EquivalentString[PropertyString[i], property] THEN { HeapString.Replace[@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 -- Solution 1 HeapString.Replace[@plist[i], userState[i]]; -- Solution 2 -- IF plist[i] # NIL THEN ERROR Error[undefinedError]; -- IF ~Storage.EmptyString[userState[i] THEN -- plist[i] ← Storage.CopyString[userState[i]]; -- Solution 3 -- IF userState[i] # NIL THEN -- HeapString.Replace[@plist[i], userState[i]]; ENDLOOP; END; END. -- of STPsB