home *** CD-ROM | disk | FTP | other *** search
- /*
- * Copyright (c) 1992, Brian Berliner and Jeff Polk
- *
- * You may distribute under the terms of the GNU General Public License as
- * specified in the README file that comes with the CVS 1.3 kit.
- *
- * The routines contained in this file do all the rcs file parsing and
- * manipulation
- */
-
- #include "cvs.h"
-
- #ifndef lint
- static char rcsid[] = "@(#)rcs.c 1.28 92/03/31";
- #endif
-
- #if __STDC__
- static char *RCS_getbranch (RCSNode * rcs, char *tag, int force_tag_match);
- static char *RCS_getdatebranch (RCSNode * rcs, char *date, char *branch);
- static int getrcskey (FILE * fp, char **keyp, char **valp);
- static int parse_rcs_proc (Node * file);
- static int checkmagic_proc (Node *p);
- static void do_branches (List * list, char *val);
- static void do_symbols (List * list, char *val);
- static void null_delproc (Node * p);
- static void rcsnode_delproc (Node * p);
- static void rcsvers_delproc (Node * p);
- #else
- static int parse_rcs_proc ();
- static int checkmagic_proc ();
- static void rcsnode_delproc ();
- static void rcsvers_delproc ();
- static void null_delproc ();
- static int getrcskey ();
- static void do_symbols ();
- static void do_branches ();
- static char *RCS_getbranch ();
- static char *RCS_getdatebranch ();
- #endif /* __STDC__ */
-
- static List *rcslist;
- static char *repository;
-
- /*
- * Parse all the rcs files specified and return a list
- */
- List *
- RCS_parsefiles (files, xrepos)
- List *files;
- char *xrepos;
- {
- /* initialize */
- repository = xrepos;
- rcslist = getlist ();
-
- /* walk the list parsing files */
- if (walklist (files, parse_rcs_proc) != 0)
- {
- /* free the list and return NULL on error */
- dellist (&rcslist);
- return ((List *) NULL);
- }
- else
- /* return the list we built */
- return (rcslist);
- }
-
- /*
- * Parse an rcs file into a node on the rcs list
- */
- static int
- parse_rcs_proc (file)
- Node *file;
- {
- Node *p;
- RCSNode *rdata;
-
- /* parse the rcs file into rdata */
- rdata = RCS_parse (file->key, repository);
-
- /* if we got a valid RCSNode back, put it on the list */
- if (rdata != (RCSNode *) NULL)
- {
- p = getnode ();
- p->key = xstrdup (file->key);
- p->delproc = rcsnode_delproc;
- p->type = RCSNODE;
- p->data = (char *) rdata;
- (void) addnode (rcslist, p);
- }
- return (0);
- }
-
- /*
- * Parse an rcsfile given a user file name and a repository
- */
- RCSNode *
- RCS_parse (file, repos)
- char *file;
- char *repos;
- {
- RCSNode *rcs;
- char rcsfile[PATH_MAX];
-
- (void) sprintf (rcsfile, "%s/%s%s", repos, file, RCSEXT);
- if (!isreadable (rcsfile))
- {
- (void) sprintf (rcsfile, "%s/%s/%s%s", repos, CVSATTIC,
- file, RCSEXT);
- if (!isreadable (rcsfile))
- return (NULL);
- rcs = RCS_parsercsfile (rcsfile);
- if (rcs != NULL)
- {
- rcs->flags |= INATTIC;
- rcs->flags |= VALID;
- }
- return (rcs);
- }
- rcs = RCS_parsercsfile (rcsfile);
- if (rcs != NULL)
- rcs->flags |= VALID;
- return (rcs);
- }
-
- /*
- * Do the real work of parsing an RCS file
- */
- RCSNode *
- RCS_parsercsfile (rcsfile)
- char *rcsfile;
- {
- Node *q, *r;
- RCSNode *rdata;
- RCSVers *vnode;
- int n;
- char *cp;
- char *key, *value;
- FILE *fp;
-
- /* open the rcsfile */
- if ((fp = fopen (rcsfile, "r")) == NULL)
- {
- error (0, errno, "Couldn't open rcs file `%s'", rcsfile);
- return (NULL);
- }
-
- /* make a node */
- rdata = (RCSNode *) xmalloc (sizeof (RCSNode));
- bzero ((char *) rdata, sizeof (RCSNode));
- rdata->refcount = 1;
- rdata->path = xstrdup (rcsfile);
- rdata->versions = getlist ();
- rdata->dates = getlist ();
-
- /*
- * process all the special header information, break out when we get to
- * the first revision delta
- */
- for (;;)
- {
- /* get the next key/value pair */
-
- /* if key is NULL here, then the file is missing some headers */
- if (getrcskey (fp, &key, &value) == -1 || key == NULL)
- {
- if (!really_quiet)
- error (0, 0, "`%s' does not appear to be a valid rcs file",
- rcsfile);
- freercsnode (&rdata);
- (void) fclose (fp);
- return (NULL);
- }
-
- /* process it */
- if (strcmp (RCSHEAD, key) == 0 && value != NULL)
- {
- rdata->head = xstrdup (value);
- continue;
- }
- if (strcmp (RCSBRANCH, key) == 0 && value != NULL)
- {
- rdata->branch = xstrdup (value);
- if ((numdots (rdata->branch) & 1) != 0)
- {
- /* turn it into a branch if it's a revision */
- cp = rindex (rdata->branch, '.');
- *cp = '\0';
- }
- continue;
- }
- if (strcmp (RCSSYMBOLS, key) == 0)
- {
- if (value != NULL)
- {
- /* if there are tags, set up the tag list */
- rdata->symbols = getlist ();
- do_symbols (rdata->symbols, value);
- continue;
- }
- }
-
- /*
- * check key for '.''s and digits (probably a rev) if it is a
- * revision, we are done with the headers and are down to the
- * revision deltas, so we break out of the loop
- */
- for (cp = key; (isdigit (*cp) || *cp == '.') && *cp != '\0'; cp++)
- /* do nothing */ ;
- if (*cp == '\0' && strncmp (RCSDATE, value, strlen (RCSDATE)) == 0)
- break;
-
- /* if we haven't grabbed it yet, we didn't want it */
- }
-
- /*
- * we got out of the loop, so we have the first part of the first
- * revision delta in our hand key=the revision and value=the date key and
- * its value
- */
- for (;;)
- {
- char *valp;
- char date[MAXDATELEN];
-
- /* grab the value of the date from value */
- valp = value + strlen (RCSDATE);/* skip the "date" keyword */
- while (isspace (*valp)) /* take space off front of value */
- valp++;
- (void) strcpy (date, valp);
-
- /* get the nodes (q is by version, r is by date) */
- q = getnode ();
- r = getnode ();
- q->type = RCSVERS;
- r->type = RCSVERS;
- q->delproc = rcsvers_delproc;
- r->delproc = null_delproc;
- q->data = r->data = xmalloc (sizeof (RCSVers));
- bzero (q->data, sizeof (RCSVers));
- vnode = (RCSVers *) q->data;
-
- /* fill in the version before we forget it */
- q->key = vnode->version = xstrdup (key);
-
- /* throw away the author field */
- (void) getrcskey (fp, &key, &value);
-
- /* throw away the state field */
- (void) getrcskey (fp, &key, &value);
-
- /* fill in the date field */
- r->key = vnode->date = xstrdup (date);
-
- /* fill in the branch list (if any branches exist) */
- (void) getrcskey (fp, &key, &value);
- if (value != (char *) NULL)
- {
- vnode->branches = getlist ();
- do_branches (vnode->branches, value);
- }
-
- /* fill in the next field if there is a next revision */
- (void) getrcskey (fp, &key, &value);
- if (value != (char *) NULL)
- vnode->next = xstrdup (value);
-
- /*
- * at this point, we skip any user defined fields XXX - this is where
- * we put the symbolic link stuff???
- */
- while ((n = getrcskey (fp, &key, &value)) >= 0)
- {
- /* if we have a revision, break and do it */
- for (cp = key; (isdigit (*cp) || *cp == '.') && *cp != '\0'; cp++)
- /* do nothing */ ;
- if (*cp == '\0' && strncmp (RCSDATE, value, strlen (RCSDATE)) == 0)
- break;
- }
-
- /* add the nodes to the lists */
- (void) addnode (rdata->versions, q);
- (void) addnode (rdata->dates, r);
-
- /*
- * if we left the loop because there were no more keys, we break out
- * of the revision processing loop
- */
- if (n < 0)
- break;
- }
-
- (void) fclose (fp);
- return (rdata);
- }
-
- /*
- * rcsnode_delproc - free up an RCS type node
- */
- static void
- rcsnode_delproc (p)
- Node *p;
- {
- freercsnode ((RCSNode **) & p->data);
- }
-
- /*
- * freercsnode - free up the info for an RCSNode
- */
- void
- freercsnode (rnodep)
- RCSNode **rnodep;
- {
- if (rnodep == NULL || *rnodep == NULL)
- return;
-
- ((*rnodep)->refcount)--;
- if ((*rnodep)->refcount != 0)
- {
- *rnodep = (RCSNode *) NULL;
- return;
- }
- free ((*rnodep)->path);
- dellist (&(*rnodep)->versions);
- dellist (&(*rnodep)->dates);
- if ((*rnodep)->symbols != (List *) NULL)
- dellist (&(*rnodep)->symbols);
- if ((*rnodep)->head != (char *) NULL)
- free ((*rnodep)->head);
- if ((*rnodep)->branch != (char *) NULL)
- free ((*rnodep)->branch);
- free ((char *) *rnodep);
- *rnodep = (RCSNode *) NULL;
- }
-
- /*
- * rcsvers_delproc - free up an RCSVers type node
- */
- static void
- rcsvers_delproc (p)
- Node *p;
- {
- RCSVers *rnode;
-
- rnode = (RCSVers *) p->data;
-
- if (rnode->branches != (List *) NULL)
- dellist (&rnode->branches);
- if (rnode->next != (char *) NULL)
- free (rnode->next);
- free ((char *) rnode);
- }
-
- /*
- * null_delproc - don't free anything since it will be free'd by someone else
- */
- /* ARGSUSED */
- static void
- null_delproc (p)
- Node *p;
- {
- /* don't do anything */
- }
-
- /*
- * getrcskey - fill in the key and value from the rcs file the algorithm is
- * as follows
- *
- * o skip whitespace o fill in key with everything up to next white
- * space or semicolon
- * o if key == "desc" then key and data are NULL and return -1
- * o if key wasn't terminated by a semicolon, skip white space and fill
- * in value with everything up to a semicolon o compress all whitespace
- * down to a single space
- * o if a word starts with @, do funky rcs processing
- * o strip whitespace off end of value or set value to NULL if it empty
- * o return 0 since we found something besides "desc"
- */
-
- static char *key = NULL;
- static int keysize = 0;
- static char *value = NULL;
- static int valsize = 0;
-
- #define ALLOCINCR 1024
-
- static int
- getrcskey (fp, keyp, valp)
- FILE *fp;
- char **keyp;
- char **valp;
- {
- char *cur, *max;
- int c;
- int funky = 0;
- int white = 1;
-
- /* skip leading whitespace */
- while (1)
- {
- c = getc (fp);
- if (c == EOF)
- {
- *keyp = (char *) NULL;
- *valp = (char *) NULL;
- return (-1);
- }
- if (!isspace (c))
- break;
- }
-
- /* fill in key */
- cur = key;
- max = key + keysize;
- while (!isspace (c) && c != ';')
- {
- if (cur < max)
- *cur++ = c;
- else
- {
- key = xrealloc (key, keysize + ALLOCINCR);
- cur = key + keysize;
- keysize += ALLOCINCR;
- max = key + keysize;
- *cur++ = c;
- }
- c = getc (fp);
- if (c == EOF)
- {
- *keyp = (char *) NULL;
- *valp = (char *) NULL;
- return (-1);
- }
- }
- *cur = '\0';
-
- /* if we got "desc", we are done with the file */
- if (strcmp (RCSDESC, key) == 0)
- {
- *keyp = (char *) NULL;
- *valp = (char *) NULL;
- return (-1);
- }
-
- /* if we ended key with a semicolon, there is no value */
- if (c == ';')
- {
- *keyp = key;
- *valp = (char *) NULL;
- return (0);
- }
-
- /* otherwise, there might be a value, so fill it in */
- (void) ungetc (c, fp);
- cur = value;
- max = value + valsize;
-
- /* process the value */
- for (;;)
- {
- /* get a character */
- c = getc (fp);
- if (c == EOF)
- {
- *keyp = (char *) NULL;
- *valp = (char *) NULL;
- return (-1);
- }
-
- /* if we are in funky mode, do the rest of this string */
- if (funky)
- {
-
- /*
- * funky mode processing does the following: o @@ means one @ o
- * all other characters are literal up to a single @ (including
- * ';')
- */
- for (;;)
- {
- if (c == '@')
- {
- c = getc (fp);
- if (c == EOF)
- {
- *keyp = (char *) NULL;
- *valp = (char *) NULL;
- return (-1);
- }
- if (c != '@')
- {
- /* @ followed by non @ turns off funky mode */
- funky = 0;
- break;
- }
- /* otherwise, we already ate one @ so copy the other one */
- }
-
- /* put the character on the value (maybe allocating space) */
- if (cur >= max)
- {
- value = xrealloc (value, valsize + ALLOCINCR);
- cur = value + valsize;
- valsize += ALLOCINCR;
- max = value + valsize;
- }
- *cur++ = c;
- c = getc (fp);
- if (c == EOF)
- {
- *keyp = (char *) NULL;
- *valp = (char *) NULL;
- return (-1);
- }
- }
- }
-
- /* if we got the semi-colon we are done with the entire value */
- if (c == ';')
- break;
-
- /* process the character we got */
- if (white && c == '@')
- {
-
- /*
- * if we are starting a word with an '@', enable funky processing
- */
- white = 0; /* you can't be funky and white :-) */
- funky = 1;
- }
- else
- {
-
- /*
- * we put the character on the list, compressing all whitespace
- * to a single space
- */
-
- /* whitespace with white set means compress it out */
- if (white && isspace (c))
- continue;
-
- if (isspace (c))
- {
- /* make c a space and set white */
- white = 1;
- c = ' ';
- }
- else
- white = 0;
-
- /* put the char on the end of value (maybe allocating space) */
- if (cur >= max)
- {
- value = xrealloc (value, valsize + ALLOCINCR);
- cur = value + valsize;
- valsize += ALLOCINCR;
- max = value + valsize;
- }
- *cur++ = c;
- }
- }
-
- /* if the last char was white space, take it off */
- if (white && cur != value)
- cur--;
-
- /* terminate the string */
- if (cur)
- *cur = '\0';
-
- /* if the string is empty, make it null */
- if (value && *value != '\0')
- *valp = value;
- else
- *valp = NULL;
- *keyp = key;
- return (0);
- }
-
- /*
- * process the symbols list of the rcs file
- */
- static void
- do_symbols (list, val)
- List *list;
- char *val;
- {
- Node *p;
- char *cp = val;
- char *tag, *rev;
-
- for (;;)
- {
- /* skip leading whitespace */
- while (isspace (*cp))
- cp++;
-
- /* if we got to the end, we are done */
- if (*cp == '\0')
- break;
-
- /* split it up into tag and rev */
- tag = cp;
- cp = index (cp, ':');
- *cp++ = '\0';
- rev = cp;
- while (!isspace (*cp) && *cp != '\0')
- cp++;
- if (*cp != '\0')
- *cp++ = '\0';
-
- /* make a new node and add it to the list */
- p = getnode ();
- p->key = xstrdup (tag);
- p->data = xstrdup (rev);
- (void) addnode (list, p);
- }
- }
-
- /*
- * process the branches list of a revision delta
- */
- static void
- do_branches (list, val)
- List *list;
- char *val;
- {
- Node *p;
- char *cp = val;
- char *branch;
-
- for (;;)
- {
- /* skip leading whitespace */
- while (isspace (*cp))
- cp++;
-
- /* if we got to the end, we are done */
- if (*cp == '\0')
- break;
-
- /* find the end of this branch */
- branch = cp;
- while (!isspace (*cp) && *cp != '\0')
- cp++;
- if (*cp != '\0')
- *cp++ = '\0';
-
- /* make a new node and add it to the list */
- p = getnode ();
- p->key = xstrdup (branch);
- (void) addnode (list, p);
- }
- }
-
- /*
- * Version Number
- *
- * Returns the requested version number of the RCS file, satisfying tags and/or
- * dates, and walking branches, if necessary.
- *
- * The result is returned; null-string if error.
- */
- char *
- RCS_getversion (rcs, tag, date, force_tag_match)
- RCSNode *rcs;
- char *tag;
- char *date;
- int force_tag_match;
- {
- /* make sure we have something to look at... */
- if (rcs == NULL)
- return ((char *) NULL);
-
- if (tag && date)
- {
- char *cp, *rev, *tagrev;
-
- /*
- * first lookup the tag; if that works, turn the revision into
- * a branch and lookup the date.
- */
- tagrev = RCS_gettag (rcs, tag, force_tag_match);
- if (tagrev == NULL)
- return ((char *) NULL);
-
- if ((cp = rindex (tagrev, '.')) != NULL)
- *cp = '\0';
- rev = RCS_getdatebranch (rcs, date, tagrev);
- free (tagrev);
- return (rev);
- }
- else if (tag)
- return (RCS_gettag (rcs, tag, force_tag_match));
- else if (date)
- return (RCS_getdate (rcs, date, force_tag_match));
- else
- return (RCS_head (rcs));
-
- }
-
- /*
- * Find the revision for a specific tag.
- * If force_tag_match is set, return NULL if an exact match is not
- * possible otherwise return RCS_head (). We are careful to look for
- * and handle "magic" revisions specially.
- *
- * If the matched tag is a branch tag, find the head of the branch.
- */
- char *
- RCS_gettag (rcs, tag, force_tag_match)
- RCSNode *rcs;
- char *tag;
- int force_tag_match;
- {
- Node *p;
-
- /* make sure we have something to look at... */
- if (rcs == NULL)
- return ((char *) NULL);
-
- /* If tag is "HEAD", special case to get head RCS revision */
- if (tag && (strcmp (tag, TAG_HEAD) == 0 || *tag == '\0'))
- if (force_tag_match && (rcs->flags & VALID) && (rcs->flags & INATTIC))
- return ((char *) NULL); /* head request for removed file */
- else
- return (RCS_head (rcs));
-
- if (!isdigit (tag[0]))
- {
- /* If we got a symbolic tag, resolve it to a numeric */
- if (rcs == NULL)
- p = NULL;
- else
- p = findnode (rcs->symbols, tag);
- if (p != NULL)
- {
- int dots;
- char *magic, *branch, *cp;
-
- tag = p->data;
-
- /*
- * If this is a magic revision, we turn it into either its
- * physical branch equivalent (if one exists) or into
- * its base revision, which we assume exists.
- */
- dots = numdots (tag);
- if (dots > 2 && (dots & 1) != 0)
- {
- branch = rindex (tag, '.');
- cp = branch++ - 1;
- while (*cp != '.')
- cp--;
-
- /* see if we have .magic-branch. (".0.") */
- magic = xmalloc (strlen (tag) + 1);
- (void) sprintf (magic, ".%d.", RCS_MAGIC_BRANCH);
- if (strncmp (magic, cp, strlen (magic)) == 0)
- {
- char *xtag;
-
- /* it's magic. See if the branch exists */
- *cp = '\0'; /* turn it into a revision */
- xtag = xstrdup (tag);
- *cp = '.'; /* and back again */
- (void) sprintf (magic, "%s.%s", xtag, branch);
- branch = RCS_getbranch (rcs, magic, 1);
- free (magic);
- if (branch != NULL)
- {
- free (xtag);
- return (branch);
- }
- return (xtag);
- }
- free (magic);
- }
- }
- else
- {
- /* The tag wasn't there, so return the head or NULL */
- if (force_tag_match)
- return (NULL);
- else
- return (RCS_head (rcs));
- }
- }
-
- /*
- * numeric tag processing:
- * 1) revision number - just return it
- * 2) branch number - find head of branch
- */
-
- /* strip trailing dots */
- while (tag[strlen (tag) - 1] == '.')
- tag[strlen (tag) - 1] = '\0';
-
- if ((numdots (tag) & 1) == 0)
- {
- /* we have a branch tag, so we need to walk the branch */
- return (RCS_getbranch (rcs, tag, force_tag_match));
- }
- else
- {
- /* we have a revision tag, so make sure it exists */
- if (rcs == NULL)
- p = NULL;
- else
- p = findnode (rcs->versions, tag);
- if (p != NULL)
- return (xstrdup (tag));
- else
- {
- /* The revision wasn't there, so return the head or NULL */
- if (force_tag_match)
- return (NULL);
- else
- return (RCS_head (rcs));
- }
- }
- }
-
- /*
- * Return a "magic" revision as a virtual branch off of REV for the RCS file.
- * A "magic" revision is one which is unique in the RCS file. By unique, I
- * mean we return a revision which:
- * - has a branch of 0 (see rcs.h RCS_MAGIC_BRANCH)
- * - has a revision component which is not an existing branch off REV
- * - has a revision component which is not an existing magic revision
- * - is an even-numbered revision, to avoid conflicts with vendor branches
- * The first point is what makes it "magic".
- *
- * As an example, if we pass in 1.37 as REV, we will look for an existing
- * branch called 1.37.2. If it did not exist, we would look for an
- * existing symbolic tag with a numeric part equal to 1.37.0.2. If that
- * didn't exist, then we know that the 1.37.2 branch can be reserved by
- * creating a symbolic tag with 1.37.0.2 as the numeric part.
- *
- * This allows us to fork development with very little overhead -- just a
- * symbolic tag is used in the RCS file. When a commit is done, a physical
- * branch is dynamically created to hold the new revision.
- *
- * Note: We assume that REV is an RCS revision and not a branch number.
- */
- static char *check_rev;
- char *
- RCS_magicrev (rcs, rev)
- RCSNode *rcs;
- char *rev;
- {
- int rev_num;
- char *xrev, *test_branch;
-
- xrev = xmalloc (strlen (rev) + 14); /* enough for .0.number */
- check_rev = xrev;
-
- /* only look at even numbered branches */
- for (rev_num = 2; ; rev_num += 2)
- {
- /* see if the physical branch exists */
- (void) sprintf (xrev, "%s.%d", rev, rev_num);
- test_branch = RCS_getbranch (rcs, xrev, 1);
- if (test_branch != NULL) /* it did, so keep looking */
- {
- free (test_branch);
- continue;
- }
-
- /* now, create a "magic" revision */
- (void) sprintf (xrev, "%s.%d.%d", rev, RCS_MAGIC_BRANCH, rev_num);
-
- /* walk the symbols list to see if a magic one already exists */
- if (walklist (rcs->symbols, checkmagic_proc) != 0)
- continue;
-
- /* we found a free magic branch. Claim it as ours */
- return (xrev);
- }
- }
-
- /*
- * walklist proc to look for a match in the symbols list.
- * Returns 0 if the symbol does not match, 1 if it does.
- */
- static int
- checkmagic_proc (p)
- Node *p;
- {
- if (strcmp (check_rev, p->data) == 0)
- return (1);
- else
- return (0);
- }
-
- /*
- * Returns non-zero if the specified revision number or symbolic tag
- * resolves to a "branch" within the rcs file. We do take into account
- * any magic branches as well.
- */
- int
- RCS_isbranch (file, rev, srcfiles)
- char *file;
- char *rev;
- List *srcfiles;
- {
- int dots;
- Node *p;
- RCSNode *rcs;
-
- /* numeric revisions are easy -- even number of dots is a branch */
- if (isdigit (*rev))
- return ((numdots (rev) & 1) == 0);
-
- /* assume a revision if you can't find the RCS info */
- p = findnode (srcfiles, file);
- if (p == NULL)
- return (0);
-
- /* now, look for a match in the symbols list */
- rcs = (RCSNode *) p->data;
- p = findnode (rcs->symbols, rev);
- if (p == NULL)
- return (0);
- dots = numdots (p->data);
- if ((dots & 1) == 0)
- return (1);
-
- /* got a symbolic tag match, but it's not a branch; see if it's magic */
- if (dots > 2)
- {
- char *magic;
- char *branch = rindex (p->data, '.');
- char *cp = branch - 1;
- while (*cp != '.')
- cp--;
-
- /* see if we have .magic-branch. (".0.") */
- magic = xmalloc (strlen (p->data) + 1);
- (void) sprintf (magic, ".%d.", RCS_MAGIC_BRANCH);
- if (strncmp (magic, cp, strlen (magic)) == 0)
- {
- free (magic);
- return (1);
- }
- free (magic);
- }
- return (0);
- }
-
- /*
- * Returns a pointer to malloc'ed memory which contains the branch
- * for the specified *symbolic* tag. Magic branches are handled correctly.
- */
- char *
- RCS_whatbranch (file, rev, srcfiles)
- char *file;
- char *rev;
- List *srcfiles;
- {
- int dots;
- Node *p;
- RCSNode *rcs;
-
- /* assume no branch if you can't find the RCS info */
- p = findnode (srcfiles, file);
- if (p == NULL)
- return ((char *) NULL);
-
- /* now, look for a match in the symbols list */
- rcs = (RCSNode *) p->data;
- p = findnode (rcs->symbols, rev);
- if (p == NULL)
- return ((char *) NULL);
- dots = numdots (p->data);
- if ((dots & 1) == 0)
- return (xstrdup (p->data));
-
- /* got a symbolic tag match, but it's not a branch; see if it's magic */
- if (dots > 2)
- {
- char *magic;
- char *branch = rindex (p->data, '.');
- char *cp = branch++ - 1;
- while (*cp != '.')
- cp--;
-
- /* see if we have .magic-branch. (".0.") */
- magic = xmalloc (strlen (p->data) + 1);
- (void) sprintf (magic, ".%d.", RCS_MAGIC_BRANCH);
- if (strncmp (magic, cp, strlen (magic)) == 0)
- {
- /* yep. it's magic. now, construct the real branch */
- *cp = '\0'; /* turn it into a revision */
- (void) sprintf (magic, "%s.%s", p->data, branch);
- *cp = '.'; /* and turn it back */
- return (magic);
- }
- free (magic);
- }
- return ((char *) NULL);
- }
-
- /*
- * Get the head of the specified branch. If the branch does not exist,
- * return NULL or RCS_head depending on force_tag_match
- */
- static char *
- RCS_getbranch (rcs, tag, force_tag_match)
- RCSNode *rcs;
- char *tag;
- int force_tag_match;
- {
- Node *p, *head;
- RCSVers *vn;
- char *xtag;
- char *nextvers;
- char *cp;
-
- /* make sure we have something to look at... */
- if (rcs == NULL)
- return ((char *) NULL);
-
- /* find out if the tag contains a dot, or is on the trunk */
- cp = rindex (tag, '.');
-
- /* trunk processing is the special case */
- if (cp == NULL)
- {
- xtag = xmalloc (strlen (tag) + 1 + 1); /* +1 for an extra . */
- (void) strcpy (xtag, tag);
- (void) strcat (xtag, ".");
- for (cp = rcs->head; cp != NULL;)
- {
- if (strncmp (xtag, cp, strlen (xtag)) == 0)
- break;
- p = findnode (rcs->versions, cp);
- if (p == NULL)
- {
- free (xtag);
- if (force_tag_match)
- return (NULL);
- else
- return (RCS_head (rcs));
- }
- vn = (RCSVers *) p->data;
- cp = vn->next;
- }
- free (xtag);
- if (cp == NULL)
- {
- if (force_tag_match)
- return (NULL);
- else
- return (RCS_head (rcs));
- }
- return (xstrdup (cp));
- }
-
- /* if it had a `.', terminate the string so we have the base revision */
- *cp = '\0';
-
- /* look up the revision this branch is based on */
- p = findnode (rcs->versions, tag);
-
- /* put the . back so we have the branch again */
- *cp = '.';
-
- if (p == NULL)
- {
- /* if the base revision didn't exist, return head or NULL */
- if (force_tag_match)
- return (NULL);
- else
- return (RCS_head (rcs));
- }
-
- /* find the first element of the branch we are looking for */
- vn = (RCSVers *) p->data;
- if (vn->branches == NULL)
- return (NULL);
- xtag = xmalloc (strlen (tag) + 1 + 1); /* 1 for the extra '.' */
- (void) strcpy (xtag, tag);
- (void) strcat (xtag, ".");
- head = vn->branches->list;
- for (p = head->next; p != head; p = p->next)
- if (strncmp (p->key, xtag, strlen (xtag)) == 0)
- break;
- free (xtag);
-
- if (p == head)
- {
- /* we didn't find a match so return head or NULL */
- if (force_tag_match)
- return (NULL);
- else
- return (RCS_head (rcs));
- }
-
- /* now walk the next pointers of the branch */
- nextvers = p->key;
- do
- {
- p = findnode (rcs->versions, nextvers);
- if (p == NULL)
- {
- /* a link in the chain is missing - return head or NULL */
- if (force_tag_match)
- return (NULL);
- else
- return (RCS_head (rcs));
- }
- vn = (RCSVers *) p->data;
- nextvers = vn->next;
- } while (nextvers != NULL);
-
- /* we have the version in our hand, so go for it */
- return (xstrdup (vn->version));
- }
-
- /*
- * Get the head of the RCS file. If branch is set, this is the head of the
- * branch, otherwise the real head
- */
- char *
- RCS_head (rcs)
- RCSNode *rcs;
- {
- /* make sure we have something to look at... */
- if (rcs == NULL)
- return ((char *) NULL);
-
- if (rcs->branch)
- return (RCS_getbranch (rcs, rcs->branch, 1));
-
- /*
- * NOTE: we call getbranch with force_tag_match set to avoid any
- * possibility of recursion
- */
- else
- return (xstrdup (rcs->head));
- }
-
- /*
- * Get the most recent revision, based on the supplied date, but use some
- * funky stuff and follow the vendor branch maybe
- */
- char *
- RCS_getdate (rcs, date, force_tag_match)
- RCSNode *rcs;
- char *date;
- int force_tag_match;
- {
- char *cur_rev = NULL;
- char *retval = NULL;
- Node *p;
- RCSVers *vers = NULL;
-
- /* make sure we have something to look at... */
- if (rcs == NULL)
- return ((char *) NULL);
-
- /* if the head is on a branch, try the branch first */
- if (rcs->branch != NULL)
- retval = RCS_getdatebranch (rcs, date, rcs->branch);
-
- /* if we found a match, we are done */
- if (retval != NULL)
- return (retval);
-
- /* otherwise if we have a trunk, try it */
- if (rcs->head)
- {
- p = findnode (rcs->versions, rcs->head);
- while (p != NULL)
- {
- /* if the date of this one is before date, take it */
- vers = (RCSVers *) p->data;
- if (RCS_datecmp (vers->date, date) <= 0)
- {
- cur_rev = vers->version;
- break;
- }
-
- /* if there is a next version, find the node */
- if (vers->next != NULL)
- p = findnode (rcs->versions, vers->next);
- else
- p = (Node *) NULL;
- }
- }
-
- /*
- * at this point, either we have the revision we want, or we have the
- * first revision on the trunk (1.1?) in our hands
- */
-
- /* if we found what we're looking for, and it's not 1.1 return it */
- if (cur_rev != NULL && strcmp (cur_rev, "1.1") != 0)
- return (xstrdup (cur_rev));
-
- /* look on the vendor branch */
- retval = RCS_getdatebranch (rcs, date, CVSBRANCH);
-
- /*
- * if we found a match, return it; otherwise, we return the first
- * revision on the trunk or NULL depending on force_tag_match and the
- * date of the first rev
- */
- if (retval != NULL)
- return (retval);
-
- if (!force_tag_match || RCS_datecmp (vers->date, date) <= 0)
- return (xstrdup (vers->version));
- else
- return (NULL);
- }
-
- /*
- * Look up the last element on a branch that was put in before the specified
- * date (return the rev or NULL)
- */
- static char *
- RCS_getdatebranch (rcs, date, branch)
- RCSNode *rcs;
- char *date;
- char *branch;
- {
- char *cur_rev = NULL;
- char *cp;
- char *xbranch, *xrev;
- Node *p;
- RCSVers *vers;
-
- /* look up the first revision on the branch */
- xrev = xstrdup (branch);
- cp = rindex (xrev, '.');
- if (cp == NULL)
- {
- free (xrev);
- return (NULL);
- }
- *cp = '\0'; /* turn it into a revision */
- p = findnode (rcs->versions, xrev);
- free (xrev);
- if (p == NULL)
- return (NULL);
- vers = (RCSVers *) p->data;
-
- /* if no branches list, return NULL */
- if (vers->branches == NULL)
- return (NULL);
-
- /* walk the branches list looking for the branch number */
- xbranch = xmalloc (strlen (branch) + 1 + 1); /* +1 for the extra dot */
- (void) strcpy (xbranch, branch);
- (void) strcat (xbranch, ".");
- for (p = vers->branches->list->next; p != vers->branches->list; p = p->next)
- if (strncmp (p->key, xbranch, strlen (xbranch)) == 0)
- break;
- free (xbranch);
- if (p == vers->branches->list)
- return (NULL);
-
- p = findnode (rcs->versions, p->key);
-
- /* walk the next pointers until you find the end, or the date is too late */
- while (p != NULL)
- {
- vers = (RCSVers *) p->data;
- if (RCS_datecmp (vers->date, date) <= 0)
- cur_rev = vers->version;
- else
- break;
-
- /* if there is a next version, find the node */
- if (vers->next != NULL)
- p = findnode (rcs->versions, vers->next);
- else
- p = (Node *) NULL;
- }
-
- /* if we found something acceptable, return it - otherwise NULL */
- if (cur_rev != NULL)
- return (xstrdup (cur_rev));
- else
- return (NULL);
- }
-
- /*
- * Compare two dates in RCS format. Beware the change in format on January 1,
- * 2000, when years go from 2-digit to full format.
- */
- int
- RCS_datecmp (date1, date2)
- char *date1, *date2;
- {
- int length_diff = strlen (date1) - strlen (date2);
-
- return (length_diff ? length_diff : strcmp (date1, date2));
- }
-
- /*
- * Lookup the specified revision in the ,v file and return, in the date
- * argument, the date specified for the revision *minus one second*, so that
- * the logically previous revision will be found later.
- *
- * Returns zero on failure, RCS revision time as a Unix "time_t" on success.
- */
- time_t
- RCS_getrevtime (rcs, rev, date, fudge)
- RCSNode *rcs;
- char *rev;
- char *date;
- int fudge;
- {
- char tdate[MAXDATELEN];
- struct tm xtm, *ftm;
- time_t revdate = 0;
- Node *p;
- RCSVers *vers;
-
- /* make sure we have something to look at... */
- if (rcs == NULL)
- return (revdate);
-
- /* look up the revision */
- p = findnode (rcs->versions, rev);
- if (p == NULL)
- return (-1);
- vers = (RCSVers *) p->data;
-
- /* split up the date */
- ftm = &xtm;
- (void) sscanf (vers->date, SDATEFORM, &ftm->tm_year, &ftm->tm_mon,
- &ftm->tm_mday, &ftm->tm_hour, &ftm->tm_min,
- &ftm->tm_sec);
- if (ftm->tm_year > 1900)
- ftm->tm_year -= 1900;
-
- /* put the date in a form getdate can grok */
- #ifdef HAVE_RCS5
- (void) sprintf (tdate, "%d/%d/%d GMT %d:%d:%d", ftm->tm_mon,
- ftm->tm_mday, ftm->tm_year, ftm->tm_hour,
- ftm->tm_min, ftm->tm_sec);
- #else
- (void) sprintf (tdate, "%d/%d/%d %d:%d:%d", ftm->tm_mon,
- ftm->tm_mday, ftm->tm_year, ftm->tm_hour,
- ftm->tm_min, ftm->tm_sec);
- #endif
-
- /* turn it into seconds since the epoch */
- revdate = get_date (tdate, (struct timeb *) NULL);
- if (revdate != (time_t) -1)
- {
- revdate -= fudge; /* remove "fudge" seconds */
- if (date)
- {
- /* put an appropriate string into ``date'' if we were given one */
- #ifdef HAVE_RCS5
- ftm = gmtime (&revdate);
- #else
- ftm = localtime (&revdate);
- #endif
- (void) sprintf (date, DATEFORM,
- ftm->tm_year + (ftm->tm_year < 100 ? 0 : 1900),
- ftm->tm_mon + 1, ftm->tm_mday, ftm->tm_hour,
- ftm->tm_min, ftm->tm_sec);
- }
- }
- return (revdate);
- }
-
- /*
- * The argument ARG is the getopt remainder of the -k option specified on the
- * command line. This function returns malloc'ed space that can be used
- * directly in calls to RCS V5, with the -k flag munged correctly.
- */
- char *
- RCS_check_kflag (arg)
- char *arg;
- {
- static char *kflags[] =
- {"kv", "kvl", "k", "v", "o", (char *) NULL};
- char karg[10];
- char **cpp = NULL;
-
- #ifndef HAVE_RCS5
- error (1, 0, "%s %s: your version of RCS does not support the -k option",
- program_name, command_name);
- #endif
-
- if (arg)
- {
- for (cpp = kflags; *cpp != NULL; cpp++)
- {
- if (strcmp (arg, *cpp) == 0)
- break;
- }
- }
-
- if (arg == NULL || *cpp == NULL)
- {
- (void) fprintf (stderr, "%s %s: invalid -k option\n",
- program_name, command_name);
- (void) fprintf (stderr, "\tvalid options are:\n");
- for (cpp = kflags; *cpp != NULL; cpp++)
- (void) fprintf (stderr, "\t\t-k%s\n", *cpp);
- error (1, 0, "Please retry with a valid -k option");
- }
-
- (void) sprintf (karg, "-k%s", *cpp);
- return (xstrdup (karg));
- }
-
- /*
- * Do some consistency checks on the symbolic tag... These should equate
- * pretty close to what RCS checks, though I don't know for certain.
- */
- void
- RCS_check_tag (tag)
- char *tag;
- {
- char *invalid = "$,.:;@"; /* invalid RCS tag characters */
- char *cp;
-
- /*
- * The first character must be an alphabetic letter. The remaining
- * characters cannot be non-visible graphic characters, and must not be
- * in the set of "invalid" RCS identifier characters.
- */
- if (isalpha (*tag))
- {
- for (cp = tag; *cp; cp++)
- {
- if (!isgraph (*cp))
- error (1, 0, "tag `%s' has non-visible graphic characters",
- tag);
- if (index (invalid, *cp))
- error (1, 0, "tag `%s' must not contain the characters `%s'",
- tag, invalid);
- }
- }
- else
- error (1, 0, "tag `%s' must start with a letter", tag);
- }
-