[comp.software-eng] Does anyone have tools for use with RCS that they can share?

brent@capmkt.COM (Brent Chapman) (08/03/89)

It seems to me that many people and organizations, over the past few
years, must have written tools to augment and compliment RCS.  If you
have, I urge you to consider sharing your tools with the rest of us,
before we all go out and "reinvent the wheel".  Things I'm particularly
interested in include, but aren't necessarily limited to, the following:

  + A report of files in a source tree that are currently checked out,
    by who, and since when.

  + A report of files in a source tree that have not yet been placed
    under RCS control.

  + Tools for creating, managing, and using "S-lists", or lists of the
    various components (and the version of each of those components) in
    a given product.
  


Thanks!

-Brent
--
Brent Chapman					Capital Market Technology, Inc.
Computer Operations Manager			1995 University Ave., Suite 390
brent@capmkt.com				Berkeley, CA  94704
{apple,lll-tis,uunet}!capmkt!brent		Phone:  415/540-6400

len@array.UUCP (Leonard Vanek) (08/04/89)

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.  If you
>have, I urge you to consider sharing your tools with the rest of us,
>before we all go out and "reinvent the wheel".  Things I'm particularly
>interested in include, but aren't necessarily limited to, the following:
>
>  + A report of files in a source tree that are currently checked out,
>    by who, and since when.
>
>  + A report of files in a source tree that have not yet been placed
>    under RCS control.
>
>  + Tools for creating, managing, and using "S-lists", or lists of the
>    various components (and the version of each of those components) in
>    a given product.
>  

The following shell/awk script partially satisifies your first
request. To get what you want you would have to modify the awk script
to skip records containing a null "locked by" field. Actually, I would
do this by passing it through awk twice, the first time as shown below
(which reduces each file to a single record) and then another pass to
select the records of interest.

With shell and awk scripts it is possible to produce lots of
interesting reports, such as a tabular revision history for a single
file or a list sorted by latest revision date. I have also included
shell scripts for these.

Of course, these tools can only produce what is already present in the
RCS files, so they cannot help in your second request and I am not
sure about the third one. Perhaps using special naming conventions can
encode what you want in the RCS files, so that a script can be used to
produce the desired report.

