bug-cvs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Feature request/ideas


From: Frank Hemer
Subject: Re: Feature request/ideas
Date: Tue, 8 Mar 2005 22:18:46 +0100
User-agent: KMail/1.5.1

Here is a more detailed description of the tag-extensions:

Terminologie:

Following the mainline (backwards):
Starting from a specific rev. number X on the trunk or a branch, the line is
followed backwards up to the first revision Z on the trunk. It doesn't matter
if there are dead or missing revisions in between. If this first revision is
dead, and its the 1.1 rev., stop here.
If 1.1 is not dead, and 1.1 was created at the same time the first vendor
branch rev. was created, jump to the vendor branch, and take the latest
revision prior to the date of Y (Z is the predecessor of Y).
Otherwise take 1.1.

Absolute tag means a tag that specifies either a revision or a trunk/branch.
'.trunk', 'n.u.m.e.r.i.c' and symbolic tags are absolute tags.

Relative tag means a tag that specifies a revision relative to a certain 
starting point.
'.prev', '.next', '.root', '.origin' '.head' and '.base' are relative tags.

The 'focused branch' is used as a synonym for either the trunk or the branch,
depending on the sticky tag of the addressed file or the workdir.
If a relative tag is in head position, the absolute position is calculated from:
1. The sandbox revision's tag
2. The sandbox revision's dir-tag
3. The sandbox revisions revision number
4. The mainline
in this order, whatever is first available.

Absolute tags and relative tags may be combined. If a relative tag follows an
absolute tag, the result will be absolute. 

All relative tags are _only_ valid for individual files, not for directories.
It doesn't matter where the relative tag appears in a combined tag.

'.trunk': Serves as a synonym for the trunk-branch. Resolves to the most recent
revision of the mainline, and allows to be used as a branch tag if combined
with a date ('xxx -r ".trunk:2005-03-08 18:00:00").

'.origin': Will always resolve to the very first revision.
If a file has been added on a branch, .origin will resolve to the first 
revision on
that branch, otherwise it will follow the mainline.

'.root': Will resolv to  the predecessor of the first revision on the focused 
branch.

'.base': The current sandbox revision. May only be used in head position.

'.head': The most recent revision on the focused branch.

'.prev': The previous revision on the focused branch. Follows the mainline.

'.next': The next revision on the focused branch. Does _not_ follow the
mainline as this would prevent access to revisions on a vendor branch
that were introduced _after_ the first commit/merge onto the trunk.

If a combined tag with relative extensions is used for commands that change
the local sandbox and set sticky tags, the resulting numeric revision is used
for sticky tagging. This is because tags like .trunk.prev.prev imho don't really
make sense -
'.prev', '.next' will force usage of numeric sticky tags in any position, and 
all 
elative tags will if used not in head position. If the combined tag ends with
'.head', this will overwrite -


I hope I have addressed all issues concerning the new tags,
and would very much appreciate some feedback and maybe
some results from testing ...:-)

>>>>>>>>>>>>>>>>>>>>>>>>>> the patch >>>>>>>>>>>>>>>>>>>>>>>


Index: src/admin.c
===================================================================
RCS file: /home/frank/CVS_ROOT/ccvs/src/admin.c,v
retrieving revision 1.1.1.3
retrieving revision 1.2
diff -b -B -u -r1.1.1.3 -r1.2
--- src/admin.c 8 Mar 2005 01:18:17 -0000       1.1.1.3
+++ src/admin.c 8 Mar 2005 19:37:41 -0000       1.2
@@ -655,6 +655,10 @@
        char *branch = &admin_data->branch[2];
        if (*branch != '\0' && ! isdigit ((unsigned char) *branch))
        {
+           if (*branch == '.' && !strcmp (branch+1, TAG_TRUNK))
+               branch = NULL;
+           else
+           {
            branch = RCS_whatbranch (rcs, admin_data->branch + 2);
            if (branch == NULL)
            {
@@ -663,6 +667,7 @@
                status = 1;
            }
        }
+       }
        if (status == 0)
            RCS_setbranch (rcs, branch);
        if (branch != NULL && branch != &admin_data->branch[2])
Index: src/commit.c
===================================================================
RCS file: /home/frank/CVS_ROOT/ccvs/src/commit.c,v
retrieving revision 1.1.1.2
retrieving revision 1.4
diff -b -B -u -r1.1.1.2 -r1.4
--- src/commit.c        2 Mar 2005 01:36:28 -0000       1.1.1.2
+++ src/commit.c        2 Mar 2005 01:40:09 -0000       1.4
@@ -698,6 +698,10 @@
     Lock_Cleanup ();
     dellist (&mulist);
 
+    char *commitid = Xasprintf ("@%s", global_session_id);
+    tag_check_valid (commitid, argc, argv, local, aflag, "", true);
+    free (commitid);
+
     /* see if we need to sleep before returning to avoid time-stamp races */
     if (
 #ifdef SERVER_SUPPORT
@@ -884,8 +888,10 @@
                           finfo->fullname);
                    goto out;
                }
