---------------------------------------------------------------------------
MODULE Tag;
IMPORT TextSeq, Text, TextExtras AS TextEx, TextUtils, Version, SMsg AS Msg;
---------------------------------------------------------------------------
PROCEDURE Decompose(tag : TEXT) : TextSeq.T =
VAR
i : CARDINAL;
res := NEW(TextSeq.T).init(5);
elem : TEXT;
BEGIN
i := 0;
WHILE TextEx.FindSub(tag, "_", i) DO
elem := Text.Sub(tag, 0, i);
tag := Text.Sub(tag, i + 1);
i := 0;
res.addhi(TextUtils.SubstChar(elem, '=', '_'));
END;
IF NOT Text.Empty(tag) THEN
res.addhi(TextUtils.SubstChar(tag, '=', '_'));
END;
RETURN res;
END Decompose;
---------------------------------------------------------------------------
PROCEDURE CheckedName(t : TEXT) : TEXT =
VAR
from := ARRAY [0..12] OF CHAR
{'_', ' ', '\t', '#', ',', '.', '/', '$', '%', '@', '^', '&', '*'};
to := ARRAY [0..12] OF CHAR
{'=', '=', '=', '=', '=', '=', '=', '=', '=', '=', '=', '=', '='};
BEGIN
RETURN TextUtils.SubstChars(t, from, to);
END CheckedName;
---------------------------------------------------------------------------
PROCEDURE Compose(seq : TextSeq.T) : TEXT =
VAR
res : TEXT;
BEGIN
IF seq.size() = 0 THEN RETURN "" END;
res := CheckedName(seq.get(0));
FOR i := 1 TO seq.size() - 1 DO
WITH elem = seq.get(i) DO
res := res & "_" & CheckedName(elem);
END;
END;
RETURN res;
END Compose;
---------------------------------------------------------------------------
PROCEDURE New(tag : TEXT) : T =
BEGIN
IF tag = NIL THEN
Msg.Error("Tag.New(NIL)");
RETURN NIL;
ELSE
Msg.D("Tag.New(" & tag & ")");
END;
IF Text.Equal(tag, "head") OR
Text.Equal(tag, "headoftrunk") OR
Text.Equal(tag, "topoftrunk") OR
Text.Equal(tag, "head-of-trunk") OR
Text.Equal(tag, "top-of-trunk") OR
Text.Equal(tag, "top") THEN
RETURN Head;
ELSIF Text.Equal(tag, "headofbranch") OR
Text.Equal(tag, "tipofbranch") OR
Text.Equal(tag, "head-of-branch") OR
Text.Equal(tag, "tip-of-branch") OR
Text.Equal(tag, "tip") THEN
RETURN Tip;
ELSIF Text.Equal(tag, "last-release-branch") OR
Text.Equal(tag, "lastreleasebranch") OR
Text.Equal(tag, "latest-release-branch") OR
Text.Equal(tag, "latestreleasebranch") OR
Text.Equal(tag, "latest-tip") OR
Text.Equal(tag, "latesttip") OR
Text.Equal(tag, "last-tip") OR
Text.Equal(tag, "lasttip") THEN
RETURN LastTip;
END;
RETURN InitFromText(NEW(T), tag);
END New;
---------------------------------------------------------------------------
PROCEDURE NewCopy(tag : T) : T =
BEGIN
RETURN InitFromTag(NEW(T), tag);
END NewCopy;
---------------------------------------------------------------------------
PROCEDURE Construct(k : Kind; pkgname : TEXT;
major, minor, pl : CARDINAL;
stable := FALSE; change : TEXT := NIL;
branch := FALSE; suffix : TEXT := NIL;
start := FALSE) : T =
BEGIN
RETURN InitFromElements(NEW(T), k, pkgname, major, minor, pl, stable,
change, branch, suffix, start);
END Construct;
---------------------------------------------------------------------------
PROCEDURE NewStableBranch(tag : T) : T =
VAR t := NewCopy(tag);
BEGIN
t.t_attr := Attributes{Attribute.Stable, Attribute.Branch};
t.t_orig := NIL;
RETURN t;
END NewStableBranch;
---------------------------------------------------------------------------
PROCEDURE NewBranchStartTag(tag : T) : T =
VAR t := NewCopy(tag);
BEGIN
t.t_attr := Attributes{Attribute.BranchStart};
t.t_orig := NIL;
RETURN t;
END NewBranchStartTag;
---------------------------------------------------------------------------
PROCEDURE NewBranch(tag : T; kind : Kind := Kind.Undefined) : T =
VAR t := NewCopy(tag);
BEGIN
t.t_attr := Attributes{Attribute.Branch};
t.t_orig := NIL;
IF kind # Kind.Undefined THEN
t.t_kind := kind;
END;
t.t_seq := Decompose(Denotation(t, FALSE));
RETURN t;
END NewBranch;
---------------------------------------------------------------------------
PROCEDURE NewBranchStart(tag : T; kind : Kind := Kind.Undefined) : T =
VAR t := NewCopy(tag);
BEGIN
t.t_attr := Attributes{Attribute.BranchStart};
t.t_orig := NIL;
IF kind # Kind.Undefined THEN
t.t_kind := kind;
END;
t.t_seq := Decompose(Denotation(t, FALSE));
RETURN t;
END NewBranchStart;
---------------------------------------------------------------------------
PROCEDURE NewMergeTag(tag : T; mversion : Version.T := NIL) : T =
VAR t := NewCopy(tag);
BEGIN
t.t_attr := Attributes{Attribute.MergeMarker};
t.t_kind := Kind.Merge;
t.t_orig := NIL;
IF mversion # NIL THEN
t.t_mversion := mversion;
END;
(* for debugging...
WITH d = Denotation(t, checkIt := FALSE) DO
WITH r = t.initFromText(d) DO
RETURN r;
END;
END;
*)
RETURN t.initFromText(Denotation(t, checkIt := FALSE));
END NewMergeTag;
---------------------------------------------------------------------------
PROCEDURE KindFromText(t : TEXT) : Kind =
BEGIN
IF Text.Equal(t, "alpha") THEN
RETURN Kind.Alpha;
ELSIF Text.Equal(t, "beta") THEN
RETURN Kind.Beta;
ELSIF Text.Equal(t, "gamma") THEN
RETURN Kind.Gamma;
ELSIF Text.Equal(t, "merge") THEN
RETURN Kind.Merge;
ELSIF Text.Equal(t, "devel") THEN
RETURN Kind.Devel;
ELSIF Text.Equal(t, "release") THEN
RETURN Kind.Release;
ELSIF Text.Equal(t, "latest") THEN
RETURN Kind.Latest;
ELSIF Text.Equal(t, "change") THEN
RETURN Kind.Change;
ELSIF Text.Equal(t, "feature") THEN
RETURN Kind.Feature;
ELSIF Text.Equal(t, "fix") THEN
RETURN Kind.Fix;
ELSE
RETURN Kind.Undefined;
END;
END KindFromText;
---------------------------------------------------------------------------
PROCEDURE KindToText(k : Kind) : TEXT =
BEGIN
CASE k OF
Kind.Alpha => RETURN "alpha";
| Kind.Beta => RETURN "beta";
| Kind.Gamma => RETURN "gamma";
| Kind.Devel => RETURN "devel";
| Kind.Merge => RETURN "merge";
| Kind.Change => RETURN "change";
| Kind.Feature => RETURN "feature";
| Kind.Fix => RETURN "fix";
| Kind.Release => RETURN "release";
| Kind.Latest => RETURN "latest";
| Kind.Undefined => RETURN "undefined";
END;
END KindToText;
---------------------------------------------------------------------------
PROCEDURE KindToVersionKind(k : Kind) : CHAR =
BEGIN
CASE k OF
Kind.Alpha => RETURN 'a';
| Kind.Beta => RETURN 'b';
| Kind.Gamma => RETURN 'g';
| Kind.Devel => RETURN 'd';
| Kind.Merge => RETURN 'm';
| Kind.Change => RETURN 'c';
| Kind.Feature => RETURN 'f';
| Kind.Fix => RETURN 'F';
| Kind.Release => RETURN 'r';
| Kind.Latest => RETURN 'l';
| Kind.Undefined => RETURN 'u';
END;
END KindToVersionKind;
---------------------------------------------------------------------------
PROCEDURE Init(self : T) : T =
BEGIN
self.t_attr := Attributes{};
self.t_seq := NIL;
self.t_kind := Kind.Undefined;
self.t_version := NEW(Version.T).init();
self.t_mversion := NEW(Version.T).init();
self.t_pkgname := "";
self.t_orig := NIL;
self.t_change := NIL;
self.t_location := NIL;
RETURN self;
END Init;
---------------------------------------------------------------------------
PROCEDURE InitFromTag(self : T; tag : T) : T =
BEGIN
self.t_orig := tag.t_orig;
self.t_seq := tag.t_seq;
self.t_pkgname := tag.t_pkgname;
self.t_change := tag.t_change;
self.t_location := tag.t_location;
self.t_kind := tag.t_kind;
self.t_attr := tag.t_attr;
self.t_version := NEW(Version.T).init();
self.t_version.major := tag.t_version.major;
self.t_version.minor := tag.t_version.minor;
self.t_version.patchlevel := tag.t_version.patchlevel;
self.t_version.kind := tag.t_version.kind;
self.t_mversion := NEW(Version.T).init();
self.t_mversion.major := tag.t_mversion.major;
self.t_mversion.minor := tag.t_mversion.minor;
self.t_mversion.patchlevel := tag.t_mversion.patchlevel;
self.t_mversion.kind := tag.t_mversion.kind;
RETURN self;
END InitFromTag;
---------------------------------------------------------------------------
PROCEDURE InitFromText(self : T; tag : TEXT) : T =
VAR
v, w : TEXT;
vstart : CARDINAL;
i,n : CARDINAL := 0;
BEGIN
self.t_orig := tag;
self.t_seq := Decompose(tag);
self.t_attr := Attributes{};
n := self.t_seq.size();
IF i < n THEN
self.t_kind := KindFromText(self.t_seq.get(i));
INC(i);
ELSE
self.t_kind := Kind.Undefined;
END;
IF self.t_kind = Kind.Change OR
self.t_kind = Kind.Feature OR
self.t_kind = Kind.Fix THEN
IF i < n THEN
self.t_change := self.t_seq.get(i);
INC(i);
ELSE
self.t_change := "";
END;
END;
IF i < n THEN
self.t_pkgname := self.t_seq.get(i);
INC(i);
ELSE
self.t_pkgname := "";
END;
IF i < n THEN
v := self.t_seq.get(i);
IF Text.Equal(v, "head") OR Text.Equal(v, "stable") OR
Text.Equal(v, "branch") THEN
self.t_attr := self.t_attr + Attributes{Attribute.Branch};
IF Text.Equal(v, "stable") THEN
self.t_attr := self.t_attr + Attributes{Attribute.Stable};
END;
END;
IF Text.Equal(v, "start") THEN
self.t_attr := self.t_attr + Attributes{Attribute.BranchStart};
self.t_attr := self.t_attr - Attributes{Attribute.Branch};
self.t_attr := self.t_attr - Attributes{Attribute.Stable};
END;
INC(i);
vstart := i;
w := NIL;
WHILE i < n DO
WITH elem = self.t_seq.get(i) DO
INC(i);
IF self.t_location = NIL THEN
IF Text.Equal(elem, "at") THEN
self.t_location := elem;
ELSIF Text.Equal(elem, "stable") THEN
self.t_attr := self.t_attr + Attributes{Attribute.Stable};
self.t_attr := self.t_attr + Attributes{Attribute.Branch};
ELSIF Text.Equal(elem, "head") THEN
self.t_attr := self.t_attr + Attributes{Attribute.Branch};
ELSIF Text.Equal(elem, "start") THEN
self.t_attr := self.t_attr + Attributes{Attribute.BranchStart};
ELSIF Text.Equal(elem, "into") THEN
self.t_attr := self.t_attr + Attributes{Attribute.MergeMarker};
ELSIF Attribute.MergeMarker IN self.t_attr THEN
IF w = NIL THEN
w := elem;
ELSE
(* FIXME: This is somewhat wanting... *)
w := w & "_" & elem;
END;
ELSE
(* FIXME: This is somewhat wanting... *)
v := v & "_" & elem;
END;
ELSE
self.t_location := self.t_location & "_" & elem;
END;
END;
END;
self.t_version := NEW(Version.T).fromDir(v);
IF self.t_kind = Kind.Merge AND w # NIL THEN
self.t_mversion := NEW(Version.T).fromDir(w);
ELSE
(* We must not override the kind of version we merged into. *)
self.t_version.kind := KindToVersionKind(self.t_kind);
self.t_mversion := NEW(Version.T).init();
END;
ELSE
self.t_version := NEW(Version.T).init();
END;
RETURN self;
END InitFromText;
---------------------------------------------------------------------------
PROCEDURE InitFromElements(self : T; k : Kind; pkgname : TEXT;
major, minor, pl : CARDINAL;
stable := FALSE; change : TEXT := NIL;
branch := FALSE; suffix : TEXT := NIL;
start := FALSE) : T =
BEGIN
self.t_kind := k;
self.t_pkgname := pkgname;
self.t_version := NEW(Version.T).init();
self.t_version.major := major;
self.t_version.minor := minor;
self.t_version.patchlevel := pl;
self.t_version.kind := KindToVersionKind(k);
self.t_mversion := NEW(Version.T).init();
self.t_change := change;
self.t_location := suffix;
self.t_attr := Attributes{};
IF start THEN
self.t_attr := self.t_attr + Attributes{Attribute.BranchStart};
ELSIF branch THEN
self.t_attr := self.t_attr + Attributes{Attribute.Branch};
ELSIF stable THEN
self.t_attr := self.t_attr + Attributes{Attribute.Stable};
self.t_attr := self.t_attr + Attributes{Attribute.Branch};
ELSE
END;
IF k = Kind.Change OR k = Kind.Feature OR k = Kind.Fix THEN
IF self.t_change = NIL THEN
self.t_change := "noname";
END;
END;
self.t_seq := Decompose(Denotation(self, FALSE));
RETURN self;
END InitFromElements;
---------------------------------------------------------------------------
PROCEDURE OriginalText(self : T) : TEXT =
BEGIN
IF self.t_orig = NIL THEN
RETURN "";
ELSE
RETURN self.t_orig;
END;
END OriginalText;
---------------------------------------------------------------------------
PROCEDURE Denotation(self : T; checkIt := TRUE) : TEXT =
VAR t : TEXT;
BEGIN
IF self = Head THEN RETURN "head" END; (* Hack for special head value *)
IF self = Tip THEN RETURN "tip" END;
IF self = LastTip THEN RETURN "lasttip" END;
IF checkIt AND NOT self.okay() THEN
Msg.D(self.debugInfo());
IF self.t_orig # NIL THEN
RETURN self.t_orig & "[undefined_format]";
ELSE
RETURN "undefined_tag";
END;
END;
IF NOT checkIt AND self.t_orig # NIL THEN
RETURN self.t_orig;
END;
IF self.t_kind = Kind.Undefined THEN
IF self.t_orig # NIL THEN
RETURN self.t_orig & "[undefined_format]";
ELSE
RETURN "undefined_tag";
END;
END;
IF self.t_kind = Kind.Merge THEN
t := KindToText(self.t_kind) & "_" & CheckedName(self.t_pkgname) & "_" &
self.t_version.toDir() & "_into_" &
self.t_mversion.toDir();
ELSIF self.t_kind = Kind.Change OR
self.t_kind = Kind.Feature OR
self.t_kind = Kind.Fix THEN
t := KindToText(self.t_kind) & "_" & CheckedName(self.t_change) & "_" &
CheckedName(self.t_pkgname) & "_";
IF Attribute.BranchStart IN self.t_attr THEN
t := t & "start";
ELSIF Attribute.Branch IN self.t_attr THEN
t := t & "head";
ELSE
t := t & self.t_version.toDir();
END;
IF self.t_location # NIL THEN
(* Msg.T("XXX1: " & self.debugInfo()); *)
WHILE TextUtils.Pos(self.t_location, "at_") = 0 DO
self.t_location := Text.Sub(self.t_location, 3);
END;
t := t & "_at_" & self.t_location;
END;
RETURN t;
ELSE
t := KindToText(self.t_kind) & "_" & CheckedName(self.t_pkgname) & "_" &
self.t_version.toDir();
END;
IF Attribute.Stable IN self.t_attr THEN
VAR i := Text.FindCharR(t, '_'); BEGIN
t := Text.Sub(t, 0, i + 1) & "stable";
END;
ELSIF Attribute.BranchStart IN self.t_attr THEN
VAR i := Text.FindCharR(t, '_'); BEGIN
t := Text.Sub(t, 0, i + 1) & "start";
END;
END;
IF self.t_location # NIL THEN
(* Msg.T("XXX2: " & self.debugInfo()); *)
WHILE TextUtils.Pos(self.t_location, "at_") = 0 DO
self.t_location := Text.Sub(self.t_location, 3);
END;
t := t & "_at_" & self.t_location;
END;
RETURN t;
END Denotation;
---------------------------------------------------------------------------
PROCEDURE Base(self : T; level := 5) : TEXT =
VAR
seq : TextSeq.T;
BEGIN
self.t_seq := Decompose(Denotation(self, FALSE));
seq := NEW(TextSeq.T).init(level);
FOR i := 0 TO level - 1 DO
seq.addhi(self.t_seq.get(i));
END;
RETURN Compose(seq);
END Base;
---------------------------------------------------------------------------
PROCEDURE Okay(self : T) : BOOLEAN =
VAR n := self.t_seq.size();
PROCEDURE def(t: TEXT) : BOOLEAN =
BEGIN
RETURN t # NIL AND Text.Length(t) > 0;
END def;
BEGIN
IF NOT def(self.t_pkgname) THEN RETURN FALSE END;
IF self.t_kind = Kind.Merge THEN
RETURN n >= 9 AND self.t_version.defined();
ELSIF self.t_kind = Kind.Change OR
self.t_kind = Kind.Feature OR
self.t_kind = Kind.Fix THEN
IF NOT def(self.t_change) THEN RETURN FALSE END;
RETURN (n = 4 AND NOT def(self.t_location) OR
n > 4 AND def(self.t_location)) AND
(Attribute.BranchStart IN self.t_attr OR
Attribute.Branch IN self.t_attr)
OR
(n = 6 AND NOT def(self.t_location) OR
n > 6 AND def(self.t_location)) AND
NOT Attribute.BranchStart IN self.t_attr AND
NOT Attribute.Branch IN self.t_attr AND
self.t_version.defined();
END;
RETURN (n = 5 AND NOT def(self.t_location) OR
n > 5 AND def(self.t_location)) AND
self.t_version.defined();
(* FIXME: check some more *)
END Okay;
---------------------------------------------------------------------------
PROCEDURE Predefined(self : T) : BOOLEAN =
BEGIN
RETURN self = Head OR self = Tip OR self = LastTip;
END Predefined;
---------------------------------------------------------------------------
PROCEDURE IsKind(tag : TEXT; k : Kind) : BOOLEAN =
VAR
seq := Decompose(tag);
elem1 : TEXT;
BEGIN
IF seq.size() < 1 THEN RETURN FALSE END;
elem1 := seq.get(0);
CASE k OF
Kind.Alpha => RETURN Text.Equal(elem1, "alpha");
| Kind.Beta => RETURN Text.Equal(elem1, "beta");
| Kind.Gamma => RETURN Text.Equal(elem1, "gamma");
| Kind.Devel => RETURN Text.Equal(elem1, "devel");
| Kind.Merge => RETURN Text.Equal(elem1, "merge");
| Kind.Release => RETURN Text.Equal(elem1, "release");
| Kind.Latest => RETURN Text.Equal(elem1, "latest");
| Kind.Change => RETURN Text.Equal(elem1, "change");
| Kind.Feature => RETURN Text.Equal(elem1, "feature");
| Kind.Fix => RETURN Text.Equal(elem1, "fix");
| Kind.Undefined =>
RETURN NOT Text.Equal(elem1, "alpha") AND
NOT Text.Equal(elem1, "beta") AND
NOT Text.Equal(elem1, "gamma") AND
NOT Text.Equal(elem1, "devel") AND
NOT Text.Equal(elem1, "merge") AND
NOT Text.Equal(elem1, "change") AND
NOT Text.Equal(elem1, "feature") AND
NOT Text.Equal(elem1, "fix") AND
NOT Text.Equal(elem1, "latest") AND
NOT Text.Equal(elem1, "release");
END;
END IsKind;
---------------------------------------------------------------------------
PROCEDURE IsStableBranchTag(self : T) : BOOLEAN =
BEGIN
IF self.t_seq.size() # 5 THEN
self.t_attr := self.t_attr - Attributes{Attribute.Stable};
RETURN FALSE;
END;
IF Text.Equal(self.t_seq.get(4), "stable") THEN
self.t_attr := self.t_attr + Attributes{Attribute.Stable};
self.t_attr := self.t_attr + Attributes{Attribute.Branch};
RETURN TRUE;
ELSE
self.t_attr := self.t_attr - Attributes{Attribute.Stable};
RETURN FALSE;
END;
END IsStableBranchTag;
---------------------------------------------------------------------------
PROCEDURE IsChangeBranchTag(self : T) : BOOLEAN =
BEGIN
self.t_kind := KindFromText(self.t_seq.get(0));
IF self.t_kind # Kind.Change AND self.t_kind # Kind.Fix AND
self.t_kind # Kind.Feature THEN
RETURN FALSE;
END;
IF Text.Equal(self.t_seq.get(3), "head") THEN
self.t_attr := self.t_attr + Attributes{Attribute.Branch};
RETURN TRUE;
ELSE
RETURN FALSE;
END;
END IsChangeBranchTag;
---------------------------------------------------------------------------
PROCEDURE IsChangeFeatureOrFixTag(self : T) : BOOLEAN =
BEGIN
self.t_kind := KindFromText(self.t_seq.get(0));
RETURN self.t_kind = Kind.Change OR self.t_kind = Kind.Fix OR
self.t_kind = Kind.Feature;
END IsChangeFeatureOrFixTag;
---------------------------------------------------------------------------
PROCEDURE GetKind(self : T) : Kind =
BEGIN
RETURN self.t_kind;
END GetKind;
---------------------------------------------------------------------------
PROCEDURE GetKindAsText(self : T) : TEXT =
BEGIN
RETURN KindToText(self.t_kind);
END GetKindAsText;
---------------------------------------------------------------------------
PROCEDURE PackageName(self : T) : TEXT =
BEGIN
RETURN self.t_pkgname;
END PackageName;
---------------------------------------------------------------------------
PROCEDURE GetChangeName(self : T) : TEXT =
BEGIN
RETURN self.t_change;
END GetChangeName;
---------------------------------------------------------------------------
PROCEDURE GetLocationSuffix(self : T) : TEXT =
BEGIN
RETURN self.t_location;
END GetLocationSuffix;
---------------------------------------------------------------------------
PROCEDURE MergeDestVersion(self : T) : Version.T =
BEGIN
RETURN self.t_mversion;
END MergeDestVersion;
---------------------------------------------------------------------------
PROCEDURE GetVersion(self: T) : Version.T =
BEGIN
RETURN self.t_version;
END GetVersion;
---------------------------------------------------------------------------
PROCEDURE VersionAsText(self: T) : TEXT =
BEGIN
RETURN self.t_version.toDir();
END VersionAsText;
---------------------------------------------------------------------------
PROCEDURE SetNextMajorVersion(self : T) =
BEGIN
self.t_version.nextMajor();
self.t_orig := NIL;
END SetNextMajorVersion;
---------------------------------------------------------------------------
PROCEDURE SetNextMinorVersion(self : T) =
BEGIN
self.t_version.nextMinor();
self.t_orig := NIL;
END SetNextMinorVersion;
---------------------------------------------------------------------------
PROCEDURE SetNextPatchLevel(self : T) =
BEGIN
self.t_version.nextPatchLevel();
self.t_orig := NIL;
END SetNextPatchLevel;
---------------------------------------------------------------------------
PROCEDURE LatestTag(list : TextSeq.T; k : Kind) : T =
VAR
max, act :T;
BEGIN
max := NIL;
FOR i := 0 TO list.size() - 1 DO
WITH elem = list.get(i) DO
act := New(elem);
(* Msg.D("LatestTag: elem " & elem); *)
IF act.isStableBranchTag() THEN
act.t_version.patchlevel := 0;
act.t_attr := act.t_attr - Attributes{Attribute.Stable};
act.t_attr := act.t_attr - Attributes{Attribute.Branch};
act.t_orig := NIL;
END;
(* Msg.D("act: " & act.debugInfo()); *)
IF act.t_version.exactDefined() THEN
IF act.kind() = k AND act.okay() THEN
IF max = NIL THEN
(* Msg.D("new max (1)"); *)
max := act;
ELSIF max.t_version.less(act.t_version) THEN
(* Msg.D("new max (2)"); *)
max := act;
END;
END;
END;
END;
END;
IF max = NIL THEN
Msg.D("LatestTag --> NIL");
ELSE
Msg.D("LatestTag --> " & max.denotation(FALSE));
END;
RETURN max;
END LatestTag;
---------------------------------------------------------------------------
PROCEDURE ContainedInList(list : TextSeq.T; tag : T) : BOOLEAN =
VAR
act :T;
BEGIN
FOR i := 0 TO list.size() - 1 DO
WITH elem = list.get(i) DO
act := New(elem);
IF Equal(act, tag) THEN
RETURN TRUE;
END;
END;
END;
RETURN FALSE;
END ContainedInList;
---------------------------------------------------------------------------
PROCEDURE Equal(self, tag : T) : BOOLEAN =
BEGIN
IF (self.t_pkgname # NIL AND tag.t_pkgname = NIL) OR
(self.t_pkgname = NIL AND tag.t_pkgname # NIL) OR
((self.t_pkgname # NIL AND tag.t_pkgname # NIL) AND
NOT Text.Equal(self.t_pkgname, tag.t_pkgname)) OR
(self.t_change # NIL AND tag.t_change = NIL) OR
(self.t_change = NIL AND tag.t_change # NIL) OR
((self.t_change # NIL AND tag.t_change # NIL) AND
NOT Text.Equal(self.t_change, tag.t_change)) OR
NOT self.t_kind = tag.t_kind OR
NOT self.t_attr = tag.t_attr THEN
RETURN FALSE;
END;
IF self.t_kind = Kind.Merge THEN
RETURN Version.Equal(self.t_version, tag.t_version) AND
Version.Equal(self.t_mversion, tag.t_mversion);
ELSE
RETURN Version.Equal(self.t_version, tag.t_version);
END;
END Equal;
---------------------------------------------------------------------------
PROCEDURE Compare(self, tag : T) : [-1..1] =
VAR rtn : [-1..1];
BEGIN
IF self.t_location = NIL AND tag.t_location # NIL THEN
RETURN -1;
ELSIF self.t_location # NIL AND tag.t_location = NIL THEN
RETURN 1;
ELSIF self.t_location = NIL AND tag.t_location = NIL THEN
rtn := 0;
ELSE
rtn := Text.Compare(self.t_location, tag.t_location);
END;
IF rtn # 0 THEN RETURN rtn END;
IF self.t_change = NIL AND tag.t_change # NIL THEN
RETURN -1;
ELSIF self.t_change # NIL AND tag.t_change = NIL THEN
RETURN 1;
ELSIF self.t_change = NIL AND tag.t_change = NIL THEN
rtn := 0;
ELSE
rtn := Text.Compare(self.t_change, tag.t_change);
END;
IF rtn # 0 THEN RETURN rtn END;
IF self.t_pkgname = NIL AND tag.t_pkgname # NIL THEN
RETURN -1;
ELSIF self.t_pkgname # NIL AND tag.t_pkgname = NIL THEN
RETURN 1;
ELSIF self.t_pkgname = NIL AND tag.t_pkgname = NIL THEN
rtn := 0;
ELSE
rtn := Text.Compare(self.t_pkgname, tag.t_pkgname);
END;
IF rtn # 0 THEN
RETURN rtn;
ELSIF self.t_kind < tag.t_kind THEN
RETURN -1;
ELSIF self.t_kind > tag.t_kind THEN
RETURN 1;
ELSIF self.t_attr < tag.t_attr THEN
RETURN -1;
ELSIF self.t_attr > tag.t_attr THEN
RETURN 1;
ELSIF Version.Less(self.t_version, tag.t_version) THEN
RETURN -1;
ELSIF NOT Version.Equal(self.t_version, tag.t_version) THEN
RETURN 1;
ELSIF self.t_kind = Kind.Merge THEN
IF Version.Less(self.t_mversion, tag.t_mversion) THEN
RETURN -1;
ELSIF NOT Version.Equal(self.t_mversion, tag.t_mversion) THEN
RETURN 1;
END;
END;
RETURN 0;
END Compare;
---------------------------------------------------------------------------
PROCEDURE CompareFromText(tag1, tag2 : TEXT) : [-1..1] =
VAR
t1 := NEW(T).initFromText(tag1);
t2 := NEW(T).initFromText(tag2);
BEGIN
RETURN t1.compare(t2);
END CompareFromText;
---------------------------------------------------------------------------
PROCEDURE DebugInfo(self : T) : TEXT =
PROCEDURE NonNil(t : TEXT) : TEXT =
BEGIN
IF t = NIL THEN RETURN "NIL" END;
RETURN t;
END NonNil;
VAR res : TEXT;
BEGIN
IF self = NIL THEN
RETURN "NIL";
END;
res := "kind: " & KindToText(self.t_kind);
res := res & ", pkgname: " & NonNil(self.t_pkgname);
res := res & ", change: " & NonNil(self.t_change);
res := res & ", location: " & NonNil(self.t_location);
res := res & "\nversion: " & self.t_version.toDir();
IF self.t_version.defined() THEN
res := res & " (defined)";
ELSE
res := res & " (undefined)";
END;
IF self.t_mversion # NIL THEN
res := res & ", mversion: " & self.t_mversion.toDir();
IF self.t_mversion.defined() THEN
res := res & " (defined)";
ELSE
res := res & " (undefined)";
END;
END;
IF Attribute.Branch IN self.t_attr THEN
res := res & ", is branch";
END;
IF Attribute.Stable IN self.t_attr THEN
res := res & ", is stable";
END;
IF Attribute.BranchStart IN self.t_attr THEN
res := res & ", is start";
END;
res := res & "\norig: " & NonNil(self.t_orig);
IF self.t_seq = NIL THEN
res := res & ", seq: NIL";
ELSE
res := res & ", seq: ";
FOR i := 0 TO self.t_seq.size() -1 DO
res := res & self.t_seq.get(i) & "|";
END;
END;
IF self.okay() THEN
res := res & " OK";
ELSE
res := res & " NOT OK";
END;
RETURN res;
END DebugInfo;
BEGIN (* Tag *)
Head := NEW(T).initFromText("head_any_0_0_0");
Tip := NEW(T).initFromText("tip_any_0_0_0");
LastTip := NEW(T).initFromText("lasttip_any_0_0_0");
END Tag.