vircs

A few years ago, while at excite@home (now defunct — that page you’re seeing is just one of the parts that got auctioned off), I ran across a nifty little script by Steve Fulling called “vircs”. What it did, in a nutshell, was automatically handle RCS-ing a file when you edited it. You’d type “vircs /etc/resolv.conf” (for instance), and it would commit the file to RCS with your comments, and after you finished editing it, would commit the change to revision control as well.

A few years ago, while at excite@home (now defunct — that page you’re seeing is just one of the parts that got auctioned off), I ran across a nifty little script by Steve Fulling called “vircs”. What it did, in a nutshell, was automatically handle RCS-ing a file when you edited it. You’d type “vircs /etc/resolv.conf” (for instance), and it would commit the file to RCS with your comments, and after you finished editing it, would commit the change to revision control as well.

Very, very convenient, and a nifty front-end to make life easier on those people administering your system.

Well, anyway, time went on, and after periodically searching the Internet for a copy of it for a couple of projects I was working on, I finally decided to contact Steve a few weeks ago and ask about this script. It’s gone through a few hands now, but I present it below for your enjoyment. It’s a quick, easy way to integrate any existing EDITOR (as in, EDITOR environment variable in UNIX) into RCS without having to worry so much about RCS commands. Having the file locked alerts other users who might want to edit it that it would be a bad idea — and who to talk to to unlock the file. Automated check-in on changes makes it really easy to keep track of what you’ve changed and why. And when someone forgets to use vircs, you’re notified that the file doesn’t match, and you can compare timestamps to figure out when someone edited it improperly.

All in all, a very handy little thing to have around. So here it is! I just tidied it up a bit — changed paths to work on a standard LSB-compliant GNU/Linux system, and fixed the exit code at the end to work with newer versions of Perl (old versions work fine with an exit code of “\n”, while newer ones want a numeric argument).

You’ll also need to install “Rcs.pm” from CPAN. I usually do it this way:

 perl -MCPAN -eshell cpan> install Rcs 

A few moments later, and you’re done! If you haven’t used CPAN before, it may prompt you for some configuration. The defaults usually work, the only thing I usually do is pick a mirror that’s closer to me.

../../vircs

 #!/usr/local/bin/perl

use Rcs; use File::Basename; use Getopt::Std;

############################################################################ # NAME: vircs (vi using RCS) # VERSION:		2.51 # CREATED: 9/19/93 # LAST MODIFIED: 3/14/00 (toml) # AUTHOR: J. Derek Roller # ORIGINAL SH AUTHOR: Stephen W. Fulling # # DESCRIPTION: this script is used to edit critical system files which # uses RCS to track changes. It assures the following: # 1. File is not previously locked by someone else. # 2. If someone changed the file being edited wihtout checking # it into RCS, the user will be notified # 3. The original file owner and mode iwll be restored after # editing # # All use of this script are logged into a log file as specified by # the $LogFile variable below in the form of: # RealUserName EffectiveUserName File Date #############################################################################

###### # # set up what options the rcs commands will use #

# General RCS defines Rcs->bindir('/usr/local/bin'); # default, but let's be sure...

###### # Look for arguments # if (! getopts('e:')) { &usage }

###### # # Misc variables that are calculated from ARGV. #

if (scalar(@ARGV) != 1) { &usage } $WFile = $ARGV[0]; $dirname = dirname($WFile); $basename = basename($WFile);

# start of our RCS object $obj = Rcs->new; $obj->file($basename); $obj->workdir($dirname); $obj->rcsdir("$dirname/RCS");

###### # # Try to find the users editor as defined in environment variables. if not, # die out of script. if editor is not defined, use vi. # if ($opt_e) { print "\nUsing specified editor: $opt_e\n"; $Editor = `which $opt_e`; } elsif (! $ENV{'EDITOR'}) { print "\nNo default editor specified, using vi\n"; $Editor = `which vi`; } else { $Editor=`which $ENV{'EDITOR'}` }

