Heading:
Mesa FTP Functional Specification
Page Numbers: Yes X: 527 Y: 10.5" First Page: 56
Appendix G: Client Communication Primitives
G.1. Description of the Option
An FTP User, Server, or Listener manipulates its local communication system by means of a family of procedures called communication primitives. This family includes, for example, procedures for opening and closing network connections, for activating and deactivating network ports (well-known sockets), and for sending and receiving data. The FTP implementation includes a set of primitives for manipulating the standard Pup communication system.
Rather than use the communication system offered by FTP, the client may, if it wishes, provide its own communication primitives to a particular FTP User or Listener. By so doing, a client can use FTP to, for example:
1. interface to another local communication system.
2. transform host names (for example, convert abstract host names to concrete ones).
3. control access to particular functions on a per-host basis.
4. maintain a log of communication system activity.
The client can also implement certain communication primitives while relying on FTP for others, or use the FTP implementations as building blocks for its own implementations. For example, a client could log incoming requests for connection by supplying an implementation of the ActivatePort procedure (described in Section G.5) that calls FTP’s ActivatePort procedure to actually activate the port.
G.2. Exercising the Option
The client exercises the option described above by means of the communicationPrimitives parameter required by both the FTPCreateUser and FTPCreateListener procedures. This parameter is a POINTER to a RECORD containing the PROCEDUREs by which the newly created FTP User or Listener (and any Servers it may create) are to access the local communication system. The client may supply its own version of this record, rather than rely upon either of the standard versions offered by FTP. In constructing the record and/or implementing the procedures it contains, the client may draw upon any of the communication primitives it finds in the FTP-provided records. Since FTP will not copy the record presented to it, the client must preserve it intact until FTPDestroyUser or FTPDestroyListener is called:
CommunicationPrimitives: TYPE = POINTER TO CommunicationPrimitivesObject;
CommunicationPrimitivesObject: TYPE = RECORD [
-- program management primitives
CreateCommunicationSystem: PROCEDURE RETURNS [communicationSystem: CommunicationSystem],
DestroyCommunicationSystem: PROCEDURE [communicationSystem: CommunicationSystem],
-- connection management primitives
OpenConnection: PROCEDURE [communicationSystem: CommunicationSystem, remoteHost: STRING, remoteSocket: LONG CARDINAL, receiveSeconds: CARDINAL] RETURNS [connection: Connection],
CloseConnection: PROCEDURE [communicationSystem: CommunicationSystem, connection: Connection],
ActivatePort: PROCEDURE [communicationSystem: CommunicationSystem, localSocket: LONG CARDINAL,
serviceConnection: PROCEDURE [UNSPECIFIED, Connection, STRING],
serviceConnectionData: UNSPECIFIED, receiveSeconds: CARDINAL] RETURNS [port: Port],
DeactivatePort: PROCEDURE [communicationSystem: CommunicationSystem, port: Port],
-- data transmission and receipt primitives
SendBytes: PROCEDURE [communicationSystem: CommunicationSystem, connection: Connection, bytePointer: BytePointer],
ReceiveBytes: PROCEDURE [communicationSystem: CommunicationSystem, connection: Connection, bytePointer: BytePointer, settleForLess: BOOLEAN],
SendByte: PROCEDURE [communicationSystem: CommunicationSystem, connection: Connection, byte: Byte],
ReceiveByte: PROCEDURE [communicationSystem: CommunicationSystem, connection: Connection, settleForNone: BOOLEAN] RETURNS [byte: Byte, settledForNone: BOOLEAN],
ProduceDiscontinuity,
ConsumeDiscontinuity,
ForceOutput: PROCEDURE [communicationSystem: CommunicationSystem, connection: Connection]];
CommunicationSystem: TYPE = POINTER TO CommunicationSystemObject;
CommunicationSystemObject: TYPE = RECORD [dummy: UNSPECIFIED];
Connection: TYPE = POINTER TO ConnectionObject;
ConnectionObject: TYPE = RECORD [dummy: UNSPECIFIED];
Port: TYPE = POINTER TO PortObject;
PortObject: TYPE = RECORD [dummy: UNSPECIFIED];
BytePointer: TYPE = POINTER TO BytePointerObject;
BytePointerObject: TYPE = RECORD [address: POINTER, offset: BOOLEAN, count: CARDINAL];
Byte: TYPE = [0..377B];
G.3. General Characteristics
The communication primitives employed by FTP and suppliable by the client are described below. Statements that apply to all valid implementations of a primitive (that is, one or both of FTP’s implementations and any a client might supply) are rendered in the standard font. Statements which apply only to one or both of the standard implementations supplied by FTP are rendered in a smaller font.
In accordance with standard Mesa exception handling conventions, communication primitives report errors by signalling. FTP catches any signal that reaches it and aborts the current transaction (with the help of the remote FTP User, Listener, or Server, as necessary). Wherever possible, client communication primitives should use the standard FTP signal, FTPError (described in Section 1.4), to report errors. Doing so enables the FTP User or Server to communicate the error to the remote FTP Server or User in a meaningful way. The description of each procedure below includes a list of the FtpError values that seem, to the author, most appropriate for that primitive. Requests by communication primitive implementors for new FtpError values will be gladly entertained.
The communication system provides stream-like connections that are used by FTP to interconnect FTP Users with FTP Servers. Over such connections flow streams of data partitioned into logical records by discontinuities in the streams. The communication system provides primitives for producing and consuming discontinuities, as well as for sending and receiving data. In general, data transmitted by the client (that is, FTP) are buffered by the communication system until an amount consistent with efficient use of the transmission medium has been accumulated. Before waiting for a response from the remote FTP User or Server to previously transmitted data, therefore, FTP must insure that it has actually been sent, by invoking the ForceOutput primitive, described in Section G.6.
Some of the data transmission and receipt primitives require among their parameters a byte pointer that defines the block of storage to be emptied or filled. A byte pointer is a POINTER to a RECORD containing the address of a storage block, the offset from that address to the first byte of data, and a count of the number of bytes to be read or written:
BytePointer: TYPE = POINTER TO BytePointerObject;
BytePointerObject: TYPE = RECORD [address: POINTER, offset: BOOLEAN, count: CARDINAL];
If offset is FALSE, the first byte of data is the left most (that is, high order) byte of the addressed word; if offset is TRUE, the first byte of data is the right most (that is, low order) byte of that word. In practice, the vast majority of calls on the primitives requiring byte pointers will specify an offset of FALSE. However, the generality of the communication protocols employed by FTP requires this same generality at the interface to the communication system.
Each primitive that accepts a byte pointer among its arguments has the side effect of updating that byte pointer to address the first byte of data beyond the last byte actually transmitted or received. Since the SendBytes procedure described in Section G.6 always transmits the entire contents of the storage block, it returns with the byte pointer updated to address the first byte beyond that block. Since the ReceiveBytes primitive described in Section G.7 may receive less than the requested number of bytes, it may return with the byte pointer updated to a place somewhere within the specified storage block.
G.4. Program Management Primitives
FTP or its client provides two procedures for creating and destroying instances of the local communication system. The first, CreateCommunicationSystem, used by both FTP User and Listener, creates a new instance of the local communication system. The procedure returns a handle, communicationSystem, to the newly created communication system instance, which the caller must retain and later present to any of the other communication primitives it invokes. The communicationSystem is a pointer to a private record containing all of the state information the communication system instance requires to function properly. The Alto implementation of this procedure turns on the Pup Package:
CreateCommunicationSystem: PROCEDURE RETURNS [communicationSystem: CommunicationSystem];
CommunicationSystem: TYPE = POINTER TO CommunicationSystemObject;
CommunicationSystemObject: TYPE = RECORD [dummy: UNSPECIFIED];
The second procedure, DestroyCommunicationSystem, used by both FTP User and Listener, destroys a previously created instance of the local communication system, reclaiming any local resources allocated to it. Before invoking this procedure, the caller must close all open connections and deactivate all active ports. The Alto implementation of this procedure destroys the Pup Package:
DestroyCommunicationSystem: PROCEDURE [communicationSystem: CommunicationSystem];
CommunicationSystem: TYPE = POINTER TO CommunicationSystemObject;
CommunicationSystemObject: TYPE = RECORD [dummy: UNSPECIFIED];
G.5. Connection Management Primitives
FTP or its client provides four procedures for creating and destroying network connections. The first, OpenConnection, used only by FTP User, establishes a connection to the specified remoteHost and remoteSocket and returns a handle to it. The caller specifies the interval in seconds, receiveSeconds, after which subsequently unfulfilled calls to the ReceiveBytes or ReceiveByte procedure described in Section G.7 are to be timed out and aborted. The distinguished value LAST[CARDINAL] requests the maximum allowed timeout interval, which may be infinite. The Alto implementation of this procedure interprets remoteHost as either a host name or Ethernet address, and creates a network stream to the specified socket at that host:
OpenConnection: PROCEDURE [communicationSystem: CommunicationSystem, remoteHost: STRING, remoteSocket: LONG CARDINAL, receiveSeconds: CARDINAL] RETURNS [connection: Connection];
Connection: TYPE = POINTER TO ConnectionObject;
ConnectionObject: TYPE = RECORD [dummy: UNSPECIFIED];
Exceptions: noSuchHost, connectionRejected, noRouteToNetwork, noNameLookupResponse.
The second procedure, CloseConnection, used only by FTP User, closes the previously opened connection with the specified handle. The Alto implementation of this procedure simply deletes the network stream:
CloseConnection: PROCEDURE [communicationSystem: CommunicationSystem, connection: Connection];
Connection: TYPE = POINTER TO ConnectionObject;
ConnectionObject: TYPE = RECORD [dummy: UNSPECIFIED];
The third procedure, ActivatePort, used only by FTP Listener, causes the local host to become responsive to incoming requests for connection to the specified localSocket, and returns a handle to the newly established local communication port. The caller specifies the interval in seconds, receiveSeconds, after which subsequently unfulfilled calls to the ReceiveBytes or ReceiveByte procedure described in Section G.7 are to be timed out and aborted. The distinguished value LAST[CARDINAL] requests the maximum allowed timeout interval, which may be infinite. As long as the port is active, the communication system will automatically create a connection for each incoming request, and invoke the caller-supplied serviceConnection procedure to service that connection. This procedure receives as arguments the serviceConnectionData supplied to ActivatePort, a handle for the newly established connection, and the host name (possibly expressed as a network address, depending upon the implementation) of the host that initiated the connection request. When serviceConnection returns, its caller (within the communication system) will close the connection. The Alto implementation of this procedure simply creates a Pup listener for the indicated socket:
ActivatePort: PROCEDURE [communicationSystem: CommunicationSystem, localSocket: LONG CARDINAL,
serviceConnection: PROCEDURE [UNSPECIFIED, Connection, STRING],
serviceConnectionData: UNSPECIFIED, receiveSeconds: CARDINAL] RETURNS [port: Port];
Connection: TYPE = POINTER TO ConnectionObject;
ConnectionObject: TYPE = RECORD [dummy: UNSPECIFIED];
Port: TYPE = POINTER TO PortObject;
PortObject: TYPE = RECORD [dummy: UNSPECIFIED];
The fourth procedure, DeactivatePort, used only by FTP Listener, deactivates the previously activated port with the specified handle. Existing connections remain unaffected and will continue to be serviced by the client, but no new incoming requests for connection to the affected socket will be honored The Alto implementation of this procedure sets a flag, pokes the listening process, and waits for it to destroy itself:
DeactivatePort: PROCEDURE [communicationSystem: CommunicationSystem, port: Port];
Port: TYPE = POINTER TO PortObject;
PortObject: TYPE = RECORD [dummy: UNSPECIFIED];
G.6. Data Transmission Primitives
FTP or its client provides four procedures for transmitting data via a previously opened connection. The first, SendBytes, used by both FTP User and Server, enqueues for transmission via the specified connection, the contents of the block of storage denoted by the specified bytePointer. As a side effect, the procedure updates the byte pointer to address the first byte beyond the storage block. The procedure returns after the caller’s storage block has been emptied but, in general, before the data has actually been transmitted to the remote host. Data are transmitted only when an amount consistent with efficient use of the transmission medium has been accumulated by the communication system, or when the ForceOutput procedure described below is invoked, whichever occurs first. The Alto implementation of this procedure simply outputs the block of data on the network stream and advances the byte pointer:
SendBytes: PROCEDURE [communicationSystem: CommunicationSystem, connection: Connection, bytePointer: BytePointer];
Connection: TYPE = POINTER TO ConnectionObject;
ConnectionObject: TYPE = RECORD [dummy: UNSPECIFIED];
BytePointer: TYPE = POINTER TO BytePointerObject;
BytePointerObject: TYPE = RECORD [address: POINTER, offset: BOOLEAN, count: CARDINAL];
Exceptions: connectionTimedOut, connectionClosed, noRouteToNetwork.
The second procedure, SendByte, used by both FTP User and Server, enqueues for transmission via the specified connection, the indicated byte of data. In general, the procedure returns before the data has actually been transmitted to the remote host. Data are transmitted only when an amount consistent with efficient use of the transmission medium has been accumulated by the communication system, or when the ForceOutput procedure described below is invoked, whichever occurs first. The Alto implementations of this procedure simply outputs the byte of data on the network stream, unless it is the first byte following a discontinuity, in which case it transmits the byte in the form of a change in subsequence type:
SendByte: PROCEDURE [communicationSystem: CommunicationSystem, connection: Connection, byte: Byte];
Connection: TYPE = POINTER TO ConnectionObject;
ConnectionObject: TYPE = RECORD [dummy: UNSPECIFIED];
Byte: TYPE = [0..377B];
Exceptions: connectionTimedOut, connectionClosed, noRouteToNetwork.
The third procedure, ProduceDiscontinuity, used by both FTP User and Server, creates a discontinuity in the stream being transmitted via the specified connection, at a point immediately following the last byte of data sent. Multiple calls upon this procedure without intervening data are treated as no operations. The Alto implementation of this procedure simply makes note of the requested discontinuity so that the next byte of data sent via SendByte will be transmitted in the form of a change in subsequence type:
ProduceDiscontinuity: PROCEDURE [communicationSystem: CommunicationSystem, connection: Connection];
Connection: TYPE = POINTER TO ConnectionObject;
ConnectionObject: TYPE = RECORD [dummy: UNSPECIFIED];
Exceptions: connectionTimedOut, connectionClosed, noRouteToNetwork.
The fourth procedure, ForceOutput, used by both FTP User and Server, causes all data previously sent via the specified connection actually to be transmitted to the remote host. In the absence of calls upon this procedure, data are transmitted only when an amount consistent with efficient use of the transmission medium has been accumulated by the communication system. Multiple calls upon this procedure without intervening data are treated as no operations. The Alto implementation of this procedure simply invokes the network stream’s SendNow primitive:
ForceOutput: PROCEDURE [communicationSystem: CommunicationSystem, connection: Connection];
Connection: TYPE = POINTER TO ConnectionObject;
ConnectionObject: TYPE = RECORD [dummy: UNSPECIFIED];
Exceptions: connectionTimedOut, connectionClosed, noRouteToNetwork.
G.7. Data Receipt Primitives
FTP or its client provides three procedures for receiving data transmitted via a previously opened connection. The first, ReceiveBytes, used by both FTP User and Server, accepts from the specified connection, some or all of the data required to fill the block of storage denoted by the specified bytePointer. If settleForLess is FALSE, the procedure completely fills the block of storage, blocking the client as long as is required to obtain the requested amount of data. If settleForLess is TRUE, the procedure returns whatever data the communication system may have already received, up to the capacity of the storage block, blocking the client if necessary to obtain at least one byte of data. Regardless of the value of settleForLess, a discontinuity in the input stream will terminate the receive operation at that point. As a side effect, the procedure updates the byte pointer to address the byte just beyond the last byte read, thereby informing the client of the quantity of data actually returned by the procedure. The Alto implementation of this procedure simply invoke the network stream’s GetBlock procedure, terminating on end of physical record (if settleForLess is TRUE) or a change in subsequence type:
ReceiveBytes: PROCEDURE [communicationSystem: CommunicationSystem, connection: Connection, bytePointer: BytePointer, settleForLess: BOOLEAN];
Connection: TYPE = POINTER TO ConnectionObject;
ConnectionObject: TYPE = RECORD [dummy: UNSPECIFIED];
BytePointer: TYPE = POINTER TO BytePointerObject;
BytePointerObject: TYPE = RECORD [address: POINTER, offset: BOOLEAN, count: CARDINAL];
Exceptions: connectionTimedOut, connectionClosed, noRouteToNetwork.
The second procedure, ReceiveByte, used by both FTP User and Server, returns to the caller the next byte of data transmited via the specified connection. A discontinuity in the input stream either aborts the procedure, if settleForNone if FALSE, or causes the procedure to return empty-handed, if settleForNone is TRUE. In either case, the procedure returns an indication of whether it settledForNone. The Alto implementation of this procedure simply invoke the network stream’s GetByte procedure, noting and acting upon a change in subsequence type:
ReceiveByte: PROCEDURE [communicationSystem: CommunicationSystem, connection: Connection, settleForNone: BOOLEAN] RETURNS [byte: Byte, settledForNone: BOOLEAN];
Connection: TYPE = POINTER TO ConnectionObject;
ConnectionObject: TYPE = RECORD [dummy: UNSPECIFIED];
Byte: TYPE = [0..377B];
Exceptions: connectionTimedOut, connectionClosed, noRouteToNetwork.
The third procedure, ConsumeDiscontinuity, used by both FTP User and Server, consumes any discontinuity that might exist at the current point in the input stream being received via the specified connection. Unnecessary calls upon this procedure are treated as no operations. The Alto implementation of this procedure simply clears the discontinuity-pending condition, if it exists:
ConsumeDiscontinuity: PROCEDURE [communicationSystem: CommunicationSystem, connection: Connection];
Connection: TYPE = POINTER TO ConnectionObject;
ConnectionObject: TYPE = RECORD [dummy: UNSPECIFIED];
Exceptions: connectionTimedOut, connectionClosed, noRouteToNetwork.
Appendix H: Sample Configuration and Program
H.1. Introduction
The sample configuration and stand-alone program presented below, which store a single file on a remote file system, illustrate FTP’s use on the Alto. The reader is referred to Appendix I for the location of the necessary object files.
H.2. Sample Configuration
The programmer must include in his configuration: FTP; file, mail, and communication primitives, as appropriate; and (on the Alto) the Pup Package. In the sample configuration below, just the FTP User code is included, since the sample program creates no FTP Listener:
FTPCSample: CONFIGURATION
-- import list
IMPORTS DiskKDDefs, FrameDefs, ImageDefs, IODefs, ProcessDefs,
SegmentDefs, StreamDefs, StringDefs, SystemDefs, TimeDefs
-- control module
CONTROL FTPSample
= BEGIN
-- sample program
FTPSample;
-- ftp
FTPUser;
-- communication
TinyPup;
END. -- of FTPCSample
H.3. Sample Program
The sample program first initializes FTP; creates an FTP User using the file and communication primitives supplied by FTP; extracts the login user name and password from the Alto operating system and uses them as its credentials; opens a connection to IRIS; stores a copy of the local file, FTPCSample.Bcd, in the remote file system; closes the connection to IRIS; destroys the FTP User; and finalizes FTP:
DIRECTORY
FTPDefs:FROM "FTPDefs"USING [
FTPCloseConnection, FTPCreateUser, FTPDestroyUser, FTPError, FTPFinalize,
FTPInitialize, FTPOpenConnection, FTPSetCredentials, FTPStoreFile, FTPUser,
PupCommunicationPrimitives, AltoFilePrimitives],
ImageDefs:FROM "ImageDefs"USING [StopMesa],
IODefs:FROM "IODefs"USING [WriteLine],
OsStaticDefs:FROM "OsStaticDefs"USING [OsStatics],
StringDefs:FROM "StringDefs"USING [BcplToMesaString];
FTPSample: PROGRAM
-- import list
IMPORTS FTPDefs, ImageDefs, IODefs, StringDefs
= BEGIN OPEN FTPDefs, ImageDefs, IODefs, OsStaticDefs, StringDefs;
-- variables
ftpInitialized: BOOLEAN ← FALSE;
ftpuser, copy: FTPUser ← NIL;
user: STRING ← [40];
password: STRING ← [40];
-- intercept errors
BEGIN ENABLE
BEGIN
FTPError =>
BEGIN
IF message # NIL THEN WriteLine[message];
CONTINUE;
END;
UNWIND =>
BEGIN
IF ftpuser # NIL THEN FTPDestroyUser[ftpuser];
IF ftpInitialized THEN FTPFinalize[];
END;
END;
-- initialize ftp
FTPInitialize[]; ftpInitialized ← TRUE;
-- create ftp user
ftpuser ← FTPCreateUser[AltoFilePrimitives[], PupCommunicationPrimitives[]];
-- set credentials to login user and password
BcplToMesaString[OsStatics↑.UserName, user];
BcplToMesaString[OsStatics↑.UserPassword, password];
FTPSetCredentials[ftpuser, primary, user, password];
-- open connection, store self, and close connection
FTPOpenConnection[ftpuser, "Iris"L, files, NIL];
[] ← FTPStoreFile[ftpuser, "FTPCSample.BCD"L, "FTPCSample.BCD"L, binary];
FTPCloseConnection[ftpuser];
-- destroy ftp user
copy ← ftpuser; ftpuser ← NIL; FTPDestroyUser[ftpuser];
-- finalize ftp
ftpInitialized ← FALSE; FTPFinalize[];
END; -- enable
-- return to exec
StopMesa[];
END. -- of FTPSample
Appendix I: Production Configurations and File Locations
I.1. Introduction
FTP is offered in a number of configurations, described below. In theory at least, it is possible to build a configuration that includes only the desired facilities. Here is a summary of what the various optional pieces do:
FTPAltoFile implements an interface to the Alto file system as described in Appendix E.
FTPPupComCool and FTPPupComHot implement the Pup version of the communication interface described in Appendix G. They require either TinyPup or FatPup to be included in your configuration.
FTPUserStore implements FTPStoreFile, as described in Section 3.5.
FTPUserRetrieve implements FTPRetrieveFile, as described in Section 3.5.
FTPUserDump implements the Dump Primitives described in Appendix B. If it is ommitted, DumpBlock (from FTPUserStore) and/or LoadBlock (from FTPUserRetrieve) will need dummy implementations to satisfy the Binder.
FTPUserFiles implements most of the other user file primitives (FTPEnumerateFiles, FTPDeleteFile, FTPRenameFile, and FTPSetFilenameDefaults) described in Section 3.
FTPUserXfer implements FTPTransferFile, as described in Appendix D.3.
FTPUserMailIn implements the mail retrieval primitives described in Appendix C.3.
FTPUserMailOut implements the mail delivery primitives described in Appendix C.2.
FTPAccessories translates error codes into text. If the text is not needed, it may be omitted if dummy routines are provided.
FTPTrace implements the trace facilities described in Appendix D, Section D.2. It may be omitted if dummy routines are provided to satisfy the Binder.
FTPSysMail plus a local file system interface of the client’s choosing is used to implement a mail server as described in Appendix F.
Dummy* implement the various routines needed to glue FTP together and/or to satisfy the Binder.
I.2. Production Configurations
The client can determine FTP’s version number by means of the following constants defined in FTPDefs:
ftpMajorVersion: Byte = 6;
ftpMinorVersion: Byte = 1;
Byte: TYPE = [0..377B];
FTP 6.0 is written in Mesa 6.0 and FTP proper imports Process, String, and Storage. The FTP-provided file, mail and, communication interfaces import additional components of Mesa, and/or Pup.
The following production FTP configurations presently exist; others will be created as need for them is expressed by the user community:
FTPUser: FTP User file primitives (that is, the primitives of Sections 2 and 3 and of Appendices B and D). In calls to FTPOpenConnection, purpose must be files.
FTPServers: FTP Listener file and mail primitives only (that is, the primitives of Sections 2 and 4, and of Appendix D, Sections D.2 and D.4).
MTPUser: FTP User mail primitives only (that is, the primitives of Appendices C.2, C.3, and D.4). In calls to FTPOpenConnection, purpose must be mail. This is a minimal system primarily for use by Laurel. It does not contain FTPAccessories or the trace facilities of Appendix D.2.
FTPMTPUser: This configuration contains all of the user facilities for files and/or mail.
FTPAll: This configuration contains all of the user and server facilities for files and/or mail.
PupAndFTP: This is simply TinyPup, EFTPSend, and FTPMTPUser all packaged into one big lump to eliminate the complexities of including the correct packages.
Appendix J: Utilities
FTP needs a few routines that may be useful to other programs. They are packaged separately so that they can be included in you configuration even if you do not need the appropiate parts of FTP.
J.1. TimeExtraDefs
TimeExtraDefs is exported by FTPUser, FTPMTPUser, and PupAndFTP. If one of them is not needed, TimeExtras can be included directly.
If the argument is NIL or its contents cannot be parsed as a reasonable time, it returns 0. There are probably many strings easily recognizable by a person that PackedTimeFromString won’t be able to handle, but it doesn’t have any trouble with the output of the existing FTP programs (Maxc, IFS, Alto BCPL, Juniper) and TimeDefs.AppendDayTime.
PackedTimeFromString: PROCEDURE [STRING] RETURNS [TimeDefs.PackedTime];
J.2. DirExtraDefs
The server half of FTPAltoFile uses DirExtras to do * and # expansion. It is not exported by any of the standard FTP configurations.
There is only one procedure in DirExtras: EnumerateDirectoryMasked. It scans the directory, and presents to proc the matching files in directory order. The trailing "." on Alto file names has already been removed when proc is called.
EnumerateDirectoryMasked: PROCEDURE [
files: STRING,
proc: PROCDURE [fp: POINTER TO FP, file: STRING] RETURNS [BOOLEAN] ];