-               if (status == T_MODIFIED && vers->tag &&
-                   !RCS_isbranch (finfo->rcs, vers->tag))
+               if (status == T_MODIFIED && vers->tag)
+               {
+                  if ( (*(vers->tag) != '.' || strcmp (vers->tag+1, TAG_TRUNK))
+                        && !RCS_isbranch (finfo->rcs, vers->tag) )
                {
                    error (0, 0,
                           "sticky tag `%s' for file `%s' is not a branch",
@@ -893,6 +899,7 @@
                    goto out;
                }
            }
+           }
            if (status == T_MODIFIED && !force_ci && vers->ts_conflict)
            {
                /*
Index: src/cvs.h
===================================================================
RCS file: /home/frank/CVS_ROOT/ccvs/src/cvs.h,v
retrieving revision 1.1.1.1
retrieving revision 1.6
diff -b -B -u -r1.1.1.1 -r1.6
--- src/cvs.h   27 Feb 2005 15:02:24 -0000      1.1.1.1
+++ src/cvs.h   8 Mar 2005 01:14:59 -0000       1.6
@@ -242,6 +242,14 @@
  */
 #define        TAG_HEAD        "HEAD"
 #define        TAG_BASE        "BASE"
+#define TAG_DOTHEAD     "head"
+#define TAG_DOTBASE     "base"
+#define TAG_COMMITID    "commitid"
+#define TAG_PREVIOUS    "prev"
+#define TAG_TRUNK       "trunk"
+#define TAG_ORIGIN      "origin"
+#define TAG_ROOT        "root"
+#define TAG_NEXT        "next"
 
 /* Environment variable used by CVS */
 #define        CVSREAD_ENV     "CVSREAD"       /* make files read-only */
Index: src/import.c
===================================================================
RCS file: /home/frank/CVS_ROOT/ccvs/src/import.c,v
retrieving revision 1.1.1.1
retrieving revision 1.4
diff -b -B -u -r1.1.1.1 -r1.4
--- src/import.c        27 Feb 2005 15:02:24 -0000      1.1.1.1
+++ src/import.c        8 Mar 2005 13:13:28 -0000       1.4
@@ -440,6 +440,10 @@
        error (0, errno, "cannot remove %s", tmpfile);
     free (tmpfile);
 
+    char *commitid = Xasprintf ("@%s", global_session_id);
+    tag_check_valid (commitid, argc, argv, 0, 1, "", true);
+    free (commitid);
+
     if (message)
        free (message);
     free (repository);
Index: src/rcs.c
===================================================================
RCS file: /home/frank/CVS_ROOT/ccvs/src/rcs.c,v
retrieving revision 1.1.1.4
retrieving revision 1.27
diff -b -B -u -r1.1.1.4 -r1.27
--- src/rcs.c   8 Mar 2005 20:57:00 -0000       1.1.1.4
+++ src/rcs.c   8 Mar 2005 20:58:20 -0000       1.27
@@ -69,6 +69,8 @@
 };
 
 static RCSNode *RCS_parsercsfile_i (FILE * fp, const char *rcsfile);
