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#includeX#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