--------------------------- cut here ------------------------------
#! /bin/sh
#  Tabular list of RCS files
#  Usage: trlst path_to_project_library
#
rlog /usr2/YOUR_BASE_PATH/$1/RCS/*,v | awk '\
BEGIN		{print "\n                 Configuration Listing\n";\
			 print "file name         revision  locked by  date and time revised";\
			 print "---------         --------  ---------  ---------------------"}\
/RCS file:/	{printf "%-16s  ", $6; flag = 0}\
/^head:/	{printf "%6s    ", $2}\
/^locks:/	{if ($2==";") $2=" "; printf "%-9s  ", $2}\
/^date:/	{if (flag==0) {\
				printf " %-s   %-s\n", $2, substr($3,1,8);\
				flag = 1\
				}
			}
END			{print " "}'
--------------------------- cut here ------------------------------
#! /bin/sh
#  Tabular list of RCS files sorted by last modification date
#  Usage: traged path_to_project_library
#
echo
echo "          Configuration Listing by Date and Time"
echo
echo "file name        revision  locked by  date and time revised"
echo "---------        --------  ---------  ---------------------";
rlog /usr2/YOUR_BASE_PATH/$1/RCS/*,v | awk '\
/RCS file:/	{printf "%-16s  ", $6; flag = 0}\
/^head:/	{printf "%6s    ", $2}\
/^locks:/	{if ($2==";") $2=" "; printf "%-9s  ", $2}\
/^date:/	{if (flag==0) {\
				printf " %-s   %-s\n", $2, substr($3,1,8);\
				flag = 1\
				}
			}' | sort +0.36
echo
--------------------------- cut here ------------------------------
#! /bin/sh
#  Tabular revision history of a single RCS file
#  Usage: trhist filename path_to_project_library
#
echo
echo Revision history summary of $1
echo
rlog /usr2/YOUR_BASE_PATH/$2/RCS/$1,v | awk '\
/^description:/	{flag=1; next}
				{if (flag==1) {print;\
					printf "\n";\
					printf "revision   date     time    who   description\n";\
					printf "-------- -------- -------- -----  -----------\n";\
					flag=0}\
				}\
/^revision /	{printf "%-6s   ", $2}\
/^date: /		{printf "%4s %4s %-6s ", $2, substr($3,1,8), $5; flag=2; next}\
				{if (flag==2) {printf "%-40s\n", substr($0,1,40);\
							   printf "%34s%-40s\n", " ", substr($0,41,40);
							   flag=0}\
				}'
--------------------------- cut here ------------------------------

Leonard Vanek                  UUCP: ... uunet!attcan!lsuc!array!len
Array Systems Computing Inc.    or ... utzoo!dciem!array!len
5000 Dufferin St. Suite 200     or lsuc!array!len@ai.toronto.edu
Downsview, Ont. M3H 5T5        Phone: (416) 736-0900
Canada                         FAX:   (416) 736-4715

len@array.UUCP (Leonard Vanek) (08/04/89)

In article <49@array.UUCP> I (len@array.UUCP) wrote:

>The following shell/awk script partially satisifies your first
>request.

>             I have also included
>shell scripts for these.
>

Sorry about the embedded tabs. I normally edit using tabstops of 4, so
they look alright when I edit the scripts. You can replace each tab by
four blanks to make the scripts read better,

Leonard Vanek                  UUCP: ... uunet!attcan!lsuc!array!len
Array Systems Computing Inc.    or ... utzoo!dciem!array!len
5000 Dufferin St. Suite 200     or lsuc!array!len@ai.toronto.edu
Downsview, Ont. M3H 5T5        Phone: (416) 736-0900
Canada                         FAX:   (416) 736-4715

diomidis@ecrcvax.UUCP (Diomidis Spinellis) (08/09/89)

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.  If you
>have, I urge you to consider sharing your tools with the rest of us,
>before we all go out and "reinvent the wheel".
> [...]

I am including the source and manual pages for three tools that can be
helpful when using RCS.

- Rcstell reports files checked out as locked and optionaly their lockers.
- Slist helps creating S-lists, for sets of files, or executables.
- Rcsclean removes all files that can be recreated by RCS.

I have used these scripts over some time and they function for the style of
work I do.  They have not been tested in other working environments.

>-Brent
Diomidis

#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	rcsclean
#	rcsclean.1
#	rcstell
#	rcstell.1
#	slist
#	slist.1
# This archive created: Mon Aug  7 17:00:49 1989
export PATH; PATH=/bin:$PATH
echo shar: extracting "'rcsclean'" '(471 characters)'
if test -f 'rcsclean'
then
	echo shar: will not over-write existing file "'rcsclean'"
else
sed 's/^X//' << \SHAR_EOF > 'rcsclean'
X#!/bin/sh -
X#
X# Delete RCS files that can be safelly recreated by RCS using co.
X#
X# (C) Copyright 1989 Diomidis Spinellis.  All rights reserved.
X# Permission to copy for any purpose is hereby granted so long
X# as this copyright notice remains intact.
X#
Xrlog -h RCS/*,v *,v 2>/dev/null |
Xawk '
X/^RCS file:/ { fname = $6 }
X/^locks:/ { 
X	if( NF == 3 ) 
X		printf "test -r %s && co -p %s 2>/dev/null | diff - %s >/dev/null && rm '$*' %s\n", fname, fname, fname, fname
X}' |
Xsh
SHAR_EOF
if test 471 -ne "`wc -c < 'rcsclean'`"
then
	echo shar: error transmitting "'rcsclean'" '(should have been 471 characters)'
fi
chmod +x 'rcsclean'
fi # end of overwriting check
echo shar: extracting "'rcsclean.1'" '(1272 characters)'
if test -f 'rcsclean.1'
then
	echo shar: will not over-write existing file "'rcsclean.1'"
else
sed 's/^X//' << \SHAR_EOF > 'rcsclean.1'
X.\" Copyright (C) 1989 Diomidis Spinellis.  All rights reserved.
X.\" Permission to copy for any purpose is hereby granted so long
X.\" as this copyright notice remains intact.
X.\"
X.TH RCSCLEAN 1 "Jun 14, 1989"
X.UC 4
X.SH NAME
Xrcsclean \- delete files that can be recreated by co
X.SH SYNOPSIS
X.B rcsclean
X[
X.B rm options
X] 
X.br
X.SH DESCRIPTION
X.I Rcsclean
Xdeletes any files in the current directory that can be recreated
Xby checking out the latest version of the revision tree.
XIt is conservative in its approach, comparing only unlocked files ending in
X.I ,v
Xin the current and the
X.I './RCS'
Xdirectories against the working versions.
X.PP
XAny of the rm options can be given as options to
X.I rcsclean.
XIn particular the
X.B \-f
Xoption can be used to force the deletion of all files without 
Xasking and the
X.B \-i 
Xoption for interactive prompting before each deletion.
X.PP
X.SH FILES
X.I *,v RCS/*,v
XThe files examined.
X.SH AUTHOR
XDiomidis Spinellis (dds@cc.ic.ac.uk)
X.SH "SEE ALSO"
Xrcs(1), co (1), ci (1), rlog (1).
X.sp 0
XWalter F. Tichy, "Design, Implementation, and Evaluation of a Revision Control
XSystem," in \fIProceedings of the 6th International Conference on Software
XEngineering\fR, IEEE, Tokyo, Sept. 1982.
X.SH BUGS
XDepends a lot on the format of the
X.I rlog
Xoutput.
SHAR_EOF
if test 1272 -ne "`wc -c < 'rcsclean.1'`"
then
	echo shar: error transmitting "'rcsclean.1'" '(should have been 1272 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'rcstell'" '(918 characters)'
if test -f 'rcstell'
then
	echo shar: will not over-write existing file "'rcstell'"
else
sed 's/^X//' << \SHAR_EOF > 'rcstell'
X#!/bin/sh
X#
X# Tell what rcs files are currently being edited (ie. are `locked' out)
X#
X# (C) Copyright 1988 D. Spinellis. All rights reserved.
X# Permission to copy for any purpose is hereby granted so long
X# as this copyright notice remains intact.
X#
Xcase $# in
X0)
X	rlog -L -h RCS/*,v *,v 2>/dev/null |
X	sed -n -e '/^RCS file:/s/.*Working file:[ 	]*\(.*\)$/\1/p'
X	;;
X1)
X	case $1 in
X	-l)
X		rlog -L -h RCS/*,v *,v 2>/dev/null |
X		sed -n -e '
X		/^RCS file:/s/.*Working file:[ 	]*\(.*\)$/\1/p
X		/locks:/s/^locks:[	 ]*\([^:]*\):[ 	]\([^;]*\).*/\1 \2/p
X		' |
X		while read FNAME && read LOCKER REV
X		do
X			echo $FNAME $REV $LOCKER `
X				(
X					ypmatch $LOCKER passwd || 
X					egrep "^$LOCKER:" /etc/passwd
X				) 2>/dev/null | 
X				sed 's/[^:]*:[^:]*:[^:]*:[^:]*:\([^,:]*\).*$/(\1)/'
X			`
X		done
X		;;
X	*)
X		echo 1>&2 "Usage `basename $0` [-l]" ; exit 1
X		;;
X	esac
X	;;
X*)
X	echo 1>&2 "Usage `basename $0` [-l]" ; exit 1
X	;;
Xesac
SHAR_EOF
if test 918 -ne "`wc -c < 'rcstell'`"
then
	echo shar: error transmitting "'rcstell'" '(should have been 918 characters)'
