TrickleChargeRemoteFileImpl.mesa
Copyright Ó 1989, 1991, 1992 by Xerox Corporation. All rights reserved.
Tim Diebert: September 19, 1989 7:19:19 am PDT
Willie-s, September 10, 1992 5:34 pm PDT
DIRECTORY
Basics USING [FWORD, HWORD, Int16FromH, Int32FromF],
BasicTime USING [GMT, Now, nullGMT],
CHNameP2V0 USING [],
Commander USING [CommandProc, Register],
CrRPC USING [BulkDataSink, CreateClientHandle, DestroyClientHandle, Error, ErrorReason, Handle],
FilingP10V5,
IO,
PFS,
PFSBackdoor USING [ErrorCode, ProduceError],
PFSClass,
PFSNames,
RefText,
Rope,
TrickleChargeP9813V411 USING [AnyBodyHome, EnumerateForInfo, Error, FileInfo, FileNotFound, Retrieve],
XNS USING [Address, unknownAddress],
XNSCH USING [LookupAddressFromRope],
XNSCHName USING [Name, RopeFromName],
XNSRemoteFileTypes,
XNSStream USING [ConnectionClosed, Timeout],
XNSWKS USING [courier];
TrickleChargeRemoteFileImpl: CEDAR PROGRAM
IMPORTS Basics, BasicTime, CrRPC, Commander, IO, PFS, PFSBackdoor, PFSClass, PFSNames, RefText, Rope, TrickleChargeP9813V411, XNSCH, XNSCHName, XNSStream
~ BEGIN OPEN XNSRemoteFileTypes;
Copied Types
GMT: TYPE = BasicTime.GMT;
ROPE: TYPE = Rope.ROPE;
STREAM: TYPE = IO.STREAM;
PATH: TYPE = PFSNames.PATH;
Version: TYPE = PFSNames.Version;
FSHandle: TYPE = PFSClass.FSHandle;
Parameters
myFlavor: ATOM ¬ $TC;
initialConnectionTTL: CARD ¬ 8;
credentialsErrorTTL: CARD ¬ 30;
PFS Class name
xnsFlavor: ATOM ~ $TC;
Maintenance Object
mo: PFSClass.MaintenanceProcs ~ NEW [PFSClass.MaintenanceProcsObject ¬ [
sweep: TCSweep,
validate: TCValidate
]];
File Manipulation Object
fmo: PFSClass.FileManipulationProcs ~ NEW[PFSClass.FileManipulationProcsObject ¬ [
delete: TCDelete,
enumerateForInfo: TCEnumerateForInfo,
enumerateForNames: TCEnumerateForNames,
fileInfo: TCFileInfo,
lookupName: TCLookup,
rename: TCRename,
copy: TCCopy,
setAttributes: TCSetAttributes,
setByteCountAndUniqueID: TCSetByteCountAndUniqueID,
setClientProperty: TCSetClientProperty,
getClientProperty: TCGetClientProperty,
enumerateClientProperties: TCEnumerateClientProperties,
read: TCRead,
write: TCWrite,
open: TCOpen,
close: TCClose,
store: TCStore,
retrieve: TCRetrieve,
-- attach: TCAttach,
getInfo: TCGetInfo
]];
Initialized Data - order is important!
vfsClass: PFSClass.FSHandle ~ NEW [PFSClass.FSObject ¬ [
flavor: xnsFlavor,
name: NIL, -- filled in upon instantiation
maintenanceProcs: mo,
procs: fmo,
data: NIL -- filled in upon instantiation
]];
Transport Management
TransportProc: TYPE ~ PROC; -- [tr: Transport];
Transport: TYPE ~ REF TransportBody;
TransportBody: TYPE ~ MONITORED RECORD [
crH: CrRPC.Handle,
vfs: VFS
];
TC Virtual File System(s)
VFS: TYPE ~ REF VFSInstance;
VFSInstance: TYPE ~ RECORD [
fs: ROPE,
flavorSpecified: BOOL,
sD: ServerData
];
Server cache
ServerData: TYPE ~ REF ServerDataObject;
ServerDataObject: TYPE ~ RECORD [
qualified: XNSCHName.Name,
serverName: ROPE,
address: REF XNS.Address,
opened: BOOL,
version: INT16
];
dcedarVersion: INT16 ~ 311;  -- magic
Copied Constants
nullGMT: BasicTime.GMT ~ BasicTime.nullGMT;
helpful variables
startTime: BasicTime.GMT ¬ BasicTime.Now[];
startEnum: BasicTime.GMT ¬ BasicTime.nullGMT;
endEnum: BasicTime.GMT ¬ BasicTime.nullGMT;
startFileInfo: BasicTime.GMT ¬ BasicTime.nullGMT;
endFileInfo: BasicTime.GMT ¬ BasicTime.nullGMT;
startRetrieve: BasicTime.GMT ¬ BasicTime.nullGMT;
endRetrieve: BasicTime.GMT ¬ BasicTime.nullGMT;
Procs to provide PFS view
TCDelete: PFSClass.DeleteProc = {
PFSBackdoor.ProduceError[notImplemented, "Delete not allowed for TC servers"];
};
TCCopy: PFSClass.CopyProc = {
PFSBackdoor.ProduceError[notImplemented, "Copy not allowed for TC servers"];
};
TCEnumerateForInfo: PFSClass.EnumerateForInfoProc ~ {
[h: FSHandle, pattern: PATH, proc: PFS.InfoProc, lbound: PATH, hbound: PATH]
InfoProc: TYPE ~ PROC [fullFName, attachedTo: PATH, uniqueID: UniqueID, bytes: INT, mutability: Mutability, fileType: FileType]
RETURNS [continue: BOOLTRUE];
P: PROC [file: PATH, bytes: INT, created: GMT, version: Version]
RETURNS
[continue: BOOL] = {
continue ¬ proc[fullFName: file, attachedTo: NIL, uniqueID: MakeUID[created], bytes: bytes, mutability: immutable, fileType: PFS.tUnspecified]
};
InnerEnumerate[h, pattern, FALSE, P];
};
TCEnumerateForNames: PFSClass.EnumerateForNamesProc ~ {
[h: FSHandle, pattern: PATH, proc: PFS.NameProc, lbound: PATH, hbound: PATH]
NameProc: TYPE ~ PROC [name: PATH] RETURNS [continue: BOOLTRUE;
P: PROC [file: PATH, bytes: INT, created: GMT, version: Version]
RETURNS
[continue: BOOL] = {
continue ¬ proc[file]
};
InnerEnumerate[h, pattern, TRUE, P];
};
EnumerateProc: TYPE ~ PROC [--file:-- PATH, --bytes:-- INT, --created:-- GMT,
--version: -- Version] RETURNS [--continue:-- BOOL];
InnerEnumerate: PROC [h: FSHandle, pattern: PATH, namesOnly: BOOL,
proc: EnumerateProc] = {
Listing: CrRPC.BulkDataSink = {
[h: CrRPC.Handle, s: STREAM, checkAbort: CrRPC.BulkDataCheckAbortProc] RETURNS [abort: BOOL]
name: ROPE;
nameLength: INT16; -- if the name of the file is bigger than this we have a real problem.
created: INT; -- really BasicTime.GMT LOOPHOLEd
size: INT; -- number of bytes in the file
version: Version;
UNTIL IO.EndOf[self: s] DO
continue: BOOL ¬ FALSE;
i: INT ¬ 0;
Proc: PROC RETURNS [c: CHAR] = {
c ¬ IO.GetChar[self: s ! IO.EndOfStream => GOTO Error];
EXITS Error => ERROR PFS.Error[[$environment, $brainDamage, "TC protocol error"]];
};
nameLength ¬ Basics.Int16FromH[IO.GetHWord[self: s ! IO.EndOfStream => EXIT]];
created ¬ Basics.Int32FromF[IO.GetFWord[self: s ! IO.EndOfStream => GOTO Error]];
size ¬ Basics.Int32FromF[IO.GetFWord[self: s ! IO.EndOfStream => GOTO Error]];
name ¬ Rope.FromProc[len: nameLength, p: Proc];
version ¬ VersionFromName[name: name];
continue ¬ proc[--file:-- PFS.PathFromRope[name], --bytes:-- size, --created:-- LOOPHOLE[created], -- version: -- version];
IF NOT continue THEN RETURN[TRUE];
IF checkAbort[h] THEN RETURN[TRUE];
ENDLOOP;
RETURN[FALSE];
EXITS Error => ERROR PFS.Error[[$environment, $brainDamage, "TC protocol error"]];
};
class: PFSClass.FSHandle ~ h;
vfs: VFS ~ NARROW[class.data];
data: ServerData ~ vfs.sD;
we need to prefix names with leadingDir, so that full path names are returned - this is a PFS thing
crH: CrRPC.Handle ¬ GetConnection[data];
patRope: ROPE ¬ PFS.RopeFromPath[pattern];
toUse: ROPE ¬ IF data.version # dcedarVersion THEN patRope
ELSE Rope.Cat["/", vfs.fs, patRope];
{ -- for ENABLE --
ENABLE UNWIND => { ReturnConnection[crH] };
P: PROC = {
TrickleChargeP9813V411.EnumerateForInfo[h: crH, pattern: toUse, info: Listing];
ReturnConnection[crH];
};
startEnum ¬ BasicTime.Now[];
CallProtected[proc: P, h: h, filePath: NIL, uid: PFS.nullUniqueID];
endEnum ¬ BasicTime.Now[];
};
};
TCGetInfo: PFSClass.GetInfoProc ~ {
h: FSHandle, file: OpenFile] RETURNS [fullFName, attachedTo: PATH, uniqueID: UniqueID, bytes: INT, mutability: PFS.Mutability, fileType: PFS.FileType
vfs: VFS ~ NARROW[h.data];
Generic Implementation
fullFName ¬ file.fullFName;
attachedTo ¬ file.attachedTo;
uniqueID ¬ file.uniqueID;
bytes ¬ file.bytes;
mutability ¬ file.mutability;
fileType ¬ file.fileType;
};
TCFileInfo: PFSClass.FileInfoProc = {
mutability ← immutable;  -- to keep mimosa happy
bytes ← 0;
fileType ← PFS.tUnspecified;
PFSBackdoor.ProduceError[code: notImplemented,
explanation: "FileInfo not allowed for TC servers"];
};
TCFileInfo: PFSClass.FileInfoProc = {
[h: FSHandle, file: PATH, wantedUniqueID: UniqueID]
RETURNS [version: Version, attachedTo: PATH, bytes: INT, uniqueID: UniqueID, mutability: PFS.Mutability, fileType: PFS.FileType]
class: PFSClass.FSHandle ~ h;
vfs: VFS ~ NARROW[class.data];
data: ServerData ~ vfs.sD;
crH: CrRPC.Handle ¬ GetConnection[data];
{ -- for ENABLE --
ENABLE UNWIND => ReturnConnection[crH];
P: PROC = {
created: GMT;
fullFName: ROPE; createInt: INT;
fileRope: ROPE ¬ PFS.RopeFromPath[file];
toUse: ROPE ¬ IF data.version # dcedarVersion THEN fileRope
ELSE Rope.Cat["/", vfs.fs, fileRope];
[fullFName: fullFName, bytes: bytes, created: createInt] ¬ TrickleChargeP9813V411.FileInfo[h: crH, name: toUse, wantedCreatedTime: LOOPHOLE[wantedUniqueID.egmt.gmt]];
ReturnConnection[crH];
version ¬ VersionFromName[name: fullFName];
created ¬ LOOPHOLE[createInt];
uniqueID ¬ MakeUID[created];
mutability ¬ immutable;
fileType ¬ PFS.tUnspecified;
attachedTo ¬ NIL;
};
startFileInfo ¬ BasicTime.Now[];
CallProtected[proc: P, h: h, filePath: file, uid: wantedUniqueID];
endFileInfo ¬ BasicTime.Now[];
};
};
TCRename: PFSClass.RenameProc = {
PFSBackdoor.ProduceError[code: notImplemented,
explanation: "Rename not allowed for TC servers"];
};
TCRetrieve: PFSClass.RetrieveProc ~ {
[h: FSHandle, file: PATH, wantedUniqueID: UniqueID, proc: PFS.RetrieveConfirmProc, checkFileType: BOOLFALSE, fileType: PFS.FileType]
RetrieveConfirmProc: TYPE ~ PROC [fullFName: PATH, bytes: INT, uniqueID: UniqueID]
RETURNS [STREAM];
class: PFSClass.FSHandle ~ h;
vfs: VFS ~ NARROW[class.data];
data: ServerData ~ vfs.sD;
crH: CrRPC.Handle ¬ GetConnection[data];
localStream: STREAM ¬ NIL;
Content: CrRPC.BulkDataSink = {
[h: CrRPC.Handle, s: STREAM, checkAbort: CrRPC.BulkDataCheckAbortProc] RETURNS [abort: BOOL]
buffer: REF TEXT = RefText.ObtainScratch[512];
nBytes: NAT;
abort ¬ FALSE;
DO
IF s.EndOf[] THEN EXIT; -- seems to end this way sometimes
nBytes ¬ s.GetBlock[buffer, 0];
IF nBytes = 0 THEN EXIT;
localStream.PutBlock[buffer, 0, nBytes];
ENDLOOP;
RefText.ReleaseScratch[buffer];
};
{ -- for ENABLE --
ENABLE UNWIND => ReturnConnection[crH];
P: PROC = {
version: Version; bytes: INT; created: GMT;
fullFName: ROPE; createInt: INT;
fileRope: ROPE ¬ PFS.RopeFromPath[file];
toUse: ROPE ¬ IF data.version # dcedarVersion THEN fileRope
ELSE Rope.Cat["/", vfs.fs, fileRope];
[fullFName: fullFName, bytes: bytes, created: createInt] ¬ TrickleChargeP9813V411.FileInfo[h: crH, name: toUse, wantedCreatedTime: LOOPHOLE[wantedUniqueID.egmt.gmt]];
version ¬ VersionFromName[name: fullFName];
created ¬ LOOPHOLE[createInt];
localStream ¬ proc[file, bytes, MakeUID[created]];
IF localStream # NIL THEN
TrickleChargeP9813V411.Retrieve[h: crH, name: toUse, wantedCreatedTime: LOOPHOLE[wantedUniqueID.egmt.gmt], data: Content];
ReturnConnection[crH];
};
startRetrieve ¬ BasicTime.Now[];
CallProtected[proc: P, h: h, filePath: file, uid: wantedUniqueID];
endRetrieve ¬ BasicTime.Now[];
};
};
TCStore: PFSClass.StoreProc = {
PFSBackdoor.ProduceError[code: notImplemented,
explanation: "Store not allowed for TC servers"];
};
TCSetAttributes: PFSClass.SetAttributesProc = {
PFSBackdoor.ProduceError[code: notImplemented,
explanation: "SetAttributes not allowed for TC servers"];
};
TCSetByteCountAndUniqueID: PFSClass.SetByteCountAndUniqueIDProc = {
PFSBackdoor.ProduceError[code: notImplemented,
explanation: "SetByteCountAndUniqueID not allowed for TC servers"];
};
TCSetClientProperty: PFSClass.SetClientPropertyProc = {
PFSBackdoor.ProduceError[code: notImplemented,
explanation: "SetClientProperty not allowed for TC servers"];
};
TCGetClientProperty: PFSClass.GetClientPropertyProc = {
PFSBackdoor.ProduceError[code: notImplemented,
explanation: "GetClientProperty not allowed for TC servers"];
};
TCEnumerateClientProperties: PFSClass.EnumerateClientPropertiesProc = {
PFSBackdoor.ProduceError[code: notImplemented,
explanation: "EnumerateClientProperties not allowed for TC servers"];
};
TCRead: PFSClass.ReadProc = {
bytesRead ¬ 0;
PFSBackdoor.ProduceError[code: notImplemented,
explanation: "Read not allowed for TC servers"];
};
TCWrite: PFSClass.WriteProc = {
bytesWritten ¬ 0;
PFSBackdoor.ProduceError[code: notImplemented,
explanation: "Write not allowed for TC servers"];
};
TCOpen: PFSClass.OpenProc = {
PFSBackdoor.ProduceError[code: notImplemented,
explanation: "Open not allowed for TC servers"];
RETURN[NIL];
};
TCClose: PFSClass.CloseProc = {
PFSBackdoor.ProduceError[code: notImplemented,
explanation: "Close not allowed for TC servers"];
};
TCLookup: PFSClass.LookupNameProc = {
PFSBackdoor.ProduceError[code: notImplemented,
explanation: "LookupName not allowed for TC servers"];
RETURN[file];
};
MakeUID: PROC[gmt: GMT] RETURNS[uid: PFS.UniqueID] =
{ uid.egmt.gmt ¬ gmt };
Utilities
EnsureOneLeadingSlash: PROC [maybeWithSlash: ROPE]
RETURNS [withSlash: ROPE] ~ {
assume no more than 2 leading //'s
IF ( maybeWithSlash.Fetch[0] # '/ ) THEN RETURN[Rope.Concat["/", maybeWithSlash] ];
IF ( maybeWithSlash.Fetch[1] # '/ ) THEN RETURN[maybeWithSlash];
RETURN[maybeWithSlash.Substr[1]];
};
RemoveLeadingSlash: PROC [maybeWithSlash: ROPE]
RETURNS [noSlash: ROPE] ~ {
noSlash ¬ IF ( maybeWithSlash.Fetch[0] = '/ ) THEN maybeWithSlash.Substr[1] ELSE maybeWithSlash;
};
ConvertFSNameToXNS: PROC [file: ROPE, op: Op] RETURNS [ROPE] = {
AddChar: PROC [c: CHAR] = {text[text.length] ¬ c; text.length ¬ text.length + 1};
name, versionR: ROPE ¬ NIL;
text: REF TEXT ¬ RefText.ObtainScratch[nChars: 200];
text.length ¬ 0;
FOR i: INT IN [0 .. Rope.Length[file]) DO
c: CHAR ~ Rope.Fetch[base: file, index: i];
SELECT c FROM
'< => LOOP; -- throw the < away.
'> => AddChar['/]; -- the XNS server uses / as a file separator
'* => {AddChar[c]; AddChar[c]}; -- kludge to handle *
'! => { -- the version part is next
versionR ¬ Rope.Substr[base: file, start: i]; -- the whole version including the !
EXIT;
};
ENDCASE => AddChar[c];
ENDLOOP;
IF versionR # NIL THEN {
SELECT Rope.Length[versionR] FROM
0 => versionR ¬ NIL; -- no version specified
1 => -- the version rope is just a ! so do the 'default' -- {
SELECT op FROM
delete => versionR ¬ "!-"; -- lowest
enumerate, enumerateNames => versionR ¬ NIL;
rename, retrieve => versionR ¬ "!+"; -- highest
store => versionR ¬ "!+"; -- is this right?
ENDCASE => ERROR; -- can't happen
};
2 => { -- is it H, L or *?
SELECT Rope.Fetch[versionR, 1] FROM
'h, 'H => versionR ¬ "!+";
'l, 'L => versionR ¬ "!-";
'* => versionR ¬ NIL;
ENDCASE => NULL;
};
ENDCASE => NULL;
};
name ¬ Rope.Concat[base: Rope.FromRefText[s: text], rest: versionR];
RefText.ReleaseScratch[t: text];
RETURN[name];
};
Connection management
GetConnection: PUBLIC PROC [data: ServerData] RETURNS [crH: CrRPC.Handle] = {
crH ¬ CrRPC.CreateClientHandle[$CMUX, data.address ! CrRPC.Error => MakeCrRPCError[errorReason, text]];
};
ReturnConnection: PUBLIC PROC [crH: CrRPC.Handle] = {
IF crH # NIL THEN CrRPC.DestroyClientHandle[crH ! CrRPC.Error => CONTINUE];
};
Registered with PFSClass
TCGetServer: PFSClass.GetHandleProc ~ {
[fs: ROPE, flavorSpecified: BOOL] RETURNS [h: FSHandle, downMsg: ROPE]
Returns [NIL, NIL] if server doesn't exist.
Returns [NIL, msg] if server exists but is down.
The flavorSpecified parameter is ignored.
vfs: VFS ~ NEW[VFSInstance ¬ [fs: fs, flavorSpecified: flavorSpecified, sD: NIL] ];
data: ServerData;
session: REF Session;
qualified: XNSCHName.Name; address: REF XNS.Address; name: ROPE;
crH: CrRPC.Handle;
opened: BOOL ¬ FALSE;
version: INT16 ¬ 0;
downMsg ¬ "can\'t connect";
{
ENABLE {
CrRPC.Error => {downMsg ¬ IO.PutFR["CrRPC Error: %g, %g", [rope[crRPCErrorRopes[errorReason]]], [rope[text]]]; CONTINUE};
TrickleChargeP9813V411.Error => {downMsg ¬ IO.PutFR1["Server reports %g", [rope[description]]]; CONTINUE};
};
add: XNS.Address;
[qualified, add] ¬ XNSCH.LookupAddressFromRope[fs];
IF add = XNS.unknownAddress THEN -- host not known -- RETURN[NIL, NIL];
name ¬ XNSCHName.RopeFromName[qualified];
add.socket ¬ XNSWKS.courier;
address ¬ NEW[XNS.Address ¬ add];
crH ¬ CrRPC.CreateClientHandle[$CMUX, address];
version ¬ TrickleChargeP9813V411.AnyBodyHome[h: crH];
CrRPC.DestroyClientHandle[crH];
opened ¬ TRUE;
};
IF NOT opened THEN RETURN [NIL, downMsg];
data ¬ NEW[ServerDataObject ¬ [qualified: qualified, serverName: name, address: address, opened: opened, version: version]];
vfs.sD ¬ data;
h ¬ NEW[PFSClass.FSObject ¬ vfsClass­];
h.name ¬ fs;
h.data ¬ vfs;
RETURN [h, NIL];
};
TCSweep: PFSClass.SweepProc -- [h: FSHandle, seconds: CARD] -- ~ {
doesn't need to do anything
vfs: VFS ~ NARROW[h.data];
data: ServerData ~ vfs.sD;
session: REF Session ¬ NIL; address: REF XNS.Address;
};
TCValidate: PFSClass.ValidateProc ~ {
[h: FSHandle] RETURNS [obsolete: BOOL, downMsg: ROPE]
class: PFSClass.FSHandle ~ h;
vfs: VFS ~ NARROW[class.data];
data: ServerData ~ vfs.sD;
RETURN [FALSE, NIL];
};
Error Reporting and Catching
CallProtected: PUBLIC PROC[proc: PROC, h: FSHandle, filePath: PATH, uid: PFS.UniqueID] ~ {
time: GMT ~ uid.egmt.gmt;
file: ROPE ~ PFS.RopeFromPath[filePath];
gName: ROPE ~ Rope.Cat["[", h.name, "]", file];
quotedGName: ROPE ~ Rope.Cat["\"", gName, "\""];
{ -- for ENABLE
ENABLE {
XNSStream.ConnectionClosed => PFSBackdoor.ProduceError[resourceLimitExceeded, IO.PutFR1["Server for %g closed the connection.", [rope[quotedGName]]]]; --???
CrRPC.Error => MakeCrRPCError[errorReason, text];
IO.EndOfStream => ERROR PFS.Error[[environment, $unexpectedClose,
IO.PutFR1["Server for %g closed the connection.", [rope[quotedGName]]]]];
IO.Error => ERROR PFS.Error[[bug, $unexpectedError,
IO.PutFR1["File: %g unexpected IO.Error.", [rope[quotedGName]]]]];
TrickleChargeP9813V411.Error => ERROR PFS.Error[[environment, $notSure,
IO.PutFR["Server for %g reported %g.", [rope[quotedGName]], [rope[description]]]]];
TrickleChargeP9813V411.FileNotFound => UnknownFile[gName, time];
PFSClass.Error => ERROR PFS.Error[[environment, code, msg]];
};
proc[ ! XNSStream.Timeout => RESUME]; -- since the timeout is retryable
};
};
VersionFromName: PROC[name: ROPE] RETURNS[Version] = {
path: PATH ~ PFS.PathFromRope[name];
RETURN[PFSNames.ShortName[path].version];
};
UnknownFile: PUBLIC PROC[name: ROPE, createdTime: GMT] = {
IF createdTime = nullGMT
THEN PFSBackdoor.ProduceError[unknownFile, IO.PutFR1["Could not find %g", [rope[name]]] ]
ELSE
PFSBackdoor.ProduceError[unknownUniqueID, IO.PutFR["Could not find \"%g\" created on %t", [rope[name]], [time[createdTime]]]];
};
REQUIREDROPE: TYPE ~ ROPE ¬;
crRPCErrorRopes: REF ARRAY CrRPC.ErrorReason OF REQUIREDROPE ~
NEW[ARRAY CrRPC.ErrorReason OF REQUIREDROPE ¬ [
unknown: "Unknown",
unknownClass: "unknownClass",
courierVersionMismatch: "courierVersionMismatch",
rejectedNoSuchProgram: "rejectedNoSuchProgram",
rejectedNoSuchVersion: "rejectedNoSuchVersion",
rejectedNoSuchProcedure: "rejectedNoSuchProcedure",
rejectedInvalidArgument: "rejectedInvalidArgument",
rejectedUnspecified: "rejectedUnspecified",
remoteError: "remoteError",
cantConnectToRemote: "Can't Connect To Remote",
argsError: "Args Error",
resultsError: "Results Error",
bulkDataError: "Bulk DataError",
protocolError: "Protocol Error",
remoteClose: "Remote Close",
communicationFailure: "Communication Failure",
notImplemented: "notImplemented",
unknownOperation: "unknownOperation",
notServerHandle: "notServerHandle",
notClientHandle: "notClientHandle",
addressInappropriateForClass: "addressInappropriateForClass",
other: "Other"
]];
MakeCrRPCError: PROC [errorReason: CrRPC.ErrorReason, text: ROPE] = {
r: ROPE ~ IO.PutFR["CrRPC Error: %g, %g", [rope[crRPCErrorRopes[errorReason]]], [rope[text]]];
ERROR PFS.Error[[$environment, $CrRPCError, r]];
};
FromPath: PROC[p: PFS.PATH] RETURNS[ROPE] ~
{ RETURN[PFS.RopeFromPath[p]] };
nullGMTRope: ROPE ~ "nullGMT";
TCStats: Commander.CommandProc ~ {
cmd.out.PutF1["***** Client started at %g\n", [time[startTime]] ];
cmd.out.PutF["startEnum: %g\n\t enumEnd: %g\n",
IF startEnum = BasicTime.nullGMT THEN [rope[nullGMTRope]] ELSE [time[startEnum]],
IF endEnum = BasicTime.nullGMT THEN [rope[nullGMTRope]] ELSE [time[endEnum]] ];
cmd.out.PutF["startFileInfo: %g\n\t enumEnd: %g\n",
IF startFileInfo = BasicTime.nullGMT THEN [rope[nullGMTRope]] ELSE [time[startFileInfo]],
IF endFileInfo = BasicTime.nullGMT THEN [rope[nullGMTRope]] ELSE [time[endFileInfo]] ];
cmd.out.PutF["startRetrieve: %g\n\t endRetrieve: %g\n",
IF startRetrieve = BasicTime.nullGMT THEN [rope[nullGMTRope]] ELSE [time[startRetrieve]],
IF endRetrieve = BasicTime.nullGMT THEN [rope[nullGMTRope]] ELSE [time[endRetrieve]] ];
};
Initialization
Init: PROC = {
PFSClass.Register[myFlavor, TCGetServer];
};
Init[];
Commander.Register["TCStats", TCStats, "Statistics about the TrickleChargeClient"];
END.