#include "cvs.h"
struct option_revlist
{
struct option_revlist *next;
char *first;
char *last;
int branchhead;
};
struct revlist
{
struct revlist *next;
char *first;
char *last;
int fields;
};
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;
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 Dtype log_dirproc PROTO ((void *callerdat, char *dir,
char *repository, char *update_dir,
List *entries));
static int log_fileproc PROTO ((void *callerdat, struct file_info *finfo));
static struct option_revlist *log_parse_revlist PROTO ((const char *));
static void log_parse_date PROTO ((struct log_data *, const char *));
static void log_parse_list PROTO ((List **, const char *));
static struct revlist *log_expand_revlist PROTO ((RCSNode *,
struct option_revlist *,
int));
static void log_free_revlist PROTO ((struct revlist *));
static int log_version_requested PROTO ((struct log_data *, struct revlist *,
RCSNode *, RCSVers *));
static int log_symbol PROTO ((Node *, void *));
static int log_count PROTO ((Node *, void *));
static int log_fix_singledate PROTO ((Node *, void *));
static int log_count_print PROTO ((Node *, void *));
static void log_tree PROTO ((struct log_data *, struct revlist *,
RCSNode *, const char *));
static void log_abranch PROTO ((struct log_data *, struct revlist *,
RCSNode *, const char *));
static void log_version PROTO ((struct log_data *, struct revlist *,
RCSNode *, RCSVers *, int));
static int log_branch PROTO ((Node *, void *));
static int version_compare PROTO ((const char *, const char *, int));
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-R\tOnly print name of RCS file.\n",
"\t-h\tOnly print header.\n",
"\t-t\tOnly print header and descriptive text.\n",
"\t-N\tDo not list tags.\n",
"\t-b\tOnly list revisions on the default branch.\n",
"\t-r[revisions]\tSpecify revision(s)s to list.\n",
"\t-d dates\tSpecify dates (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
};
int
cvslog (argc, argv)
int argc;
char **argv;
{
int c;
int err = 0;
int local = 0;
struct log_data log_data;
struct option_revlist *rl, **prl;
if (argc == -1)
usage (log_usage);
memset (&log_data, 0, sizeof log_data);
optind = 0;
while ((c = getopt (argc, argv, "+bd:hlNRr::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 'R':
log_data.nameonly = 1;
break;
case 'r':
rl = log_parse_revlist (optarg);
for (prl = &log_data.revlist;
*prl != NULL;
prl = &(*prl)->next)
;
*prl = rl;
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, getcaller ());
break;
case '?':
default:
usage (log_usage);
break;
}
}
wrap_setup ();
#ifdef CLIENT_SUPPORT
if (client_active)
{
int i;
start_server ();
ign_setup ();
for (i = 1; i < argc && argv[i][0] == '-'; i++)
send_arg (argv[i]);
send_file_names (argc - i, argv + i, SEND_EXPAND_WILD);
send_files (argc - i, argv + i, local, 0, SEND_NO_CONTENTS);
send_to_server ("log\012", 0);
err = get_responses_and_close ();
return err;
}
#endif
err = start_recursion (log_fileproc, (FILESDONEPROC) NULL, log_dirproc,
(DIRLEAVEPROC) NULL, (void *) &log_data,
argc - optind, argv + optind, local,
W_LOCAL | W_REPOS | W_ATTIC, 0, 1,
(char *) NULL, 1);
return (err);
}
static struct option_revlist *
log_parse_revlist (argstring)
const char *argstring;
{
char *copy;
struct option_revlist *ret, **pr;
if (argstring == NULL)
{
ret = (struct option_revlist *) xmalloc (sizeof *ret);
ret->first = NULL;
ret->last = NULL;
ret->next = NULL;
ret->branchhead = 0;
return ret;
}
ret = NULL;
pr = &ret;
copy = xstrdup (argstring);
while (copy != NULL)
{
char *comma;
char *cp;
char *first, *last;
struct option_revlist *r;
comma = strchr (copy, ',');
if (comma != NULL)
*comma++ = '\0';
first = copy;
cp = strchr (copy, ':');
if (cp == NULL)
last = copy;
else
{
*cp++ = '\0';
last = cp;
}
if (*first == '\0')
first = NULL;
if (*last == '\0')
last = NULL;
r = (struct option_revlist *) xmalloc (sizeof *r);
r->next = NULL;
r->first = first;
r->last = last;
if (first != last
|| first[strlen (first) - 1] != '.')
{
r->branchhead = 0;
}
else
{
r->branchhead = 1;
first[strlen (first) - 1] = '\0';
}
*pr = r;
pr = &r->next;
copy = comma;
}
return ret;
}
static void
log_parse_date (log_data, argstring)
struct log_data *log_data;
const char *argstring;
{
char *orig_copy, *copy;
copy = xstrdup (argstring);
orig_copy = copy;
while (copy != NULL)
{
struct datelist *nd, **pd;
char *cpend, *cp, *ds, *de;
nd = (struct datelist *) 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 (plist, argstring)
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 + 1] = '\0';
}
if (*plist == NULL)
*plist = getlist ();
if (addnode (*plist, p) != 0)
freenode (p);
if (cp == NULL)
break;
argstring = cp + 1;
}
}
static int printlock_proc PROTO ((Node *, void *));
static int
printlock_proc (lock, foo)
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 (callerdat, finfo)
void *callerdat;
struct file_info *finfo;
{
struct log_data *log_data = (struct log_data *) callerdat;
Node *p;
RCSNode *rcsfile;
char buf[50];
struct revlist *revlist;
struct log_data_and_rcs log_data_and_rcs;
if ((rcsfile = finfo->rcs) == NULL)
{
p = findnode (finfo->entries, finfo->file);
if (p != NULL)
{
Entnode *e;
e = (Entnode *) p->data;
if (e->version[0] == '0' && e->version[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->nameonly)
{
cvs_output (rcsfile->path, 0);
cvs_output ("\n", 1);
return 0;
}
RCS_fully_parse (rcsfile);
revlist = log_expand_revlist (rcsfile, log_data->revlist,
log_data->default_branch);
cvs_output ("\n", 1);
cvs_output ("RCS file: ", 0);
cvs_output (rcsfile->path, 0);
cvs_output ("\nWorking file: ", 0);
if (finfo->update_dir[0] == '\0')
cvs_output (finfo->file, 0);
else
{
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 (*cp2) && *cp2 != '\0')
++cp2;
cvs_output (cp, cp2 - cp);
cp = cp2;
while (isspace (*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 (! log_data->header && ! log_data->long_header)
{
cvs_output (";\tselected revisions: ", 0);
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,
(void *) &log_data_and_rcs);
sprintf (buf, "%d", walklist (rcsfile->versions, log_count_print,
(void *) &log_data_and_rcs));
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;
vers = (RCSVers *) 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 (rcs, revlist, default_branch)
RCSNode *rcs;
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 = (struct revlist *) xmalloc (sizeof *nr);
if (r->first == NULL && r->last == NULL)
{
nr->first = RCS_head (rcs);
nr->last = xstrdup (nr->first);
nr->fields = numdots (nr->first) + 1;
}
else if (r->branchhead)
{
char *branch;
if (isdigit (r->first[0]))
nr->first = RCS_getbranch (rcs, r->first, 1);
else
{
branch = RCS_whatbranch (rcs, r->first);
if (branch == NULL)
{
error (0, 0, "warning: `%s' is not a branch in `%s'",
r->first, rcs->path);
free (nr);
continue;
}
nr->first = RCS_getbranch (rcs, branch, 1);
free (branch);
}
if (nr->first == NULL)
{
error (0, 0, "warning: no revision `%s' in `%s'",
r->first, rcs->path);
free (nr);
continue;
}
nr->last = xstrdup (nr->first);
nr->fields = numdots (nr->first) + 1;
}
else
{
if (r->first == NULL || isdigit (r->first[0]))
nr->first = xstrdup (r->first);
else
{
if (RCS_nodeisbranch (rcs, r->first))
nr->first = RCS_whatbranch (rcs, r->first);
else
nr->first = RCS_gettag (rcs, r->first, 1, (int *) NULL);
if (nr->first == NULL)
{
error (0, 0, "warning: no revision `%s' in `%s'",
r->first, rcs->path);
free (nr);
continue;
}
}
if (r->last == r->first)
nr->last = xstrdup (nr->first);
else if (r->last == NULL || isdigit (r->last[0]))
nr->last = xstrdup (r->last);
else
{
if (RCS_nodeisbranch (rcs, r->last))
nr->last = RCS_whatbranch (rcs, r->last);
else
nr->last = RCS_gettag (rcs, r->last, 1, (int *) NULL);
if (nr->last == NULL)
{
error (0, 0, "warning: no revision `%s' in `%s'",
r->last, rcs->path);
if (nr->first != NULL)
free (nr->first);
free (nr);
continue;
}
}
if (r->first == 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, '.');
strcpy (cp, ".0");
}
}
else if (r->last == 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, '.');
*cp = '\0';
}
}
else
{
nr->fields = numdots (nr->first) + 1;
if (nr->fields != numdots (nr->last) + 1
|| (nr->fields > 2
&& version_compare (nr->first, nr->last,
nr->fields - 1) != 0))
{
error (0, 0,
"invalid branch or revision pair %s:%s in `%s'",
r->first, r->last, rcs->path);
free (nr->first);
free (nr->last);
free (nr);
continue;
}
if (version_compare (nr->first, nr->last, nr->fields) > 0)
{
char *tmp;
tmp = nr->first;
nr->first = nr->last;
nr->last = tmp;
}
}
}
nr->next = NULL;
*pr = nr;
pr = &nr->next;
}
if (default_branch
&& (rcs->head != NULL || rcs->branch != NULL))
{
struct revlist *nr;
nr = (struct revlist *) xmalloc (sizeof *nr);
if (rcs->branch != NULL)
nr->first = xstrdup (rcs->branch);
else
{
char *cp;
nr->first = xstrdup (rcs->head);
cp = strrchr (nr->first, '.');
*cp = '\0';
}
nr->last = xstrdup (nr->first);
nr->fields = numdots (nr->first) + 1;
nr->next = NULL;
*pr = nr;
}
return ret;
}
static void
log_free_revlist (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 (log_data, revlist, rcs, vnode)
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)
&& 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 (p, closure)
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 (p, closure)
Node *p;
void *closure;
{
return 1;
}
static int
log_fix_singledate (p, closure)
Node *p;
void *closure;
{
struct log_data_and_rcs *data = (struct log_data_and_rcs *) 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->path);
vnode = (RCSVers *) 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 (p, closure)
Node *p;
void *closure;
{
struct log_data_and_rcs *data = (struct log_data_and_rcs *) 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->path);
if (log_version_requested (data->log_data, data->revlist, data->rcs,
(RCSVers *) pv->data))
return 1;
else
return 0;
}
static void
log_tree (log_data, revlist, rcs, ver)
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->path);
vnode = (RCSVers *) 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 (log_data, revlist, rcs, ver)
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->path);
vnode = (RCSVers *) 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 (log_data, revlist, rcs, ver, trunk)
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 ("\ndate: ", 0);
(void) sscanf (ver->date, SDATEFORM, &year, &mon, &mday, &hour, &min,
&sec);
if (year < 1900)
year += 1900;
sprintf (buf, "%04d/%02d/%02d %02d:%02d:%02d", year, mon, mday,
hour, min, sec);
cvs_output (buf, 0);
cvs_output ("; author: ", 0);
cvs_output (ver->author, 0);
cvs_output ("; state: ", 0);
cvs_output (ver->state, 0);
cvs_output (";", 1);
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->path);
nextver = (RCSVers *) nextp->data;
pdel = findnode (nextver->other, ";add");
padd = findnode (nextver->other, ";delete");
}
if (padd != NULL)
{
cvs_output (" lines: +", 0);
cvs_output (padd->data, 0);
cvs_output (" -", 2);
cvs_output (pdel->data, 0);
}
if (ver->branches != NULL)
{
cvs_output ("\nbranches:", 0);
walklist (ver->branches, log_branch, (void *) NULL);
}
cvs_output ("\n", 1);
p = findnode (ver->other, "log");
if (p == NULL || p->data == NULL || p->data[0] == '\0')
cvs_output ("*** empty log message ***\n", 0);
else
{
cvs_output (p->data, 0);
if (p->data[strlen (p->data) - 1] != '\n')
cvs_output ("\n", 1);
}
}
static int
log_branch (p, closure)
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 (callerdat, dir, repository, update_dir, entries)
void *callerdat;
char *dir;
char *repository;
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 (v1, v2, len)
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 (v1[d1]); ++d1)
;
while (*v2 == '0')
++v2;
for (d2 = 0; isdigit (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;
}
}