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;
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: ROPE ← NIL, resumable: BOOLEAN ← FALSE] = 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
ATOM ←
NIL] =
{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:
BOOLEAN ←
FALSE]
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:
BOOLEAN ←
FALSE, endOfPropertiesOK:
BOOL ←
FALSE]
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:
BOOLEAN ←
FALSE, resumable:
BOOLEAN ←
FALSE]
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:
ROPE ←
NIL, sendEOC:
BOOLEAN ←
TRUE] =
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:
BOOLEAN ←
TRUE] =
BEGIN
PutDateProp:
PROC [date: BasicTime.
GMT] =
BEGIN
unpack: BasicTime.Unpacked = BasicTime.Unpack[date];
dst: BOOL = unpack.dst = yes;
absZone: INT ← ABS[IF dst THEN unpack.zone-60 ELSE unpack.zone];
zoneRope: ROPE ← NIL;
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:
BOOLEAN ←
SELECT 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:
BOOLEAN ←
FALSE] = {
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.
ROPE ←
NIL] =
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.
ROPE ←
NIL, resumable:
BOOLEAN ←
FALSE] =
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.
ROPE ←
NIL, sendEOC:
BOOLEAN ←
FALSE] =
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.
ROPE ←
NIL, resumable:
BOOLEAN ←
FALSE] =
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:
BOOLEAN ←
FALSE] =
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: ROPE ← NIL;
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.