Copyright 1997-2003 John D. Polstra.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgment:
* This product includes software developed by John D. Polstra.
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
MODULE Receive;
Subroutines used by the various file updaters for receiving file data
from the network.
IMPORT
EscapedRd, RCSDelta, RCSError, RCSFile, RCSPhrase, RCSPhrases,
RCSRevNum, RCSString, Rd, RdCopy, SupMisc, Text, TextWr, Thread,
TokScan, Wr;
PROCEDURE Counted(rd: Rd.T;
wr: Wr.T;
size: CARDINAL;
withChecksum: BOOLEAN): TEXT
RAISES {Error, Rd.EndOfFile, Rd.Failure, Thread.Alerted, TokScan.Error,
Wr.Failure} =
VAR
ts: TokScan.T;
term: TEXT;
wantSum: TEXT;
errMsg: TEXT;
BEGIN
IF RdCopy.ToWriter(rd, wr, size) < size THEN
RAISE Rd.EndOfFile; (* Premature EOF from the wire. *)
END;
term := SupMisc.GetCmdLine(rd);
(* We used to raise an error if the file grew on the server while it
was being transferred. Now we just ignore that case and take the
number of bytes we originally decided upon, assuming we'll get the
rest on the next update. The previous policy caused big problems
for huge mail archive files which took a very long time to transfer
but were also grown frequently on the server host. *)
IF Text.Equal(term, ".") OR Text.Equal(term, ".>") THEN
errMsg := NIL;
ELSIF Text.Equal(term, ".<") THEN
errMsg := "File shrank on server";
ELSE
RAISE TokScan.Error("Invalid terminator for counted transfer");
END;
IF withChecksum THEN
ts := TokScan.New(SupMisc.GetCmdLine(rd));
ts.getLiteral("5");
wantSum := ts.getToken("checksum");
ts.getEnd("end of checksum for counted transfer");
ELSE
wantSum := NIL;
END;
IF errMsg # NIL THEN RAISE Error(errMsg) END;
RETURN wantSum;
END Counted;
PROCEDURE Delta(rd: Rd.T;
rf: RCSFile.T;
revNum: RCSRevNum.T;
diffBaseRev: RCSRevNum.T;
date: TEXT;
author: TEXT): RCSDelta.T
RAISES {RCSError.E, Rd.EndOfFile, Rd.Failure, Thread.Alerted,
TokScan.Error} =
VAR
line: TEXT;
ts: TokScan.T;
cmd: TEXT;
cmdCh: CHAR;
state: TEXT := "";
log: RCSString.T := NIL;
text: RCSString.T := NIL;
treePhrases: RCSPhrases.T := NIL;
textPhrases: RCSPhrases.T := NIL;
key: TEXT;
diffBase: RCSDelta.T;
wr: TextWr.T;
BEGIN
LOOP
line := SupMisc.GetCmdLine(rd);
IF Text.Equal(line, ".") THEN EXIT END;
ts := TokScan.New(line);
cmdCh := ts.getChar("AddDelta command");
cmd := Text.FromChar(cmdCh);
CASE cmdCh OF
| 'L' => (* Log. *)
ts.getEnd("end of \"" & cmd & "\" command");
<* FATAL Wr.Failure *>
BEGIN
wr := TextWr.New();
EVAL Escaped(rd, wr, withChecksum := FALSE);
log := RCSString.FromText(TextWr.ToText(wr));
END;
| 'N' => (* Tree newphrases. *)
key := ts.getToken("Newphrase key");
ts.getEnd("end of \"" & cmd & "\" command");
Phrase(rd, key, treePhrases);
| 'n' => (* Text newphrases. *)
key := ts.getToken("Newphrase key");
ts.getEnd("end of \"" & cmd & "\" command");
Phrase(rd, key, textPhrases);
| 'S' => (* State. *)
state := ts.getToken("AddDelta state");
ts.getEnd("end of \"" & cmd & "\" command");
| 'T' => (* Text. *)
ts.getEnd("end of \"" & cmd & "\" command");
<* FATAL Wr.Failure *>
BEGIN
wr := TextWr.New();
EVAL Escaped(rd, wr, withChecksum := FALSE);
text := RCSString.FromText(TextWr.ToText(wr));
END;
ELSE
RAISE TokScan.Error("Invalid AddDelta command \"" & cmd & "\"");
END;
END;
IF log = NIL THEN (* Just use an empty log. *)
log := RCSString.FromText("");
END;
IF text = NIL THEN
RAISE TokScan.Error("New delta has no TEXT");
END;
IF Text.Equal(diffBaseRev, ".") THEN
diffBase := NIL;
ELSE
diffBase := RCSFile.GetDelta(rf, diffBaseRev);
END;
RETURN RCSFile.AddDelta(rf,
revNum := revNum,
diffBase := diffBase,
date := date,
author := author,
state := state,
log := log,
text := text,
treePhrases := treePhrases,
textPhrases := textPhrases);
END Delta;
PROCEDURE Phrase(rd: Rd.T;
key: TEXT;
VAR phrases: RCSPhrases.T)
RAISES {Rd.EndOfFile, Rd.Failure, Thread.Alerted, TokScan.Error} =
VAR
line: TEXT;
ts: TokScan.T;
cmd: TEXT;
cmdCh: CHAR;
word: TEXT;
phrase := RCSPhrase.New(key);
BEGIN
LOOP
line := SupMisc.GetCmdLine(rd);
IF Text.Equal(line, ".") THEN EXIT END;
ts := TokScan.New(line);
cmdCh := ts.getChar("AddDelta command");
cmd := Text.FromChar(cmdCh);
CASE cmdCh OF
| 'W' =>
word := ts.getToken("Newphrase word");
ts.getEnd("end of \"" & cmd & "\" command");
RCSPhrase.Append(phrase, word, isString := FALSE);
| 'S' =>
ts.getEnd("end of \"" & cmd & "\" command");
<* FATAL Wr.Failure *>
VAR
wr := TextWr.New();
BEGIN
EVAL Escaped(rd, wr, withChecksum := FALSE);
word := TextWr.ToText(wr);
END;
RCSPhrase.Append(phrase, word, isString := TRUE);
ELSE
RAISE TokScan.Error("Invalid newphrases command \"" & cmd & "\"");
END;
END;
IF phrases = NIL THEN
phrases := RCSPhrases.New();
END;
RCSPhrases.Append(phrases, phrase);
END Phrase;
PROCEDURE Escaped(rd: Rd.T;
wr: Wr.T;
withChecksum: BOOLEAN): TEXT
RAISES {Rd.EndOfFile, Rd.Failure, Thread.Alerted, TokScan.Error,
Wr.Failure} =
VAR
eRd := NEW(EscapedRd.T).init(rd, closeChild := FALSE);
ts: TokScan.T;
checkSum: TEXT := NIL;
BEGIN
TRY
TRY
EVAL RdCopy.ToWriter(eRd, wr);
EXCEPT Rd.Failure(l) =>
IF l.head = EscapedRd.PrematureEOF THEN
RAISE Rd.EndOfFile;
ELSE
RAISE Rd.Failure(l);
END;
END;
FINALLY
Rd.Close(eRd);
END;
IF withChecksum THEN
ts := TokScan.New(SupMisc.GetCmdLine(rd));
ts.getLiteral("5");
checkSum := ts.getToken("checksum");
ts.getEnd("end of checksum for counted transfer");
END;
RETURN checkSum;
END Escaped;
BEGIN
END Receive.