+static char *RCS_getdatetrunk (RCSNode * rcs, const char *date,
+                               int force_tag_match);
 static char *RCS_getdatebranch (RCSNode * rcs, const char *date,
                                 const char *branch);
 static void rcsbuf_open (struct rcsbuffer *, FILE *fp,
@@ -98,6 +100,13 @@
 static void free_rcsnode_contents (RCSNode *);
 static void free_rcsvers_contents (RCSVers *);
 static void rcsvers_delproc (Node * p);
+static char *RCS_getroot (RCSNode *, const char *);
+static char *RCS_getprevious (RCSNode *, const char *);
+static char *RCS_getnext (RCSNode *, const char *);
+static char *RCS_getorigin (RCSNode *, const char *);
+static char *RCS_getcommitid (RCSNode *, const char *, bool);
+static char *RCS_gethead (RCSNode *, const char *);
+static char *translate_tag (RCSNode *, const char *);
 static char *translate_symtag (RCSNode *, const char *);
 static char *RCS_addbranch (RCSNode *, const char *);
 static char *truncate_revnum_in_place (char *);
@@ -2130,6 +2139,12 @@
     {
        char *branch, *rev;
 
+       if (*tag == '.' && !strcmp (tag+1, TAG_TRUNK))
+       {
+           return RCS_getdatetrunk (rcs, date, force_tag_match);
+       }
+       else
+       {
        if (! RCS_nodeisbranch (rcs, tag))
        {
            /* We can't get a particular date if the tag is not a
@@ -2148,6 +2163,7 @@
        free (branch);
        return rev;
     }
+    }
     else if (tag)
        return RCS_gettag (rcs, tag, force_tag_match, simple_tag);
     else if (date)
@@ -2167,6 +2183,8 @@
  *    returns the magic branch number.
  * -- If tag is a branch tag, returns the branch number, not
  *    the revision of the head of the branch.
+ * -- An exception is made for '.trunk' as it returns the
+ *    head revision of the trunk
  * If tag or revision is not valid or does not exist in file,
  * return NULL.
  */
@@ -2232,15 +2250,15 @@
        error (1, 0, "revision `%s' does not exist", tag);
     }
 
-
-    RCS_check_tag (tag); /* exit if not a valid tag */
-
     /* If tag is "HEAD", special case to get head RCS revision */
     if (tag && STREQ (tag, TAG_HEAD))
         return RCS_head (rcs);
 
-    /* If valid tag let translate_symtag say yea or nay. */
-    rev = translate_symtag (rcs, tag);
+    /* If valid tag let translate_tag say yea or nay. */
+    char *tmp = RCS_extract_tag (tag, true);
+    if (tmp)
+        free (tmp);
+    rev = translate_tag (rcs, tag);
 
     if (rev)
         return rev;
@@ -2286,12 +2304,12 @@
 #endif
            return RCS_head (rcs);
 
-    if (!isdigit ((unsigned char) symtag[0]))
+    if (!isrevnumonly (symtag))
     {
        char *version;
 
        /* If we got a symbolic tag, resolve it to a numeric */
-       version = translate_symtag (rcs, symtag);
+       version = translate_tag (rcs, symtag);
        if (version != NULL)
        {
            int dots;
@@ -2521,10 +2539,10 @@
     assert (rcs != NULL);
 
     /* numeric revisions are easy -- even number of dots is a branch */
-    if (isdigit ((unsigned char) *rev))
+    if (isrevnumonly (rev))
        return (numdots (rev) & 1) == 0;
 
-    version = translate_symtag (rcs, rev);
+    version = translate_tag (rcs, rev);
     if (version == NULL)
        return 0;
     dots = numdots (version);
@@ -2575,7 +2593,7 @@
        return NULL;
 
     /* now, look for a match in the symbols list */
-    version = translate_symtag (rcs, rev);
+    version = translate_tag (rcs, rev);
     if (version == NULL)
        return NULL;
     dots = numdots (version);
@@ -2746,11 +2764,11 @@
     if (RCS_nodeisbranch (rcs, rev))
        return RCS_getbranch (rcs, rev, 1);
 
-    if (isdigit ((unsigned char) *rev))
+    if (isrevnumonly (rev))
        num = xstrdup (rev);
     else
     {
-       num = translate_symtag (rcs, rev);
+       num = translate_tag (rcs, rev);
        if (num == NULL)
            return NULL;
     }
@@ -2889,7 +2907,29 @@
            return retval;
     }
 
-    /* otherwise if we have a trunk, try it */
+    /* otherwise if we have a trunk, use it */
+    return RCS_getdatetrunk (rcs, date, force_tag_match);
+}
+
+
+
+/*
+ * Look up the last element on the trunk that was put in before or on
+ * the specified date and time (return the rev or NULL)
+ * Follow the vendor branch if not found on the trunk
+ */
+static char *
+RCS_getdatetrunk (RCSNode *rcs, const char *date, int force_tag_match)
+{
+    Node *p = NULL;
+    RCSVers *vers = NULL;
+    char *retval = NULL;
+
+    assert(rcs);
+
+    if (rcs->flags & PARTIAL)
+        RCS_reparsercsfile (rcs, NULL, NULL);
+
     if (rcs->head)
     {
        p = findnode (rcs->versions, rcs->head);
@@ -2904,7 +2944,7 @@
            vers = p->data;
            if (RCS_datecmp (vers->date, date) <= 0)
            {
-               cur_rev = vers->version;
+                retval = vers->version;
                break;
            }
 
@@ -2925,10 +2965,10 @@
      */
 
     /* if we found what we're looking for, and it's not 1.1 return it */
-    if (cur_rev != NULL)
+    if (retval != NULL)
     {
-       if (! STREQ (cur_rev, "1.1"))
-           return xstrdup (cur_rev);
+       if (! STREQ (retval, "1.1"))
+           return xstrdup (retval);
 
        /* This is 1.1;  if the date of 1.1 is not the same as that for the
           1.1.1.1 version, then return 1.1.  This happens when the first
@@ -2945,7 +2985,6 @@
        }
     }
 
-    /* look on the vendor branch */
     retval = RCS_getdatebranch (rcs, date, CVSBRANCH);
 
     /*
@@ -3182,15 +3221,613 @@
 
 
 /*
- * Return the version associated with a particular symbolic tag.
+ * Find the previous revision
+ *
+ * RETURN
+ *     Branch: return the HEAD-1 revision or NULL
+ *     Revision: return the previous revision or NULL
+ */
+static char *
+RCS_getprevious (RCSNode *rcs, const char *rev)
+{
+    char *retval = NULL;
+    int dots = numdots (rev);
+
+    if (!dots)
+        return NULL;
+
+    char *tmp = xstrdup (rev);
+    bool magic = false;
+
+    /* remove ev. magic branch num */
+    if (dots > 2 && (dots & 1))
+    {
+        int i;
+        const char *p = rev;
+        for (i = 1; i < dots; ++i)
+            p = strchr (p+1, '.');
+        assert (p);
+        if (!strncmp (p, ".0.", 3))
+        {
+            strcpy (tmp + (p - rev), p + 2);
+       --dots;
+       magic = true;
+        }
+    }
+
+    if (!(dots & 1)) /* branch handling */
+    {
+        /* we must prevent jumping from vendor branch to trunk */
+        char *p = RCS_branch_head (rcs, tmp);
+        if (p)
+        {
+            retval = previous_rev (rcs, p);
+           if (STREQ (retval, "1.1") && !magic)
+           {
+                free (retval);
+               retval = NULL;
+           }
+        }
+        else
+        {
+            p = xstrdup (rev);
+           if (RCS_exist_rev (rcs, p))
+            {
+               *strrchr (tmp, '.') = '\0';
+               if (magic || dots != 2 || !STREQ (tmp, "1.1"))
+                   retval = xstrdup (tmp);
+           }
+        }
+        free (p);
+    }
+    else if (dots > 2) /* revision on a branch */
+    {
+        if (rcs->flags & PARTIAL)
+           RCS_reparsercsfile (rcs, NULL, NULL);
+
+        /* find the root revision */
+        *strrchr (tmp, '.') = '\0';
+        *strrchr (tmp, '.') = '\0';
+
+        RCSVers *vers = NULL;
+        Node *node = findnode (rcs->versions, tmp);
+        if (node)
+       {
+            vers = node->data;
+           Node *head = vers->branches->list;
+           Node *br;
+           for (br = head->next; br != head; br = br->next)
+           {
+               if (node = findnode (rcs->versions, br->key))
+               {
+                   char *p = NULL;
+                   while (node != NULL)
+                   {
+                       vers = node->data;
+                       if (STREQ (vers->version, rev))
+                       {
+                           if (p)
+                               retval = xstrdup (p);
+                           else if (magic || dots > 3 || strncmp (rev, "1.1.", 
4))
+                               retval = xstrdup (tmp);
+                           break;
+                       }
+
+                       /* if there is a next version, find the node */
+                       if (vers->next != NULL)
+                       {
+                           p = vers->version;
+                           node = findnode (rcs->versions, vers->next);
+                       }
+                       else
+                           node = NULL;
+                   }
+               }
+           }
+       }
+    }
+    else /* revision on trunk */
+    {
+        char *prev = NULL;
+       char *curdate = NULL;
+
+       if (rcs->flags & PARTIAL)
+           RCS_reparsercsfile (rcs, NULL, NULL);
+
+        if (!STREQ (tmp, "1.1"))
+       {
+            Node *node = findnode (rcs->versions, tmp);
+           if (node)
+           {
+               RCSVers *v = node->data;
+               curdate = v->date;
+               if (v->next && (node = findnode (rcs->versions, v->next)))
+               {
+                   free (tmp);
+                   prev = ((RCSVers *)node->data)->version;
+                   tmp = xstrdup (prev);
+               }
+           }
+        }
+
+        if (prev && STREQ (tmp, "1.1"))
+        {
+            /* This is 1.1;  if the date of 1.1 is the same as that for
+            * the VENDOR.1 version, then return VENDOR.1 version. The date
+            * differs if the first version of a file is created by a
+            * regular cvs add and commit, and there is a subsequent cvs
+            * import of the same file ==> return 1.1.
+            * If 1.1 is dead, the file was initially added on a branch
+            * ==> return NULL.
+            */
+            RCSVers *vers = NULL;
+           Node *node = findnode (rcs->versions, tmp);
+           if (node)
+           {
+               vers = node->data;
+               if (!vers->dead)
+               {
+                   char *date_1_1 = vers->date;//vers 1.1
+
+                   Node *head = vers->branches->list;
+                   Node *br;
+                   for (br = head->next; br != head; br = br->next)
+                   {
+                       if (node = findnode (rcs->versions, br->key))
+                       {
+                           vers = node->data;
+                           if (!RCS_datecmp (vers->date, date_1_1))
+                           {
+                               retval = vers->version;
+                               while (vers->next)
+                               {
+                                   node = findnode (rcs->versions, vers->next);
+                                   if (node)
+                                   {
+                                       vers = node->data;
+                                       if (RCS_datecmp (curdate, vers->date) > 
0)
+                                       {
+                                           vers = node->data;
+                                           retval = vers->version;
+                                           continue;
+                                       }
+                                   }
+                                   break;
+                               }
+                               retval = xstrdup (retval);
+                           }
+                       }
+                   }
+               }
+           }
+        }
+       if (!retval && prev)
+           retval = xstrdup (prev);
+    }
+    free (tmp);
+
+    return retval;
+}
+
+
+
+/*
+ * Find the next revision
+ *
+ * RETURN
+ *     Branch: NULL (There cannot be a revision)
+ *     Revision: returns the next revision or NULL
+ */
+static char *
+RCS_getnext (RCSNode *rcs, const char *rev)
+{
+    Node *node = NULL;
+    RCSVers *vers = NULL;
+    char *retval = NULL;
+    const char *cmp = rev;
+    int dots = numdots (rev);
+
+    if (rcs->flags & PARTIAL)
+       RCS_reparsercsfile (rcs, NULL, NULL);
+
+    if (dots > 1)
+    {
+        node = findnode (rcs->versions, rev);
+       if (!node)
+           return NULL;
+
+       vers = node->data;
+
+       /* if there is a next version, find the node */
+       if (vers->next != NULL)
+       {
+           node = findnode (rcs->versions, vers->next);
+           if (node)
+               return xstrdup (((RCSVers *)node->data)->version);
+       }
+    }
+    else if (rcs->head)
+    {
+       node = findnode (rcs->versions, rcs->head);
+       if (node)
+       {
+           RCSVers *next = NULL;
+           vers = NULL;
+           while (node != NULL)
+           {
+               vers = next;
+               next = node->data;
+               if (next && STREQ (next->version, cmp))
+               {
+                   if (vers)
+                       retval = xstrdup (vers->version);
+                   break;
+               }
+                  
+               /* if there is a next version, find the node */
+               if (next->next != NULL)
+                   node = findnode (rcs->versions, next->next);
+               else
+                   node = NULL;
+           }
+       }
+    }
+
+    return retval;
+}
+
+
+
+/*
+ * Find the origin revision, which is the first revision
+ * on either the trunk or the branch that rev lives on.
+ */
+static char *
+RCS_getorigin (RCSNode *rcs, const char * rev)
+{
+    char *prev = NULL;
+    char *retval = xstrdup (rev);
+
+    if (RCS_exist_rev (rcs, retval))
+    {
+        bool magic = false;
+        int dots = numdots (retval);
+
+       if (dots > 2)
+       {
+           int i;
+           char *p = retval;
+           for (i = 1; i < dots; ++i)
+               p = strchr (p+1, '.');
+           if (!strncmp (p, ".0.", 3))
+           {
+               memmove (p, p+2, strlen (p+2) + 1);
+               magic = true;
+               --dots;
+           }
+       }
+       
+       while (dots > 1)
+       {
+           char *tmp = retval;
+           retval = RCS_getbranchpoint (rcs, tmp);
+           free (tmp);
+
+           if (retval && (((dots = numdots (retval)) > 3) || strncmp (retval, 
"1.1.", 4)) )
+           {
+               *strrchr (retval, '.') = '\0';
+               *strrchr (retval, '.') = '\0';
+               dots -= 2;
+           }
+           else
+           {
+               break;
+           }
+
+       }
+
+       if (retval && dots == 1)
+       {
+           RCSVers *vers;
+           char *prev = NULL;
+
+           Node *p = findnode (rcs->versions, retval);
+           while (p)
+           {
+               vers = p->data;
+
+               /* if there is a next version, find the node */
+               if (vers->next != NULL)
+               {
+                   prev = vers->version;
+                   p = findnode (rcs->versions, vers->next);
+               }
+               else
+               {
+                   char *p = retval;
+                   retval = xstrdup (vers->version);
+                   free (p);
+                   break;
+               }
+           }
+
+           if (STREQ (retval, "1.1"))
+           {
+               /* This is 1.1;  if the date of 1.1 is the same as that for
+                * the VENDOR.1 version, then return VENDOR.1 version. The date
+                * differs if the first version of a file is created by a
+                * regular cvs add and commit, and there is a subsequent cvs
+                * import of the same file.
+                */
+               char *tmp = retval;
+               retval = NULL;
+               RCSVers *vers = NULL;
+               Node *node = findnode (rcs->versions, tmp);
+               if (node)
+               {
+                   vers = node->data;
+                   if (!vers->dead)
+                   {
+                       char *date_1_1 = vers->date;//vers 1.1
+           
+                       Node *head = vers->branches->list;
+                       Node *br;
+                       for (br = head->next; br != head; br = br->next)
+                       {
+                           if (node = findnode (rcs->versions, br->key))
+                           {
+                               vers = node->data;
+                               if (!RCS_datecmp (vers->date, date_1_1))
+                               {
+                                   retval = xstrdup (vers->version);
+                                   free (tmp);
+                                   break;
+                               }
+                           }
+                       }
+                       if (!retval)
+                           retval = tmp;
+                   }
+                   else
+                   {
+                       retval = xstrdup (prev);
+                       free (tmp);
+                   }
+               }
+           }
+       }
+    }
+    else
+    {
+        free (retval);
+       retval = NULL;
+    }
+
+    return retval;
+}
+
+
+
+/*
+ * Find the branchpoint, no matter if rev points
+ * to a branch or a revision
+ */
+static char *
+RCS_getroot (RCSNode *rcs, const char *rev)
+{
+    char *retval = NULL;
+    int dots = numdots (rev);
+    
+    if (dots > 1)
+    {
+        retval = xstrdup (rev);
+        if (dots & 1)
+       {
+           int i;
+           char *p = retval;
+           for (i = 1; i < dots; ++i)
+               p = strchr (p+1, '.');
+           *p = '\0';
+       }
+       else
+       {
+           char *p = strrchr (retval, '.');
+           *p = '\0';
+       }
+    }
+
+    return retval;
+}
+
+
+
+/*
+ * Find the commitid, if prev is true return the
+ * previous revision 
+ */
+static char *
+RCS_getcommitid (RCSNode *rcs, const char *commitid, bool prev)
+{
+    Node *p;
+    RCSVers *oldvers = NULL;
+
+    if (rcs->flags & PARTIAL)
+        RCS_reparsercsfile (rcs, NULL, NULL);
+
+    Node *head = rcs->versions->list;
+    for (p = head->next; p != head; p = p->next)
+    {
+        RCSVers *vers = (RCSVers *)p->data;
+
+       Node *info = findnode (vers->other_delta, "commitid");
+       if (info != NULL)
+           if (!strcmp (info->data, commitid))
+           {
+               RCSVers *newvers = NULL;
+               if (!prev)
+                   return xstrdup (vers->version);
+               if (p->next)
+                   newvers = (RCSVers *)p->next->data;
+               else
+                   newvers = NULL;
+               if (numdots (vers->version)==1)
+                   return newvers ? xstrdup (newvers->version) : NULL;
+               else
+                   return oldvers ? xstrdup (oldvers->version) : NULL;
+           }
+       oldvers = vers;
+    }
+
+    return NULL;
+}
+
+
+
+/*
+ * Find the head of branch of current revision
+ */
+static char *
+RCS_gethead (RCSNode *rcs, const char *rev)
+{
+    char *retval = NULL;
+    int dots = numdots (rev);
+    if (!dots)
+        return NULL;
+
+    char *tmp = xstrdup (rev);
+
+    /* remove ev. magic branch num */
+    if (dots > 2 && (dots & 1))
+    {
+        int i;
+        const char *p = rev;
+        for (i = 1; i < dots; ++i)
+            p = strchr (p+1, '.');
+        assert (p);
+        if (!strncmp (p, ".0.", 3))
+        {
+            strcpy (tmp + (p - rev), p + 2);
+           --dots;
+        }
+    }
+
+    if (dots > 1)
+    {
+        if (dots & 1)
+           *(strrchr (tmp, '.')) = '\0';
+       retval = RCS_branch_head (rcs, tmp);
+    }
+    else
+        retval = xstrdup (rcs->head);
+    free (tmp);
+
+    return retval;
+}
+
+
+
+/* Translate special/symbolic tags into their revision
+ * If tag is numeric, rely on its formating.
+ * If tag is symbolic, resolv it. Then check for
+ * tag extensions. If an extension is found, resolv it.
  * Returns NULL or a newly malloc'd string.
  */
 static char *
-translate_symtag (RCSNode *rcs, const char *tag)
+translate_tag (RCSNode *rcs, const char *tag)
 {
+    char c;
+    char *tmp = NULL;
+    char *retval = NULL;
+    char *tmpval = NULL;
+    bool dotstart = false;
+
     if (rcs->flags & PARTIAL)
        RCS_reparsercsfile (rcs, NULL, NULL);
 
+    if (tag[0] == '@')
+    {
+        /* handle cvsnt compatibility stuff */
+        if ( tag[1] == '<')
+           return RCS_getcommitid (rcs, tag+2, true);
+       else
+           return RCS_getcommitid (rcs, tag+1, false);
+    }
+
+    tmpval = xstrdup (tag);
+    if (isdigit ((unsigned char) *tmpval)) {
+        /* extract initial revision num */
+        char * p = tmpval;
+        do
+           ++p;
+       while (isdigit ((unsigned char) *p) || *p == '.');
+       tmp = p;
+       *(--p) = '\0';
+       retval = xstrdup (tmpval);
+    }
+    else
+        tmp = tmpval;
+
+    char *token = strtok(tmp,".");
+    if (token)
+    {
+        bool force;
+       do
+       {
+           force = false;
+           if (!retval)
+           {
+               if (token == tmp)
+                   retval = translate_symtag (rcs, token);
+               else if (STREQ (token, TAG_TRUNK))
+                   retval = RCS_head (rcs);
+               else if (STREQ (token, TAG_COMMITID))
+               {
+                   char *commitid = commitid = strtok (NULL,".");
+                   if (token = strtok (NULL,"."))
+                       force = true;
+                   if (commitid)
+                   {
+                      bool previous = (token && STREQ (token, TAG_PREVIOUS));
+                      retval = RCS_getcommitid (rcs, commitid, previous);
+                   }
+               }
+               else
+                   error (1, 0, "Tag '%s': invalid head: '%s'", token, tag);
+           }
+           else
+           {
+               char *p = retval;
+               if (STREQ (token, TAG_DOTHEAD))
+                   retval = RCS_gethead (rcs, p);
+               else if (STREQ (token, TAG_PREVIOUS))
+                   retval = RCS_getprevious (rcs, p);
+               else if (STREQ (token, TAG_ORIGIN))
+                   retval = RCS_getorigin (rcs, p);
+               else if (STREQ (token, TAG_ROOT))
+                   retval = RCS_getroot (rcs, p);
+               else if (STREQ (token, TAG_NEXT))
+                   retval = RCS_getnext (rcs, p);
+               else
+                   error (1, 0, "Tag '%s': invalid extension: '%s'", token, 
tag);
+               free (p);
+           }
+       }
+       while (force || (retval && (token = strtok (NULL, "."))) );
+    }
+    free (tmpval);
+
+    return retval;
+}
+
+
+
+/*
+ * Return the version associated with a particular symbolic tag.
+ * Returns NULL or a newly malloc'd string.
+ */
+static char *
+translate_symtag (RCSNode *rcs, const char *tag)
+{
     if (rcs->symbols != NULL)
     {
        Node *p;
@@ -3335,6 +3972,159 @@
 
 
 /*
+ * Do some consistency checks ...
+ * If a symbolic tag is found, return a newly allocated string
+ * Allow only:
+ * SYMTAG | SYMTAG[.prev]* | .trunk[.prev]* | HEAD | BASE | x.x.x[.prev]*
+ *
+ * ERRORS
+ *   fatal errors are generated for illegal syntax
+ */
+char *
+RCS_extract_tag (const char *tag, bool files)
+{
+    char *retval = NULL;
+    const char *p = tag;
+    bool first = true;
+
+    assert(p);
+
+    if (*p == '@')
+    {
+        if (*(p+1) == '<')
+       {
+           retval = xstrdup (p+1);
+           *retval = '@';
+       }
+       else
+           retval = xstrdup (p);
+       return retval;
+    }
+    else
+        p = tag;
+
+    if (strstr (p,".."))
+        error (1, 0, "\
+Numeric tag %s invalid.  Numeric tags should be of the form X[.X]...", tag);
+
+    bool dot = false;
+    while (p)
+    {
+        if (*p == '.')
+           dot = true;
+        else if (!isdigit ((unsigned char) *p))
+           break;
+       else if (dot && first)
+           error (1, 0, "\
+Numeric tag '%s' invalid. Numeric tags should be of the form X[.X]...", tag);
+       else
+       {
+           dot = false;
+           first = false;
+       }
+       ++p;
+    }
+
+    if ( (*p && !first && !dot) || tag[strlen (tag)-1] == '.')
+        error (1, 0, "\
+Tag '%s' is invalid. Combined tags should be of the form X[.X]...", tag);
+
+    /* handle HEAD and BASE */
+    if (first
+         && (STREQ (tag, TAG_HEAD)
+               || (STREQ (tag, TAG_BASE)) ))
+       return NULL;
+
+    char *prev = NULL;
+    char *tmp = xstrdup (p);
+    char *token = strtok(tmp,".");
+    if (token)
+    {
+       do
+       {
+           if (first)
+           {
+               if (!dot)
+               {
+                   if (!STREQ (token, TAG_TRUNK)
+                       && !STREQ (token, TAG_COMMITID)
+                       && !STREQ (token, TAG_PREVIOUS)
+                       && !STREQ (token, TAG_NEXT)
+                       && !STREQ (token, TAG_DOTHEAD)
+                       && !STREQ (token, TAG_DOTBASE)
+                       && !STREQ (token, TAG_ORIGIN)
+                       && !STREQ (token, TAG_ROOT))
+                   {
+                      RCS_check_tag (token);
+                      retval = xstrdup (token);
+                      first = false;
+                   }
+                   else
+                       error (1, 0,"\
+Tag '%s' invalid. Reserved expression without leading dot: '%s'", tag, token);
+               }
+               else if (STREQ (token, TAG_TRUNK))
+                   first = false;
+               else if (STREQ (token, TAG_COMMITID))
+               {
+                   token = strtok (NULL,".");
+                   if (token)
+                   {
+                       retval = Xasprintf ("@%s",token);
+                       first = false;
+                   }
+               }
+               else if (!files)
+                   error (1, 0,"\
+Tag '%s' invalid. Tag must not be relative: '.%s'", tag, token);
+               else if (STREQ (token, TAG_ORIGIN)
+                        || STREQ (token, TAG_DOTHEAD))
+               {
+                  first = false;
+                  prev = token;
+               }
+               else if (STREQ (token, TAG_PREVIOUS)
+                        || STREQ (token, TAG_NEXT)
+                        || STREQ (token, TAG_DOTBASE)
+                        || STREQ (token, TAG_ROOT))
+               {
+                  first = false;
+               }
+               else
+                   error (1, 0,"\
+Tag '%s' invalid. Cannot resolve head: '.%s'", tag, token);
+           }
+           else if (!files)
+               error (1, 0,"\
+Tag '%s' invalid. Cannot resolve extension: '%s'", tag, token);
+           else if (STREQ (token, TAG_ORIGIN)
+                    || STREQ (token, TAG_DOTHEAD))
+           {
+               if (!prev || !STREQ (prev, token))
+                   prev = token;
+               else
+                   error (1, 0,"\
+Tag '%s' invalid. Duplicate extension: '%s'", tag, token);
+
+           }
+           else if ((!STREQ (token, TAG_PREVIOUS))
+                     && (!STREQ (token, TAG_NEXT))
+                     && (!STREQ (token, TAG_ROOT)))
+               error (1, 0,"\
+Tag '%s' invalid. Cannot resolve extension: '%s'", tag, token);
+           else
+               prev = NULL;
+       }
+       while (!first && (token = strtok (NULL, ".")) );
+    }
+    free (tmp);
+
+    return retval;
+}
+
+
+
+/*
  * TRUE if argument has valid syntax for an RCS revision or 
  * branch number.  All characters must be digits or dots, first 
  * and last characters must be digits, and no two consecutive 
Index: src/rcs.h
===================================================================
RCS file: /home/frank/CVS_ROOT/ccvs/src/rcs.h,v
retrieving revision 1.1.1.1
retrieving revision 1.5
diff -b -B -u -r1.1.1.1 -r1.5
--- src/rcs.h   27 Feb 2005 15:02:24 -0000      1.1.1.1
+++ src/rcs.h   8 Mar 2005 01:15:00 -0000       1.5
@@ -215,6 +215,7 @@
 time_t RCS_getrevtime (RCSNode * rcs, const char *rev, char *date, int fudge);
 List *RCS_symbols (RCSNode *rcs);
 void RCS_check_tag (const char *tag);
+char *RCS_extract_tag (const char *tag, bool files);
 int RCS_valid_rev (const char *rev);
 List *RCS_getlocks (RCSNode *rcs);
 void freercsnode (RCSNode ** rnodep);
Index: src/sanity.sh
===================================================================
RCS file: /home/frank/CVS_ROOT/ccvs/src/sanity.sh,v
retrieving revision 1.1.1.2
retrieving revision 1.3
diff -b -B -u -r1.1.1.2 -r1.3
--- src/sanity.sh       4 Mar 2005 22:35:02 -0000       1.1.1.2
+++ src/sanity.sh       4 Mar 2005 22:36:19 -0000       1.3
@@ -26166,7 +26166,7 @@
          #
          dotest_fail admin-28-4 "${testcvs} admin -ntagnine:1.a.2 file2"  \
 "RCS file: ${CVSROOT_DIRNAME}/first-dir/file2,v
-${SPROG} \[admin aborted\]: tag .1\.a\.2. must start with a letter"
+${SPROG} \[admin aborted\]: Tag .1\.a\.2. invalid. Cannot resolve extension: 
'a'"
 
          # Confirm that a missing tag is not a fatal error.
          dotest admin-28-5.1 "${testcvs} -Q tag BO+GUS file1" ''
Index: src/subr.c
===================================================================
RCS file: /home/frank/CVS_ROOT/ccvs/src/subr.c,v
retrieving revision 1.1.1.2
retrieving revision 1.4
diff -b -B -u -r1.1.1.2 -r1.4
--- src/subr.c  2 Mar 2005 01:36:28 -0000       1.1.1.2
+++ src/subr.c  2 Mar 2005 01:40:09 -0000       1.4
@@ -217,6 +217,32 @@
 
 
 
+/*
+ * Returns 1 if this is a plain revision number without extensions
+ * A plain rev num never has a dot followed by an alpha 
+ */
+int
+isrevnumonly (const char *s)
+{
+    if (!isdigit ((unsigned char) *s))
+    {
+        return 0;
+    }
+    else
+    {
+        const char * c = s+1;
+       while (c = strchr (c,'.'))
+        {
+           ++c;
+           if (*c && isalpha ((unsigned char) *c))
+               return 0;
+       }
+    }
+    return 1;
+}
+
+
+
 /* Compare revision numbers REV1 and REV2 by consecutive fields.
    Return negative, zero, or positive in the manner of strcmp.  The
    two revision numbers must have the same number of fields, or else
Index: src/subr.h
===================================================================
RCS file: /home/frank/CVS_ROOT/ccvs/src/subr.h,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -b -B -u -r1.1.1.1 -r1.2
--- src/subr.h  27 Feb 2005 15:02:24 -0000      1.1.1.1
+++ src/subr.h  27 Feb 2005 15:10:03 -0000      1.2
@@ -26,6 +26,7 @@
 void free_names (int *pargc, char *argv[]);
 void line2argv (int *pargc, char ***argv, char *line, char *sepchars);
 int numdots (const char *s);
+int isrevnumonly (const char *s);
 int compare_revnums (const char *, const char *);
 char *increment_revnum (const char *);
 char *getcaller (void);
Index: src/tag.c
===================================================================
RCS file: /home/frank/CVS_ROOT/ccvs/src/tag.c,v
retrieving revision 1.1.1.1
retrieving revision 1.7
diff -b -B -u -r1.1.1.1 -r1.7
--- src/tag.c   27 Feb 2005 15:02:24 -0000      1.1.1.1
+++ src/tag.c   8 Mar 2005 13:13:28 -0000       1.7
@@ -1442,11 +1442,12 @@
  *   Nothing.
  */
 void
-tag_check_valid (const char *name, int argc, char **argv, int local, int aflag,
+tag_check_valid (const char *oriname, int argc, char **argv, int local, int 
aflag,
                  char *repository, bool valid)
 {
     DBM *db;
     char *valtags_filename;
+    char *name;
     int nowrite = 0;
     datum mytag, val;
     struct val_args the_val_args;
@@ -1457,43 +1458,33 @@
     TRACE (TRACE_FUNCTION,
           "tag_check_valid (name=%s, argc=%d, argv=%p, local=%d,\n"
       "                      aflag=%d, repository=%s, valid=%s)",
-          name ? name : "(name)", argc, (void *)argv, local, aflag,
+          oriname ? oriname : "(name)", argc, (void *)argv, local, aflag,
           repository ? repository : "(null)",
           valid ? "true" : "false");
 #else
     TRACE (TRACE_FUNCTION,
           "tag_check_valid (name=%s, argc=%d, argv=%lx, local=%d,\n"
       "                      aflag=%d, repository=%s, valid=%s)",
-          name ? name : "(name)", argc, (unsigned long)argv, local, aflag,
+          oriname ? oriname : "(name)", argc, (unsigned long)argv, local, 
aflag,
           repository ? repository : "(null)",
           valid ? "true" : "false");
 #endif
 
-    /* Numeric tags require only a syntactic check.  */
-    if (isdigit ((unsigned char) name[0]))
+    /* validate tag and return symbolic tag if any */
+    bool files = (argc > 0);
+    int i;
+    for (i=0; i<argc; ++i)
+       if (isdir (argv[i]))
     {
-       /* insert is not possible for numeric revisions */
-       assert (!valid);
-       if (RCS_valid_rev (name)) return;
-       else
-           error (1, 0, "\
-Numeric tag %s invalid.  Numeric tags should be of the form X[.X]...", name);
+          files = false;
+          break;
     }
-
-    /* Special tags are always valid.  */
-    if (strcmp (name, TAG_BASE) == 0
-       || strcmp (name, TAG_HEAD) == 0)
+    if (!(name = RCS_extract_tag (oriname, files)))
     {
-       /* insert is not possible for numeric revisions */
        assert (!valid);
        return;
     }
 
-    /* Verify that the tag is valid syntactically.  Some later code once made
-     * assumptions about this.
-     */
-    RCS_check_tag (name);
-
     /* FIXME: This routine doesn't seem to do any locking whatsoever
        (and it is called from places which don't have locks in place).
        If two processes try to write val-tags at the same time, it would
@@ -1530,6 +1521,7 @@
             */
            dbm_close (db);
            free (valtags_filename);
+           free (name);
            return;
        }
        /* FIXME: should check errors somehow (add dbm_error to myndbm.c?).  */
@@ -1579,6 +1571,7 @@
        if (db != NULL)
            dbm_close (db);
        free (valtags_filename);
+       free (name);
        return;
     }
 
@@ -1593,6 +1586,7 @@
        {
            error (0, errno, "warning: cannot create %s", valtags_filename);
            free (valtags_filename);
+           free (name);
            return;
        }
     }
@@ -1604,4 +1598,5 @@
     dbm_close (db);
     free (mytag.dptr);
     free (valtags_filename);
+    free (name);
 }
Index: src/vers_ts.c
===================================================================
RCS file: /home/frank/CVS_ROOT/ccvs/src/vers_ts.c,v
retrieving revision 1.1.1.1
retrieving revision 1.5
diff -b -B -u -r1.1.1.1 -r1.5
--- src/vers_ts.c       27 Feb 2005 15:02:24 -0000      1.1.1.1
+++ src/vers_ts.c       8 Mar 2005 13:49:36 -0000       1.5
@@ -46,6 +46,7 @@
     struct stickydirtag *sdtp;
     Entnode *entdata;
     char *rcsexpand = NULL;
+    bool setstickynumtag = false;
 
     /* get a new Vers_TS struct */
     vers_ts = xmalloc (sizeof (Vers_TS));
@@ -159,9 +160,121 @@
      */
     if (tag || date)
     {
+        if (tag)
+       {
+           /* If a relative tag extension is used,
+            * prepend the local revision number, and set setstickynumtag
+            * so the sticky tag gets set to the numeric revision number
+            */
+           int position = 0;
+           bool head = false;
+           bool base = false;
+           bool root = false;
+           bool origin = false;
+           bool relative = true;
+           bool prepversion = false;
+           bool dot = false;
+
+           if (*tag == '.')
+              dot = true;
+
+           char *tmp = xstrdup (tag);
+           char *token = strtok (tmp, ".");
+           while (token)
+           {
+               if (position == 0)
+               {
+                   if (dot)
+                   {
+                       if (!strcmp (token, TAG_ROOT))
+                           root = true;
+                       else if (!strcmp (token, TAG_ORIGIN))
+                           origin = true;
+                       else if (!strcmp (token, TAG_DOTHEAD))
+                           head = true;
+                       else if (!strcmp (token, TAG_DOTBASE))
+                       {
+                           base = true;
+                           relative = false;
+                       }
+                       else if (strcmp (token, TAG_TRUNK)
+                                && strcmp (token, TAG_COMMITID))
+                       {
+                           prepversion = true;
+                           setstickynumtag = true;
+                           break;
+                       }
+                       else
+                           relative = false;
+                   }
+                   else
+                       relative = false;
+               }
+               else if (!isdigit ((unsigned char) *token))
+               {
+                   if (!strcmp (token, TAG_DOTHEAD))
+                       setstickynumtag = false;
+                   else if (!strcmp (token, TAG_PREVIOUS)
+                            || !strcmp (token, TAG_NEXT))
+                       setstickynumtag = true;
+               }
+               token = strtok (NULL, ".");
+               ++position;
+           }
+           free (tmp);
+
+           char *preptag = NULL;
+           if (prepversion)
+               preptag = vers_ts->vn_user;
+           else if (head || root || origin)
+           {
+               if (vers_ts->entdata && vers_ts->entdata->tag)
+                   preptag = vers_ts->entdata->tag;
+               else if (sdtp && !sdtp->aflag && !isdigit (*sdtp->tag))
+                   preptag = sdtp->tag;
+               else
+                   preptag = vers_ts->vn_user;
+           }
+
+           if (preptag)
+           {
+               char *p = NULL;
+           if (origin
+               && (p = strrchr (preptag, '.'))
+               && !strcmp (p+1, TAG_ORIGIN))
+           {
+               tmp = xstrdup (preptag);
+               tmp[p-preptag] = '\0';
+               vers_ts->tag = Xasprintf ("%s%s", tmp, tag);
+               free (tmp);
+           }
+           else if (head
+               && (p = strrchr (preptag, '.'))
+               && !strcmp (p+1, TAG_DOTHEAD))
+           {
+               tmp = xstrdup (preptag);
+               tmp[p-preptag] = '\0';
+               vers_ts->tag = Xasprintf ("%s%s", tmp, tag);
+               free (tmp);
+           }
+           else
+               vers_ts->tag = Xasprintf ("%s%s", preptag, tag);
+           }
+           else if (base && position > 1)
+           {
+               const char *p = strchr (tag+1, '.');
+               vers_ts->tag = Xasprintf ("%s%s", vers_ts->vn_user, p);
+           }
+           else if (relative)
+               vers_ts->tag = Xasprintf (".%s%s", TAG_TRUNK, tag);
+           else
        vers_ts->tag = xstrdup (tag);
+       }
+       if (date)
+       {
        vers_ts->date = xstrdup (date);
     }
+    }
     else if (!vers_ts->entdata && (sdtp && sdtp->aflag == 0))
     {
        if (!vers_ts->tag)
@@ -189,7 +302,10 @@
        /* squirrel away the rcsdata pointer for others */
        vers_ts->srcfile = rcsdata;
 
-       if (vers_ts->tag && strcmp (vers_ts->tag, TAG_BASE) == 0)
+       if (vers_ts->tag
+           && (!strcmp (vers_ts->tag, TAG_BASE)
+               || (*(vers_ts->tag) == '.'
+                   && !strcmp (vers_ts->tag+1, TAG_DOTBASE)) ) )
        {
            vers_ts->vn_rcs = xstrdup (vers_ts->vn_user);
            vers_ts->vn_tag = xstrdup (vers_ts->vn_user);
@@ -207,6 +323,15 @@
                vers_ts->vn_tag = xstrdup (vers_ts->tag);
            else
                vers_ts->vn_tag = xstrdup (vers_ts->vn_rcs);
+
+           if (setstickynumtag)
+           {
+               /* for some relative tags, the sticky tag
+                * needs to be set to its numeric equivalent
+                */
+               free (vers_ts->tag);
+               vers_ts->tag = xstrdup (vers_ts->vn_rcs);
+           }
        }
 
        /*



<<<<<<<<<<<<<<<<<<<<<<<<<< the patch <<<<<<<<<<<<<<<<<<<<<<<

Best regards
Frank Hemer

-- 
- The LinCVS Team -
http://www.lincvs.com





reply via email to

[Prev in Thread] Current Thread [Next in Thread]