summaryrefslogtreecommitdiffstats
path: root/contrib/cvs/src/admin.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/cvs/src/admin.c')
-rw-r--r--contrib/cvs/src/admin.c721
1 files changed, 686 insertions, 35 deletions
diff --git a/contrib/cvs/src/admin.c b/contrib/cvs/src/admin.c
index d1401f4..a78bada 100644
--- a/contrib/cvs/src/admin.c
+++ b/contrib/cvs/src/admin.c
@@ -3,18 +3,17 @@
* Copyright (c) 1989-1992, Brian Berliner
*
* You may distribute under the terms of the GNU General Public License as
- * specified in the README file that comes with the CVS 1.4 kit.
+ * specified in the README file that comes with the CVS source distribution.
*
- * Administration
+ * Administration ("cvs admin")
*
- * For now, this is basically a front end for rcs. All options are passed
- * directly on.
*/
#include "cvs.h"
#ifdef CVS_ADMIN_GROUP
#include <grp.h>
#endif
+#include <assert.h>
static Dtype admin_dirproc PROTO ((void *callerdat, char *dir,
char *repos, char *update_dir,
@@ -24,11 +23,89 @@ static int admin_fileproc PROTO ((void *callerdat, struct file_info *finfo));
static const char *const admin_usage[] =
{
"Usage: %s %s rcs-options files...\n",
+ "(Specify the --help global option for a list of other help options)\n",
NULL
};
-static int ac;
-static char **av;
+/* This structure is used to pass information through start_recursion. */
+struct admin_data
+{
+ /* Set default branch (-b). It is "-b" followed by the value
+ given, or NULL if not specified, or merely "-b" if -b is
+ specified without a value. */
+ char *branch;
+
+ /* Set comment leader (-c). It is "-c" followed by the value
+ given, or NULL if not specified. The comment leader is
+ relevant only for old versions of RCS, but we let people set it
+ anyway. */
+ char *comment;
+
+ /* Set strict locking (-L). */
+ int set_strict;
+
+ /* Set nonstrict locking (-U). */
+ int set_nonstrict;
+
+ /* Delete revisions (-o). It is "-o" followed by the value specified. */
+ char *delete_revs;
+
+ /* Keyword substitution mode (-k), e.g. "-kb". */
+ char *kflag;
+
+ /* Description (-t). See sanity.sh for various moanings about
+ files and stdin and such. "" if -t specified without an
+ argument. It is "-t" followed by the argument. */
+ char *desc;
+
+ /* Interactive (-I). Problematic with client/server. */
+ int interactive;
+
+ /* Quiet (-q). Not the same as the global -q option, which is a bit
+ on the confusing side, perhaps. */
+ int quiet;
+
+ /* This is the cheesy part. It is a vector with the options which
+ we don't deal with above (e.g. "-afoo" "-abar,baz"). In the future
+ this presumably will be replaced by other variables which break
+ out the data in a more convenient fashion. AV as well as each of
+ the strings it points to is malloc'd. */
+ int ac;
+ char **av;
+ int av_alloc;
+};
+
+/* Add an argument. OPT is the option letter, e.g. 'a'. ARG is the
+ argument to that option, or NULL if omitted (whether NULL can actually
+ happen depends on whether the option was specified as optional to
+ getopt). */
+static void
+arg_add (dat, opt, arg)
+ struct admin_data *dat;
+ int opt;
+ char *arg;
+{
+ char *newelt = xmalloc ((arg == NULL ? 0 : strlen (arg)) + 3);
+ strcpy (newelt, "-");
+ newelt[1] = opt;
+ if (arg == NULL)
+ newelt[2] = '\0';
+ else
+ strcpy (newelt + 2, arg);
+
+ if (dat->av_alloc == 0)
+ {
+ dat->av_alloc = 1;
+ dat->av = (char **) xmalloc (dat->av_alloc * sizeof (*dat->av));
+ }
+ else if (dat->ac >= dat->av_alloc)
+ {
+ dat->av_alloc *= 2;
+ dat->av = (char **) xrealloc (dat->av,
+ dat->av_alloc * sizeof (*dat->av));
+ }
+ dat->av[dat->ac++] = newelt;
+}
int
admin (argc, argv)
@@ -40,6 +117,10 @@ admin (argc, argv)
struct group *grp;
struct group *getgrnam();
#endif
+ struct admin_data admin_data;
+ int c;
+ int i;
+
if (argc <= 1)
usage (admin_usage);
@@ -70,39 +151,301 @@ admin (argc, argv)
wrap_setup ();
- /* skip all optional arguments to see if we have any file names */
- for (ac = 1; ac < argc; ac++)
- if (argv[ac][0] != '-')
- break;
- argc -= ac;
- av = argv + 1;
- argv += ac;
- ac--;
+ memset (&admin_data, 0, sizeof admin_data);
+
+ /* TODO: get rid of `-' switch notation in admin_data. For
+ example, admin_data->branch should be not `-bfoo' but simply `foo'. */
+
+ optind = 0;
+ while ((c = getopt (argc, argv,
+ "+ib::c:a:A:e:l::u::LUn:N:m:o:s:t::IqxV:k:")) != -1)
+ {
+ switch (c)
+ {
+ case 'i':
+ /* This has always been documented as useless in cvs.texinfo
+ and it really is--admin_fileproc silently does nothing
+ if vers->vn_user is NULL. */
+ error (0, 0, "the -i option to admin is not supported");
+ error (0, 0, "run add or import to create an RCS file");
+ goto usage_error;
+
+ case 'b':
+ if (admin_data.branch != NULL)
+ {
+ error (0, 0, "duplicate 'b' option");
+ goto usage_error;
+ }
+ if (optarg == NULL)
+ admin_data.branch = xstrdup ("-b");
+ else
+ {
+ admin_data.branch = xmalloc (strlen (optarg) + 5);
+ strcpy (admin_data.branch, "-b");
+ strcat (admin_data.branch, optarg);
+ }
+ break;
+
+ case 'c':
+ if (admin_data.comment != NULL)
+ {
+ error (0, 0, "duplicate 'c' option");
+ goto usage_error;
+ }
+ admin_data.comment = xmalloc (strlen (optarg) + 5);
+ strcpy (admin_data.comment, "-c");
+ strcat (admin_data.comment, optarg);
+ break;
+
+ case 'a':
+ arg_add (&admin_data, 'a', optarg);
+ break;
+
+ case 'A':
+ /* In the client/server case, this is cheesy because
+ we just pass along the name of the RCS file, which
+ then will want to exist on the server. This is
+ accidental; having the client specify a pathname on
+ the server is not a design feature of the protocol. */
+ arg_add (&admin_data, 'A', optarg);
+ break;
+
+ case 'e':
+ arg_add (&admin_data, 'e', optarg);
+ break;
+
+ case 'l':
+ /* Note that multiple -l options are legal. */
+ arg_add (&admin_data, 'l', optarg);
+ break;
+
+ case 'u':
+ /* Note that multiple -u options are legal. */
+ arg_add (&admin_data, 'u', optarg);
+ break;
+
+ case 'L':
+ /* Probably could also complain if -L is specified multiple
+ times, although RCS doesn't and I suppose it is reasonable
+ just to have it mean the same as a single -L. */
+ if (admin_data.set_nonstrict)
+ {
+ error (0, 0, "-U and -L are incompatible");
+ goto usage_error;
+ }
+ admin_data.set_strict = 1;
+ break;
+
+ case 'U':
+ /* Probably could also complain if -U is specified multiple
+ times, although RCS doesn't and I suppose it is reasonable
+ just to have it mean the same as a single -U. */
+ if (admin_data.set_strict)
+ {
+ error (0, 0, "-U and -L are incompatible");
+ goto usage_error;
+ }
+ admin_data.set_nonstrict = 1;
+ break;
+
+ case 'n':
+ /* Mostly similar to cvs tag. Could also be parsing
+ the syntax of optarg, although for now we just pass
+ it to rcs as-is. Note that multiple -n options are
+ legal. */
+ arg_add (&admin_data, 'n', optarg);
+ break;
+
+ case 'N':
+ /* Mostly similar to cvs tag. Could also be parsing
+ the syntax of optarg, although for now we just pass
+ it to rcs as-is. Note that multiple -N options are
+ legal. */
+ arg_add (&admin_data, 'N', optarg);
+ break;
+
+ case 'm':
+ /* Change log message. Could also be parsing the syntax
+ of optarg, although for now we just pass it to rcs
+ as-is. Note that multiple -m options are legal. */
+ arg_add (&admin_data, 'm', optarg);
+ break;
+
+ case 'o':
+ /* Delete revisions. Probably should also be parsing the
+ syntax of optarg, so that the client can give errors
+ rather than making the server take care of that.
+ Other than that I'm not sure whether it matters much
+ whether we parse it here or in admin_fileproc.
+
+ Note that multiple -o options are illegal, in RCS
+ as well as here. */
+
+ if (admin_data.delete_revs != NULL)
+ {
+ error (0, 0, "duplicate '-o' option");
+ goto usage_error;
+ }
+ admin_data.delete_revs = xmalloc (strlen (optarg) + 5);
+ strcpy (admin_data.delete_revs, "-o");
+ strcat (admin_data.delete_revs, optarg);
+ break;
+
+ case 's':
+ /* Note that multiple -s options are legal. */
+ arg_add (&admin_data, 's', optarg);
+ break;
+
+ case 't':
+ if (admin_data.desc != NULL)
+ {
+ error (0, 0, "duplicate 't' option");
+ goto usage_error;
+ }
+ if (optarg == NULL)
+ admin_data.desc = xstrdup ("-t");
+ else
+ {
+ admin_data.desc = xmalloc (strlen (optarg) + 5);
+ strcpy (admin_data.desc, "-t");
+ strcat (admin_data.desc, optarg);
+ }
+ break;
+
+ case 'I':
+ /* At least in RCS this can be specified several times,
+ with the same meaning as being specified once. */
+ admin_data.interactive = 1;
+ break;
+
+ case 'q':
+ admin_data.quiet = 1;
+ break;
+
+ case 'x':
+ error (0, 0, "the -x option has never done anything useful");
+ error (0, 0, "RCS files in CVS always end in ,v");
+ goto usage_error;
+
+ case 'V':
+ /* No longer supported. */
+ error (0, 0, "the `-V' option is obsolete");
+ break;
+
+ case 'k':
+ if (admin_data.kflag != NULL)
+ {
+ error (0, 0, "duplicate '-k' option");
+ goto usage_error;
+ }
+ admin_data.kflag = RCS_check_kflag (optarg);
+ break;
+ default:
+ case '?':
+ /* getopt will have printed an error message. */
+
+ usage_error:
+ /* Don't use command_name; it might be "server". */
+ error (1, 0, "specify %s -H admin for usage information",
+ program_name);
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ for (i = 0; i < admin_data.ac; ++i)
+ {
+ assert (admin_data.av[i][0] == '-');
+ switch (admin_data.av[i][1])
+ {
+ case 'm':
+ case 'l':
+ case 'u':
+ check_numeric (&admin_data.av[i][2], argc, argv);
+ break;
+ default:
+ break;
+ }
+ }
+ if (admin_data.branch != NULL)
+ check_numeric (admin_data.branch + 2, argc, argv);
+ if (admin_data.delete_revs != NULL)
+ {
+ char *p;
+
+ check_numeric (admin_data.delete_revs + 2, argc, argv);
+ p = strchr (admin_data.delete_revs + 2, ':');
+ if (p != NULL && isdigit (p[1]))
+ check_numeric (p + 1, argc, argv);
+ else if (p != NULL && p[1] == ':' && isdigit(p[2]))
+ check_numeric (p + 2, argc, argv);
+ }
#ifdef CLIENT_SUPPORT
if (client_active)
{
- int i;
-
/* We're the client side. Fire up the remote server. */
start_server ();
ign_setup ();
- for (i = 0; i < ac; ++i) /* XXX send -ko too with i = 0 */
- send_arg (av[i]);
+ /* Note that option_with_arg does not work for us, because some
+ of the options must be sent without a space between the option
+ and its argument. */
+ if (admin_data.interactive)
+ error (1, 0, "-I option not useful with client/server");
+ if (admin_data.branch != NULL)
+ send_arg (admin_data.branch);
+ if (admin_data.comment != NULL)
+ send_arg (admin_data.comment);
+ if (admin_data.set_strict)
+ send_arg ("-L");
+ if (admin_data.set_nonstrict)
+ send_arg ("-U");
+ if (admin_data.delete_revs != NULL)
+ send_arg (admin_data.delete_revs);
+ if (admin_data.desc != NULL)
+ send_arg (admin_data.desc);
+ if (admin_data.quiet)
+ send_arg ("-q");
+ if (admin_data.kflag != NULL)
+ send_arg (admin_data.kflag);
+
+ for (i = 0; i < admin_data.ac; ++i)
+ send_arg (admin_data.av[i]);
send_file_names (argc, argv, SEND_EXPAND_WILD);
send_files (argc, argv, 0, 0, SEND_NO_CONTENTS);
send_to_server ("admin\012", 0);
- return get_responses_and_close ();
+ err = get_responses_and_close ();
+ goto return_it;
}
#endif /* CLIENT_SUPPORT */
- /* start the recursion processor */
+ lock_tree_for_write (argc, argv, 0, 0);
+
err = start_recursion (admin_fileproc, (FILESDONEPROC) NULL, admin_dirproc,
- (DIRLEAVEPROC) NULL, NULL, argc, argv, 0,
- W_LOCAL, 0, 1, (char *) NULL, 1);
+ (DIRLEAVEPROC) NULL, (void *)&admin_data,
+ argc, argv, 0,
+ W_LOCAL, 0, 0, (char *) NULL, 1);
+ Lock_Cleanup ();
+
+ return_it:
+ if (admin_data.branch != NULL)
+ free (admin_data.branch);
+ if (admin_data.comment != NULL)
+ free (admin_data.comment);
+ if (admin_data.delete_revs != NULL)
+ free (admin_data.delete_revs);
+ if (admin_data.kflag != NULL)
+ free (admin_data.kflag);
+ if (admin_data.desc != NULL)
+ free (admin_data.desc);
+ for (i = 0; i < admin_data.ac; ++i)
+ free (admin_data.av[i]);
+ if (admin_data.av != NULL)
+ free (admin_data.av);
+
return (err);
}
@@ -115,12 +458,12 @@ admin_fileproc (callerdat, finfo)
void *callerdat;
struct file_info *finfo;
{
+ struct admin_data *admin_data = (struct admin_data *) callerdat;
Vers_TS *vers;
char *version;
- char **argv;
- int argc;
- int retcode = 0;
+ int i;
int status = 0;
+ RCSNode *rcs, *rcs2;
vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0);
@@ -133,18 +476,326 @@ admin_fileproc (callerdat, finfo)
goto exitfunc;
}
- run_setup ("%s%s -x,v/", Rcsbin, RCS);
- for (argc = ac, argv = av; argc; argc--, argv++)
- run_arg (*argv);
- run_arg (vers->srcfile->path);
- if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL)) != 0)
+ rcs = vers->srcfile;
+ if (rcs->flags & PARTIAL)
+ RCS_reparsercsfile (rcs, NULL);
+
+ status = 0;
+
+ if (!admin_data->quiet)
{
- if (!quiet)
- error (0, retcode == -1 ? errno : 0,
- "%s failed for `%s'", RCS, finfo->file);
- status = 1;
- goto exitfunc;
+ cvs_output ("RCS file: ", 0);
+ cvs_output (rcs->path, 0);
+ cvs_output ("\n", 1);
+ }
+
+ if (admin_data->branch != NULL)
+ RCS_setbranch (rcs, (admin_data->branch[2] == '\0'
+ ? NULL
+ : admin_data->branch + 2));
+ if (admin_data->comment != NULL)
+ {
+ if (rcs->comment != NULL)
+ free (rcs->comment);
+ rcs->comment = xstrdup (admin_data->comment + 2);
+ }
+ if (admin_data->set_strict)
+ rcs->strict_locks = 1;
+ if (admin_data->set_nonstrict)
+ rcs->strict_locks = 0;
+ if (admin_data->delete_revs != NULL)
+ {
+ char *s, *t, *rev1, *rev2;
+ /* Set for :, clear for ::. */
+ int inclusive;
+ char *t2;
+
+ s = admin_data->delete_revs + 2;
+ inclusive = 1;
+ t = strchr (s, ':');
+ if (t != NULL)
+ {
+ if (t[1] == ':')
+ {
+ inclusive = 0;
+ t2 = t + 2;
+ }
+ else
+ t2 = t + 1;
+ }
+
+ /* Note that we don't support '-' for ranges. RCS considers it
+ obsolete and it is problematic with tags containing '-'. "cvs log"
+ has made the same decision. */
+
+ if (t == NULL)
+ {
+ /* -orev */
+ rev1 = xstrdup (s);
+ rev2 = xstrdup (s);
+ }
+ else if (t == s)
+ {
+ /* -o:rev2 */
+ rev1 = NULL;
+ rev2 = xstrdup (t2);
+ }
+ else
+ {
+ *t = '\0';
+ rev1 = xstrdup (s);
+ *t = ':'; /* probably unnecessary */
+ if (*t2 == '\0')
+ /* -orev1: */
+ rev2 = NULL;
+ else
+ /* -orev1:rev2 */
+ rev2 = xstrdup (t2);
+ }
+
+ if (rev1 == NULL && rev2 == NULL)
+ {
+ /* RCS segfaults if `-o:' is given */
+ error (0, 0, "no valid revisions specified in `%s' option",
+ admin_data->delete_revs);
+ status = 1;
+ }
+ else
+ {
+ status |= RCS_delete_revs (rcs, rev1, rev2, inclusive);
+ if (rev1)
+ free (rev1);
+ if (rev2)
+ free (rev2);
+ }
+ }
+ if (admin_data->desc != NULL)
+ {
+ free (rcs->desc);
+ rcs->desc = NULL;
+ if (admin_data->desc[2] == '-')
+ rcs->desc = xstrdup (admin_data->desc + 3);
+ else
+ {
+ char *descfile = admin_data->desc + 2;
+ size_t bufsize = 0;
+ size_t len;
+
+ /* If -t specified with no argument, read from stdin. */
+ if (*descfile == '\0')
+ descfile = NULL;
+ get_file (descfile, descfile, "r", &rcs->desc, &bufsize, &len);
+ }
+ }
+ if (admin_data->kflag != NULL)
+ {
+ char *kflag = admin_data->kflag + 2;
+ if (!rcs->expand || strcmp (rcs->expand, kflag) != 0)
+ {
+ if (rcs->expand)
+ free (rcs->expand);
+ rcs->expand = xstrdup (kflag);
+ }
}
+
+ /* Handle miscellaneous options. TODO: decide whether any or all
+ of these should have their own fields in the admin_data
+ structure. */
+ for (i = 0; i < admin_data->ac; ++i)
+ {
+ char *arg;
+ char *p, *rev, *revnum, *tag, *msg;
+ char **users;
+ int argc, u;
+ Node *n;
+ RCSVers *delta;
+
+ arg = admin_data->av[i];
+ switch (arg[1])
+ {
+ case 'a': /* fall through */
+ case 'e':
+ line2argv (&argc, &users, arg + 2, " ,\t\n");
+ if (arg[1] == 'a')
+ for (u = 0; u < argc; ++u)
+ RCS_addaccess (rcs, users[u]);
+ else
+ for (u = 0; u < argc; ++u)
+ RCS_delaccess (rcs, users[u]);
+ free_names (&argc, users);
+ break;
+ case 'A':
+
+ /* See admin-19a-admin and friends in sanity.sh for
+ relative pathnames. It makes sense to think in
+ terms of a syntax which give pathnames relative to
+ the repository or repository corresponding to the
+ current directory or some such (and perhaps don't
+ include ,v), but trying to worry about such things
+ is a little pointless unless you first worry about
+ whether "cvs admin -A" as a whole makes any sense
+ (currently probably not, as access lists don't
+ affect the behavior of CVS). */
+
+ rcs2 = RCS_parsercsfile (arg + 2);
+ if (rcs2 == NULL)
+ error (1, 0, "cannot continue");
+
+ p = xstrdup (RCS_getaccess (rcs2));
+ line2argv (&argc, &users, p, " \t\n");
+ free (p);
+ freercsnode (&rcs2);
+
+ for (u = 0; u < argc; ++u)
+ RCS_addaccess (rcs, users[u]);
+ free_names (&argc, users);
+ break;
+ case 'n': /* fall through */
+ case 'N':
+ if (arg[2] == '\0')
+ {
+ cvs_outerr ("missing symbolic name after ", 0);
+ cvs_outerr (arg, 0);
+ cvs_outerr ("\n", 1);
+ break;
+ }
+ p = strchr (arg, ':');
+ if (p == NULL)
+ {
+ if (RCS_deltag (rcs, arg + 2) != 0)
+ {
+ error (0, 0, "%s: Symbolic name %s is undefined.",
+ rcs->path,
+ arg + 2);
+ status = 1;
+ continue;
+ }
+ break;
+ }
+ *p = '\0';
+ tag = xstrdup (arg + 2);
+ *p++ = ':';
+
+ /* Option `n' signals an error if this tag is already bound. */
+ if (arg[1] == 'n')
+ {
+ n = findnode (RCS_symbols (rcs), tag);
+ if (n != NULL)
+ {
+ error (0, 0,
+ "%s: symbolic name %s already bound to %s",
+ rcs->path,
+ tag, n->data);
+ status = 1;
+ continue;
+ }
+ }
+
+ /* Expand rev if necessary. */
+ rev = RCS_gettag (rcs, p, 0, NULL);
+ RCS_settag (rcs, tag, rev);
+ if (rev != NULL)
+ free (rev);
+ free (tag);
+ break;
+ case 's':
+ p = strchr (arg, ':');
+ if (p == NULL)
+ {
+ tag = xstrdup (arg + 2);
+ rev = RCS_head (rcs);
+ }
+ else
+ {
+ *p = '\0';
+ tag = xstrdup (arg + 2);
+ *p++ = ':';
+ rev = xstrdup (p);
+ }
+ revnum = RCS_gettag (rcs, rev, 0, NULL);
+ free (rev);
+ if (revnum != NULL)
+ n = findnode (rcs->versions, revnum);
+ if (revnum == NULL || n == NULL)
+ {
+ error (0, 0,
+ "%s: can't set state of nonexisting revision %s",
+ rcs->path,
+ rev);
+ if (revnum != NULL)
+ free (revnum);
+ status = 1;
+ continue;
+ }
+ delta = (RCSVers *) n->data;
+ free (delta->state);
+ delta->state = tag;
+ break;
+
+ case 'm':
+ p = strchr (arg, ':');
+ if (p == NULL)
+ {
+ error (0, 0, "%s: -m option lacks revision number",
+ rcs->path);
+ status = 1;
+ continue;
+ }
+ *p = '\0';
+ rev = RCS_gettag (rcs, arg + 2, 0, NULL);
+ if (rev == NULL)
+ {
+ error (0, 0, "%s: no such revision %s", rcs->path, rev);
+ status = 1;
+ continue;
+ }
+ *p++ = ':';
+ msg = p;
+
+ n = findnode (rcs->versions, rev);
+ delta = (RCSVers *) n->data;
+ if (delta->text == NULL)
+ {
+ delta->text = (Deltatext *) xmalloc (sizeof (Deltatext));
+ memset ((void *) delta->text, 0, sizeof (Deltatext));
+ }
+ delta->text->version = xstrdup (delta->version);
+ delta->text->log = make_message_rcslegal (msg);
+ break;
+
+ case 'l':
+ status |= RCS_lock (rcs, arg[2] ? arg + 2 : NULL, 0);
+ break;
+ case 'u':
+ status |= RCS_unlock (rcs, arg[2] ? arg + 2 : NULL, 0);
+ break;
+ default: assert(0); /* can't happen */
+ }
+ }
+
+ /* TODO: reconcile the weird discrepancies between
+ admin_data->quiet and quiet. */
+ if (status == 0)
+ {
+ RCS_rewrite (rcs, NULL, NULL);
+ if (!admin_data->quiet)
+ cvs_output ("done\n", 5);
+ }
+ else
+ {
+ /* Note that this message should only occur after another
+ message has given a more specific error. The point of this
+ additional message is to make it clear that the previous problems
+ caused CVS to forget about the idea of modifying the RCS file. */
+ error (0, 0, "cannot modify RCS file for `%s'", finfo->file);
+
+ /* Upon failure, we want to abandon any changes made to the
+ RCS data structure. Forcing a reparse does the trick,
+ but leaks memory and is kludgey. Should we export
+ free_rcsnode_contents for this purpose? */
+ RCS_reparsercsfile (rcs, NULL);
+ }
+
exitfunc:
freevers_ts (&vers);
return status;
OpenPOWER on IntegriCloud