Path: utzoo!attcan!utgpu!jarvis.csri.toronto.edu!mailrus!tut.cis.ohio-state.edu!bloom-beacon!adam.pika.mit.edu!scs
From: scs@adam.pika.mit.edu (Steve Summit)
Newsgroups: comp.software-eng
Subject: Re: Does anyone have tools for use with RCS that they can share?
Summary: Complete RCS project history
Keywords: RCS rlog rcshist
Message-ID: <13608@bloom-beacon.MIT.EDU>
Date: 18 Aug 89 07:41:29 GMT
References: <342@capmkt.COM>
Sender: daemon@bloom-beacon.MIT.EDU
Reply-To: scs@adam.pika.mit.edu (Steve Summit)
Lines: 348

In article <342@capmkt.COM> brent@capmkt.COM (Brent Chapman) writes:
>It seems to me that many people and organizations, over the past few
>years, must have written tools to augment and compliment RCS.

Here's one I threw together some years ago.  It performs rlog's
on multiple files, shuffling all of the logs together and sorting
on the time.  Performed on all RCS files in a project directory,
usually with a command like

	cd RCS; rcshist *,v

, this command gives you a chronological listing of everything
that's been done to any of the source files.

This code was built under 4bsd and has not, as yet, been ported
much.  No particular efforts were expended to ensure its
widespread portability, other than my usual care, so it may need
slight adjustments to run in your environment.  Beware: it's a
memory hog; I wrote it on a virtual memory system and took
advantage of that fact.  All of the log messages for all the
revisions of all of the files being examined are kept in memory
for sorting, which can be a considerable amount of text for a
large project with frequent checkins.

The usual disclaimers apply: if you find this code useful, you're
welcome to it, but if you spread it around, please leave my name
on it.

                                            Steve Summit
                                            scs@adam.pika.mit.edu

-------8<--------8<--------8<--------8<--------8<--------8<------
echo extracting rcshist.c
sed 's/^X//' > rcshist.c <<\%
X#include 
X#include 
X#include 
X
X/*
X *  rcshist RCSfiles
X *
X *  Performs rlog's on multiple files, shuffling all of the logs
X *  together and sorting on the time.  Performed on all RCS files
X *  in a project directory, gives a chronological listing of
X *  everything that's been done to any of the source files.
X *
X *  Usual usage is something like
X *
X *	cd RCS
X *	rcshist *,v
X *
X *  Steve Summit 1/5/87
X */
X
X#define TRUE 1
X#define FALSE 0
X
X#define SEP "----------------------------"
X
Xstruct logmess
X	{
X	time_t date;
X	char **text;
X	int ntext;
X	} *messages = NULL;
X
Xint nmess = 0;
Xint allocmess = 0;
X
Xint messcmp();
Xchar *alloc();
Xextern time_t makedate();
X
Xextern char *index();
Xextern char *malloc();
Xextern FILE *popen();
Xextern char *realloc();
Xextern char *strcpy();
X
Xmain(argc, argv)
Xint argc;
Xchar *argv[];
X{
Xchar *p;
Xint i, j;
Xstruct logmess *messp;
X
Xwhile(argc > 1 && argv[1][0] == '-')
X	{
X	for(p = &argv[1][1]; *p != '\0'; p++)
X		{
X		switch(*p)
X			{
X			default:
X				fprintf(stderr, "rcshist: unknown flag -%c\n",
X									*p);
X			}
X		}
X
X	argv++;
X	argc--;
X	}
X
Xif(argc < 2)
X	{
X	fprintf(stderr, "usage: rcshist rcsfiles...\n");
X	exit(1);
X	}
X
Xfor(i = 1; i < argc; i++)
X	rlog(argv[i]);
X
Xqsort((char *)messages, nmess, sizeof(struct logmess), messcmp);
X
Xfor(i = 0; i < nmess; i++)
X	{
X	if(i > 0)
X		printf("%s\n", SEP);
X	messp = &messages[i];
X	for(j = 0; j < messp->ntext; j++)
X		printf("%s\n", messp->text[j]);
X	}
X
Xexit(0);
X}
X
Xrlog(rcsname)
Xchar *rcsname;
X{
Xchar command[200];
XFILE *fd;
Xchar buf[BUFSIZ];
Xchar *line;
Xint r;
Xint lineno;
Xint year, month, day;
Xint hour, minute, second;
Xstruct logmess *messp;
X
X(void)sprintf(command, "rlog %s", rcsname);
X
Xif((fd = popen(command, "r")) == NULL)
X	{
X	fprintf(stderr, "rcshist: can't popen %s\n", command);
X	exit(1);
X	}
X
Xlineno = 0;
X
Xwhile(getline(fd, buf, BUFSIZ) != EOF)
X	{
X	if(strncmp(buf, "==========", 10) == 0)
X			break;
X
X	if(strncmp(buf, SEP, sizeof(SEP) - 1) == 0)
X		{
X		if(nmess >= allocmess)
X			{
X			allocmess += 10;
X			messages = (struct logmess *)
X				realloc((char *)messages,
X					allocmess * sizeof(struct logmess));
X			if(messages == NULL)
X				{
X				fprintf(stderr, "rcshist: out of memory\n");
X				exit(1);
X				}
X			}
X
X		messp = &messages[nmess];
X
X		messp->text = NULL;
X		messp->ntext = 0;
X
X		nmess++;
X
X		lineno = 1;
X		continue;
X		}
X
X	switch(lineno)
X		{
X		case 0:		/* somewhere in header */
X			continue;
X
X		case 1:		/* revision line */
X			line = alloc(strlen(rcsname) + 1 + strlen(buf) + 1);
X			(void)sprintf(line, "%s %s", rcsname, buf);
X			break;
X
X		case 2:		/* date/author line */
X
X			r = sscanf(buf, "date: %d/%d/%d %d:%d:%d;",
X				&year, &month, &day, &hour, &minute, &second);
X
X			if(r != 6)
X				fprintf(stderr,
X					"rcshist: garbage in rlog: \"%s\"\n",
X									buf);
X			else	messp->date =
X					makedate(year, month, day,
X							hour, minute, second);
X			/* FALL THROUGH */
X
X		default:	/* log message */
X			line = alloc(strlen(buf) + 1);
X			(void)strcpy(line, buf);
X		}
X
X	messp->text = (char **)realloc((char *)messp->text,
X					(messp->ntext + 1) * sizeof(char **));
X
X	messp->text[messp->ntext] = line;
X	messp->ntext++;
X
X	lineno++;
X	}
X
Xpclose(fd);
X}
X
Xmesscmp(m1, m2)
Xstruct logmess *m1, *m2;
X{
Xif(m1->date < m2->date)
X	return(-1);
X
Xif(m1->date > m2->date)
X	return(1);
X
Xreturn(strcmp(m1->text[0], m2->text[0]));
X}
X
Xchar *
Xalloc(size)
Xint size;
X{
Xchar *ret;
X
Xret = malloc((unsigned)size);
X
Xif(ret == NULL)
X	{
X	fprintf(stderr, "rcshist: out of memory\n");
X	exit(1);
X	}
X
Xreturn(ret);
X}
%
chmod 644 rcshist.c
if test `wc -c < rcshist.c` -ne 3486; then
	echo "error extracting rcshist.c" 1>&2
