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.
*
* $Id: RCSUpdater.m3.html,v 1.3 2010-04-29 17:17:58 wagner Exp $
MODULE RCSUpdater;
IMPORT
Attic, CVProto, ErrMsg, FileAttr, FileStatus, FileUpdater, Logger,
OSError, Pathname, RCSError, RCSFile, RCSKeyword, RCSRevNum, Rd,
Receive, SupFileRec, SupMisc, Text, Thread, TokScan, Wr;
REVEAL
T = Public BRANDED OBJECT
attr: FileAttr.T;
rcsOptions: RCSFile.Options;
wantSum: TEXT;
OVERRIDES
init := Init;
update := Update;
isRCS := IsRCS;
END;
PROCEDURE Init(self: T;
attr: FileAttr.T;
rcsOptions: RCSFile.Options;
wantSum: TEXT): T =
BEGIN
self.attr := attr;
self.rcsOptions := rcsOptions;
self.wantSum := wantSum;
RETURN self;
END Init;
PROCEDURE IsRCS(<*UNUSED*> self: T): BOOLEAN =
BEGIN
RETURN TRUE;
END IsRCS;
PROCEDURE Update(self: T;
sfr: SupFileRec.T;
name: Pathname.T;
toAttic: BOOLEAN;
proto: CVProto.T;
trace: Logger.T;
protoRd: Rd.T;
wr: Wr.T;
VAR status: FileUpdater.Status)
RAISES {FileUpdater.Error, FileUpdater.FixupNeeded, Rd.EndOfFile,
Rd.Failure, Thread.Alerted, TokScan.Error, Wr.Failure} =
VAR
srcPath := SupMisc.CatPath(sfr.clientPrefix, name);
origSrcPath := srcPath;
fileChanged := FALSE;
oldAttr: FileAttr.T;
rf: RCSFile.T;
ts: TokScan.T;
cmd: TEXT;
cmdCh: CHAR;
branch: TEXT;
tagName: TEXT;
revNum: RCSRevNum.T;
diffBase: RCSRevNum.T;
date: TEXT;
author: TEXT;
expandText: TEXT;
expandMode: RCSKeyword.ExpandMode;
PROCEDURE NoteChange() =
BEGIN
IF NOT fileChanged THEN
IF toAttic THEN
Logger.Notice(trace, " Edit " & name & " -> Attic");
ELSE
Logger.Notice(trace, " Edit " & name);
END;
fileChanged := TRUE;
END;
END NoteChange;
BEGIN
TRY
rf := Attic.RCSFileOpenReadonly(srcPath);
TRY
oldAttr := RCSFile.GetAttr(rf);
self.attr := FileAttr.Merge(self.attr, oldAttr);
(* If the user wants to keep his files exactly the same as
the server, then we aim for byte-for-byte equality. In
that case, we have to write the file using the same
whitespace conventions that were used in the server's
file. Otherwise, we stick with whatever format is already
being used in the client's file. *)
IF SupFileRec.Option.ExactRCS IN sfr.options
AND rf.options # self.rcsOptions THEN
rf.options := self.rcsOptions;
NoteChange();
END;
LOOP
ts := proto.getCmd(protoRd);
cmdCh := ts.getChar("edit command");
cmd := Text.FromChar(cmdCh);
CASE cmdCh OF
| '.' =>
EXIT;
| 'B' => (* Set default branch. *)
NoteChange();
branch := ts.getToken("default branch");
ts.getEnd("end of \"" & cmd & "\" command");
Logger.Info(trace, " Set default branch to " & branch);
rf.branch := branch;
| 'b' => (* Clear default branch. *)
NoteChange();
ts.getEnd("end of \"" & cmd & "\" command");
Logger.Info(trace, " Clear default branch");
rf.branch := NIL;
| 'D' => (* Add delta. *)
NoteChange();
revNum := ts.getToken("revision number");
diffBase := ts.getToken("diffBase");
date := ts.getToken("date");
author := ts.getToken("author");
ts.getEnd("end of \"" & cmd & "\" command");
Logger.Info(trace, " Add delta " & revNum & " "
& date & " " & author);
TRY
EVAL Receive.Delta(protoRd, rf, revNum, diffBase,
date, author);
EXCEPT RCSError.E(msg) =>
RAISE FileUpdater.Error("Error adding delta: " & msg);
END;
| 'd' => (* Delete delta. *)
NoteChange();
revNum := ts.getToken("revision number");
ts.getEnd("end of \"" & cmd & "\" command");
Logger.Info(trace, " Delete delta " & revNum);
TRY
RCSFile.DeleteDelta(rf, RCSFile.GetDelta(rf, revNum));
EXCEPT RCSError.E(msg) =>
RAISE FileUpdater.Error("Error deleting delta: " & msg);
END;
| 'E' => (* Change keyword expansion mode. *)
NoteChange();
expandText := ts.getToken("expand mode");
ts.getEnd("end of \"" & cmd & "\" command");
TRY
expandMode := RCSKeyword.DecodeExpand(expandText);
EXCEPT RCSError.E(msg) => RAISE TokScan.Error(msg) END;
IF expandMode = RCSKeyword.ExpandMode.Default THEN
Logger.Info(trace, " Set keyword expansion to default");
ELSE
Logger.Info(trace, " Set keyword expansion to \""
& expandText & "\"");
END;
rf.expand := expandMode;
| 'T' => (* Add tag. *)
NoteChange();
tagName := ts.getToken("tag name");
revNum := ts.getToken("revision number");
ts.getEnd("end of \"" & cmd & "\" command");
Logger.Info(trace, " Add tag " & tagName & " -> " & revNum);
TRY
EVAL RCSFile.AddTag(rf, tagName, revNum);
EXCEPT RCSError.E(msg) =>
RAISE FileUpdater.Error("Error adding tag: " & msg);
END;
| 't' => (* Delete tag. *)
NoteChange();
tagName := ts.getToken("tag name");
revNum := ts.getToken("revision number");
ts.getEnd("end of \"" & cmd & "\" command");
Logger.Info(trace, " Delete tag " & tagName
& " -> " & revNum);
TRY
RCSFile.DeleteTag(rf, tagName, revNum);
EXCEPT RCSError.E(msg) =>
RAISE FileUpdater.Error("Error deleting tag: " & msg);
END;
ELSE
RAISE TokScan.Error("Invalid edit command \"" & cmd & "\"");
END;
END;
IF fileChanged THEN
status.updateType := FileUpdater.UpdateType.Edit;
ELSE
VAR
msg: TEXT;
BEGIN
status.updateType := FileUpdater.UpdateType.Touch;
IF FileAttr.Equal(self.attr,
FileAttr.MaskOut(oldAttr, FileAttr.AllButModTime)) THEN
msg := " SetAttrs ";
ELSE
msg := " Touch ";
END;
msg := msg & name;
IF toAttic THEN msg := msg & " -> Attic" END;
Logger.Notice(trace, msg);
END;
END;
TRY
RCSFile.ToWr(rf, wr);
EXCEPT RCSError.E(msg) =>
WITH errMsg = "Invalid RCS file: " & msg DO
IF SupFileRec.Option.ExactRCS IN sfr.options THEN
RAISE FileUpdater.FixupNeeded(errMsg);
ELSE (* No fixups allowed if local mods might be present. *)
RAISE FileUpdater.Error(errMsg);
END;
END;
END;
status.fs := NEW(FileStatus.T, name := name,
clientAttr := self.attr, serverAttr := self.attr);
IF toAttic THEN
status.fs.type := FileStatus.Type.FileDead;
ELSE
status.fs.type := FileStatus.Type.FileLive;
END;
status.fromAttic := srcPath # origSrcPath;
status.modified := fileChanged;
IF SupFileRec.Option.CheckRCS IN sfr.options THEN
status.wantSum := self.wantSum;
ELSE
status.wantSum := NIL;
END;
FINALLY
RCSFile.Close(rf);
END;
EXCEPT
| OSError.E(l) =>
FIXME - This exception can come from the Close(). We should change it
to raise RCSError.E instead, analogous to Wr.Close().
RAISE FileUpdater.Error("Cannot open: " & ErrMsg.StrError(l));
| RCSError.E(msg) =>
RAISE FileUpdater.Error("RCS file error: " & msg);
END;
END Update;
BEGIN
END RCSUpdater.