<> <> <> <> <> <> <> <> 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; <> 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] = { <> 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 { <> 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; }; <<>> <<>> <> 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; <> 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; <> GetBuffer: PUBLIC PROC [h: Handle] RETURNS [buffer: REF TEXT] = { buffer _ NEW[TEXT[100]]; }; ReleaseBuffer: PUBLIC PROC [h: Handle, buffer: REF TEXT] = { }; <<>> <<>> <> HasReplyCode: PROC [mark: Mark] RETURNS [BOOLEAN] = <> BEGIN RETURN [SELECT mark FROM yes, no, version, mailboxException => TRUE, ENDCASE => FALSE]; END; MakeCanonicalAtom: PROC [text: REF TEXT] RETURNS [atom: ATOM] = <> 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]; }; <> 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 ]; }; <<>> <<>> <> 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. <> <> <<>>