#include "cvs.h"
#include <assert.h>
struct option_revlist
{
struct option_revlist *next;
char *first;
char *last;
int branchhead;
int inclusive;
};
struct revlist
{
struct revlist *next;
char *first;
char *last;
int fields;
int inclusive;
};
struct datelist
{
struct datelist *next;
char *start;
char *end;
int inclusive;
};
struct log_data
{
int nameonly;
int header;
int long_header;
int notags;
int default_branch;
int sup_header;
struct option_revlist *revlist;
struct datelist *datelist;
struct datelist *singledatelist;
List *statelist;
List *authorlist;
};
struct log_data_and_rcs
{
struct log_data *log_data;
struct revlist *revlist;
RCSNode *rcs;
};
static int rlog_proc (int argc, char **argv, char *xwhere,
char *mwhere, char *mfile, int shorten,
int local_specified, char *mname, char *msg);
static Dtype log_dirproc (void *callerdat, const char *dir,
const char *repository, const char *update_dir,
List *entries);
static int log_fileproc (void *callerdat, struct file_info *finfo);
static struct option_revlist *log_parse_revlist (const char *);
static void log_parse_date (struct log_data *, const char *);
static void log_parse_list (List **, const char *);
static struct revlist *log_expand_revlist (RCSNode *, char *,
struct option_revlist *, int);
static void log_free_revlist (struct revlist *);
static int log_version_requested (struct log_data *, struct revlist *,
RCSNode *, RCSVers *);
static int log_symbol (Node *, void *);
static int log_count (Node *, void *);
static int log_fix_singledate (Node *, void *);
static int log_count_print (Node *, void *);
static void log_tree (struct log_data *, struct revlist *,
RCSNode *, const char *);
static void log_abranch (struct log_data *, struct revlist *,
RCSNode *, const char *);
static void log_version (struct log_data *, struct revlist *,
RCSNode *, RCSVers *, int);
static int log_branch (Node *, void *);
static int version_compare (const char *, const char *, int);
static struct log_data log_data;
static int is_rlog;
static const char *const log_usage[] =
{
"Usage: %s %s [-lRhtNb] [-r[revisions]] [-d dates] [-s states]\n",
" [-w[logins]] [files...]\n",
"\t-l\tLocal directory only, no recursion.\n",
"\t-b\tOnly list revisions on the default branch.\n",
"\t-h\tOnly print header.\n",
"\t-R\tOnly print name of RCS file.\n",
"\t-t\tOnly print header and descriptive text.\n",
"\t-N\tDo not list tags.\n",
"\t-S\tDo not print name/header if no revisions selected. -d, -r,\n",
"\t\t-s, & -w have little effect in conjunction with -b, -h, -R, and\n",
"\t\t-t without this option.\n",
"\t-r[revisions]\tA comma-separated list of revisions to print:\n",
"\t rev1:rev2 Between rev1 and rev2, including rev1 and rev2.\n",
"\t rev1::rev2 Between rev1 and rev2, excluding rev1.\n",
"\t rev: rev and following revisions on the same branch.\n",
"\t rev:: After rev on the same branch.\n",
"\t :rev rev and previous revisions on the same branch.\n",
"\t ::rev rev and previous revisions on the same branch.\n",
"\t rev Just rev.\n",
"\t branch All revisions on the branch.\n",
"\t branch. The last revision on the branch.\n",
"\t-d dates\tA semicolon-separated list of dates\n",
"\t \t(D1<D2 for range, D for latest before).\n",
"\t-s states\tOnly list revisions with specified states.\n",
"\t-w[logins]\tOnly list revisions checked in by specified logins.\n",
"(Specify the --help global option for a list of other help options)\n",
NULL
};
#ifdef CLIENT_SUPPORT
static int
send_one (Node *node, void *closure)
{
char *option = closure;
send_to_server ("Argument ", 0);
send_to_server (option, 0);
if (strcmp (node->key, "@@MYSELF") == 0)
;
else
send_to_server (node->key, 0);
send_to_server ("\012", 0);
return 0;
}
static void
send_arg_list (char *option, List *arg)
{
if (arg == NULL)
return;
walklist (arg, send_one, option);
}
#endif
int
cvslog (int argc, char **argv)
{
int c;
int err = 0;
int local = 0;
struct option_revlist **prl;
is_rlog = (strcmp (cvs_cmd_name, "rlog") == 0);
if (argc == -1)
usage (log_usage);
memset (&log_data, 0, sizeof log_data);
prl = &log_data.revlist;
optind = 0;
while ((c = getopt (argc, argv, "+bd:hlNSRr::s:tw::")) != -1)
{
switch (c)
{
case 'b':
log_data.default_branch = 1;
break;
case 'd':
log_parse_date (&log_data, optarg);
break;
case 'h':
log_data.header = 1;
break;
case 'l':
local = 1;
break;
case 'N':
log_data.notags = 1;
break;
case 'S':
log_data.sup_header = 1;
break;
case 'R':
log_data.nameonly = 1;
break;
case 'r':
*prl = log_parse_revlist (optarg);
prl = &(*prl)->next;
break;
case 's':
log_parse_list (&log_data.statelist, optarg);
break;
case 't':
log_data.long_header = 1;
break;
case 'w':
if (optarg != NULL)
log_parse_list (&log_data.authorlist, optarg);
else
log_parse_list (&log_data.authorlist, "@@MYSELF");
break;
case '?':
default:
usage (log_usage);
break;
}
}
argc -= optind;
argv += optind;
wrap_setup ();
#ifdef CLIENT_SUPPORT
if (current_parsed_root->isremote)
{
struct datelist *p;
struct option_revlist *rp;
char datetmp[MAXDATELEN];
start_server ();
if (is_rlog && !supported_request ("rlog"))
error (1, 0, "server does not support rlog");
ign_setup ();
if (log_data.default_branch)
send_arg ("-b");
while (log_data.datelist != NULL)
{
p = log_data.datelist;
log_data.datelist = p->next;
send_to_server ("Argument -d\012", 0);
send_to_server ("Argument ", 0);
date_to_internet (datetmp, p->start);
send_to_server (datetmp, 0);
if (p->inclusive)
send_to_server ("<=", 0);
else
send_to_server ("<", 0);
date_to_internet (datetmp, p->end);
send_to_server (datetmp, 0);
send_to_server ("\012", 0);
if (p->start)
free (p->start);
if (p->end)
free (p->end);
free (p);
}
while (log_data.singledatelist != NULL)
{
p = log_data.singledatelist;
log_data.singledatelist = p->next;
send_to_server ("Argument -d\012", 0);
send_to_server ("Argument ", 0);
date_to_internet (datetmp, p->end);
send_to_server (datetmp, 0);
send_to_server ("\012", 0);
if (p->end)
free (p->end);
free (p);
}
if (log_data.header)
send_arg ("-h");
if (local)
send_arg("-l");
if (log_data.notags)
send_arg("-N");
if (log_data.sup_header)
send_arg("-S");
if (log_data.nameonly)
send_arg("-R");
if (log_data.long_header)
send_arg("-t");
while (log_data.revlist != NULL)
{
rp = log_data.revlist;
log_data.revlist = rp->next;
send_to_server ("Argument -r", 0);
if (rp->branchhead)
{
if (rp->first != NULL)
send_to_server (rp->first, 0);
send_to_server (".", 1);
}
else
{
if (rp->first != NULL)
send_to_server (rp->first, 0);
send_to_server (":", 1);
if (!rp->inclusive)
send_to_server (":", 1);
if (rp->last != NULL)
send_to_server (rp->last, 0);
}
send_to_server ("\012", 0);
if (rp->first)
free (rp->first);
if (rp->last)
free (rp->last);
free (rp);
}
send_arg_list ("-s", log_data.statelist);
dellist (&log_data.statelist);
send_arg_list ("-w", log_data.authorlist);
dellist (&log_data.authorlist);
send_arg ("--");
if (is_rlog)
{
int i;
for (i = 0; i < argc; i++)
send_arg (argv[i]);
send_to_server ("rlog\012", 0);
}
else
{
send_files (argc, argv, local, 0, SEND_NO_CONTENTS);
send_file_names (argc, argv, SEND_EXPAND_WILD);
send_to_server ("log\012", 0);
}
err = get_responses_and_close ();
return err;
}
#endif
if (findnode (log_data.authorlist, "@@MYSELF") != NULL)
log_parse_list (&log_data.authorlist, getcaller ());
if (is_rlog)
{
DBM *db;
int i;
db = open_module ();
for (i = 0; i < argc; i++)
{
err += do_module (db, argv[i], MISC, "Logging", rlog_proc,
NULL, 0, local, 0, 0, NULL);
}
close_module (db);
}
else
{
err = rlog_proc (argc + 1, argv - 1, NULL, NULL, NULL, 0, local, NULL,
NULL);
}
while (log_data.revlist)
{
struct option_revlist *rl = log_data.revlist->next;
if (log_data.revlist->first)
free (log_data.revlist->first);
if (log_data.revlist->last)
free (log_data.revlist->last);
free (log_data.revlist);
log_data.revlist = rl;
}
while (log_data.datelist)
{
struct datelist *nd = log_data.datelist->next;
if (log_data.datelist->start)
free (log_data.datelist->start);
if (log_data.datelist->end)
free (log_data.datelist->end);
free (log_data.datelist);
log_data.datelist = nd;
}
while (log_data.singledatelist)
{
struct datelist *nd = log_data.singledatelist->next;
if (log_data.singledatelist->start)
free (log_data.singledatelist->start);
if (log_data.singledatelist->end)
free (log_data.singledatelist->end);
free (log_data.singledatelist);
log_data.singledatelist = nd;
}
dellist (&log_data.statelist);
dellist (&log_data.authorlist);
return err;
}
static int
rlog_proc (int argc, char **argv, char *xwhere, char *mwhere, char *mfile,
int shorten, int local, char *mname, char *msg)
{
char *myargv[2];
int err = 0;
int which;
char *repository = NULL;
char *where;
if (is_rlog)
{
repository = xmalloc (strlen (current_parsed_root->directory)
+ strlen (argv[0])
+ (mfile == NULL ? 0 : strlen (mfile) + 1) + 2);
(void)sprintf (repository, "%s/%s",
current_parsed_root->directory, argv[0]);
where = xmalloc (strlen (argv[0])
+ (mfile == NULL ? 0 : strlen (mfile) + 1)
+ 1);
(void)strcpy (where, argv[0]);
if (mfile != NULL)
{
char *cp;
char *path;
if ((cp = strrchr (mfile, '/')) != NULL)
{
*cp = '\0';
(void)strcat (repository, "/");
(void)strcat (repository, mfile);
(void)strcat (where, "/");
(void)strcat (where, mfile);
mfile = cp + 1;
}
path = Xasprintf ("%s/%s", repository, mfile);
if (isdir (path))
{
(void)strcpy (repository, path);
(void)strcat (where, "/");
(void)strcat (where, mfile);
}
else
{
myargv[0] = argv[0];
myargv[1] = mfile;
argc = 2;
argv = myargv;
}
free (path);
}
if (CVS_CHDIR (repository) < 0)
{
error (0, errno, "cannot chdir to %s", repository);
free (repository);
free (where);
return 1;
}
which = W_REPOS | W_ATTIC;
}
else
{
repository = NULL;
where = NULL;
which = W_LOCAL | W_REPOS | W_ATTIC;
}
err = start_recursion (log_fileproc, NULL, log_dirproc,
NULL, &log_data,
argc - 1, argv + 1, local, which, 0, CVS_LOCK_READ,
where, 1, repository);
if (!(which & W_LOCAL)) free (repository);
if (where) free (where);
return err;
}
static struct option_revlist *
log_parse_revlist (const char *argstring)
{
char *orig_copy, *copy;
struct option_revlist *ret, **pr;
if (argstring == NULL)
argstring = "";
ret = NULL;
pr = &ret;
orig_copy = copy = xstrdup (argstring);
while (copy != NULL)
{
char *comma;
struct option_revlist *r;
comma = strchr (copy, ',');
if (comma != NULL)
*comma++ = '\0';
r = xmalloc (sizeof *r);
r->next = NULL;
r->first = copy;
r->branchhead = 0;
r->last = strchr (copy, ':');
if (r->last != NULL)
{
*r->last++ = '\0';
r->inclusive = (*r->last != ':');
if (!r->inclusive)
r->last++;
}
else
{
r->last = r->first;
r->inclusive = 1;
if (r->first[0] != '\0' && r->first[strlen (r->first) - 1] == '.')
{
r->branchhead = 1;
r->first[strlen (r->first) - 1] = '\0';
}
}
if (*r->first == '\0')
r->first = NULL;
if (*r->last == '\0')
r->last = NULL;
if (r->first != NULL)
r->first = xstrdup (r->first);
if (r->last != NULL)
r->last = xstrdup (r->last);
*pr = r;
pr = &r->next;
copy = comma;
}
free (orig_copy);
return ret;
}
static void
log_parse_date (struct log_data *log_data, const char *argstring)
{
char *orig_copy, *copy;
orig_copy = copy = xstrdup (argstring);
while (copy != NULL)
{
struct datelist *nd, **pd;
char *cpend, *cp, *ds, *de;
nd = xmalloc (sizeof *nd);
cpend = strchr (copy, ';');
if (cpend != NULL)
*cpend++ = '\0';
pd = &log_data->datelist;
nd->inclusive = 0;
if ((cp = strchr (copy, '>')) != NULL)
{
*cp++ = '\0';
if (*cp == '=')
{
++cp;
nd->inclusive = 1;
}
ds = cp;
de = copy;
}
else if ((cp = strchr (copy, '<')) != NULL)
{
*cp++ = '\0';
if (*cp == '=')
{
++cp;
nd->inclusive = 1;
}
ds = copy;
de = cp;
}
else
{
ds = NULL;
de = copy;
pd = &log_data->singledatelist;
}
if (ds == NULL)
nd->start = NULL;
else if (*ds != '\0')
nd->start = Make_Date (ds);
else
{
nd->start = Make_Date ("1/1/1970 UTC");
}
if (*de != '\0')
nd->end = Make_Date (de);
else
{
nd->end = Make_Date ("2038-01-01");
}
nd->next = *pd;
*pd = nd;
copy = cpend;
}
free (orig_copy);
}
static void
log_parse_list (List **plist, const char *argstring)
{
while (1)
{
Node *p;
char *cp;
p = getnode ();
cp = strchr (argstring, ',');
if (cp == NULL)
p->key = xstrdup (argstring);
else
{
size_t len;
len = cp - argstring;
p->key = xmalloc (len + 1);
strncpy (p->key, argstring, len);
p->key[len] = '\0';
}
if (*plist == NULL)
*plist = getlist ();
if (addnode (*plist, p) != 0)
freenode (p);
if (cp == NULL)
break;
argstring = cp + 1;
}
}
static int
printlock_proc (Node *lock, void *foo)
{
cvs_output ("\n\t", 2);
cvs_output (lock->data, 0);
cvs_output (": ", 2);
cvs_output (lock->key, 0);
return 0;
}
static int
log_fileproc (void *callerdat, struct file_info *finfo)
{
struct log_data *log_data = callerdat;
Node *p;
char *baserev;
int selrev = -1;
RCSNode *rcsfile;
char buf[50];
struct revlist *revlist = NULL;
struct log_data_and_rcs log_data_and_rcs;
rcsfile = finfo->rcs;
p = findnode (finfo->entries, finfo->file);
if (p != NULL)
{
Entnode *e = p->data;
baserev = e->version;
if (baserev[0] == '-') ++baserev;
}
else
baserev = NULL;
if (rcsfile == NULL)
{
if (baserev != NULL)
{
if (baserev[0] == '0' && baserev[1] == '\0')
{
if (!really_quiet)
error (0, 0, "%s has been added, but not committed",
finfo->file);
return 0;
}
}
if (!really_quiet)
error (0, 0, "nothing known about %s", finfo->file);
return 1;
}
if (log_data->sup_header || !log_data->nameonly)
{
RCS_fully_parse (rcsfile);
revlist = log_expand_revlist (rcsfile, baserev, log_data->revlist,
log_data->default_branch);
if (log_data->sup_header
|| (!log_data->header && !log_data->long_header))
{
log_data_and_rcs.log_data = log_data;
log_data_and_rcs.revlist = revlist;
log_data_and_rcs.rcs = rcsfile;
if (log_data->singledatelist != NULL)
walklist (rcsfile->versions, log_fix_singledate,
&log_data_and_rcs);
selrev = walklist (rcsfile->versions, log_count_print,
&log_data_and_rcs);
if (log_data->sup_header && selrev == 0)
{
log_free_revlist (revlist);
return 0;
}
}
}
if (log_data->nameonly)
{
cvs_output (rcsfile->print_path, 0);
cvs_output ("\n", 1);
log_free_revlist (revlist);
return 0;
}
cvs_output ("\n", 1);
cvs_output ("RCS file: ", 0);
cvs_output (rcsfile->print_path, 0);
if (!is_rlog)
{
cvs_output ("\nWorking file: ", 0);
if (finfo->update_dir[0] != '\0')
{
cvs_output (finfo->update_dir, 0);
cvs_output ("/", 0);
}
cvs_output (finfo->file, 0);
}
cvs_output ("\nhead:", 0);
if (rcsfile->head != NULL)
{
cvs_output (" ", 1);
cvs_output (rcsfile->head, 0);
}
cvs_output ("\nbranch:", 0);
if (rcsfile->branch != NULL)
{
cvs_output (" ", 1);
cvs_output (rcsfile->branch, 0);
}
cvs_output ("\nlocks:", 0);
if (rcsfile->strict_locks)
cvs_output (" strict", 0);
walklist (RCS_getlocks (rcsfile), printlock_proc, NULL);
cvs_output ("\naccess list:", 0);
if (rcsfile->access != NULL)
{
const char *cp;
cp = rcsfile->access;
while (*cp != '\0')
{
const char *cp2;
cvs_output ("\n\t", 2);
cp2 = cp;
while (!isspace ((unsigned char)*cp2) && *cp2 != '\0')
++cp2;
cvs_output (cp, cp2 - cp);
cp = cp2;
while (isspace ((unsigned char)*cp) && *cp != '\0')
++cp;
}
}
if (!log_data->notags)
{
List *syms;
cvs_output ("\nsymbolic names:", 0);
syms = RCS_symbols (rcsfile);
walklist (syms, log_symbol, NULL);
}
cvs_output ("\nkeyword substitution: ", 0);
if (rcsfile->expand == NULL)
cvs_output ("kv", 2);
else
cvs_output (rcsfile->expand, 0);
cvs_output ("\ntotal revisions: ", 0);
sprintf (buf, "%d", walklist (rcsfile->versions, log_count, NULL));
cvs_output (buf, 0);
if (selrev >= 0)
{
cvs_output (";\tselected revisions: ", 0);
sprintf (buf, "%d", selrev);
cvs_output (buf, 0);
}
cvs_output ("\n", 1);
if (!log_data->header || log_data->long_header)
{
cvs_output ("description:\n", 0);
if (rcsfile->desc != NULL)
cvs_output (rcsfile->desc, 0);
}
if (!log_data->header && ! log_data->long_header && rcsfile->head != NULL)
{
p = findnode (rcsfile->versions, rcsfile->head);
if (p == NULL)
error (1, 0, "can not find head revision in `%s'",
finfo->fullname);
while (p != NULL)
{
RCSVers *vers = p->data;
log_version (log_data, revlist, rcsfile, vers, 1);
if (vers->next == NULL)
p = NULL;
else
{
p = findnode (rcsfile->versions, vers->next);
if (p == NULL)
error (1, 0, "can not find next revision `%s' in `%s'",
vers->next, finfo->fullname);
}
}
log_tree (log_data, revlist, rcsfile, rcsfile->head);
}
cvs_output("\
=============================================================================\n",
0);
log_free_revlist (revlist);
if (log_data->singledatelist != NULL)
{
struct datelist *d;
for (d = log_data->singledatelist; d != NULL; d = d->next)
{
if (d->start != NULL)
free (d->start);
d->start = NULL;
}
}
return 0;
}
static struct revlist *
log_expand_revlist (RCSNode *rcs, char *baserev,
struct option_revlist *revlist, int default_branch)
{
struct option_revlist *r;
struct revlist *ret, **pr;
ret = NULL;
pr = &ret;
for (r = revlist; r != NULL; r = r->next)
{
struct revlist *nr;
nr = xmalloc (sizeof *nr);
nr->inclusive = r->inclusive;
if (r->first == NULL && r->last == NULL)
{
nr->first = RCS_head (rcs);
if (!nr->first)
{
if (!really_quiet)
error (0, 0, "No head revision in archive `%s'.",
rcs->path);
nr->last = NULL;
nr->fields = 0;
}
else
{
nr->last = xstrdup (nr->first);
nr->fields = numdots (nr->first) + 1;
}
}
else if (r->branchhead)
{
char *branch;
if (isdigit ((unsigned char) r->first[0]))
nr->first = RCS_getbranch (rcs, r->first, 1);
else
{
branch = RCS_whatbranch (rcs, r->first);
if (branch == NULL)
nr->first = NULL;
else
{
nr->first = RCS_getbranch (rcs, branch, 1);
free (branch);
}
}
if (!nr->first)
{
if (!really_quiet)
error (0, 0, "warning: no branch `%s' in `%s'",
r->first, rcs->print_path);
nr->last = NULL;
nr->fields = 0;
}
else
{
nr->last = xstrdup (nr->first);
nr->fields = numdots (nr->first) + 1;
}
}
else
{
if (r->first == NULL || isdigit ((unsigned char) r->first[0]))
nr->first = xstrdup (r->first);
else
{
if (baserev && strcmp (r->first, TAG_BASE) == 0)
nr->first = xstrdup (baserev);
else if (RCS_nodeisbranch (rcs, r->first))
nr->first = RCS_whatbranch (rcs, r->first);
else
nr->first = RCS_gettag (rcs, r->first, 1, NULL);
if (nr->first == NULL && !really_quiet)
{
error (0, 0, "warning: no revision `%s' in `%s'",
r->first, rcs->print_path);
}
}
if (r->last == r->first || (r->last != NULL && r->first != NULL &&
strcmp (r->last, r->first) == 0))
nr->last = xstrdup (nr->first);
else if (r->last == NULL || isdigit ((unsigned char) r->last[0]))
nr->last = xstrdup (r->last);
else
{
if (baserev && strcmp (r->last, TAG_BASE) == 0)
nr->last = xstrdup (baserev);
else if (RCS_nodeisbranch (rcs, r->last))
nr->last = RCS_whatbranch (rcs, r->last);
else
nr->last = RCS_gettag (rcs, r->last, 1, NULL);
if (nr->last == NULL && !really_quiet)
{
error (0, 0, "warning: no revision `%s' in `%s'",
r->last, rcs->print_path);
}
}
if (r->first == NULL && nr->last != NULL)
{
nr->fields = numdots (nr->last) + 1;
if (nr->fields < 2)
nr->first = xstrdup (".0");
else
{
char *cp;
nr->first = xstrdup (nr->last);
cp = strrchr (nr->first, '.');
assert (cp);
strcpy (cp + 1, "0");
}
}
else if (r->last == NULL && nr->first != NULL)
{
nr->fields = numdots (nr->first) + 1;
nr->last = xstrdup (nr->first);
if (nr->fields < 2)
nr->last[0] = '\0';
else
{
char *cp;
cp = strrchr (nr->last, '.');
assert (cp);
*cp = '\0';
}
}
else if (nr->first == NULL || nr->last == NULL)
nr->fields = 0;
else if (strcmp (nr->first, nr->last) == 0)
nr->fields = numdots (nr->last) + 1;
else
{
int ord;
int dots1 = numdots (nr->first);
int dots2 = numdots (nr->last);
if (dots1 > dots2 || (dots1 == dots2 &&
version_compare (nr->first, nr->last, dots1 + 1) > 0))
{
char *tmp = nr->first;
nr->first = nr->last;
nr->last = tmp;
nr->fields = dots2 + 1;
dots2 = dots1;
dots1 = nr->fields - 1;
}
else
nr->fields = dots1 + 1;
dots1 += (nr->fields & 1);
ord = version_compare (nr->first, nr->last, dots1);
if (ord > 0 || (nr->fields > 2 && ord < 0))
{
error (0, 0,
"invalid branch or revision pair %s:%s in `%s'",
r->first, r->last, rcs->print_path);
free (nr->first);
nr->first = NULL;
free (nr->last);
nr->last = NULL;
nr->fields = 0;
}
else
{
if (nr->fields <= dots2 && (nr->fields & 1))
{
char *p = Xasprintf ("%s.0", nr->first);
free (nr->first);
nr->first = p;
++nr->fields;
}
while (nr->fields <= dots2)
{
char *p;
int i;
nr->next = NULL;
*pr = nr;
nr = xmalloc (sizeof *nr);
nr->inclusive = 1;
nr->first = xstrdup ((*pr)->last);
nr->last = xstrdup ((*pr)->last);
nr->fields = (*pr)->fields;
p = (*pr)->last;
for (i = 0; i < nr->fields; i++)
p = strchr (p, '.') + 1;
p[-1] = '\0';
p = strchr (nr->first + (p - (*pr)->last), '.');
if (p != NULL)
{
*++p = '0';
*++p = '\0';
nr->fields += 2;
}
else
++nr->fields;
pr = &(*pr)->next;
}
}
}
}
nr->next = NULL;
*pr = nr;
pr = &nr->next;
}
if (default_branch
&& (rcs->head != NULL || rcs->branch != NULL))
{
struct revlist *nr;
nr = xmalloc (sizeof *nr);
if (rcs->branch != NULL)
nr->first = xstrdup (rcs->branch);
else
{
char *cp;
nr->first = xstrdup (rcs->head);
assert (nr->first);
cp = strrchr (nr->first, '.');
assert (cp);
*cp = '\0';
}
nr->last = xstrdup (nr->first);
nr->fields = numdots (nr->first) + 1;
nr->inclusive = 1;
nr->next = NULL;
*pr = nr;
}
return ret;
}
static void
log_free_revlist (struct revlist *revlist)
{
struct revlist *r;
r = revlist;
while (r != NULL)
{
struct revlist *next;
if (r->first != NULL)
free (r->first);
if (r->last != NULL)
free (r->last);
next = r->next;
free (r);
r = next;
}
}
static int
log_version_requested (struct log_data *log_data, struct revlist *revlist,
RCSNode *rcs, RCSVers *vnode)
{
if (log_data->statelist != NULL
&& findnode (log_data->statelist, vnode->state) == NULL)
{
return 0;
}
if (log_data->authorlist != NULL)
{
if (vnode->author != NULL
&& findnode (log_data->authorlist, vnode->author) == NULL)
{
return 0;
}
}
if (log_data->datelist != NULL || log_data->singledatelist != NULL)
{
struct datelist *d;
for (d = log_data->datelist; d != NULL; d = d->next)
{
int cmp;
cmp = RCS_datecmp (vnode->date, d->start);
if (cmp > 0 || (cmp == 0 && d->inclusive))
{
cmp = RCS_datecmp (vnode->date, d->end);
if (cmp < 0 || (cmp == 0 && d->inclusive))
break;
}
}
if (d == NULL)
{
for (d = log_data->singledatelist; d != NULL; d = d->next)
{
if (d->start != NULL
&& RCS_datecmp (vnode->date, d->start) == 0)
{
break;
}
}
if (d == NULL)
return 0;
}
}
if (revlist != NULL)
{
char *v;
int vfields;
struct revlist *r;
v = vnode->version;
vfields = numdots (v) + 1;
for (r = revlist; r != NULL; r = r->next)
{
if (vfields == r->fields + (r->fields & 1) &&
(r->inclusive ? version_compare (v, r->first, r->fields) >= 0 :
version_compare (v, r->first, r->fields) > 0)
&& version_compare (v, r->last, r->fields) <= 0)
{
return 1;
}
}
return 0;
}
return 1;
}
static int
log_symbol (Node *p, void *closure)
{
cvs_output ("\n\t", 2);
cvs_output (p->key, 0);
cvs_output (": ", 2);
cvs_output (p->data, 0);
return 0;
}
static int
log_count (Node *p, void *closure)
{
return 1;
}
static int
log_fix_singledate (Node *p, void *closure)
{
struct log_data_and_rcs *data = closure;
Node *pv;
RCSVers *vnode;
struct datelist *holdsingle, *holddate;
int requested;
pv = findnode (data->rcs->versions, p->key);
if (pv == NULL)
error (1, 0, "missing version `%s' in RCS file `%s'",
p->key, data->rcs->print_path);
vnode = pv->data;
holdsingle = data->log_data->singledatelist;
data->log_data->singledatelist = NULL;
holddate = data->log_data->datelist;
data->log_data->datelist = NULL;
requested = log_version_requested (data->log_data, data->revlist,
data->rcs, vnode);
data->log_data->singledatelist = holdsingle;
data->log_data->datelist = holddate;
if (requested)
{
struct datelist *d;
for (d = data->log_data->singledatelist; d != NULL; d = d->next)
{
if (RCS_datecmp (vnode->date, d->end) <= 0
&& (d->start == NULL
|| RCS_datecmp (vnode->date, d->start) > 0))
{
if (d->start != NULL)
free (d->start);
d->start = xstrdup (vnode->date);
}
}
}
return 0;
}
static int
log_count_print (Node *p, void *closure)
{
struct log_data_and_rcs *data = closure;
Node *pv;
pv = findnode (data->rcs->versions, p->key);
if (pv == NULL)
error (1, 0, "missing version `%s' in RCS file `%s'",
p->key, data->rcs->print_path);
if (log_version_requested (data->log_data, data->revlist, data->rcs,
pv->data))
return 1;
else
return 0;
}
static void
log_tree (struct log_data *log_data, struct revlist *revlist, RCSNode *rcs,
const char *ver)
{
Node *p;
RCSVers *vnode;
p = findnode (rcs->versions, ver);
if (p == NULL)
error (1, 0, "missing version `%s' in RCS file `%s'",
ver, rcs->print_path);
vnode = p->data;
if (vnode->next != NULL)
log_tree (log_data, revlist, rcs, vnode->next);
if (vnode->branches != NULL)
{
Node *head, *branch;
head = vnode->branches->list;
for (branch = head->prev; branch != head; branch = branch->prev)
{
log_abranch (log_data, revlist, rcs, branch->key);
log_tree (log_data, revlist, rcs, branch->key);
}
}
}
static void
log_abranch (struct log_data *log_data, struct revlist *revlist, RCSNode *rcs,
const char *ver)
{
Node *p;
RCSVers *vnode;
p = findnode (rcs->versions, ver);
if (p == NULL)
error (1, 0, "missing version `%s' in RCS file `%s'",
ver, rcs->print_path);
vnode = p->data;
if (vnode->next != NULL)
log_abranch (log_data, revlist, rcs, vnode->next);
log_version (log_data, revlist, rcs, vnode, 0);
}
static void
log_version (struct log_data *log_data, struct revlist *revlist, RCSNode *rcs,
RCSVers *ver, int trunk)
{
Node *p;
int year, mon, mday, hour, min, sec;
char buf[100];
Node *padd, *pdel;
if (! log_version_requested (log_data, revlist, rcs, ver))
return;
cvs_output ("----------------------------\nrevision ", 0);
cvs_output (ver->version, 0);
p = findnode (RCS_getlocks (rcs), ver->version);
if (p != NULL)
{
cvs_output ("\tlocked by: ", 0);
cvs_output (p->data, 0);
cvs_output (";", 1);
}
cvs_output ("\n", 1);
cvs_output_tagged ("text", "date: ");
(void)sscanf (ver->date, SDATEFORM, &year, &mon, &mday, &hour, &min,
&sec);
if (year < 1900)
year += 1900;
sprintf (buf, "%04d-%02d-%02d %02d:%02d:%02d +0000", year, mon, mday,
hour, min, sec);
cvs_output_tagged ("date", buf);
cvs_output_tagged ("text", "; author: ");
cvs_output_tagged ("text", ver->author);
cvs_output_tagged ("text", "; state: ");
cvs_output_tagged ("text", ver->state);
cvs_output_tagged ("text", ";");
if (! trunk)
{
padd = findnode (ver->other, ";add");
pdel = findnode (ver->other, ";delete");
}
else if (ver->next == NULL)
{
padd = NULL;
pdel = NULL;
}
else
{
Node *nextp;
RCSVers *nextver;
nextp = findnode (rcs->versions, ver->next);
if (nextp == NULL)
error (1, 0, "missing version `%s' in `%s'", ver->next,
rcs->print_path);
nextver = nextp->data;
pdel = findnode (nextver->other, ";add");
padd = findnode (nextver->other, ";delete");
}
if (padd != NULL)
{
assert (pdel);
cvs_output_tagged ("text", " lines: +");
cvs_output_tagged ("text", padd->data);
cvs_output_tagged ("text", " -");
cvs_output_tagged ("text", pdel->data);
cvs_output_tagged ("text", ";");
}
p = findnode(ver->other_delta,"commitid");
if(p && p->data)
{
cvs_output_tagged ("text", " commitid: ");
cvs_output_tagged ("text", p->data);
cvs_output_tagged ("text", ";");
}
cvs_output_tagged ("newline", NULL);
if (ver->branches != NULL)
{
cvs_output ("branches:", 0);
walklist (ver->branches, log_branch, NULL);
cvs_output ("\n", 1);
}
p = findnode (ver->other, "log");
if (p == NULL || p->data == NULL || *(char *)p->data == '\0')
cvs_output ("*** empty log message ***\n", 0);
else
{
cvs_output (p->data, 0);
if (((char *)p->data)[strlen (p->data) - 1] != '\n')
cvs_output ("\n", 1);
}
}
static int
log_branch (Node *p, void *closure)
{
cvs_output (" ", 2);
if ((numdots (p->key) & 1) == 0)
cvs_output (p->key, 0);
else
{
char *f, *cp;
f = xstrdup (p->key);
cp = strrchr (f, '.');
*cp = '\0';
cvs_output (f, 0);
free (f);
}
cvs_output (";", 1);
return 0;
}
static Dtype
log_dirproc (void *callerdat, const char *dir, const char *repository,
const char *update_dir, List *entries)
{
if (!isdir (dir))
return R_SKIP_ALL;
if (!quiet)
error (0, 0, "Logging %s", update_dir);
return R_PROCESS;
}
static int
version_compare (const char *v1, const char *v2, int len)
{
while (1)
{
int d1, d2, r;
if (*v1 == '\0')
return 1;
if (*v2 == '\0')
return -1;
while (*v1 == '0')
++v1;
for (d1 = 0; isdigit ((unsigned char) v1[d1]); ++d1)
;
while (*v2 == '0')
++v2;
for (d2 = 0; isdigit ((unsigned char) v2[d2]); ++d2)
;
if (d1 != d2)
return d1 < d2 ? -1 : 1;
r = memcmp (v1, v2, d1);
if (r != 0)
return r;
--len;
if (len == 0)
return 0;
v1 += d1;
v2 += d1;
if (*v1 == '.')
++v1;
if (*v2 == '.')
++v2;
}
}