fi
echo extracting Makefile
sed 's/^X//' > Makefile <<\%
XCFLAGS = -DBSD42
X
Xrcshist: rcshist.o makedate.o getline.o
X	cc -o rcshist rcshist.o makedate.o getline.o
X
Xmdt: mdt.o makedate.o getline.o
X	cc -o mdt mdt.o makedate.o getline.o
%
chmod 644 Makefile
if test `wc -c < Makefile` -ne 175; then
	echo "error extracting Makefile" 1>&2
fi
echo extracting makedate.c
sed 's/^X//' > makedate.c <<\%
X#include 
X#include 
X#ifndef BSD42
X#include 
X#else
X#include 		/* !@#$^%* berkeley 4.2... */
X#endif
X
Xextern struct tm *localtime();
X
Xint mday[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
X
Xtime_t
Xmakedate(year, month, day, hour, minute, second)
Xint year, month, day;
Xint hour, minute, second;
X{
Xtime_t ret;
Xint i;
Xstruct timeb tp;
X
Xret = (year - 70) * 365;
Xret += (year - 69) / 4;		/* leap years in previous years */
Xfor(i = 1; i < month; i++)
X	ret += mday[i];
Xif(year % 4 == 0 && month > 2)
X	ret++;
Xret += day - 1;
Xret *= 24;
Xret += hour;
Xret *= 60;
Xret += minute;
Xftime(&tp);
Xret += tp.timezone;
Xret *= 60;
Xret += second;
X
Xret -= 60 * 60;			/* assume dst */
X
Xif(!localtime(&ret)->tm_isdst)
X	ret += 60 * 60;		/* it wasn't */
X
Xreturn(ret);
X}
%
chmod 644 makedate.c
if test `wc -c < makedate.c` -ne 797; then
	echo "error extracting makedate.c" 1>&2
fi
echo extracting getline.c
sed 's/^X//' > getline.c <<\%
X/*
X * reads from fi until newline or EOF.  Puts at most max characters (but never
X * the newline) into string and appends \0.  Returns number of characters in
X * string, exclusive of \0.  (i.e. returns 0 for blank line terminated by
X * newline.)  Reads properly a string terminated with an EOF, but returns EOF
X * on a blank line with no newline, terminated with an EOF.
X */
X
X#include 
X
Xgetline(fi, string, max)
XFILE *fi;
Xchar string[];
Xregister int max;
X{
Xregister int i;
Xregister int c;
Xi = 0;
Xwhile((c = getc(fi)) != EOF && c != '\n') if(i < max) string[i++] = c;
Xstring[i] = '\0';
Xreturn(c == EOF && i == 0 ? EOF : i);
X}
%
chmod 644 getline.c
if test `wc -c < getline.c` -ne 634; then
	echo "error extracting getline.c" 1>&2
fi