if ($Editor !~ /^\//) { print "\nSorry, I cannot find the specified or default editor\n"; print "Use the '-e' option, check your path, and/or check your\n"; print "EDITOR environment variable\n\n"; exit; }

chomp($Editor);

###### # # check if file exists. # if not create it, if it can't be created, die. # if the file exists, make sure we can write in that directory, if not, die #

if ( ! -f $WFile ) { if ( -e $WFile) { &usage } if (! open(TFILE, ">$WFile")) { 	die("\nProblem creating file '$WFile': $!\n\n"); } print "\nCreated file $WFile\n"; close(TFILE); } else { if (! open(TFILE, ">$dirname/test.$$.bic")) { 	die("\nProblem writing to '$dirname': $!\n\n"); } close(TFILE); unlink("$dirname/test.$$.bic"); }

###### # # check for RCS dir. if it doesn't exist, create it. if we can't create # it, die. # if it does exist, try to write to it. if we can't, die #

if ( ! -d "$dirname/RCS" ) { if (! mkdir("$dirname/RCS", 0777)) { # mode is modified by umask 	die("\nProblem creating directory '$dirname/RCS': $!\n\n"); } print "\nCreated directory 'RCS' under '$dirname'\n"; } else { if (! open(TFILE, ">$dirname/RCS/test.$$.bic")) { 	die("\nPermission denied writing to '$dirname/RCS'\n\n"); } close(TFILE); unlink("$dirname/RCS/test.$$.bic"); }

###### # # Store uid, gid, and mode from the original work file #

(undef,undef,$mode,undef,$uid,$gid) = stat($WFile); $mode &= 0777; $FileMode = $mode;

###### # # If no previous RCS entry, make one #

if ( ! -f "$dirname/RCS/$basename,v" ) { print "\nNOTE: '$WFile' has never been checked into RCS\n"; print "checking in via 'ci -u'\n"; $obj->ci('-u'); # toml }

###### # # Check for someone else having $WFile checked out. If so, die #

# toml if ($locker = $obj->lock) { print "\nERROR: '$WFile' locked (check out) by: $locker\n"; print " you can force an unlock via 'rcs -u <file>', however you\n"; print " BETTER be sure that they're not really editing it.\n"; exit "\n\n"; }

###### # # Run rcsdiff for bozo changes (someone changed $WFile w/o checking in). # if changes have been found prompt for checkin, quit or ignore. #

@diff_output = $obj->rcsdiff; if (scalar @diff_output) { print "\nERROR: This file has been changed since the last checkin. This\n"; print " means that someone has edited the file '$basename' without "; print "checking\n in their changes to RCS\n"; print "\nHere are the changes:\n"; print "@diff_output"; print "\n\nNOTE: '<' = RCS has information that file '$basename' does not\n"; print " '>' = FILE '$basename' has information that RCS does not\n";

InputLoop: { print "\nHere are your options (in the order you should consider):\n"; 	print " 1. Update RCS per current file - (trust file over RCS)\n"; print " 2. Exit and go get a beer - (and buy a round for "; 	print "the support staff)\n"; print " 3. Overwrite current file from RCS - (trust RCS over file"; 	print " - DANGEROUS!!)\n\n> "; chomp ($answer = <STDIN>); if ( $answer eq "1" ) { 	 print "\n\nChecking in file '$WFile' with unrecorded changes "; 	 print "into RCS\n"; 	 $obj->rcs('-l'); 	 $obj->ci('-u'); 	 last InputLoop; } if ( $answer eq "2" and die "\nGoodbye\n\n") {} 	if ( $answer eq "3" ) { 	 print "\n\nIgnoring RCS changes for file '$WFile'\n"; 	 last InputLoop; } redo InputLoop; } }

###### # # Finally, we get to edit the file. #

chmod 0400, $WFile;			# this one is for steve :-) $obj->co('-l'); if (! $obj->lock) { chown $uid, $gid, $WFile; chmod $FileMode, $WFile; die "\n\nGoodbye\n\n"; } system "$Editor $WFile"; $obj->ci('-u'); if ($obj->lock) { $obj->ci('-u'); print "\nFile '$WFile' has been unlocked\n"; }

###### # # After editing is completed, change the file ownership and mode back to # what it was #

chown $uid, $gid, $WFile; chmod $FileMode, $WFile;

###### # # Log use into logfile #

#open(lFile, ">> $LogFile"); #($rname) = getpwuid($<); ##($ename) = getpwuid($>); #$Pwd = `pwd`; chop($Pwd); #printf(lFile "%8s %8s ", $rname, $ename); #printf(lFile "%s/%s\t %s", $Pwd, $WFile, `date '+%m/%d/%y %H:%M:%S'`); #close(lFile);

###### # # finish cleanup and give the user a useless message #

print "\nThank you so much for using vircs. Brick thyself.\n\n"; exit "\n";

sub usage { die "\nUsage: vircs [-e editor] filename\n\n" }


syntax highlighted by Code2HTML, v. 0.9.1