fi
chmod +x 'rcstell'
fi # end of overwriting check
echo shar: extracting "'rcstell.1'" '(1231 characters)'
if test -f 'rcstell.1'
then
	echo shar: will not over-write existing file "'rcstell.1'"
else
sed 's/^X//' << \SHAR_EOF > 'rcstell.1'
X.\" Copyright (C) 1988 Diomidis Spinellis.  All rights reserved.
X.\" Permission to copy for any purpose is hereby granted so long
X.\" as this copyright notice remains intact.
X.\"
X.TH RCSTELL 1 "Nov 11, 1988"
X.UC 4
X.SH NAME
Xrcstell \- tell which RCS files are checked out as locked
X.SH SYNOPSIS
X.B rcstell
X[
X.B \-l
X] 
X.br
X.SH DESCRIPTION
X.I Rcstell
Xprints a list of any files that are checked out as locked in the 
Xcurrent directory.  It does so by examining all the files ending in
X.I ,v
Xin the current and the
X.I './RCS'
Xdirectories.
X.PP
XThe
X.B \-l
Xoption displays a longer output that consists of the file name, the
Xrevision name, the locker login name and the locker real name for
Xeach locked file.
X.PP
X.SH FILES
X.I *,v RCS/*,v
XThe files examined.
X.SH AUTHOR
XDiomidis Spinellis (dds@cc.ic.ac.uk)
X.SH "SEE ALSO"
Xrcs(1), co (1), ci (1), rlog (1).
X.sp 0
XWalter F. Tichy, "Design, Implementation, and Evaluation of a Revision Control
XSystem," in \fIProceedings of the 6th International Conference on Software
XEngineering\fR, IEEE, Tokyo, Sept. 1982.
X.SH BUGS
XDepends a lot on the format of the
X.I rlog
Xoutput as well as the the 
X.I passwd 
Xfile.  In particular it uses the ``,'' to distinguish the real name from the
X.I GCOS
Xfield.
SHAR_EOF
if test 1231 -ne "`wc -c < 'rcstell.1'`"
then
	echo shar: error transmitting "'rcstell.1'" '(should have been 1231 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'slist'" '(802 characters)'
if test -f 'slist'
then
	echo shar: will not over-write existing file "'slist'"
else
sed 's/^X//' << \SHAR_EOF > 'slist'
X#!/bin/sh
X#
X# Create a list of files and their revisions.
X#
X# (C) Copyright 1989 Diomidis Spinellis.  All rights reserved.
X# Permission to copy for any purpose is hereby granted so long
X# as this copyright notice remains intact.
X#
Xcase $# in
X0)
X	echo 1>&2 "Usage `basename $0` [-h] file ..." ; exit 1
X	;;
X1)
X	case $1 in
X	-*)
X		echo 1>&2 "Usage `basename $0` [-h] file ..." ; exit 1
X		;;
X	*)
X		ident $* |
X		awk '/\$Header/ {print substr($2, 1, length($2)-2), $3}' |
X		sort -u
X		;;
X	esac
X	;;
X*)
X	case $1 in
X	-h)
X		rlog -h $* |
X		awk '/^RCS file/ {printf "%s ", substr($3, 5, length($3)-7)} /^head:/ { print $2}' |
X		sort -u
X		;;
X	-*)
X		echo 1>&2 "Usage `basename $0` [-h] file ..." ; exit 1
X		;;
X	*)
X		ident $* |
X		awk '/\$Header/ {print substr($2, 1, length($2)-2), $3}' |
X		sort -u
X		;;
X	esac
X	;;
Xesac
SHAR_EOF
if test 802 -ne "`wc -c < 'slist'`"
then
	echo shar: error transmitting "'slist'" '(should have been 802 characters)'
fi
chmod +x 'slist'
fi # end of overwriting check
echo shar: extracting "'slist.1'" '(1778 characters)'
if test -f 'slist.1'
then
	echo shar: will not over-write existing file "'slist.1'"
else
sed 's/^X//' << \SHAR_EOF > 'slist.1'
X.\" Copyright (C) 1989 Diomidis Spinellis.  All rights reserved.
X.\" Permission to copy for any purpose is hereby granted so long
X.\" as this copyright notice remains intact.
X.\"
X.TH SLIST 1 "Aug 7, 1989"
X.UC 4
X.SH NAME
Xslist \- display the names and revision numbers of RCS files
X.SH SYNOPSIS
X.B slist
X[
X.B \-h
X] file ...
X.br
X.SH DESCRIPTION
X.I Slist
Xprints the names and revision numbers of the files given as arguments.
X.PP
XThe
X.B \-h
Xoption prints the revision number of the latest file in the trunk,
Xthus an slist of the 
X.I head 
X(frontier) of the RCS tree is printed.  This is useful for comparing against
Xthe output of
X.I slist
Xfrom an executable to check that the latest revisions were indeed used.
X.PP
XIf the
X.B \-h
Xoption is not used then the file name arguments need be
Xworking or executable
Xfile names containing valid 
X.I Header
Xidentifiers.
X.SH EXAMPLES
XCreate an slist from an executable:
X.RS
X.nf
X.ft B
X% slist executable >executable.slist
X.ft R
X.RE
X.br
X.ne 4
XCompare the working files against the latest revisions:
X.RS
X.nf
X.ft B
X% slist -h RCS/*.c,v >latest.slist
X% slist *.c | diff - latest.slist
X.ft R
X.fi
X.RE
X.SH DIAGNOSTICS
XComplaints from ident for files not containing identifiers.
X.br
XComplaints from rlog about non-existing RCS files.
X.SH AUTHOR
XDiomidis Spinellis (dds@cc.ic.ac.uk)
X.SH "SEE ALSO"
Xrcs(1), co (1), ci (1), rlog (1), ident(1).
X.sp 0
XWalter F. Tichy, "Design, Implementation, and Evaluation of a Revision Control
XSystem," in \fIProceedings of the 6th International Conference on Software
XEngineering\fR, IEEE, Tokyo, Sept. 1982.
X.SH BUGS
XDepends a lot on the format of the
X.I rlog
Xand
X.I ident
Xoutput.
X.PP
XIf a file contains no header keyword, but contains other keywords no
Xentry will be generated for that file, unless 
X.B \-h 
Xis used.
SHAR_EOF
if test 1778 -ne "`wc -c < 'slist.1'`"
then
	echo shar: error transmitting "'slist.1'" '(should have been 1778 characters)'
fi
fi # end of overwriting check
#	End of shell archive
exit 0
-- 
Diomidis Spinellis          European Computer-Industry Research Centre (ECRC)
Arabellastrasse 17, D-8000 Muenchen 81, West Germany        +49 (89) 92699199
USA: diomidis%ecrcvax.uucp@pyramid.pyramid.com   ...!pyramid!ecrcvax!diomidis
Europe: diomidis@ecrcvax.uucp                      ...!unido!ecrcvax!diomidis

johnm@occam.ksr.com (John Martin) (08/11/89)

In article <342@capmkt.COM> brent@capmkt.COM (Brent Chapman) requests:
>
>  + A report of files in a source tree that are currently checked out,
>    by who, and since when.
>
>  + A report of files in a source tree that have not yet been placed
>    under RCS control.
>
>  + Tools for creating, managing, and using "S-lists", or lists of the
>    various components (and the version of each of those components) in
>    a given product.
>

len@array.UUCP (Leonard Vanek) has recently provided a set of scripts
that can be used for the first of these.  Below I provide a UNIX
script that I sometimes use to perform the second.  I've added some
comments to help any non-experts who want to modify it.  It runs for
me on Sun-3's and -4's; I'd expect any BSD4.x-derived *X to do OK on
it, and I think it would work on a SysV system but don't have one
handy to try it out.  With that caveat:

--------------------------- cut here ------------------------------
#! /bin/sh
#
# find .c, .h, and [Mm]akefile without RCS equivalents
# Usage:  norcs [srcdir] [-r rcsdirspec] [-s wildfilespec] [-v]
# Defaults to norcs . -r RCS
#
#
# John Martin, Kendall Square Research Corp., Waltham, MA
#
#
defaults() {
    srcdir="${srcdir:-.}"
    rcsdir="${rcsdir:-RCS}"
    srcspec="${srcpec:-*.[ch] [Mm]akefile}"
}
#
usage() {
    echo "Usage: norcs [srcdirs] [-s srcspec] [-r rcsdirspec ] [-v]"
    echo "Deflt: norcs . -r RCS -s \"*.[ch] [Mm]akefile\""
    echo " You may have multiple -s and -r options; remember to quote srcspecs"
    echo " to avoid immediate filename expansion."
    exit 1
}
#
verbose() { if [ -z "$vbose" ]; then return 1; else return 0; fi }
# 
# parse the args; you can give as many specs as you want for the source
# and for the RCS specs (i.e., you can do something like
# norcs src/* -r RCS -r /usr/local/include/RCS )
#
while [ ! -z "$1" ]; do
    if [ $1 = "-v" ]; then
        vbose=T
	shift
    elif [ $1 = "-s" ]; then
         if [ -z "$2" ]; then usage; fi
	 srcspec="$srcspec $2"
	 shift 2
    elif [ $1 = "-r" ]; then
        if [ -z "$2" ]; then usage; fi
        rcsdir="$rcsdir $2"
	shift 2
    else
        srcdir="$srcdir $1"
	shift
    fi
done
#  load defaults for anything not found above
defaults

if verbose; then
    echo "Source  spec: < $srcspec >"
    echo "Source  dirs: < $srcdir >"
    echo "RCSfile dirs: < $rcsdir >"
    echo ""
fi
origdir=`pwd`
#
for sd in $srcdir; do
#
#  only report nonexistent source dir's if verbose (makes a better filter)
#
    cd $origdir
    if [ ! -d $sd ]; then
        if verbose; then echo "($sd is not a directory)"; fi
        continue
    fi
#
    cd $sd

# The shell's filename expansion will leave things it can't find; "*.c *.h" 
# may expand to "a.c b.c *.h".  Accordingly, first we check that
# "file" is actually a file.  The -f establishes that file isn't too
# weird, like a directory.  However, a symbolic link to a plain file is
# OK with -f, and we're not interested in symbolic links, so we check
# that separately.  Then we see if the RCS file exists somewhere in
# rcsdir (normally the ./RCS directory relative to the source dir, but
# bigger projects may have special tools and rules).

# Some folks might want to test "-w", on the theory that a file that's
# read-only doesn't need to be checked for RCSness.  That can be a
# bad idea in some development environments, so this script checks
# everything.
#

    for file in $srcspec; do
        if [ ! -f "$file" ]; then continue; fi
        if [ -h $file ]; then continue; fi
        for rd in $rcsdir; do
            if [ -d "$rd" ]; then
                if [ -f $rd/$file,v ]; then continue 2; fi
	    else
		if verbose; then echo " ($rd is not a directory for $sd)"; fi
            fi
        done     # rd (RCS directory)
#
# if we didn't "continue 2", we didn't find the RCS file, so say the name.
        echo $sd/$file
#
    done    #  file
#
done     # sd (source directory)
#
# EOF norcs
--------------------------- cut here ------------------------------


John H. Martin					harvard!ksr!johnm
Kendall Square Research Corporation		johnm@ksr.com
170 Tracer Lane
Waltham, MA 02154
"A creative economy is the fuel of magnificence." -- Emerson
Disclaimer:  My only organizationally-endorsed position is prone.

scs@adam.pika.mit.edu (Steve Summit) (08/18/89)

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 <stdio.h>
X#include <sys/types.h>
X#include <sys/stat.h>
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 <sys/types.h>
X#include <sys/timeb.h>
X#ifndef BSD42
X#include <time.h>
X#else
X#include <sys/time.h>		/* !@#$^%* 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 <stdio.h>
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