-- 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