BridgeFTPOpsImpl.mesa
Copyright Ó 1987, 1988, 1989, 1992 by Xerox Corporation. All rights reserved.
Demers, October 12, 1990 12:22 pm PDT
Tim Diebert: September 11, 1989 9:49:57 am PDT
Willie-s, January 29, 1992 6:21 pm PST
DIRECTORY
BasicTime,
BridgeComm,
BridgeFTPOps,
Convert,
IO,
NetworkStream,
Process,
Rope
;
BridgeFTPOpsImpl: CEDAR PROGRAM
IMPORTS BasicTime, BridgeComm, Convert, IO, NetworkStream, Process, Rope
EXPORTS BridgeFTPOps
~ {
ROPE: TYPE ~ Rope.ROPE;
STREAM: TYPE ~ IO.STREAM;
NetworkStreamPair: TYPE ~ BridgeComm.NetworkStreamPair;
ActionProc: TYPE ~ BridgeFTPOps.ActionProc;
RetrieveStream: PUBLIC PROC [nsp: NetworkStreamPair, remoteName: ROPE, action: ActionProc, clientData: REF ¬ NIL, msgStream: STREAM ¬ NIL] RETURNS [excuse: ROPE ¬ NIL]
~ {
ENABLE {
ABORTED => {
excuse ¬ "aborted";
CONTINUE };
BridgeComm.Error => {
PutMsg[msgStream, "Communication error: %g\n", msg];
excuse ¬ "Bridge communication error";
CONTINUE };
IO.Error, NetworkStream.Error => {
PutMsg[msgStream, "I/O error\n"];
excuse ¬ "I/O error";
CONTINUE };
};
ansMsg: CHAR;
ansArg: ROPE;
ansArgNumeric: CARD;
actionProcQuit: BOOL;
PutMsg[msgStream, "Retrieving from %g ... ", remoteName];
[ansMsg, ansArg] ¬ BridgeComm.PutMsgWithReply[nsp, 'I, remoteName]; -- Open for input.
SELECT ansMsg FROM
'Y => NULL; -- ansArg is really aaa.bbb where aaa is the iNode and bbb is the mtime
'N => { PutMsg[msgStream, "%g\n", ansArg]; RETURN [ansArg] }; -- ansArg = error like not found
ENDCASE => { PutMsg[msgStream, "Protocol error\n"]; RETURN ["Protocol error"] };
[ansMsg, ansArg] ¬ BridgeComm.PutMsgWithReply[nsp, 'R]; -- Read current file
SELECT ansMsg FROM
'D => NULL; -- ansArg is the number of bytes in the file as reported by stat
'N => { PutMsg[msgStream, "%g\n", ansArg]; RETURN [ansArg] }; -- stat error
ENDCASE => { PutMsg[msgStream, "Protocol error\n"]; RETURN ["Protocol error"] };
The following tries to work around host failure to send back-to-back EOM's reliably ...
ansArgNumeric ¬ Convert.CardFromRope[ansArg -- really bytes in the file
! Convert.Error => ansArgNumeric ¬ CARD.LAST];
IF ansArgNumeric = 0
THEN {
Process.PauseMsec[500] -- Kludge -- ; actionProcQuit ¬ FALSE;
}
ELSE {
actionProcQuit ¬ action[nsp, ansArgNumeric, NIL]; -- send the bits. Should only get ansArgNumeric (byte count)
IF actionProcQuit THEN IO.Reset[nsp.in];
};
WHILE NetworkStream.GetStreamState[nsp.in, TRUE].streamState # open DO NULL ENDLOOP;
[] ¬ BridgeComm.PutMsgWithReply[nsp, 'i]; -- close the file
PutMsg[msgStream, IF actionProcQuit THEN "aborted\n" ELSE "ok\n"];
};
MkTempName: PROC [name: ROPE] RETURNS [tempName: ROPE] ~ {
tempName ¬ IO.PutFR["%g.%g", [rope[name]], [cardinal[BasicTime.ToNSTime[BasicTime.Now[]]]] ];
};
StoreStream: PUBLIC PROC [nsp: NetworkStreamPair, remoteName: ROPE, overwrite: BOOL, action: ActionProc, clientData: REF ¬ NIL, msgStream: STREAM ¬ NIL] RETURNS [excuse: ROPE ¬ NIL]
~ {
ENABLE {
ABORTED => {
excuse ¬ "aborted";
CONTINUE };
BridgeComm.Error => {
PutMsg[msgStream, "Communication error: %g\n", msg];
excuse ¬ "Bridge communication error";
CONTINUE };
IO.Error, NetworkStream.Error => {
PutMsg[msgStream, "I/O error\n"];
excuse ¬ "I/O error";
CONTINUE };
};
ansMsg: CHAR;
ansArg: ROPE;
tempName: ROPE;
actionProcQuit: BOOL;
IF overwrite
THEN {
PutMsg[msgStream, "Writing %g ... ", remoteName];
[ansMsg, ansArg] ¬ BridgeComm.PutMsgWithReply[nsp, 'O, remoteName];
SELECT ansMsg FROM
'Y => NULL;
'N => { PutMsg[msgStream, "%g\n", ansArg]; RETURN [ansArg] };
ENDCASE => { PutMsg[msgStream, "Protocol error\n"]; RETURN ["Protocol error"] };
BridgeComm.PutMsg[nsp, 'W];
actionProcQuit ¬ action[nsp, -1, NIL];
NetworkStream.SendEndOfMessage[nsp.out];
[ansMsg, ansArg] ¬ BridgeComm.GetMsg[nsp];
SELECT ansMsg FROM
'Y => NULL;
'N => { PutMsg[msgStream, "%g\n", ansArg]; RETURN [ansArg] };
ENDCASE => { PutMsg[msgStream, "Protocol error\n"]; RETURN ["Protocol error"] };
[] ¬ BridgeComm.PutMsgWithReply[nsp, 'o];
TODO: IF actionProcQuit then delete the file! (someday we'll have file delete?)
PutMsg[msgStream, IF actionProcQuit THEN "aborted\n" ELSE "ok\n"];
}
ELSE {
PutMsg[msgStream, "Storing to temporary ... "];
tempName ¬ MkTempName[remoteName];
[ansMsg, ansArg] ¬ BridgeComm.PutMsgWithReply[nsp, 'N, tempName];
SELECT ansMsg FROM
'Y => NULL;
'N => { PutMsg[msgStream, "%g\n", ansArg]; RETURN [ansArg] };
ENDCASE => { PutMsg[msgStream, "Protocol error\n"]; RETURN ["Protocol error"] };
BridgeComm.PutMsg[nsp, 'W];
actionProcQuit ¬ action[nsp, -1, NIL];
NetworkStream.SendEndOfMessage[nsp.out];
[ansMsg, ansArg] ¬ BridgeComm.GetMsg[nsp];
SELECT ansMsg FROM
'Y => NULL;
'N => { PutMsg[msgStream, "%g\n", ansArg]; RETURN [ansArg] };
ENDCASE => { PutMsg[msgStream, "Protocol error\n"]; RETURN ["Protocol error"] };
[ansMsg, ansArg] ¬ BridgeComm.PutMsgWithReply[nsp, 'o];
SELECT ansMsg FROM
'Y => NULL;
'N => { PutMsg[msgStream, "%g\n", ansArg]; RETURN [ansArg] };
ENDCASE => { PutMsg[msgStream, "Protocol error\n"]; RETURN ["Protocol error"] };
IF actionProcQuit
THEN {
PutMsg[msgStream, "aborted\n"];
}
ELSE {
PutMsg[msgStream, "renaming temporary to %g ... ", remoteName];
[ansMsg, ansArg] ¬ BridgeComm.PutMsgWithReply[nsp, 'M,
Rope.Cat[tempName, " ", remoteName]];
SELECT ansMsg FROM
'Y => NULL;
'N => { PutMsg[msgStream, "%g\n", ansArg]; RETURN [ansArg] };
ENDCASE => { PutMsg[msgStream, "Protocol error\n"]; RETURN ["Protocol error"] };
PutMsg[msgStream, "ok\n"];
};
};
};
PutMsg: PROC [s: STREAM, fmt: ROPE, msg: ROPE ¬ NIL] ~ {
ENABLE IO.Error => CONTINUE;
IF (s # NIL) THEN IO.PutF1[s, fmt, IF msg # NIL THEN [rope[msg]] ELSE [null[]]];
};
}...