FTPCommon.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Common protocol and property lists
Last edited by:
Taft, October 7, 1983 2:23 pm
Bob Hagmann July 11, 1985 9:25:08 am PDT
Carl Hauser, May 1, 1986 1:26:17 pm PDT
Hal Murray, April 10, 1986 1:22:44 pm PST
DIRECTORY
Atom USING [GetPName, GetPropFromList, MakeAtom, MakeAtomFromRefText, PropList, PutPropOnList],
Basics USING [BYTE],
BasicTime USING [GMT, MonthOfYear, minutesPerHour, nullGMT, Unpack, Unpacked],
Convert USING [Error, IntFromRope, TimeFromRope],
FTP,
FTPInternal,
IO,
PupName USING [Code],
PupStream USING [CloseReason, ConsumeMark, SendMark],
RefText USING [TrustTextAsRope],
Rope;
FTPCommon: CEDAR PROGRAM
IMPORTS Atom, BasicTime, Convert, FTPInternal, IO, PupStream, RefText, Rope
EXPORTS FTP, FTPInternal = BEGIN
ROPE: TYPE = Rope.ROPE;
FailureCode: TYPE = FTP.FailureCode;
Property: TYPE = FTP.Property;
DateProperty: TYPE = FTP.DateProperty;
EnumeratedProperty: TYPE = FTP.EnumeratedProperty;
NumberProperty: TYPE = FTP.NumberProperty;
LocalOrRemote: TYPE = FTP.LocalOrRemote;
Object: PUBLIC TYPE = FTPInternal.Object;
Handle: TYPE = FTPInternal.Handle;
PList: TYPE = FTPInternal.PList;
Mark: TYPE = FTPInternal.Mark;
FTP.
Failed: PUBLIC SIGNAL [h: Handle, code: FailureCode, text: ROPENIL, resumable: BOOLEANFALSE] = CODE;
IsOpen: PUBLIC PROC [h: Handle] RETURNS [open: BOOLEAN] =
{RETURN[h.byteStream # NIL]};
GetClientData: PUBLIC PROC [h: Handle] RETURNS [data: REF ANY] =
{RETURN[h.clientData]};
SetClientData: PUBLIC PROC [h: Handle, data: REF ANY] =
{h.clientData ← data};
GetDateProperty: PUBLIC PROC [h: Handle, prop: DateProperty, list: LocalOrRemote ← remote] RETURNS [value: BasicTime.GMT] =
{RETURN[h.pList[list].date[prop]]};
GetEnumeratedProperty: PUBLIC PROC [h: Handle, prop: EnumeratedProperty, list: LocalOrRemote ← remote] RETURNS [propValue: FTP.EnumPropValue] =
{RETURN[h.pList[list].enumerated[prop]]};
GetNumberProperty: PUBLIC PROC [h: Handle, prop: NumberProperty, list: LocalOrRemote ← remote] RETURNS [value: INT] =
{RETURN[h.pList[list].number[prop]]};
GetTextProperty: PUBLIC PROC [h: Handle, prop: FTP.TextProperty, list: LocalOrRemote ← remote] RETURNS [value: ROPE] =
{RETURN[h.pList[list].text[prop]]};
GetUserDefinedProperty: PUBLIC PROC [h: Handle, prop: ATOM, list: LocalOrRemote ← remote] RETURNS [value: ROPE] = {
refAny: REF ANY ← h.pList[list].userDefined.GetPropFromList[prop];
value ← NARROW[refAny];
};
SetDateProperty: PUBLIC PROC [h: Handle, prop: DateProperty, value: BasicTime.GMT] =
{h.pList[local].date[prop] ← value};
SetEnumeratedProperty: PUBLIC PROC [h: Handle, prop: EnumeratedProperty, value: FTP.EnumPropValue] = {
SELECT prop FROM
eolConvention => {
h.pList[local].enumerated[eolConvention].eolConvention ← LOOPHOLE[value];
};
type => {
h.pList[local].enumerated[type].type ← LOOPHOLE[value];
};
ENDCASE;
};
SetNumberProperty: PUBLIC PROC [h: Handle, prop: NumberProperty, value: INT] =
{h.pList[local].number[prop] ← value};
SetTextProperty: PUBLIC PROC [h: Handle, prop: FTP.TextProperty, value: ROPE] =
{h.pList[local].text[prop] ← value};
SetUserDefinedProperty: PUBLIC PROC [h: Handle, prop: ATOM, value: ROPE] =
{[] ← h.pList[local].userDefined.PutPropOnList[prop: prop, val: value]};
ResetProperties: PUBLIC PROC [h: Handle] =
{h.pList[local]^ ← []};
GetDesiredProperties: PUBLIC PROC [h: Handle] RETURNS [props: FTP.PropertySet, userDefinedProps: LIST OF ATOM] =
{RETURN [h.pList[remote].desiredProps, h.pList[remote].desiredUserDefinedProps]};
SetDesiredProperties: PUBLIC PROC [h: Handle, props: FTP.PropertySet, userDefinedProps: LIST OF ATOMNIL] =
{h.pList[local].desiredProps ← props; h.pList[local].desiredUserDefinedProps ← userDefinedProps};
FileType: PUBLIC PROC [stream: IO.STREAM] RETURNS [type: FTP.Type ← text] = {
This is supposed to be fast. It isn't. I don't know what trick was supposed to be used, except for GetBlock instead of GetChar. If you know, please fix -- rbh
char: CHAR;
char ← stream.GetChar[! IO.EndOfStream => GOTO unknownReturn];
DO
IF LOOPHOLE[char, Basics.BYTE] >= 200B THEN RETURN[binary];
char ← stream.GetChar[! IO.EndOfStream => GOTO textReturn];
ENDLOOP;
EXITS
unknownReturn => RETURN [unknown];
textReturn => RETURN [text];
};
DataType: PUBLIC PROC [block: IO.UnsafeBlock]RETURNS [type: FTP.Type ← text] = TRUSTED {
This is supposed to be fast. It isn't. I don't know what trick was supposed to be used. If you know, please fix -- rbh
count: INT ← block.count;
IF count <= 0 THEN RETURN[unknown];
FOR index: INT ← block.startIndex, index+1 UNTIL count <= 0 DO
byte: Basics.BYTE = block.base[index];
IF byte >= 200B THEN RETURN[binary];
count ← count - 1;
ENDLOOP;
};
FTPInternal.
GetCommand: PUBLIC PROC [h: Handle] RETURNS [mark: Mark, code: FTP.ReplyCode] =
BEGIN
DO
FlushDataUntilMark[h];
mark ← LOOPHOLE[PupStream.ConsumeMark[h.byteStream]];
IF mark#comment THEN EXIT
ENDLOOP;
code ← IF HasReplyCode[mark] THEN LOOPHOLE[h.byteStream.GetChar[! IO.EndOfStream => GOTO error]] ELSE unspecified;
EXITS
error => h.GenerateFailed[protocolError];
END;
GetText: PUBLIC PROC [h: Handle, gobbleEOC: BOOLEANFALSE] RETURNS [text: ROPE] =
BEGIN
buffer: REF TEXT = h.GetBuffer[];
text ← NIL;
DO
buffer.length ← h.byteStream.GetBlock[buffer, 0, buffer.maxLength];
text ← text.Cat[Rope.FromRefText[buffer]];
IF h.byteStream.EndOf[] THEN EXIT;
ENDLOOP;
h.ReleaseBuffer[buffer];
IF gobbleEOC THEN
BEGIN
mark: Mark ← GetCommand[h].mark;
IF mark#endOfCommand THEN h.GenerateFailed[protocolError];
END;
END;
GetPList: PUBLIC PROC [h: Handle, gobbleEOC: BOOLEANFALSE, endOfPropertiesOK: BOOLFALSE] RETURNS [pList: PList] =
BEGIN
Append: PROC [c: CHAR] =
BEGIN
IF buffer.length = buffer.maxLength THEN h.GenerateFailed[badPList, "Property name or value too long"];
buffer[buffer.length] ← c;
buffer.length ← buffer.length+1;
END;
buffer: REF TEXT = h.GetBuffer[];
char: CHARACTER;
pList ← NEW [FTPInternal.PListObject ← []];
char ← h.byteStream.GetChar[ !
IO.EndOfStream => IF endOfPropertiesOK THEN GOTO empty ELSE GOTO badPList];
IF char#'( THEN GOTO badPList;
DO
ENABLE IO.EndOfStream => GOTO badPList;
propName: ATOM;
property: Property;
found: BOOLEAN;
char ← h.byteStream.GetChar[];
SELECT char FROM
'( => NULL;
') => EXIT;
ENDCASE => GOTO badPList;
buffer.length ← 0;
DO -- accumulate property name
char ← h.byteStream.GetChar[];
SELECT char FROM
'' => Append[h.byteStream.GetChar[]];
'(, ') => GOTO badPList;
' => EXIT;
ENDCASE => Append[char];
ENDLOOP;
propName ← MakeCanonicalAtom[buffer];
[found, property] ← PropertyLookup[propertyNames, propName];
buffer.length ← 0;
DO -- accumulate property value
char ← h.byteStream.GetChar[];
SELECT char FROM
'' => Append[h.byteStream.GetChar[]];
'( => GOTO badPList;
') => EXIT;
ENDCASE => Append[char];
ENDLOOP;
IF found THEN
BEGIN ENABLE Convert.Error => GOTO badProp;
SELECT property FROM
IN DateProperty =>
pList.date[property] ← Convert.TimeFromRope[RefText.TrustTextAsRope[buffer] ];
eolConvention => {
eolConvention: FTP.EOLConvention;
propName ← MakeCanonicalAtom[buffer];
[found, eolConvention] ← EOLConventionLookup[eolConventionNames, propName];
IF found THEN pList.enumerated[eolConvention].eolConvention ← eolConvention
ELSE GOTO badProp;
};
type => {
myTypeIndex: FTP.Type;
propName ← MakeCanonicalAtom[buffer];
[found, myTypeIndex] ← TypeLookup[typeNames, propName];
IF found THEN pList.enumerated[type].type ← myTypeIndex
ELSE GOTO badProp;
};
IN NumberProperty =>
pList.number[property] ← Convert.IntFromRope[RefText.TrustTextAsRope[buffer]];
IN FTP.TextProperty =>
pList.text[property] ← Rope.FromRefText[buffer];
desiredProperty =>
BEGIN
propName ← MakeCanonicalAtom[buffer];
[found, property] ← PropertyLookup[propertyNames, propName];
IF found THEN pList.desiredProps[property] ← TRUE
ELSE pList.desiredUserDefinedProps ← CONS[propName, pList.desiredUserDefinedProps];
END;
ENDCASE;
EXITS
badProp => h.GenerateFailed[propertyError[property]];
END
ELSE pList.userDefined ← Atom.PutPropOnList[propList: pList.userDefined, prop: propName, val: Rope.FromRefText[buffer]];
ENDLOOP;
h.ReleaseBuffer[buffer];
IF gobbleEOC THEN
BEGIN
mark: Mark ← GetCommand[h].mark;
IF mark#endOfCommand THEN h.GenerateFailed[protocolError];
END;
EXITS
empty => RETURN [NIL];
badPList => h.GenerateFailed[badPList];
END;
GetYesNo: PUBLIC PROC [h: Handle, gobbleEOC: BOOLEANFALSE, resumable: BOOLEANFALSE] RETURNS [ok: BOOLEAN] =
BEGIN
mark: Mark;
code: FTP.ReplyCode;
[mark, code] ← h.GetCommand[];
SELECT mark FROM
yes => [] ← h.GetText[gobbleEOC];
no => h.GenerateFailed[code, h.GetText[gobbleEOC: gobbleEOC], resumable];
ENDCASE => h.GenerateFailed[protocolError];
RETURN [mark=yes];
END;
GetEOC: PUBLIC PROC [h: Handle] =
BEGIN
mark: Mark ← h.GetCommand[].mark;
IF mark#endOfCommand THEN GenerateProtocolError[h, eocExpected, mark];
END;
PutCommand: PUBLIC PROC [h: Handle, mark: Mark, code: FTP.ReplyCode ← unspecified, text: ROPENIL, sendEOC: BOOLEANTRUE] =
BEGIN
PupStream.SendMark[h.byteStream, LOOPHOLE[mark]];
IF HasReplyCode[mark] THEN h.byteStream.PutChar[LOOPHOLE[code]];
IF text.Length[]#0 THEN h.byteStream.PutRope[text];
IF sendEOC THEN PupStream.SendMark[h.byteStream, LOOPHOLE[Mark.endOfCommand]];
END;
PutEOC: PUBLIC PROC [h: Handle] = {
PupStream.SendMark[h.byteStream, LOOPHOLE[Mark.endOfCommand]];
};
PutPList: PUBLIC PROC [h: Handle, pList: PList, sendEOC: BOOLEANTRUE] =
BEGIN
PutDateProp: PROC [date: BasicTime.GMT] =
BEGIN
unpack: BasicTime.Unpacked = BasicTime.Unpack[date];
dst: BOOL = unpack.dst = yes;
absZone: INTABS[IF dst THEN unpack.zone-60 ELSE unpack.zone];
zoneRope: ROPENIL;
Desired syntax is: "dd-mmm-yy hh:mm:ss +hh:mm" or "dd-mmm-yy hh:mm:ss zoneAbbrev"
h.byteStream.PutF["%2d-%g-%02d %2d", [integer[unpack.day]], [rope[monthName[unpack.month]]], [integer[unpack.year MOD 100]], [integer[unpack.hour]] ];
SELECT unpack.zone FROM
0 => IF ~dst THEN zoneRope ← "GMT";
NAT[5*BasicTime.minutesPerHour] => zoneRope ← IF dst THEN "EDT" ELSE "EST";
NAT[6*BasicTime.minutesPerHour] => zoneRope ← IF dst THEN "CDT" ELSE "CST";
NAT[7*BasicTime.minutesPerHour] => zoneRope ← IF dst THEN "MDT" ELSE "MST";
NAT[8*BasicTime.minutesPerHour] => zoneRope ← IF dst THEN "PDT" ELSE "PST";
ENDCASE;
IF zoneRope = NIL THEN zoneRope ← IO.PutFR["%g%02d:%02d",
IO.char[IF unpack.zone < 0 THEN '- ELSE '+],
IO.card[absZone/BasicTime.minutesPerHour],
IO.card[absZone MOD BasicTime.minutesPerHour]
];
h.byteStream.PutF[":%02d:%02d %g", [integer[unpack.minute]], [integer[unpack.second]], [rope[zoneRope]] ];
END;
PutRopeProp: PROC [rope: ROPE] =
BEGIN
FOR i: INT IN [0..rope.Length[]) DO
char: CHARACTER ← rope.Fetch[i];
SELECT char FROM
'(, '), '' => h.byteStream.PutChar[''];
ENDCASE;
h.byteStream.PutChar[char];
ENDLOOP;
END;
h.byteStream.PutChar['(];
FOR property: Property IN Property DO
nonNilProp: BOOLEANSELECT property FROM
IN DateProperty => pList.date[property]#BasicTime.nullGMT,
IN EnumeratedProperty => pList.enumerated[property] # FTP.nullEnumPropValue,
IN NumberProperty => pList.number[property] # 0,
IN FTP.TextProperty => pList.text[property] # NIL,
desiredProperty => FALSE,
ENDCASE => ERROR;
IF nonNilProp THEN
BEGIN
h.byteStream.PutChar['(];
h.byteStream.PutRope[Atom.GetPName[propertyNames[property.ORD]]];
h.byteStream.PutChar[' ];
SELECT property FROM
IN DateProperty => PutDateProp[pList.date[property]];
eolConvention => PutRopeProp[Atom.GetPName[eolConventionNames[ pList.enumerated[property].eolConvention.ORD]]];
type => PutRopeProp[Atom.GetPName[typeNames[pList.enumerated[property].type.ORD]]];
IN NumberProperty => h.byteStream.Put[IO.int[pList.number[property]]];
IN FTP.TextProperty => PutRopeProp[pList.text[property]];
ENDCASE => ERROR;
h.byteStream.PutChar[')];
END;
ENDLOOP;
FOR prop: Atom.PropList ← pList.userDefined, prop.rest UNTIL prop=NIL DO
key: ATOM = NARROW[prop.first.key];
val: ROPE = NARROW[prop.first.val];
h.byteStream.PutChar['(];
PutRopeProp[Atom.GetPName[key]];
h.byteStream.PutChar[' ];
PutRopeProp[val];
h.byteStream.PutChar[')];
ENDLOOP;
IF (pList.desiredProps#ALL[FALSE] AND pList.desiredProps#ALL[TRUE]) OR pList.desiredUserDefinedProps#NIL THEN {
FOR property: Property IN FTP.BuiltInProperty DO
IF pList.desiredProps[property] THEN {
h.byteStream.PutRope["(Desired-property "];
h.byteStream.PutRope[Atom.GetPName[propertyNames[property.ORD]]];
h.byteStream.PutChar[')];
};
ENDLOOP;
FOR propItem: LIST OF ATOM ← pList.desiredUserDefinedProps, propItem.rest UNTIL propItem=NIL DO
h.byteStream.PutRope["(Desired-property "];
PutRopeProp[Atom.GetPName[propItem.first]];
h.byteStream.PutChar[')];
ENDLOOP;
};
h.byteStream.PutChar[')];
IF sendEOC THEN PupStream.SendMark[h.byteStream, LOOPHOLE[Mark.endOfCommand]];
END;
PutCommandAndPList: PUBLIC PROC [h: Handle, mark: Mark, pList: PList, sendEOC: BOOLEANFALSE] = {
h.PutCommand[mark: mark, sendEOC: FALSE];
h.PutPList[pList: pList, sendEOC: sendEOC];
};
NameToPList: PUBLIC PROC [plist: PList, name: Rope.ROPE, type: FTPInternal.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.text[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.text[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.text[version] ← name.Substr[end+1, length-end-1] ELSE end ← length;
plist.text[nameBody] ← name.Substr[pos, end-pos];
END;
END;
PListToName: PUBLIC PROC [plist: PList, type: FTPInternal.FilenameType] RETURNS [name: Rope.ROPENIL] =
BEGIN
dir: Rope.ROPE = plist.text[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.text[nameBody].Length[] # 0 THEN name ← name.Cat[plist.text[nameBody]];
IF plist.text[version].Length[] # 0 THEN
name ← name.Cat[IF type = alto THEN "!" ELSE ";", plist.text[version]];
END;
For now use REFs for buffers, but put a cache in here later
GetBuffer: PUBLIC PROC [h: Handle] RETURNS [buffer: REF TEXT] = {
buffer ← NEW[TEXT[100]];
};
ReleaseBuffer: PUBLIC PROC [h: Handle, buffer: REF TEXT] = {
};
Private
HasReplyCode: PROC [mark: Mark] RETURNS [BOOLEAN] =
Returns TRUE if the mark is among those that carry a ReplyCode in the immediately following data byte.
BEGIN
RETURN [SELECT mark FROM
yes, no, version, mailboxException => TRUE,
ENDCASE => FALSE];
END;
MakeCanonicalAtom: PROC [text: REF TEXT] RETURNS [atom: ATOM] =
Returns an ATOM for the canonical form of the specified text (property name or other keyword). May modify the text.
BEGIN
FOR i: CARDINAL IN [0..text.length) DO
IF text[i] IN ['A..'Z] THEN text[i] ← text[i]+('a-'A);
ENDLOOP;
RETURN [Atom.MakeAtomFromRefText[text]];
END;
AtomLookup: PROC [table: REF NameTable, name: ATOM] RETURNS [found: BOOLEAN, ordinal: CARDINAL] =
BEGIN
found ← TRUE;
FOR ordinal IN [0..table.length) DO
IF table[ordinal]=name THEN RETURN;
ENDLOOP;
found ← FALSE;
END;
PropertyLookup: PROC [table: REF NameTable, name: ATOM] RETURNS [found: BOOLEAN, property: Property] = LOOPHOLE [AtomLookup];
EOLConventionLookup: PROC [table: REF NameTable, name: ATOM] RETURNS [found: BOOLEAN, eolConvention: FTP.EOLConvention] = LOOPHOLE [AtomLookup];
TypeLookup: PROC [table: REF NameTable, name: ATOM] RETURNS [found: BOOLEAN, type: FTP.Type] = LOOPHOLE [AtomLookup];
FlushDataUntilMark: PROC [h: Handle] = {
buffer: REF TEXT = h.GetBuffer[];
DO
buffer.length ← h.byteStream.GetBlock[buffer, 0, buffer.maxLength];
IF h.byteStream.EndOf[] THEN EXIT;
ENDLOOP;
h.ReleaseBuffer[buffer];
};
CollectCode: PROC [h: Handle] RETURNS [char: CHARACTER ← ' ] = {
char ← h.byteStream.GetChar[]; -- may raise IO.EndOfStream
};
CollectString: PROC [h: Handle] RETURNS [string: ROPE ← NIL] = {
buffer: REF TEXT = h.GetBuffer[];
DO
buffer.length ← h.byteStream.GetBlock[buffer, 0, buffer.maxLength];
string ← string.Cat[Rope.FromRefText[buffer]];
IF h.byteStream.EndOf[] THEN EXIT;
ENDLOOP;
h.ReleaseBuffer[buffer];
};
Error handling
GenerateFailed: PUBLIC PROC [h: Handle, code: FailureCode, text: Rope.ROPENIL, resumable: BOOLEANFALSE] =
BEGIN
IF text=NIL THEN text ← GenerateErrorText[code];
IF resumable THEN SIGNAL Failed[h, code, text, TRUE]
ELSE ERROR Failed[h, code, text, FALSE];
END;
GenerateNo: PUBLIC PROC [h: Handle, code: FailureCode, text: Rope.ROPENIL, sendEOC: BOOLEANFALSE] =
BEGIN
IF text=NIL THEN text ← GenerateErrorText[code];
h.PutCommand[mark: no, code: code, text: text, sendEOC: sendEOC];
END;
GenerateNoAndFailed: PUBLIC PROC [h: Handle, code: FailureCode, text: Rope.ROPENIL, resumable: BOOLEANFALSE] =
BEGIN
h.GenerateNo[code, text];
h.GenerateFailed[code, text, resumable];
END;
GenerateErrorText: PROC [errorCode: FailureCode] RETURNS [text: Rope.ROPE]= {
text ← SELECT errorCode FROM
noSuchHost => "No such host!",
noRouteToNetwork => "No route to network!",
noNameLookupResponse => "Name lookup server is not responding",
alreadyAConnection => "You already have a connection!",
noConnection => "Please open a connection!",
connectionClosed => "Connection closed (local or remote)!",
connectionRejected => "Connection rejected by remote host!",
connectionTimedOut => "Connection timed out!",
accessDenied => "Access denied by remote server!",
illegalUserName => "Invalid or illegal UserName!",
illegalUserPassword => "Invalid or illegal UserPassword!",
illegalUserAccount => "Invalid or illegal UserAccount!",
illegalConnectName => "Invalid or illegal ConnectName!",
illegalConnectPassword => "Invalid or illegal ConnectPassword!",
credentailsMissing => "Name and/or Password not supplied!",
protocolError => "Internal FTP protocol error!",
illegalFileName => "Illegal filename!",
noSuchFile => "File not found!",
requestRefused => "Request refused by remote host!",
accessError => "Illegal access attempt on remote stream!",
undefinedError => "Undefined error!",
ENDCASE => ERROR;
};
GenerateStreamClosingError: PUBLIC PROC [h: Handle, why: PupStream.CloseReason] =
BEGIN
GenerateFailed[
h,
SELECT why FROM
localClose, remoteClose => connectionClosed,
noRouteToNetwork => noRouteToNetwork,
transmissionTimeout => connectionTimedOut,
remoteReject => connectionRejected,
ENDCASE => ERROR,
NIL];
END;
GenerateProtocolError: PUBLIC PROC [h: Handle, type: FTP.ProtocolError, mark: Mark, code: CHARACTER ← 0C, resumable: BOOLEANFALSE] =
BEGIN
text: Rope.ROPE = IO.PutFR["%g, mark = %b, code = %b",
[rope[SELECT type FROM
badVersion => "Incompatible protocol version",
badMark => "Invalid or undefined mark byte",
badPList => "Invalid or malformed property list",
eocExpected => "End-Of-Command mark byte expected",
noCode => "error code is required after error mark byte",
ENDCASE => ERROR]],
[integer[mark.ORD]],
[integer[code.ORD-0C.ORD]] ];
ERROR Failed[h, protocolError, text, resumable];
END;
SelectError: PROC [h: Handle, s: Rope.ROPE , mark: Mark] =
BEGIN
code: CHARACTER ← 0C;
remoteString: ROPENIL;
IF mark = no OR mark = comment THEN
BEGIN
IF mark # comment THEN code ← CollectCode[h];
remoteString ← CollectString[h];
GenerateFailed[h, requestRefused, IF remoteString.Length[] = 0 THEN s ELSE remoteString];
END
ELSE GenerateProtocolError[h, badMark, mark, code];
END;
MapNameLookupError: PUBLIC PROC [error: PupName.Code] RETURNS [FailureCode] = {
RETURN [SELECT error FROM
noRoute => noRouteToNetwork,
noResponse => noNameLookupResponse,
errorFromServer => noSuchHost,
ENDCASE => undefinedError
];
};
MapStreamClosingError: PUBLIC PROC [error: PupStream.CloseReason] RETURNS [FailureCode] = {
RETURN [SELECT error FROM
localClose => connectionClosed,
localAbort => aborted,
remoteClose => connectionClosed,
noRouteToNetwork => noRouteToNetwork,
transmissionTimeout => connectionTimedOut,
remoteReject => connectionRejected,
ENDCASE => undefinedError
];
};
Global data and initialization
NameTable: TYPE = RECORD [
SEQUENCE length: CARDINAL OF ATOM];
propertyNames, typeNames, eolConventionNames: REF NameTable;
nProperties: CARDINAL = Property.LAST.ORD+1;
nTypes: CARDINAL = FTP.Type.LAST.ORD+1;
nEOLConventions: CARDINAL = FTP.EOLConvention.LAST.ORD+1;
monthName: 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"];
propertyError: ARRAY Property OF FailureCode = [createDate: illegalCreationDate, readDate: illegalReadDate, writeDate: illegalWriteDate, eolConvention: illegalEOLConversion, type: illegalType, byteSize: illegalByteSize, checksum: badPList, size: badPList, author: illegalAuthor, connectName: illegalConnectName, connectPassword: illegalConnectPassword, device: illegalDevice, directory: illegalDirectory, nameBody: illegalNameBody, serverFileName: illegalServerFileName, userAccount: illegalUserAccount, userName: illegalUserName, userPassword: illegalUserPassword, version: illegalVersion, desiredProperty: badPList];
Init: PROC =
BEGIN OPEN Atom;
propertyNames ← NEW [NameTable[nProperties]];
propertyNames[Property.author.ORD] ← $author;
propertyNames[Property.byteSize.ORD] ← MakeAtom["byte-size"];
propertyNames[Property.checksum.ORD] ← $checksum;
propertyNames[Property.connectName.ORD] ← MakeAtom["connect-name"];
propertyNames[Property.connectPassword.ORD] ← MakeAtom["connect-password"];
propertyNames[Property.createDate.ORD] ← MakeAtom["creation-date"];
propertyNames[Property.desiredProperty.ORD] ← MakeAtom["desired-property"];
propertyNames[Property.device.ORD] ← $device;
propertyNames[Property.directory.ORD] ← $directory;
propertyNames[Property.eolConvention.ORD] ← MakeAtom["end-of-line-convention"];
propertyNames[Property.nameBody.ORD] ← MakeAtom["name-body"];
propertyNames[Property.readDate.ORD] ← MakeAtom["read-date"];
propertyNames[Property.serverFileName.ORD] ← MakeAtom["server-filename"];
propertyNames[Property.size.ORD] ← $size;
propertyNames[Property.type.ORD] ← $type;
propertyNames[Property.userAccount.ORD] ← MakeAtom["user-account"];
propertyNames[Property.userName.ORD] ← MakeAtom["user-name"];
propertyNames[Property.userPassword.ORD] ← MakeAtom["user-password"];
propertyNames[Property.version.ORD] ← $version;
propertyNames[Property.writeDate.ORD] ← MakeAtom["write-date"];
typeNames ← NEW [NameTable[nTypes]];
typeNames[FTP.Type.binary.ORD] ← $binary;
typeNames[FTP.Type.text.ORD] ← $text;
typeNames[FTP.Type.unknown.ORD] ← $unknown;
eolConventionNames ← NEW [NameTable[nEOLConventions]];
eolConventionNames[FTP.EOLConvention.cr.ORD] ← $cr;
eolConventionNames[FTP.EOLConvention.crlf.ORD] ← $crlf;
eolConventionNames[FTP.EOLConvention.transparent.ORD] ← $transparent;
eolConventionNames[FTP.EOLConvention.unknown.ORD] ← $unknown;
END;
Init[];
END.
Bob Hagmann February 11, 1985 2:05:39 pm PST
changes to: DIRECTORY