This commit was generated by cvs2svn to compensate for changes in r12,
which included commits to RCS files with non-trunk default branches.
This commit is contained in:
@@ -0,0 +1,3 @@
|
|||||||
|
SUBDIR = lib cvs mkmodules
|
||||||
|
|
||||||
|
.include <bsd.subdir.mk>
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
@(#)README 1.8 92/04/10
|
||||||
|
|
||||||
|
This "contrib" directory is a place holder for code/scripts sent to
|
||||||
|
me by contributors around the world. This READM file will be kept
|
||||||
|
up-to-date from release to release. BUT, I must point out that these
|
||||||
|
contributions are really, REALLY UNSSUPPORTED. In fact, I probably
|
||||||
|
don't even know what they do. Nor do I guarantee to have tried them,
|
||||||
|
or ported them to work with this CVS distribution. If you have questions,
|
||||||
|
you might contact the author, but you should not necessarily expect
|
||||||
|
a reply. USE AT YOUR OWN RISK -- and all that stuff.
|
||||||
|
|
||||||
|
Contents of this directory:
|
||||||
|
|
||||||
|
README This file.
|
||||||
|
log.pl A perl script suitable for including in your
|
||||||
|
$CVSROOT/CVSROOT/loginfo file for logging commit
|
||||||
|
changes. Includes the RCS revision of the change
|
||||||
|
as part of the log.
|
||||||
|
Contributed by Kevin Samborn <samborn@sunrise.com>.
|
||||||
|
pcl-cvs A directory that contains GNU Emacs lisp code which
|
||||||
|
implements a CVS-mode for emacs.
|
||||||
|
Contributed by Per Cederqvist <ceder@lysator.liu.se>.
|
||||||
|
commit_prep.pl A perl script, to be combined with log_accum.pl, to
|
||||||
|
log_accum.pl provide for a way to combine the individual log
|
||||||
|
messages of a multi-directory "commit" into a
|
||||||
|
single log message, and mail the result somewhere.
|
||||||
|
Also does other checks for $Id and that you are
|
||||||
|
committing the correct revision of the file.
|
||||||
|
Read the comments carefully.
|
||||||
|
Contributed by David Hampton <hampton@cisco.com>.
|
||||||
|
mfpipe.pl Another perl script for logging. Allows you to
|
||||||
|
pipe the log message to a file and/or send mail
|
||||||
|
to some alias.
|
||||||
|
Contributed by John Clyne <clyne@niwot.scd.ucar.edu>.
|
||||||
|
rcs-to-cvs Script to import sources that may have been under
|
||||||
|
RCS control already.
|
||||||
|
Contributed by Per Cederqvist <ceder@lysator.liu.se>.
|
||||||
|
cvscheck Identifies files added, changed, or removed in a
|
||||||
|
cvscheck.man checked out CVS tree; also notices unknown files.
|
||||||
|
Contributed by Lowell Skoog <fluke!lowell@uunet.uu.net>
|
||||||
|
cvshelp.man An introductory manual page written by Lowell Skoog
|
||||||
|
<fluke!lowell@uunet.uu.net>. It is most likely
|
||||||
|
out-of-date relative to CVS 1.3, but still may be
|
||||||
|
useful.
|
||||||
|
dirfns A shar file which contains some code that might
|
||||||
|
help your system support opendir/readdir/closedir,
|
||||||
|
if it does not already.
|
||||||
|
Copied from the C-News distribution.
|
||||||
|
rcslock.pl A perl script that can be added to your commitinfo
|
||||||
|
file that tries to determine if your RCS file is
|
||||||
|
currently locked by someone else, as might be the
|
||||||
|
case for a binary file.
|
||||||
|
Contributed by John Rouillard <rouilj@cs.umb.edu>.
|
||||||
|
cvs_acls.pl A perl script that implements Access Control Lists
|
||||||
|
by using the "commitinfo" hook provided with the
|
||||||
|
"cvs commit" command.
|
||||||
|
Contributed by David G. Grubbs <dgg@ksr.com>.
|
||||||
|
descend A shell script that can be used to recursively
|
||||||
|
descend.man descend through a directory. In CVS 1.2, this was
|
||||||
|
very useful, since many of the commands were not
|
||||||
|
recursive. In CVS 1.3 (and later), however, most of
|
||||||
|
the commands are recursive. However, this may still
|
||||||
|
come in handy.
|
||||||
|
Contributed by Lowell Skoog <fluke!lowell@uunet.uu.net>
|
||||||
|
cln_hist.pl A perl script to compress your
|
||||||
|
$CVSROOT/CVSROOT/history file, as it can grow quite
|
||||||
|
large after extended use.
|
||||||
|
Contributed by David G. Grubbs <dgg@ksr.com>
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
#!/usr/bin/perl -- # -*-Perl-*-
|
||||||
|
#
|
||||||
|
# cln_hist.pl,v 1.1 1992/04/10 03:04:15 berliner Exp
|
||||||
|
# Contributed by David G. Grubbs <dgg@ksr.com>
|
||||||
|
#
|
||||||
|
# Clean up the history file. 10 Record types: MAR OFT WUCG
|
||||||
|
#
|
||||||
|
# WUCG records are thrown out.
|
||||||
|
# MAR records are retained.
|
||||||
|
# T records: retain only last tag with same combined tag/module.
|
||||||
|
#
|
||||||
|
# Two passes: Walk through the first time and remember the
|
||||||
|
# 1. Last Tag record with same "tag" and "module" names.
|
||||||
|
# 2. Last O record with unique user/module/directory, unless followed
|
||||||
|
# by a matching F record.
|
||||||
|
#
|
||||||
|
|
||||||
|
$r = $ENV{"CVSROOT"};
|
||||||
|
$c = "$r/CVSROOT";
|
||||||
|
$h = "$c/history";
|
||||||
|
|
||||||
|
eval "print STDERR \$die='Unknown parameter $1\n' if !defined \$$1; \$$1=\$';"
|
||||||
|
while ($ARGV[0] =~ /^(\w+)=/ && shift(@ARGV));
|
||||||
|
exit 255 if $die; # process any variable=value switches
|
||||||
|
|
||||||
|
%tags = ();
|
||||||
|
%outs = ();
|
||||||
|
|
||||||
|
#
|
||||||
|
# Move history file to safe place and re-initialize a new one.
|
||||||
|
#
|
||||||
|
rename($h, "$h.bak");
|
||||||
|
open(XX, ">$h");
|
||||||
|
close(XX);
|
||||||
|
|
||||||
|
#
|
||||||
|
# Pass1 -- remember last tag and checkout.
|
||||||
|
#
|
||||||
|
open(HIST, "$h.bak");
|
||||||
|
while (<HIST>) {
|
||||||
|
next if /^[MARWUCG]/;
|
||||||
|
|
||||||
|
# Save whole line keyed by tag|module
|
||||||
|
if (/^T/) {
|
||||||
|
@tmp = split(/\|/, $_);
|
||||||
|
$tags{$tmp[4] . '|' . $tmp[5]} = $_;
|
||||||
|
}
|
||||||
|
# Save whole line
|
||||||
|
if (/^[OF]/) {
|
||||||
|
@tmp = split(/\|/, $_);
|
||||||
|
$outs{$tmp[1] . '|' . $tmp[2] . '|' . $tmp[5]} = $_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Pass2 -- print out what we want to save.
|
||||||
|
#
|
||||||
|
open(SAVE, ">$h.work");
|
||||||
|
open(HIST, "$h.bak");
|
||||||
|
while (<HIST>) {
|
||||||
|
next if /^[FWUCG]/;
|
||||||
|
|
||||||
|
# If whole line matches saved (i.e. "last") one, print it.
|
||||||
|
if (/^T/) {
|
||||||
|
@tmp = split(/\|/, $_);
|
||||||
|
next if $tags{$tmp[4] . '|' . $tmp[5]} ne $_;
|
||||||
|
}
|
||||||
|
# Save whole line
|
||||||
|
if (/^O/) {
|
||||||
|
@tmp = split(/\|/, $_);
|
||||||
|
next if $outs{$tmp[1] . '|' . $tmp[2] . '|' . $tmp[5]} ne $_;
|
||||||
|
}
|
||||||
|
|
||||||
|
print SAVE $_;
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Put back the saved stuff
|
||||||
|
#
|
||||||
|
system "cat $h >> $h.work";
|
||||||
|
|
||||||
|
if (-s $h) {
|
||||||
|
rename ($h, "$h.interim");
|
||||||
|
print "history.interim has non-zero size.\n";
|
||||||
|
} else {
|
||||||
|
unlink($h);
|
||||||
|
}
|
||||||
|
|
||||||
|
rename ("$h.work", $h);
|
||||||
|
|
||||||
|
exit(0);
|
||||||
@@ -0,0 +1,168 @@
|
|||||||
|
#!/usr/local/bin/perl -w
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# Perl filter to handle pre-commit checking of files. This program
|
||||||
|
# records the last directory where commits will be taking place for
|
||||||
|
# use by the log_accumulate script. For new file, it forcing the
|
||||||
|
# existence of a RCS "Id" keyword in the first ten lines of the file.
|
||||||
|
# For existing files, it checks version number in the "Id" line to
|
||||||
|
# prevent losing changes because an old version of a file was copied
|
||||||
|
# into the direcory.
|
||||||
|
#
|
||||||
|
# Possible future enhancements:
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# Check for cruft left by unresolved conflicts. Search for
|
||||||
|
# "^<<<<<<<$", "^-------$", and "^>>>>>>>$".
|
||||||
|
#
|
||||||
|
# Look for a copyright and automagically update it to the
|
||||||
|
# current year.
|
||||||
|
#
|
||||||
|
# Contributed by David Hampton <hampton@cisco.com>
|
||||||
|
#
|
||||||
|
|
||||||
|
############################################################
|
||||||
|
#
|
||||||
|
# Configurable options
|
||||||
|
#
|
||||||
|
############################################################
|
||||||
|
#
|
||||||
|
# Check each file (except dot files) for an RCS "Id" keyword.
|
||||||
|
#
|
||||||
|
$check_id = 1;
|
||||||
|
|
||||||
|
#
|
||||||
|
# Record the directory for later use by the log_accumulate stript.
|
||||||
|
#
|
||||||
|
$record_directory = 1;
|
||||||
|
|
||||||
|
############################################################
|
||||||
|
#
|
||||||
|
# Constants
|
||||||
|
#
|
||||||
|
############################################################
|
||||||
|
$LAST_FILE = "/tmp/#cvs.lastdir";
|
||||||
|
$ENTRIES = "CVS/Entries";
|
||||||
|
|
||||||
|
$NoId = "
|
||||||
|
%s - Does not contain a line with the keyword \"Id:\".
|
||||||
|
Please see the template files for an example.\n";
|
||||||
|
|
||||||
|
# Protect string from substitution by RCS.
|
||||||
|
$NoName = "
|
||||||
|
%s - The ID line should contain only \"\$\I\d\:\ \$\" for a newly created file.\n";
|
||||||
|
|
||||||
|
$BadName = "
|
||||||
|
%s - The file name '%s' in the ID line does not match
|
||||||
|
the actual filename.\n";
|
||||||
|
|
||||||
|
$BadVersion = "
|
||||||
|
%s - How dare you!! You replaced your copy of the file '%s',
|
||||||
|
which was based upon version %s, with an %s version based
|
||||||
|
upon %s. Please move your '%s' out of the way, perform an
|
||||||
|
update to get the current version, and them merge your changes
|
||||||
|
into that file.\n";
|
||||||
|
|
||||||
|
############################################################
|
||||||
|
#
|
||||||
|
# Subroutines
|
||||||
|
#
|
||||||
|
############################################################
|
||||||
|
|
||||||
|
sub write_line {
|
||||||
|
local($filename, $line) = @_;
|
||||||
|
open(FILE, ">$filename") || die("Cannot open $filename, stopped");
|
||||||
|
print(FILE $line, "\n");
|
||||||
|
close(FILE);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub check_version {
|
||||||
|
local($i, $id, $rname, $version);
|
||||||
|
local($filename, $cvsversion) = @_;
|
||||||
|
|
||||||
|
open(FILE, $filename) || die("Cannot open $filename, stopped");
|
||||||
|
for ($i = 1; $i < 10; $i++) {
|
||||||
|
$pos = -1;
|
||||||
|
last if eof(FILE);
|
||||||
|
$line = <FILE>;
|
||||||
|
$pos = index($line, "Id: ");
|
||||||
|
last if ($pos >= 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($pos == -1) {
|
||||||
|
printf($NoId, $filename);
|
||||||
|
return(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
($id, $rname, $version) = split(' ', substr($line, $pos));
|
||||||
|
if ($cvsversion{$filename} == 0) {
|
||||||
|
if ($rname ne "\$") {
|
||||||
|
printf($NoName, $filename);
|
||||||
|
return(1);
|
||||||
|
}
|
||||||
|
return(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($rname ne "$filename,v") {
|
||||||
|
printf($BadName, $filename, substr($rname, 0, length($rname)-2));
|
||||||
|
return(1);
|
||||||
|
}
|
||||||
|
if ($cvsversion{$filename} < $version) {
|
||||||
|
printf($BadVersion, $filename, $filename, $cvsversion{$filename},
|
||||||
|
"newer", $version, $filename);
|
||||||
|
return(1);
|
||||||
|
}
|
||||||
|
if ($cvsversion{$filename} > $version) {
|
||||||
|
printf($BadVersion, $filename, $filename, $cvsversion{$filename},
|
||||||
|
"older", $version, $filename);
|
||||||
|
return(1);
|
||||||
|
}
|
||||||
|
return(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#############################################################
|
||||||
|
#
|
||||||
|
# Main Body
|
||||||
|
#
|
||||||
|
############################################################
|
||||||
|
|
||||||
|
$id = getpgrp();
|
||||||
|
#print("ARGV - ", join(":", @ARGV), "\n");
|
||||||
|
#print("id - ", id, "\n");
|
||||||
|
|
||||||
|
#
|
||||||
|
# Suck in the Entries file
|
||||||
|
#
|
||||||
|
open(ENTRIES, $ENTRIES) || die("Cannot open $ENTRIES.\n");
|
||||||
|
while (<ENTRIES>) {
|
||||||
|
local($filename, $version) = split('/', substr($_, 1));
|
||||||
|
$cvsversion{$filename} = $version;
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Now check each file name passed in, except for dot files. Dot files
|
||||||
|
# are considered to be administrative files by this script.
|
||||||
|
#
|
||||||
|
if ($check_id != 0) {
|
||||||
|
$failed = 0;
|
||||||
|
$directory = $ARGV[0];
|
||||||
|
shift @ARGV;
|
||||||
|
foreach $arg (@ARGV) {
|
||||||
|
next if (index($arg, ".") == 0);
|
||||||
|
$failed += &check_version($arg);
|
||||||
|
}
|
||||||
|
if ($failed) {
|
||||||
|
print "\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Record this directory as the last one checked. This will be used
|
||||||
|
# by the log_accumulate script to determine when it is processing
|
||||||
|
# the final directory of a multi-directory commit.
|
||||||
|
#
|
||||||
|
if ($record_directory != 0) {
|
||||||
|
&write_line("$LAST_FILE.$id", $directory);
|
||||||
|
}
|
||||||
|
exit(0);
|
||||||
@@ -0,0 +1,142 @@
|
|||||||
|
#!/usr/bin/perl -- # -*-Perl-*-
|
||||||
|
#
|
||||||
|
# cvs_acls.pl,v 1.2 1992/04/11 16:01:24 berliner Exp
|
||||||
|
#
|
||||||
|
# Access control lists for CVS. dgg@ksr.com (David G. Grubbs)
|
||||||
|
#
|
||||||
|
# CVS "commitinfo" for matching repository names, running the program it finds
|
||||||
|
# on the same line. More information is available in the CVS man pages.
|
||||||
|
#
|
||||||
|
# ==== INSTALLATION:
|
||||||
|
#
|
||||||
|
# To use this program as I intended, do the following four things:
|
||||||
|
#
|
||||||
|
# 0. Install PERL. :-)
|
||||||
|
#
|
||||||
|
# 1. Put one line, as the *only* non-comment line, in your commitinfo file:
|
||||||
|
#
|
||||||
|
# DEFAULT /usr/local/bin/cvs_acls
|
||||||
|
#
|
||||||
|
# 2. Install this file as /usr/local/bin/cvs_acls and make it executable.
|
||||||
|
#
|
||||||
|
# 3. Create a file named $CVSROOT/CVSROOT/avail.
|
||||||
|
#
|
||||||
|
# ==== FORMAT OF THE avail FILE:
|
||||||
|
#
|
||||||
|
# The avail file determines whether you may commit files. It contains lines
|
||||||
|
# read from top to bottom, keeping track of a single "bit". The "bit"
|
||||||
|
# defaults to "on". It can be turned "off" by "unavail" lines and "on" by
|
||||||
|
# "avail" lines. ==> Last one counts.
|
||||||
|
#
|
||||||
|
# Any line not beginning with "avail" or "unavail" is ignored.
|
||||||
|
#
|
||||||
|
# Lines beginning with "avail" or "unavail" are assumed to be '|'-separated
|
||||||
|
# triples: (All spaces and tabs are ignored in a line.)
|
||||||
|
#
|
||||||
|
# {avail.*,unavail.*} [| user,user,... [| repos,repos,...]]
|
||||||
|
#
|
||||||
|
# 1. String starting with "avail" or "unavail".
|
||||||
|
# 2. Optional, comma-separated list of usernames.
|
||||||
|
# 3. Optional, comma-separated list of repository pathnames.
|
||||||
|
# These are pathnames relative to $CVSROOT. They can be directories or
|
||||||
|
# filenames. A directory name allows access to all files and
|
||||||
|
# directories below it.
|
||||||
|
#
|
||||||
|
# Example: (Text from the ';;' rightward may not appear in the file.)
|
||||||
|
#
|
||||||
|
# unavail ;; Make whole repository unavailable.
|
||||||
|
# avail|dgg ;; Except for user "dgg".
|
||||||
|
# avail|fred, john|bin/ls ;; Except when "fred" or "john" commit to
|
||||||
|
# ;; the module whose repository is "bin/ls"
|
||||||
|
#
|
||||||
|
# PROGRAM LOGIC:
|
||||||
|
#
|
||||||
|
# CVS passes to @ARGV an absolute directory pathname (the repository
|
||||||
|
# appended to your $CVSROOT variable), followed by a list of filenames
|
||||||
|
# within that directory.
|
||||||
|
#
|
||||||
|
# We walk through the avail file looking for a line that matches both
|
||||||
|
# the username and repository.
|
||||||
|
#
|
||||||
|
# A username match is simply the user's name appearing in the second
|
||||||
|
# column of the avail line in a space-or-comma separate list.
|
||||||
|
#
|
||||||
|
# A repository match is either:
|
||||||
|
# - One element of the third column matches $ARGV[0], or some
|
||||||
|
# parent directory of $ARGV[0].
|
||||||
|
# - Otherwise *all* file arguments ($ARGV[1..$#ARGV]) must be
|
||||||
|
# in the file list in one avail line.
|
||||||
|
# - In other words, using directory names in the third column of
|
||||||
|
# the avail file allows committing of any file (or group of
|
||||||
|
# files in a single commit) in the tree below that directory.
|
||||||
|
# - If individual file names are used in the third column of
|
||||||
|
# the avail file, then files must be committed individually or
|
||||||
|
# all files specified in a single commit must all appear in
|
||||||
|
# third column of a single avail line.
|
||||||
|
#
|
||||||
|
|
||||||
|
$debug = 0;
|
||||||
|
$cvsroot = $ENV{'CVSROOT'};
|
||||||
|
$availfile = $cvsroot . "/CVSROOT/avail";
|
||||||
|
$myname = $ENV{"USER"} if !($myname = $ENV{"LOGNAME"});
|
||||||
|
|
||||||
|
eval "print STDERR \$die='Unknown parameter $1\n' if !defined \$$1; \$$1=\$';"
|
||||||
|
while ($ARGV[0] =~ /^(\w+)=/ && shift(@ARGV));
|
||||||
|
exit 255 if $die; # process any variable=value switches
|
||||||
|
|
||||||
|
die "Must set CVSROOT\n" if !$cvsroot;
|
||||||
|
($repos = shift) =~ s:^$cvsroot/::;
|
||||||
|
grep($_ = $repos . '/' . $_, @ARGV);
|
||||||
|
|
||||||
|
print "$$ Repos: $repos\n","$$ ==== ",join("\n$$ ==== ",@ARGV),"\n" if $debug;
|
||||||
|
|
||||||
|
$exit_val = 0; # Good Exit value
|
||||||
|
|
||||||
|
$universal_off = 0;
|
||||||
|
open (AVAIL, $availfile) || exit(0); # It is ok for avail file not to exist
|
||||||
|
while (<AVAIL>) {
|
||||||
|
chop;
|
||||||
|
next if /^\s*\#/;
|
||||||
|
next if /^\s*$/;
|
||||||
|
($flagstr, $u, $m) = split(/[\s,]*\|[\s,]*/, $_);
|
||||||
|
|
||||||
|
# Skip anything not starting with "avail" or "unavail" and complain.
|
||||||
|
(print "Bad avail line: $_\n"), next
|
||||||
|
if ($flagstr !~ /^avail/ && $flagstr !~ /^unavail/);
|
||||||
|
|
||||||
|
# Set which bit we are playing with. ('0' is OK == Available).
|
||||||
|
$flag = (($& eq "avail") ? 0 : 1);
|
||||||
|
|
||||||
|
# If we find a "universal off" flag (i.e. a simple "unavail") remember it
|
||||||
|
$universal_off = 1 if ($flag && !$u && !$m);
|
||||||
|
|
||||||
|
# $myname considered "in user list" if actually in list or is NULL
|
||||||
|
$in_user = (!$u || grep ($_ eq $myname, split(/[\s,]+/,$u)));
|
||||||
|
print "$$ \$myname($myname) in user list: $_\n" if $debug && $in_user;
|
||||||
|
|
||||||
|
# Module matches if it is a NULL module list in the avail line. If module
|
||||||
|
# list is not null, we check every argument combination.
|
||||||
|
if (!($in_repo = !$m)) {
|
||||||
|
@tmp = split(/[\s,]+/,$m);
|
||||||
|
for $j (@tmp) {
|
||||||
|
# If the repos from avail is a parent(or equal) dir of $repos, OK
|
||||||
|
$in_repo = 1, last if ($repos eq $j || $repos =~ /^$j\//);
|
||||||
|
}
|
||||||
|
if (!$in_repo) {
|
||||||
|
$in_repo = 1;
|
||||||
|
for $j (@ARGV) {
|
||||||
|
last if !($in_repo = grep ($_ eq $j, @tmp));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
print "$$ \$repos($repos) in repository list: $_\n" if $debug && $in_repo;
|
||||||
|
|
||||||
|
$exit_val = $flag if ($in_user && $in_repo);
|
||||||
|
print "$$ ==== \$exit_val = $exit_val\n$$ ==== \$flag = $flag\n" if $debug;
|
||||||
|
}
|
||||||
|
close(AVAIL);
|
||||||
|
print "$$ ==== \$exit_val = $exit_val\n" if $debug;
|
||||||
|
print "**** Access denied: Insufficient Karma ($myname|$repos)\n" if $exit_val;
|
||||||
|
print "**** Access allowed: Personal Karma exceeds Environmental Karma.\n"
|
||||||
|
if $universal_off && !$exit_val;
|
||||||
|
exit($exit_val);
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
#! /bin/sh
|
||||||
|
# cvscheck,v 1.2 1992/04/10 03:04:19 berliner Exp
|
||||||
|
#
|
||||||
|
# cvscheck - identify files added, changed, or removed
|
||||||
|
# in CVS working directory
|
||||||
|
#
|
||||||
|
# Contributed by Lowell Skoog <fluke!lowell@uunet.uu.net>
|
||||||
|
#
|
||||||
|
# This program should be run in a working directory that has been
|
||||||
|
# checked out using CVS. It identifies files that have been added,
|
||||||
|
# changed, or removed in the working directory, but not "cvs
|
||||||
|
# committed". It also determines whether the files have been "cvs
|
||||||
|
# added" or "cvs removed". For directories, it is only practical to
|
||||||
|
# determine whether they have been added.
|
||||||
|
|
||||||
|
name=cvscheck
|
||||||
|
changes=0
|
||||||
|
|
||||||
|
# If we can't run CVS commands in this directory
|
||||||
|
cvs status . > /dev/null 2>&1
|
||||||
|
if [ $? != 0 ] ; then
|
||||||
|
|
||||||
|
# Bail out
|
||||||
|
echo "$name: there is no version here; bailing out" 1>&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Identify files added to working directory
|
||||||
|
for file in .* * ; do
|
||||||
|
|
||||||
|
# Skip '.' and '..'
|
||||||
|
if [ $file = '.' -o $file = '..' ] ; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# If a regular file
|
||||||
|
if [ -f $file ] ; then
|
||||||
|
if cvs status $file | grep -s '^From:[ ]*New file' ; then
|
||||||
|
echo "file added: $file - not CVS committed"
|
||||||
|
changes=`expr $changes + 1`
|
||||||
|
elif cvs status $file | grep -s '^From:[ ]*no entry for' ; then
|
||||||
|
echo "file added: $file - not CVS added, not CVS committed"
|
||||||
|
changes=`expr $changes + 1`
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Else if a directory
|
||||||
|
elif [ -d $file -a $file != CVS.adm ] ; then
|
||||||
|
|
||||||
|
# Move into it
|
||||||
|
cd $file
|
||||||
|
|
||||||
|
# If CVS commands don't work inside
|
||||||
|
cvs status . > /dev/null 2>&1
|
||||||
|
if [ $? != 0 ] ; then
|
||||||
|
echo "directory added: $file - not CVS added"
|
||||||
|
changes=`expr $changes + 1`
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Move back up
|
||||||
|
cd ..
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Identify changed files
|
||||||
|
changedfiles=`cvs diff | egrep '^diff' | awk '{print $3}'`
|
||||||
|
for file in $changedfiles ; do
|
||||||
|
echo "file changed: $file - not CVS committed"
|
||||||
|
changes=`expr $changes + 1`
|
||||||
|
done
|
||||||
|
|
||||||
|
# Identify files removed from working directory
|
||||||
|
removedfiles=`cvs status | egrep '^File:[ ]*no file' | awk '{print $4}'`
|
||||||
|
|
||||||
|
# Determine whether each file has been cvs removed
|
||||||
|
for file in $removedfiles ; do
|
||||||
|
if cvs status $file | grep -s '^From:[ ]*-' ; then
|
||||||
|
echo "file removed: $file - not CVS committed"
|
||||||
|
else
|
||||||
|
echo "file removed: $file - not CVS removed, not CVS committed"
|
||||||
|
fi
|
||||||
|
changes=`expr $changes + 1`
|
||||||
|
done
|
||||||
|
|
||||||
|
exit $changes
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
.\" cvscheck.man,v 1.1 1992/04/10 03:04:20 berliner Exp
|
||||||
|
.\" Contributed by Lowell Skoog <fluke!lowell@uunet.uu.net>
|
||||||
|
.TH CVSCHECK LOCAL "4 March 1991" FLUKE
|
||||||
|
.SH NAME
|
||||||
|
cvscheck \- identify files added, changed, or removed in a CVS working
|
||||||
|
directory
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.B cvscheck
|
||||||
|
.SH DESCRIPTION
|
||||||
|
This command is a housekeeping aid. It should be run in a working
|
||||||
|
directory that has been checked out using CVS. It identifies files
|
||||||
|
that have been added, changed, or removed in the working directory, but
|
||||||
|
not CVS
|
||||||
|
.BR commit ted.
|
||||||
|
It also determines whether the files have been CVS
|
||||||
|
.BR add ed
|
||||||
|
or CVS
|
||||||
|
.BR remove d.
|
||||||
|
For directories, this command determines only whether they have been
|
||||||
|
.BR add ed.
|
||||||
|
It operates in the current directory only.
|
||||||
|
.LP
|
||||||
|
This command provides information that is available using CVS
|
||||||
|
.B status
|
||||||
|
and CVS
|
||||||
|
.BR diff .
|
||||||
|
The advantage of
|
||||||
|
.B cvscheck
|
||||||
|
is that its output is very concise. It saves you the strain (and
|
||||||
|
potential error) of interpreting the output of CVS
|
||||||
|
.B status
|
||||||
|
and
|
||||||
|
.BR diff .
|
||||||
|
.LP
|
||||||
|
See
|
||||||
|
.BR cvs (local)
|
||||||
|
or
|
||||||
|
.BR cvshelp (local)
|
||||||
|
for instructions on how to add or remove a file or directory in a
|
||||||
|
CVS-controlled package.
|
||||||
|
.SH DIAGNOSTICS
|
||||||
|
The exit status is 0 if no files have been added, changed, or removed
|
||||||
|
from the current directory. Otherwise, the command returns a count of
|
||||||
|
the adds, changes, and deletes.
|
||||||
|
.SH SEE ALSO
|
||||||
|
.BR cvs (local),
|
||||||
|
.BR cvshelp (local)
|
||||||
|
.SH AUTHOR
|
||||||
|
Lowell Skoog
|
||||||
|
.br
|
||||||
|
Software Technology Group
|
||||||
|
.br
|
||||||
|
Technical Computing
|
||||||
@@ -0,0 +1,562 @@
|
|||||||
|
.\" cvshelp.man,v 1.1 1992/04/10 03:04:21 berliner Exp
|
||||||
|
.\" Contributed by Lowell Skoog <fluke!lowell@uunet.uu.net>
|
||||||
|
.\" Full space in nroff; half space in troff
|
||||||
|
.de SP
|
||||||
|
.if n .sp
|
||||||
|
.if t .sp .5
|
||||||
|
..
|
||||||
|
.\" Start a command example
|
||||||
|
.de XS
|
||||||
|
.SP
|
||||||
|
.in +.5i
|
||||||
|
.ft B
|
||||||
|
.nf
|
||||||
|
..
|
||||||
|
.\" End a command example
|
||||||
|
.de XE
|
||||||
|
.fi
|
||||||
|
.ft P
|
||||||
|
.in -.5i
|
||||||
|
.SP
|
||||||
|
..
|
||||||
|
.TH CVSHELP LOCAL "17 March 1991" FLUKE
|
||||||
|
.SH NAME
|
||||||
|
cvshelp \- advice on using the Concurrent Versions System
|
||||||
|
.SH DESCRIPTION
|
||||||
|
This man page is based on experience using CVS.
|
||||||
|
It is bound to change as we gain more experience.
|
||||||
|
If you come up with better advice than is found here,
|
||||||
|
contact the Software Technology
|
||||||
|
Group and we will add it to this page.
|
||||||
|
.SS "Getting Started"
|
||||||
|
Use the following steps to prepare to use CVS:
|
||||||
|
.TP
|
||||||
|
\(bu
|
||||||
|
Take a look at the CVS manual page to see what it can do for you, and
|
||||||
|
if it fits your environment (or can possibly be made to fit your
|
||||||
|
environment).
|
||||||
|
.XS
|
||||||
|
man cvs
|
||||||
|
.XE
|
||||||
|
If things look good, continue on...
|
||||||
|
.TP
|
||||||
|
\(bu
|
||||||
|
Setup the master source repository. Choose a directory with
|
||||||
|
ample disk space available for source files. This is where the RCS
|
||||||
|
`,v' files will be stored. Say you choose
|
||||||
|
.B /src/master
|
||||||
|
as the root
|
||||||
|
of your source repository. Make the
|
||||||
|
.SB CVSROOT.adm
|
||||||
|
directory in the root of the source repository:
|
||||||
|
.XS
|
||||||
|
mkdir /src/master/CVSROOT.adm
|
||||||
|
.XE
|
||||||
|
.TP
|
||||||
|
\(bu
|
||||||
|
Populate this directory with the
|
||||||
|
.I loginfo
|
||||||
|
and
|
||||||
|
.I modules
|
||||||
|
files from the
|
||||||
|
.B "/usr/doc/local/cvs"
|
||||||
|
directory. Edit these files to reflect your local source repository
|
||||||
|
environment \- they may be quite small initially, but will grow as
|
||||||
|
sources are added to your source repository. Turn these files into
|
||||||
|
RCS controlled files:
|
||||||
|
.XS
|
||||||
|
cd /src/master/CVSROOT.adm
|
||||||
|
ci \-m'Initial loginfo file' loginfo
|
||||||
|
ci \-m'Initial modules file' modules
|
||||||
|
.XE
|
||||||
|
.TP
|
||||||
|
\(bu
|
||||||
|
Run the command:
|
||||||
|
.XS
|
||||||
|
mkmodules /src/master/CVSROOT.adm
|
||||||
|
.XE
|
||||||
|
This will build the
|
||||||
|
.BR ndbm (3)
|
||||||
|
file for the modules database.
|
||||||
|
.TP
|
||||||
|
\(bu
|
||||||
|
Remember to edit the
|
||||||
|
.I modules
|
||||||
|
file manually when sources are checked
|
||||||
|
in with
|
||||||
|
.B checkin
|
||||||
|
or CVS
|
||||||
|
.BR add .
|
||||||
|
A copy of the
|
||||||
|
.I modules
|
||||||
|
file for editing can be retrieved with the command:
|
||||||
|
.XS
|
||||||
|
cvs checkout CVSROOT.adm
|
||||||
|
.XE
|
||||||
|
.TP
|
||||||
|
\(bu
|
||||||
|
Have all users of the CVS system set the
|
||||||
|
.SM CVSROOT
|
||||||
|
environment variable appropriately to reflect the placement of your
|
||||||
|
source repository. If the above example is used, the following
|
||||||
|
commands can be placed in a
|
||||||
|
.I .login
|
||||||
|
or
|
||||||
|
.I .profile
|
||||||
|
file:
|
||||||
|
.XS
|
||||||
|
setenv CVSROOT /src/master
|
||||||
|
.XE
|
||||||
|
for csh users, and
|
||||||
|
.XS
|
||||||
|
CVSROOT=/src/master; export CVSROOT
|
||||||
|
.XE
|
||||||
|
for sh users.
|
||||||
|
.SS "Placing Locally Written Sources Under CVS Control"
|
||||||
|
Say you want to place the `whizbang' sources under
|
||||||
|
CVS control. Say further that the sources have never
|
||||||
|
been under revision control before.
|
||||||
|
.TP
|
||||||
|
\(bu
|
||||||
|
Move the source hierarchy (lock, stock, and barrel)
|
||||||
|
into the master source repository:
|
||||||
|
.XS
|
||||||
|
mv ~/whizbang $CVSROOT
|
||||||
|
.XE
|
||||||
|
.TP
|
||||||
|
\(bu
|
||||||
|
Clean out unwanted object files:
|
||||||
|
.XS
|
||||||
|
cd $CVSROOT/whizbang
|
||||||
|
make clean
|
||||||
|
.XE
|
||||||
|
.TP
|
||||||
|
\(bu
|
||||||
|
Turn every file in the hierarchy into an RCS controlled file:
|
||||||
|
.XS
|
||||||
|
descend \-f 'ci \-t/dev/null \-m"Placed under CVS control" \-nV\fR\fIx\fR\fB_\fR\fIy\fR\fB *'
|
||||||
|
.XE
|
||||||
|
In this example, the initial release tag is \fBV\fIx\fB_\fIy\fR,
|
||||||
|
representing version \fIx\fR.\fIy\fR.
|
||||||
|
.LP
|
||||||
|
You can use CVS on sources that are already under RCS control.
|
||||||
|
The following example shows how.
|
||||||
|
In this example, the source package is called `skunkworks'.
|
||||||
|
.TP
|
||||||
|
\(bu
|
||||||
|
Move the source hierarchy into the master source
|
||||||
|
repository:
|
||||||
|
.XS
|
||||||
|
mv ~/skunkworks $CVSROOT
|
||||||
|
.XE
|
||||||
|
.TP
|
||||||
|
\(bu
|
||||||
|
Clean out unwanted object files:
|
||||||
|
.XS
|
||||||
|
cd $CVSROOT/skunkworks
|
||||||
|
make clean
|
||||||
|
.XE
|
||||||
|
.TP
|
||||||
|
\(bu
|
||||||
|
Clean out unwanted working files, leaving only the RCS `,v' files:
|
||||||
|
.XS
|
||||||
|
descend \-r rcsclean
|
||||||
|
.XE
|
||||||
|
Note: If any working files have been checked out and changed,
|
||||||
|
.B rcsclean
|
||||||
|
will fail. Check in the modified working files
|
||||||
|
and run the command again.
|
||||||
|
.TP
|
||||||
|
\(bu
|
||||||
|
Get rid of
|
||||||
|
.SB RCS
|
||||||
|
subdirectories. CVS does not use them.
|
||||||
|
.XS
|
||||||
|
descend \-r \-f 'mv RCS/*,v .'
|
||||||
|
descend \-r \-f 'rmdir RCS'
|
||||||
|
.XE
|
||||||
|
.TP
|
||||||
|
\(bu
|
||||||
|
Delete any unwanted files that remain in the source hierarchy. Then
|
||||||
|
make sure all files are under RCS control:
|
||||||
|
.XS
|
||||||
|
descend \-f 'ci \-t/dev/null \-m"Placed under CVS control" \-n\fR\fItag\fR\fB *'
|
||||||
|
.XE
|
||||||
|
.I tag
|
||||||
|
is the latest symbolic revision tag that you applied to your package
|
||||||
|
(if any). Note: This command will probably generate lots of error
|
||||||
|
messages (for directories and existing RCS files) that you can
|
||||||
|
ignore.
|
||||||
|
.SS "Placing a Third-Party Source Distribution Under CVS Control"
|
||||||
|
The
|
||||||
|
.B checkin
|
||||||
|
command checks third-party sources into CVS. The
|
||||||
|
difference between third-party sources and locally
|
||||||
|
written sources is that third-party sources must be checked into a
|
||||||
|
separate branch (called the
|
||||||
|
.IR "vendor branch" )
|
||||||
|
of the RCS tree. This makes it possible to merge local changes to
|
||||||
|
the sources with later releases from the vendor.
|
||||||
|
.TP
|
||||||
|
\(bu
|
||||||
|
Save the original distribution kit somewhere. For example, if the
|
||||||
|
master source repository is
|
||||||
|
.B /src/master
|
||||||
|
the distribution kit could be saved in
|
||||||
|
.BR /src/dist .
|
||||||
|
Organize the distribution directory so that each release
|
||||||
|
is clearly identifiable.
|
||||||
|
.TP
|
||||||
|
\(bu
|
||||||
|
Unpack the package in a scratch directory, for example
|
||||||
|
.BR ~/scratch .
|
||||||
|
.TP
|
||||||
|
\(bu
|
||||||
|
Create a repository for the package.
|
||||||
|
In this example, the package is called `Bugs-R-Us 4.3'.
|
||||||
|
.XS
|
||||||
|
mkdir $CVSROOT/bugs
|
||||||
|
.XE
|
||||||
|
.TP
|
||||||
|
\(bu
|
||||||
|
Check in the unpacked files:
|
||||||
|
.XS
|
||||||
|
cd ~/scratch
|
||||||
|
checkin \-m 'Bugs-R-Us 4.3 distribution' bugs VENDOR V4_3
|
||||||
|
.XE
|
||||||
|
There is nothing magic about the tag `VENDOR', which is applied to
|
||||||
|
the vendor branch. You can use whatever tag you want. `VENDOR' is a
|
||||||
|
useful convention.
|
||||||
|
.TP
|
||||||
|
\(bu
|
||||||
|
Never modify vendor files before checking them in.
|
||||||
|
Check in the files
|
||||||
|
.I exactly
|
||||||
|
as you unpacked them.
|
||||||
|
If you check in locally modified files, future vendor releases may
|
||||||
|
wipe out your local changes.
|
||||||
|
.SS "Working With CVS-Controlled Sources"
|
||||||
|
To use or edit the sources, you must check out a private copy.
|
||||||
|
For the following examples, the master files are assumed to reside in
|
||||||
|
.BR "$CVSROOT/behemoth" .
|
||||||
|
The working directory is
|
||||||
|
.BR "~/work" .
|
||||||
|
See
|
||||||
|
.BR cvs (local)
|
||||||
|
for more details on the commands mentioned below.
|
||||||
|
.TP
|
||||||
|
.I "To Check Out Working Files
|
||||||
|
Use CVS
|
||||||
|
.BR checkout :
|
||||||
|
.XS
|
||||||
|
cd ~/work
|
||||||
|
cvs checkout behemoth
|
||||||
|
.XE
|
||||||
|
There is nothing magic about the working directory. CVS will check
|
||||||
|
out sources anywhere you like. Once you have a working copy of the
|
||||||
|
sources, you can compile or edit them as desired.
|
||||||
|
.TP
|
||||||
|
.I "To Display Changes You Have Made"
|
||||||
|
Use CVS
|
||||||
|
.BR diff
|
||||||
|
to display detailed changes, equivalent to
|
||||||
|
.BR rcsdiff (local).
|
||||||
|
You can also use
|
||||||
|
.BR cvscheck (local)
|
||||||
|
to list files added, changed, and removed in
|
||||||
|
the directory, but not yet
|
||||||
|
.BR commit ted.
|
||||||
|
You must be in a directory containing working files.
|
||||||
|
.TP
|
||||||
|
.I "To Display Revision Information"
|
||||||
|
Use CVS
|
||||||
|
.BR log ,
|
||||||
|
which is equivalent to
|
||||||
|
.BR rlog (local).
|
||||||
|
You must be in a directory containing working files.
|
||||||
|
.TP
|
||||||
|
.I "To Update Working Files"
|
||||||
|
Use CVS
|
||||||
|
.BR update
|
||||||
|
in a directory containing working files.
|
||||||
|
This command brings your working files up
|
||||||
|
to date with changes checked into the
|
||||||
|
master repository since you last checked out or updated
|
||||||
|
your files.
|
||||||
|
.TP
|
||||||
|
.I "To Check In Your Changes"
|
||||||
|
Use CVS
|
||||||
|
.BR commit
|
||||||
|
in a directory containing working files.
|
||||||
|
This command checks your changes into the master repository.
|
||||||
|
You can specify files by name or use
|
||||||
|
.XS
|
||||||
|
cvs commit \-a
|
||||||
|
.XE
|
||||||
|
to
|
||||||
|
.B commit
|
||||||
|
all the files you have changed.
|
||||||
|
.TP
|
||||||
|
.I "To Add a File"
|
||||||
|
Add the file to the working directory.
|
||||||
|
Use CVS
|
||||||
|
.B add
|
||||||
|
to mark the file as added.
|
||||||
|
Use CVS
|
||||||
|
.B commit
|
||||||
|
to add the file to the master repository.
|
||||||
|
.TP
|
||||||
|
.I "To Remove a File"
|
||||||
|
Remove the file from the working directory.
|
||||||
|
Use CVS
|
||||||
|
.B remove
|
||||||
|
to mark the file as removed.
|
||||||
|
Use CVS
|
||||||
|
.B commit
|
||||||
|
to move the file from its current location in the master repository
|
||||||
|
to the CVS
|
||||||
|
.IR Attic
|
||||||
|
directory.
|
||||||
|
.TP
|
||||||
|
.I "To Add a Directory"
|
||||||
|
Add the directory to the working directory.
|
||||||
|
Use CVS
|
||||||
|
.B add
|
||||||
|
to add the directory to the master repository.
|
||||||
|
.TP
|
||||||
|
.I "To Remove a Directory"
|
||||||
|
.br
|
||||||
|
You shouldn't remove directories under CVS. You should instead remove
|
||||||
|
their contents and then prune them (using the
|
||||||
|
.B \-f
|
||||||
|
and
|
||||||
|
.B \-p
|
||||||
|
options) when you
|
||||||
|
.B checkout
|
||||||
|
or
|
||||||
|
.B update
|
||||||
|
your working files.
|
||||||
|
.TP
|
||||||
|
.I "To Tag a Release"
|
||||||
|
Use CVS
|
||||||
|
.B tag
|
||||||
|
to apply a symbolic tag to the latest revision of each file in the
|
||||||
|
master repository. For example:
|
||||||
|
.XS
|
||||||
|
cvs tag V2_1 behemoth
|
||||||
|
.XE
|
||||||
|
.TP
|
||||||
|
.I "To Retrieve an Exact Copy of a Previous Release"
|
||||||
|
During a CVS
|
||||||
|
.B checkout
|
||||||
|
or
|
||||||
|
.BR update ,
|
||||||
|
use the
|
||||||
|
.B \-r
|
||||||
|
option to retrieve revisions associated with a symbolic tag.
|
||||||
|
Use the
|
||||||
|
.B \-f
|
||||||
|
option to ignore all RCS files that do not contain the
|
||||||
|
tag.
|
||||||
|
Use the
|
||||||
|
.B \-p
|
||||||
|
option to prune directories that wind up empty because none
|
||||||
|
of their files matched the tag. Example:
|
||||||
|
.XS
|
||||||
|
cd ~/work
|
||||||
|
cvs checkout \-r V2_1 \-f \-p behemoth
|
||||||
|
.XE
|
||||||
|
.SS "Logging Changes"
|
||||||
|
It is a good idea to keep a change log together with the
|
||||||
|
sources. As a minimum, the change log should name and describe each
|
||||||
|
tagged release. The change log should also be under CVS control and
|
||||||
|
should be tagged along with the sources.
|
||||||
|
.LP
|
||||||
|
.BR cvslog (local)
|
||||||
|
can help. This command logs
|
||||||
|
changes reported during CVS
|
||||||
|
.B commit
|
||||||
|
operations. It automatically
|
||||||
|
updates a change log file in your working directory. When you are
|
||||||
|
finished making changes, you (optionally) edit the change log file and
|
||||||
|
then commit it to the master repository.
|
||||||
|
.LP
|
||||||
|
Note: You must edit the change log to describe a new release
|
||||||
|
and
|
||||||
|
.B commit
|
||||||
|
it to the master repository
|
||||||
|
.I before
|
||||||
|
.BR tag ging
|
||||||
|
the release using CVS. Otherwise, the release description will not be
|
||||||
|
included in the tagged package.
|
||||||
|
.LP
|
||||||
|
See
|
||||||
|
.BR cvslog (local)
|
||||||
|
for more information.
|
||||||
|
.SS "Merging a Subsequent Third-Party Distribution"
|
||||||
|
The initial steps in this process are identical to placing a
|
||||||
|
third-party distribution under CVS for the first time: save the
|
||||||
|
distribution kit and unpack the package in a scratch directory. From
|
||||||
|
that point the steps diverge.
|
||||||
|
The following example considers release 5.0 of the
|
||||||
|
Bugs-R-Us package.
|
||||||
|
.TP
|
||||||
|
\(bu
|
||||||
|
Check in the sources after unpacking them:
|
||||||
|
.XS
|
||||||
|
cd ~/scratch
|
||||||
|
checkin \-m 'Bugs-R-Us 5.0 distribution' bugs VENDOR V5_0 \\
|
||||||
|
| tee ~/WARNINGS
|
||||||
|
.XE
|
||||||
|
It is important to save the output of
|
||||||
|
.B checkin
|
||||||
|
in a file
|
||||||
|
because it lists the sources that have been locally modified.
|
||||||
|
It is best to save the file in a different directory (for example,
|
||||||
|
your home directory). Otherwise,
|
||||||
|
.B checkin
|
||||||
|
will try to check it into the master repository.
|
||||||
|
.TP
|
||||||
|
\(bu
|
||||||
|
In your usual working directory, check out a fresh copy of the
|
||||||
|
distribution that you just checked in.
|
||||||
|
.XS
|
||||||
|
cd ~/work
|
||||||
|
cvs checkout \-r VENDOR bugs
|
||||||
|
.XE
|
||||||
|
The
|
||||||
|
.B checkout
|
||||||
|
command shown above retrieves the latest revision on the vendor branch.
|
||||||
|
.TP
|
||||||
|
\(bu
|
||||||
|
See the `WARNINGS' file for a list of all locally modified
|
||||||
|
sources.
|
||||||
|
For each locally modified source,
|
||||||
|
look at the differences between
|
||||||
|
the new distribution and the latest local revision:
|
||||||
|
.XS
|
||||||
|
cvs diff \-r \fR\fILocalRev file\fR\fB
|
||||||
|
.XE
|
||||||
|
In this command,
|
||||||
|
.I LocalRev
|
||||||
|
is the latest
|
||||||
|
numeric or symbolic revision
|
||||||
|
on the RCS trunk of
|
||||||
|
.IR file .
|
||||||
|
You can use CVS
|
||||||
|
.B log
|
||||||
|
to get the revision history.
|
||||||
|
.TP
|
||||||
|
\(bu
|
||||||
|
If your local modifications to a file have been incorporated into
|
||||||
|
the vendor's distribution, then you should reset the default RCS
|
||||||
|
branch for that file to the vendor branch. CVS doesn't provide a
|
||||||
|
mechanism to do this. You have to do it by hand in the master
|
||||||
|
repository:
|
||||||
|
.XS
|
||||||
|
rcs \-bVENDOR \fR\fIfile\fR\fB,v
|
||||||
|
.XE
|
||||||
|
.TP
|
||||||
|
\(bu
|
||||||
|
If your local modifications need to be merged with the
|
||||||
|
new distribution, use CVS
|
||||||
|
.B join
|
||||||
|
to do it:
|
||||||
|
.XS
|
||||||
|
cvs join \-r VENDOR \fR\fIfile\fR\fB
|
||||||
|
.XE
|
||||||
|
The resulting file will be placed in your working directory.
|
||||||
|
Edit it to resolve any overlaps.
|
||||||
|
.TP
|
||||||
|
\(bu
|
||||||
|
Test the merged package.
|
||||||
|
.TP
|
||||||
|
\(bu
|
||||||
|
Commit all modified files to the repository:
|
||||||
|
.XS
|
||||||
|
cvs commit \-a
|
||||||
|
.XE
|
||||||
|
.TP
|
||||||
|
\(bu
|
||||||
|
Tag the repository with a new local tag.
|
||||||
|
.SS "Applying Patches to Third-Party Sources"
|
||||||
|
Patches are handled in a manner very similar to complete
|
||||||
|
third-party distributions. This example considers patches applied to
|
||||||
|
Bugs-R-Us release 5.0.
|
||||||
|
.TP
|
||||||
|
\(bu
|
||||||
|
Save the patch files together with the distribution kit
|
||||||
|
to which they apply.
|
||||||
|
The patch file names should clearly indicate the patch
|
||||||
|
level.
|
||||||
|
.TP
|
||||||
|
\(bu
|
||||||
|
In a scratch directory, check out the last `clean' vendor copy \- the
|
||||||
|
highest revision on the vendor branch with
|
||||||
|
.IR "no local changes" :
|
||||||
|
.XS
|
||||||
|
cd ~/scratch
|
||||||
|
cvs checkout \-r VENDOR bugs
|
||||||
|
.XE
|
||||||
|
.TP
|
||||||
|
\(bu
|
||||||
|
Use
|
||||||
|
.BR patch (local)
|
||||||
|
to apply the patches. You should now have an image of the
|
||||||
|
vendor's software just as though you had received a complete,
|
||||||
|
new release.
|
||||||
|
.TP
|
||||||
|
\(bu
|
||||||
|
Proceed with the steps described for merging a subsequent third-party
|
||||||
|
distribution.
|
||||||
|
.TP
|
||||||
|
\(bu
|
||||||
|
Note: When you get to the step that requires you
|
||||||
|
to check out the new distribution after you have
|
||||||
|
checked it into the vendor branch, you should move to a different
|
||||||
|
directory. Do not attempt to
|
||||||
|
.B checkout
|
||||||
|
files in the directory in
|
||||||
|
which you applied the patches. If you do, CVS will try to merge the
|
||||||
|
changes that you made during patching with the version being checked
|
||||||
|
out and things will get very confusing. Instead,
|
||||||
|
go to a different directory (like your working directory) and
|
||||||
|
check out the files there.
|
||||||
|
.SS "Advice to Third-Party Source Hackers"
|
||||||
|
As you can see from the preceding sections, merging local changes
|
||||||
|
into third-party distributions remains difficult, and probably
|
||||||
|
always will. This fact suggests some guidelines:
|
||||||
|
.TP
|
||||||
|
\(bu
|
||||||
|
Minimize local changes.
|
||||||
|
.I Never
|
||||||
|
make stylistic changes.
|
||||||
|
Change makefiles only as much as needed for installation. Avoid
|
||||||
|
overhauling anything. Pray that the vendor does the same.
|
||||||
|
.TP
|
||||||
|
\(bu
|
||||||
|
Avoid renaming files or moving them around.
|
||||||
|
.TP
|
||||||
|
\(bu
|
||||||
|
Put independent, locally written files like help documents, local
|
||||||
|
tools, or man pages in a sub-directory called `local-additions'.
|
||||||
|
Locally written files that are linked into an existing executable
|
||||||
|
should be added right in with the vendor's sources (not in a
|
||||||
|
`local-additions' directory).
|
||||||
|
If, in the future,
|
||||||
|
the vendor distributes something
|
||||||
|
equivalent to your locally written files
|
||||||
|
you can CVS
|
||||||
|
.B remove
|
||||||
|
the files from the `local-additions' directory at that time.
|
||||||
|
.SH SEE ALSO
|
||||||
|
.BR cvs (local),
|
||||||
|
.BR checkin (local),
|
||||||
|
.BR cvslog (local),
|
||||||
|
.BR cvscheck (local)
|
||||||
|
.SH AUTHOR
|
||||||
|
Lowell Skoog
|
||||||
|
.br
|
||||||
|
Software Technology Group
|
||||||
|
.br
|
||||||
|
Technical Computing
|
||||||
@@ -0,0 +1,116 @@
|
|||||||
|
#! /bin/sh
|
||||||
|
# descend,v 1.1 1992/04/03 05:22:52 berliner Exp
|
||||||
|
#
|
||||||
|
# descend - walk down a directory tree and execute a command at each node
|
||||||
|
|
||||||
|
fullname=$0
|
||||||
|
name=descend
|
||||||
|
usage="Usage: $name [-afqrv] command [directory ...]\n
|
||||||
|
\040\040-a\040\040All: descend into directories starting with '.'\n
|
||||||
|
\040\040-f\040\040Force: ignore errors during descent\n
|
||||||
|
\040\040-q\040\040Quiet: don't print directory names\n
|
||||||
|
\040\040-r\040\040Restricted: don't descend into RCS, CVS.adm, SCCS directories\n
|
||||||
|
\040\040-v\040\040Verbose: print command before executing it"
|
||||||
|
|
||||||
|
# Scan for options
|
||||||
|
while getopts afqrv option; do
|
||||||
|
case $option in
|
||||||
|
a)
|
||||||
|
alldirs=$option
|
||||||
|
options=$options" "-$option
|
||||||
|
;;
|
||||||
|
f)
|
||||||
|
force=$option
|
||||||
|
options=$options" "-$option
|
||||||
|
;;
|
||||||
|
q)
|
||||||
|
verbose=
|
||||||
|
quiet=$option
|
||||||
|
options=$options" "-$option
|
||||||
|
;;
|
||||||
|
r)
|
||||||
|
restricted=$option
|
||||||
|
options=$options" "-$option
|
||||||
|
;;
|
||||||
|
v)
|
||||||
|
verbose=$option
|
||||||
|
quiet=
|
||||||
|
options=$options" "-$option
|
||||||
|
;;
|
||||||
|
\?)
|
||||||
|
/usr/5bin/echo $usage 1>&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
shift `expr $OPTIND - 1`
|
||||||
|
|
||||||
|
# Get command to execute
|
||||||
|
if [ $# -lt 1 ] ; then
|
||||||
|
/usr/5bin/echo $usage 1>&2
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
command=$1
|
||||||
|
shift
|
||||||
|
fi
|
||||||
|
|
||||||
|
# If no directory specified, use '.'
|
||||||
|
if [ $# -lt 1 ] ; then
|
||||||
|
default_dir=.
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For each directory specified
|
||||||
|
for dir in $default_dir "$@" ; do
|
||||||
|
|
||||||
|
# Spawn sub-shell so we return to starting directory afterward
|
||||||
|
(cd $dir
|
||||||
|
|
||||||
|
# Execute specified command
|
||||||
|
if [ -z "$quiet" ] ; then
|
||||||
|
echo In directory `hostname`:`pwd`
|
||||||
|
fi
|
||||||
|
if [ -n "$verbose" ] ; then
|
||||||
|
echo $command
|
||||||
|
fi
|
||||||
|
eval "$command" || if [ -z "$force" ] ; then exit 1; fi
|
||||||
|
|
||||||
|
# Collect dot file names if necessary
|
||||||
|
if [ -n "$alldirs" ] ; then
|
||||||
|
dotfiles=.*
|
||||||
|
else
|
||||||
|
dotfiles=
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For each file in current directory
|
||||||
|
for file in $dotfiles * ; do
|
||||||
|
|
||||||
|
# Skip '.' and '..'
|
||||||
|
if [ "$file" = "." -o "$file" = ".." ] ; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# If a directory but not a symbolic link
|
||||||
|
if [ -d "$file" -a ! -h "$file" ] ; then
|
||||||
|
|
||||||
|
# If not skipping this type of directory
|
||||||
|
if [ \( "$file" != "RCS" -a \
|
||||||
|
"$file" != "SCCS" -a \
|
||||||
|
"$file" != "CVS" -a \
|
||||||
|
"$file" != "CVS.adm" \) \
|
||||||
|
-o -z "$restricted" ] ; then
|
||||||
|
|
||||||
|
# Recursively descend into it
|
||||||
|
$fullname $options "$command" "$file" \
|
||||||
|
|| if [ -z "$force" ] ; then exit 1; fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Else if a directory AND a symbolic link
|
||||||
|
elif [ -d "$file" -a -h "$file" ] ; then
|
||||||
|
|
||||||
|
if [ -z "$quiet" ] ; then
|
||||||
|
echo In directory `hostname`:`pwd`/$file: symbolic link: skipping
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
) || if [ -z "$force" ] ; then exit 1; fi
|
||||||
|
done
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
.\" descend.man,v 1.1 1992/04/03 05:22:53 berliner Exp
|
||||||
|
.TH DESCEND 1 "31 March 1992"
|
||||||
|
.SH NAME
|
||||||
|
descend \- walk directory tree and execute a command at each node
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.B descend
|
||||||
|
[
|
||||||
|
.B \-afqrv
|
||||||
|
]
|
||||||
|
.I command
|
||||||
|
[
|
||||||
|
.I directory
|
||||||
|
\&.\|.\|.
|
||||||
|
]
|
||||||
|
.SH DESCRIPTION
|
||||||
|
.B descend
|
||||||
|
walks down a directory tree and executes a command at each node. It
|
||||||
|
is not as versatile as
|
||||||
|
.BR find (1),
|
||||||
|
but it has a simpler syntax. If no
|
||||||
|
.I directory
|
||||||
|
is specified,
|
||||||
|
.B descend
|
||||||
|
starts at the current one.
|
||||||
|
.LP
|
||||||
|
Unlike
|
||||||
|
.BR find ,
|
||||||
|
.B descend
|
||||||
|
can be told to skip the special directories associated with RCS,
|
||||||
|
CVS, and SCCS. This makes
|
||||||
|
.B descend
|
||||||
|
especially handy for use with these packages. It can be used with
|
||||||
|
other commands too, of course.
|
||||||
|
.LP
|
||||||
|
.B descend
|
||||||
|
is a poor man's way to make any command recursive. Note:
|
||||||
|
.B descend
|
||||||
|
does not follow symbolic links to directories unless they are
|
||||||
|
specified on the command line.
|
||||||
|
.SH OPTIONS
|
||||||
|
.TP 15
|
||||||
|
.B \-a
|
||||||
|
.I All.
|
||||||
|
Descend into directories that begin with '.'.
|
||||||
|
.TP
|
||||||
|
.B \-f
|
||||||
|
.I Force.
|
||||||
|
Ignore errors during descent. Normally,
|
||||||
|
.B descend
|
||||||
|
quits when an error occurs.
|
||||||
|
.TP
|
||||||
|
.B \-q
|
||||||
|
.I Quiet.
|
||||||
|
Suppress the message `In directory
|
||||||
|
.IR directory '
|
||||||
|
that is normally printed during the descent.
|
||||||
|
.TP
|
||||||
|
.B \-r
|
||||||
|
.I Restricted.
|
||||||
|
Don't descend into the special directories
|
||||||
|
.SB RCS,
|
||||||
|
.SB CVS,
|
||||||
|
.SB CVS.adm,
|
||||||
|
and
|
||||||
|
.SB SCCS.
|
||||||
|
.TP
|
||||||
|
.B \-v
|
||||||
|
.I Verbose.
|
||||||
|
Print
|
||||||
|
.I command
|
||||||
|
before executing it.
|
||||||
|
.SH EXAMPLES
|
||||||
|
.TP 15
|
||||||
|
.B "descend ls"
|
||||||
|
Cheap substitute for `ls -R'.
|
||||||
|
.TP 15
|
||||||
|
.B "descend -f 'rm *' tree"
|
||||||
|
Strip `tree' of its leaves. This command descends the `tree'
|
||||||
|
directory, removing all regular files. Since
|
||||||
|
.BR rm (1)
|
||||||
|
does not remove directories, this command leaves the directory
|
||||||
|
structure of `tree' intact, but denuded. The
|
||||||
|
.B \-f
|
||||||
|
option is required to keep
|
||||||
|
.B descend
|
||||||
|
from quitting. You could use `rm \-f' instead.
|
||||||
|
.TP
|
||||||
|
.B "descend -r 'co RCS/*'" /project/src/
|
||||||
|
Check out every RCS file under the directory
|
||||||
|
.BR "/project/src" .
|
||||||
|
.TP
|
||||||
|
.B "descend -r 'cvs diff'"
|
||||||
|
Perform CVS `diff' operation on every directory below (and including)
|
||||||
|
the current one.
|
||||||
|
.SH DIAGNOSTICS
|
||||||
|
Returns 1 if errors occur (and the
|
||||||
|
.B \-f
|
||||||
|
option is not used). Otherwise returns 0.
|
||||||
|
.SH SEE ALSO
|
||||||
|
.BR find (1),
|
||||||
|
.BR rcsintro (1),
|
||||||
|
.BR cvs (1),
|
||||||
|
.BR sccs (1)
|
||||||
|
.SH AUTHOR
|
||||||
|
Lowell Skoog
|
||||||
|
.br
|
||||||
|
Software Technology Group
|
||||||
|
.br
|
||||||
|
John Fluke Mfg. Co., Inc.
|
||||||
|
.SH BUGS
|
||||||
|
Shell metacharacters in
|
||||||
|
.I command
|
||||||
|
may have bizarre effects. In particular, compound commands
|
||||||
|
(containing ';', '[', and ']' characters) will not work. It is best
|
||||||
|
to enclose complicated commands in single quotes \(aa\ \(aa.
|
||||||
@@ -0,0 +1,481 @@
|
|||||||
|
echo 'directory.3':
|
||||||
|
sed 's/^X//' >'directory.3' <<'!'
|
||||||
|
X.TH DIRECTORY 3 imported
|
||||||
|
X.DA 9 Oct 1985
|
||||||
|
X.SH NAME
|
||||||
|
Xopendir, readdir, telldir, seekdir, rewinddir, closedir \- high-level directory operations
|
||||||
|
X.SH SYNOPSIS
|
||||||
|
X.B #include <sys/types.h>
|
||||||
|
X.br
|
||||||
|
X.B #include <ndir.h>
|
||||||
|
X.PP
|
||||||
|
X.SM
|
||||||
|
X.B DIR
|
||||||
|
X.B *opendir(filename)
|
||||||
|
X.br
|
||||||
|
X.B char *filename;
|
||||||
|
X.PP
|
||||||
|
X.SM
|
||||||
|
X.B struct direct
|
||||||
|
X.B *readdir(dirp)
|
||||||
|
X.br
|
||||||
|
X.B DIR *dirp;
|
||||||
|
X.PP
|
||||||
|
X.SM
|
||||||
|
X.B long
|
||||||
|
X.B telldir(dirp)
|
||||||
|
X.br
|
||||||
|
X.B DIR *dirp;
|
||||||
|
X.PP
|
||||||
|
X.SM
|
||||||
|
X.B seekdir(dirp, loc)
|
||||||
|
X.br
|
||||||
|
X.B DIR *dirp;
|
||||||
|
X.br
|
||||||
|
X.B long loc;
|
||||||
|
X.PP
|
||||||
|
X.SM
|
||||||
|
X.B rewinddir(dirp)
|
||||||
|
X.br
|
||||||
|
X.B DIR *dirp;
|
||||||
|
X.PP
|
||||||
|
X.SM
|
||||||
|
X.B closedir(dirp)
|
||||||
|
X.br
|
||||||
|
X.B DIR *dirp;
|
||||||
|
X.SH DESCRIPTION
|
||||||
|
XThis library provides high-level primitives for directory scanning,
|
||||||
|
Xsimilar to those available for 4.2BSD's (very different) directory system.
|
||||||
|
X.\"The purpose of this library is to simulate
|
||||||
|
X.\"the new flexible length directory names of 4.2bsd UNIX
|
||||||
|
X.\"on top of the old directory structure of v7.
|
||||||
|
XIt incidentally provides easy portability to and from 4.2BSD (insofar
|
||||||
|
Xas such portability is not compromised by other 4.2/VAX dependencies).
|
||||||
|
X.\"It allows programs to be converted immediately
|
||||||
|
X.\"to the new directory access interface,
|
||||||
|
X.\"so that they need only be relinked
|
||||||
|
X.\"when moved to 4.2bsd.
|
||||||
|
X.\"It is obtained with the loader option
|
||||||
|
X.\".BR \-lndir .
|
||||||
|
X.PP
|
||||||
|
X.I Opendir
|
||||||
|
Xopens the directory named by
|
||||||
|
X.I filename
|
||||||
|
Xand associates a
|
||||||
|
X.I directory stream
|
||||||
|
Xwith it.
|
||||||
|
X.I Opendir
|
||||||
|
Xreturns a pointer to be used to identify the
|
||||||
|
X.I directory stream
|
||||||
|
Xin subsequent operations.
|
||||||
|
XThe pointer
|
||||||
|
X.SM
|
||||||
|
X.B NULL
|
||||||
|
Xis returned if
|
||||||
|
X.I filename
|
||||||
|
Xcannot be accessed or is not a directory.
|
||||||
|
X.PP
|
||||||
|
X.I Readdir
|
||||||
|
Xreturns a pointer to the next directory entry.
|
||||||
|
XIt returns
|
||||||
|
X.B NULL
|
||||||
|
Xupon reaching the end of the directory or detecting
|
||||||
|
Xan invalid
|
||||||
|
X.I seekdir
|
||||||
|
Xoperation.
|
||||||
|
X.PP
|
||||||
|
X.I Telldir
|
||||||
|
Xreturns the current location associated with the named
|
||||||
|
X.I directory stream.
|
||||||
|
X.PP
|
||||||
|
X.I Seekdir
|
||||||
|
Xsets the position of the next
|
||||||
|
X.I readdir
|
||||||
|
Xoperation on the
|
||||||
|
X.I directory stream.
|
||||||
|
XThe new position reverts to the one associated with the
|
||||||
|
X.I directory stream
|
||||||
|
Xwhen the
|
||||||
|
X.I telldir
|
||||||
|
Xoperation was performed.
|
||||||
|
XValues returned by
|
||||||
|
X.I telldir
|
||||||
|
Xare good only for the lifetime of the DIR pointer from
|
||||||
|
Xwhich they are derived.
|
||||||
|
XIf the directory is closed and then reopened,
|
||||||
|
Xthe
|
||||||
|
X.I telldir
|
||||||
|
Xvalue may be invalidated
|
||||||
|
Xdue to undetected directory compaction in 4.2BSD.
|
||||||
|
XIt is safe to use a previous
|
||||||
|
X.I telldir
|
||||||
|
Xvalue immediately after a call to
|
||||||
|
X.I opendir
|
||||||
|
Xand before any calls to
|
||||||
|
X.I readdir.
|
||||||
|
X.PP
|
||||||
|
X.I Rewinddir
|
||||||
|
Xresets the position of the named
|
||||||
|
X.I directory stream
|
||||||
|
Xto the beginning of the directory.
|
||||||
|
X.PP
|
||||||
|
X.I Closedir
|
||||||
|
Xcauses the named
|
||||||
|
X.I directory stream
|
||||||
|
Xto be closed,
|
||||||
|
Xand the structure associated with the DIR pointer to be freed.
|
||||||
|
X.PP
|
||||||
|
XA
|
||||||
|
X.I direct
|
||||||
|
Xstructure is as follows:
|
||||||
|
X.PP
|
||||||
|
X.RS
|
||||||
|
X.nf
|
||||||
|
Xstruct direct {
|
||||||
|
X /* unsigned */ long d_ino; /* inode number of entry */
|
||||||
|
X unsigned short d_reclen; /* length of this record */
|
||||||
|
X unsigned short d_namlen; /* length of string in d_name */
|
||||||
|
X char d_name[MAXNAMLEN + 1]; /* name must be no longer than this */
|
||||||
|
X};
|
||||||
|
X.fi
|
||||||
|
X.RE
|
||||||
|
X.PP
|
||||||
|
XThe
|
||||||
|
X.I d_reclen
|
||||||
|
Xfield is meaningless in non-4.2BSD systems and should be ignored.
|
||||||
|
XThe use of a
|
||||||
|
X.I long
|
||||||
|
Xfor
|
||||||
|
X.I d_ino
|
||||||
|
Xis also a 4.2BSDism;
|
||||||
|
X.I ino_t
|
||||||
|
X(see
|
||||||
|
X.IR types (5))
|
||||||
|
Xshould be used elsewhere.
|
||||||
|
XThe macro
|
||||||
|
X.I DIRSIZ(dp)
|
||||||
|
Xgives the minimum memory size needed to hold the
|
||||||
|
X.I direct
|
||||||
|
Xvalue pointed to by
|
||||||
|
X.IR dp ,
|
||||||
|
Xwith the minimum necessary allocation for
|
||||||
|
X.IR d_name .
|
||||||
|
X.PP
|
||||||
|
XThe preferred way to search the current directory for entry ``name'' is:
|
||||||
|
X.PP
|
||||||
|
X.RS
|
||||||
|
X.nf
|
||||||
|
X len = strlen(name);
|
||||||
|
X dirp = opendir(".");
|
||||||
|
X if (dirp == NULL) {
|
||||||
|
X fprintf(stderr, "%s: can't read directory .\\n", argv[0]);
|
||||||
|
X return NOT_FOUND;
|
||||||
|
X }
|
||||||
|
X while ((dp = readdir(dirp)) != NULL)
|
||||||
|
X if (dp->d_namlen == len && strcmp(dp->d_name, name) == 0) {
|
||||||
|
X closedir(dirp);
|
||||||
|
X return FOUND;
|
||||||
|
X }
|
||||||
|
X closedir(dirp);
|
||||||
|
X return NOT_FOUND;
|
||||||
|
X.RE
|
||||||
|
X.\".SH LINKING
|
||||||
|
X.\"This library is accessed by specifying ``-lndir'' as the
|
||||||
|
X.\"last argument to the compile line, e.g.:
|
||||||
|
X.\".PP
|
||||||
|
X.\" cc -I/usr/include/ndir -o prog prog.c -lndir
|
||||||
|
X.SH "SEE ALSO"
|
||||||
|
Xopen(2),
|
||||||
|
Xclose(2),
|
||||||
|
Xread(2),
|
||||||
|
Xlseek(2)
|
||||||
|
X.SH HISTORY
|
||||||
|
XWritten by
|
||||||
|
XKirk McKusick at Berkeley (ucbvax!mckusick).
|
||||||
|
XMiscellaneous bug fixes from elsewhere.
|
||||||
|
XThe size of the data structure has been decreased to avoid excessive
|
||||||
|
Xspace waste under V7 (where filenames are 14 characters at most).
|
||||||
|
XFor obscure historical reasons, the include file is also available
|
||||||
|
Xas
|
||||||
|
X.IR <ndir/sys/dir.h> .
|
||||||
|
XThe Berkeley version lived in a separate library (\fI\-lndir\fR),
|
||||||
|
Xwhereas ours is
|
||||||
|
Xpart of the C library, although the separate library is retained to
|
||||||
|
Xmaximize compatibility.
|
||||||
|
X.PP
|
||||||
|
XThis manual page has been substantially rewritten to be informative in
|
||||||
|
Xthe absence of a 4.2BSD manual.
|
||||||
|
X.SH BUGS
|
||||||
|
XThe
|
||||||
|
X.I DIRSIZ
|
||||||
|
Xmacro actually wastes a bit of space due to some padding requirements
|
||||||
|
Xthat are an artifact of 4.2BSD.
|
||||||
|
X.PP
|
||||||
|
XThe returned value of
|
||||||
|
X.I readdir
|
||||||
|
Xpoints to a static area that will be overwritten by subsequent calls.
|
||||||
|
X.PP
|
||||||
|
XThere are some unfortunate name conflicts with the \fIreal\fR V7
|
||||||
|
Xdirectory structure definitions.
|
||||||
|
!
|
||||||
|
echo 'dir.h':
|
||||||
|
sed 's/^X//' >'dir.h' <<'!'
|
||||||
|
X/* dir.h 4.4 82/07/25 */
|
||||||
|
X
|
||||||
|
X/*
|
||||||
|
X * A directory consists of some number of blocks of DIRBLKSIZ
|
||||||
|
X * bytes, where DIRBLKSIZ is chosen such that it can be transferred
|
||||||
|
X * to disk in a single atomic operation (e.g. 512 bytes on most machines).
|
||||||
|
X *
|
||||||
|
X * Each DIRBLKSIZ byte block contains some number of directory entry
|
||||||
|
X * structures, which are of variable length. Each directory entry has
|
||||||
|
X * a struct direct at the front of it, containing its inode number,
|
||||||
|
X * the length of the entry, and the length of the name contained in
|
||||||
|
X * the entry. These are followed by the name padded to a 4 byte boundary
|
||||||
|
X * with null bytes. All names are guaranteed null terminated.
|
||||||
|
X * The maximum length of a name in a directory is MAXNAMLEN.
|
||||||
|
X *
|
||||||
|
X * The macro DIRSIZ(dp) gives the amount of space required to represent
|
||||||
|
X * a directory entry. Free space in a directory is represented by
|
||||||
|
X * entries which have dp->d_reclen >= DIRSIZ(dp). All DIRBLKSIZ bytes
|
||||||
|
X * in a directory block are claimed by the directory entries. This
|
||||||
|
X * usually results in the last entry in a directory having a large
|
||||||
|
X * dp->d_reclen. When entries are deleted from a directory, the
|
||||||
|
X * space is returned to the previous entry in the same directory
|
||||||
|
X * block by increasing its dp->d_reclen. If the first entry of
|
||||||
|
X * a directory block is free, then its dp->d_ino is set to 0.
|
||||||
|
X * Entries other than the first in a directory do not normally have
|
||||||
|
X * dp->d_ino set to 0.
|
||||||
|
X */
|
||||||
|
X#define DIRBLKSIZ 512
|
||||||
|
X#ifdef VMUNIX
|
||||||
|
X#define MAXNAMLEN 255
|
||||||
|
X#else
|
||||||
|
X#define MAXNAMLEN 14
|
||||||
|
X#endif
|
||||||
|
X
|
||||||
|
Xstruct direct {
|
||||||
|
X /* unsigned */ long d_ino; /* inode number of entry */
|
||||||
|
X unsigned short d_reclen; /* length of this record */
|
||||||
|
X unsigned short d_namlen; /* length of string in d_name */
|
||||||
|
X char d_name[MAXNAMLEN + 1]; /* name must be no longer than this */
|
||||||
|
X};
|
||||||
|
X
|
||||||
|
X/*
|
||||||
|
X * The DIRSIZ macro gives the minimum record length which will hold
|
||||||
|
X * the directory entry. This requires the amount of space in struct direct
|
||||||
|
X * without the d_name field, plus enough space for the name with a terminating
|
||||||
|
X * null byte (dp->d_namlen+1), rounded up to a 4 byte boundary.
|
||||||
|
X */
|
||||||
|
X#undef DIRSIZ
|
||||||
|
X#define DIRSIZ(dp) \
|
||||||
|
X ((sizeof (struct direct) - (MAXNAMLEN+1)) + (((dp)->d_namlen+1 + 3) &~ 3))
|
||||||
|
X
|
||||||
|
X#ifndef KERNEL
|
||||||
|
X/*
|
||||||
|
X * Definitions for library routines operating on directories.
|
||||||
|
X */
|
||||||
|
Xtypedef struct _dirdesc {
|
||||||
|
X int dd_fd;
|
||||||
|
X long dd_loc;
|
||||||
|
X long dd_size;
|
||||||
|
X char dd_buf[DIRBLKSIZ];
|
||||||
|
X} DIR;
|
||||||
|
X#ifndef NULL
|
||||||
|
X#define NULL 0
|
||||||
|
X#endif
|
||||||
|
Xextern DIR *opendir();
|
||||||
|
Xextern struct direct *readdir();
|
||||||
|
Xextern long telldir();
|
||||||
|
X#ifdef void
|
||||||
|
Xextern void seekdir();
|
||||||
|
Xextern void closedir();
|
||||||
|
X#endif
|
||||||
|
X#define rewinddir(dirp) seekdir((dirp), (long)0)
|
||||||
|
X#endif KERNEL
|
||||||
|
!
|
||||||
|
echo 'makefile':
|
||||||
|
sed 's/^X//' >'makefile' <<'!'
|
||||||
|
XDIR = closedir.o opendir.o readdir.o seekdir.o telldir.o
|
||||||
|
XCFLAGS=-O -I. -Dvoid=int
|
||||||
|
XDEST=..
|
||||||
|
X
|
||||||
|
Xall: $(DIR)
|
||||||
|
X
|
||||||
|
Xmv: $(DIR)
|
||||||
|
X mv $(DIR) $(DEST)
|
||||||
|
X
|
||||||
|
Xcpif: dir.h
|
||||||
|
X cp dir.h /usr/include/ndir.h
|
||||||
|
X
|
||||||
|
Xclean:
|
||||||
|
X rm -f *.o
|
||||||
|
!
|
||||||
|
echo 'closedir.c':
|
||||||
|
sed 's/^X//' >'closedir.c' <<'!'
|
||||||
|
Xstatic char sccsid[] = "@(#)closedir.c 4.2 3/10/82";
|
||||||
|
X
|
||||||
|
X#include <sys/types.h>
|
||||||
|
X#include <dir.h>
|
||||||
|
X
|
||||||
|
X/*
|
||||||
|
X * close a directory.
|
||||||
|
X */
|
||||||
|
Xvoid
|
||||||
|
Xclosedir(dirp)
|
||||||
|
X register DIR *dirp;
|
||||||
|
X{
|
||||||
|
X close(dirp->dd_fd);
|
||||||
|
X dirp->dd_fd = -1;
|
||||||
|
X dirp->dd_loc = 0;
|
||||||
|
X free((char *)dirp);
|
||||||
|
X}
|
||||||
|
!
|
||||||
|
echo 'opendir.c':
|
||||||
|
sed 's/^X//' >'opendir.c' <<'!'
|
||||||
|
X/* Copyright (c) 1982 Regents of the University of California */
|
||||||
|
X
|
||||||
|
Xstatic char sccsid[] = "@(#)opendir.c 4.4 11/12/82";
|
||||||
|
X
|
||||||
|
X#include <sys/types.h>
|
||||||
|
X#include <sys/stat.h>
|
||||||
|
X#include <dir.h>
|
||||||
|
X
|
||||||
|
X/*
|
||||||
|
X * open a directory.
|
||||||
|
X */
|
||||||
|
XDIR *
|
||||||
|
Xopendir(name)
|
||||||
|
X char *name;
|
||||||
|
X{
|
||||||
|
X register DIR *dirp;
|
||||||
|
X register int fd;
|
||||||
|
X struct stat statbuf;
|
||||||
|
X char *malloc();
|
||||||
|
X
|
||||||
|
X if ((fd = open(name, 0)) == -1)
|
||||||
|
X return NULL;
|
||||||
|
X if (fstat(fd, &statbuf) == -1 || !(statbuf.st_mode & S_IFDIR)) {
|
||||||
|
X close(fd);
|
||||||
|
X return NULL;
|
||||||
|
X }
|
||||||
|
X if ((dirp = (DIR *)malloc(sizeof(DIR))) == NULL) {
|
||||||
|
X close (fd);
|
||||||
|
X return NULL;
|
||||||
|
X }
|
||||||
|
X dirp->dd_fd = fd;
|
||||||
|
X dirp->dd_loc = 0;
|
||||||
|
X dirp->dd_size = 0; /* so that telldir will work before readdir */
|
||||||
|
X return dirp;
|
||||||
|
X}
|
||||||
|
!
|
||||||
|
echo 'readdir.c':
|
||||||
|
sed 's/^X//' >'readdir.c' <<'!'
|
||||||
|
X/* Copyright (c) 1982 Regents of the University of California */
|
||||||
|
X
|
||||||
|
Xstatic char sccsid[] = "@(#)readdir.c 4.3 8/8/82";
|
||||||
|
X
|
||||||
|
X#include <sys/types.h>
|
||||||
|
X#include <dir.h>
|
||||||
|
X
|
||||||
|
X/*
|
||||||
|
X * read an old stlye directory entry and present it as a new one
|
||||||
|
X */
|
||||||
|
X#define ODIRSIZ 14
|
||||||
|
X
|
||||||
|
Xstruct olddirect {
|
||||||
|
X ino_t od_ino;
|
||||||
|
X char od_name[ODIRSIZ];
|
||||||
|
X};
|
||||||
|
X
|
||||||
|
X/*
|
||||||
|
X * get next entry in a directory.
|
||||||
|
X */
|
||||||
|
Xstruct direct *
|
||||||
|
Xreaddir(dirp)
|
||||||
|
X register DIR *dirp;
|
||||||
|
X{
|
||||||
|
X register struct olddirect *dp;
|
||||||
|
X static struct direct dir;
|
||||||
|
X
|
||||||
|
X for (;;) {
|
||||||
|
X if (dirp->dd_loc == 0) {
|
||||||
|
X dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf,
|
||||||
|
X DIRBLKSIZ);
|
||||||
|
X if (dirp->dd_size <= 0) {
|
||||||
|
X dirp->dd_size = 0;
|
||||||
|
X return NULL;
|
||||||
|
X }
|
||||||
|
X }
|
||||||
|
X if (dirp->dd_loc >= dirp->dd_size) {
|
||||||
|
X dirp->dd_loc = 0;
|
||||||
|
X continue;
|
||||||
|
X }
|
||||||
|
X dp = (struct olddirect *)(dirp->dd_buf + dirp->dd_loc);
|
||||||
|
X dirp->dd_loc += sizeof(struct olddirect);
|
||||||
|
X if (dp->od_ino == 0)
|
||||||
|
X continue;
|
||||||
|
X dir.d_ino = dp->od_ino;
|
||||||
|
X strncpy(dir.d_name, dp->od_name, ODIRSIZ);
|
||||||
|
X dir.d_name[ODIRSIZ] = '\0'; /* insure null termination */
|
||||||
|
X dir.d_namlen = strlen(dir.d_name);
|
||||||
|
X dir.d_reclen = DIRBLKSIZ;
|
||||||
|
X return (&dir);
|
||||||
|
X }
|
||||||
|
X}
|
||||||
|
!
|
||||||
|
echo 'seekdir.c':
|
||||||
|
sed 's/^X//' >'seekdir.c' <<'!'
|
||||||
|
Xstatic char sccsid[] = "@(#)seekdir.c 4.9 3/25/83";
|
||||||
|
X
|
||||||
|
X#include <sys/param.h>
|
||||||
|
X#include <dir.h>
|
||||||
|
X
|
||||||
|
X/*
|
||||||
|
X * seek to an entry in a directory.
|
||||||
|
X * Only values returned by "telldir" should be passed to seekdir.
|
||||||
|
X */
|
||||||
|
Xvoid
|
||||||
|
Xseekdir(dirp, loc)
|
||||||
|
X register DIR *dirp;
|
||||||
|
X long loc;
|
||||||
|
X{
|
||||||
|
X long curloc, base, offset;
|
||||||
|
X struct direct *dp;
|
||||||
|
X extern long lseek();
|
||||||
|
X
|
||||||
|
X curloc = telldir(dirp);
|
||||||
|
X if (loc == curloc)
|
||||||
|
X return;
|
||||||
|
X base = loc & ~(DIRBLKSIZ - 1);
|
||||||
|
X offset = loc & (DIRBLKSIZ - 1);
|
||||||
|
X (void) lseek(dirp->dd_fd, base, 0);
|
||||||
|
X dirp->dd_size = 0;
|
||||||
|
X dirp->dd_loc = 0;
|
||||||
|
X while (dirp->dd_loc < offset) {
|
||||||
|
X dp = readdir(dirp);
|
||||||
|
X if (dp == NULL)
|
||||||
|
X return;
|
||||||
|
X }
|
||||||
|
X}
|
||||||
|
!
|
||||||
|
echo 'telldir.c':
|
||||||
|
sed 's/^X//' >'telldir.c' <<'!'
|
||||||
|
Xstatic char sccsid[] = "@(#)telldir.c 4.1 2/21/82";
|
||||||
|
X
|
||||||
|
X#include <sys/types.h>
|
||||||
|
X#include <dir.h>
|
||||||
|
X
|
||||||
|
X/*
|
||||||
|
X * return a pointer into a directory
|
||||||
|
X */
|
||||||
|
Xlong
|
||||||
|
Xtelldir(dirp)
|
||||||
|
X DIR *dirp;
|
||||||
|
X{
|
||||||
|
X long lseek();
|
||||||
|
X
|
||||||
|
X return (lseek(dirp->dd_fd, 0L, 1) - dirp->dd_size + dirp->dd_loc);
|
||||||
|
X}
|
||||||
|
!
|
||||||
|
echo done
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
#!/usr/bin/perl
|
||||||
|
|
||||||
|
# Modified by berliner@Sun.COM to add support for CVS 1.3 2/27/92
|
||||||
|
#
|
||||||
|
# Date: Tue, 6 Aug 91 13:27 EDT
|
||||||
|
# From: samborn@sunrise.com (Kevin Samborn)
|
||||||
|
#
|
||||||
|
# I revised the perl script I sent you yesterday to use the info you
|
||||||
|
# send in on stdin. (I am appending the newer script to the end)
|
||||||
|
#
|
||||||
|
# now the output looks like this:
|
||||||
|
#
|
||||||
|
# **************************************
|
||||||
|
# date: Tuesday, August 6, 1991 @ 13:17
|
||||||
|
# author: samborn
|
||||||
|
# Update of /elmer/cvs/CVSROOT.adm
|
||||||
|
# In directory astro:/home/samborn/CVSROOT.adm
|
||||||
|
#
|
||||||
|
# Modified Files:
|
||||||
|
# test3
|
||||||
|
#
|
||||||
|
# Added Files:
|
||||||
|
# test6
|
||||||
|
#
|
||||||
|
# Removed Files:
|
||||||
|
# test4
|
||||||
|
#
|
||||||
|
# Log Message:
|
||||||
|
# wow, what a test
|
||||||
|
#
|
||||||
|
# RCS: 1.4 /elmer/cvs/CVSROOT.adm/test3,v
|
||||||
|
# RCS: 1.1 /elmer/cvs/CVSROOT.adm/test6,v
|
||||||
|
# RCS: 1.1 /elmer/cvs/CVSROOT.adm/Attic/test4,v
|
||||||
|
#
|
||||||
|
|
||||||
|
#
|
||||||
|
# turn off setgid
|
||||||
|
#
|
||||||
|
$) = $(;
|
||||||
|
|
||||||
|
#
|
||||||
|
# parse command line arguments
|
||||||
|
#
|
||||||
|
@files = split(/ /,$ARGV[0]);
|
||||||
|
$logfile = $ARGV[1];
|
||||||
|
$cvsroot = $ENV{'CVSROOT'};
|
||||||
|
|
||||||
|
#
|
||||||
|
# Some date and time arrays
|
||||||
|
#
|
||||||
|
@mos = (January,February,March,April,May,June,July,August,September,
|
||||||
|
October,November,December);
|
||||||
|
@days = (Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday);
|
||||||
|
($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime;
|
||||||
|
|
||||||
|
#
|
||||||
|
# get login name
|
||||||
|
#
|
||||||
|
$login = getlogin || (getpwuid($<))[0] || "nobody";
|
||||||
|
|
||||||
|
#
|
||||||
|
# open log file for appending
|
||||||
|
#
|
||||||
|
if ((open(OUT, ">>" . $logfile)) != 1) {
|
||||||
|
die "Could not open logfile " . $logfile . "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Header
|
||||||
|
#
|
||||||
|
print OUT "\n";
|
||||||
|
print OUT "**************************************\n";
|
||||||
|
print OUT "date: " . $days[$wday] . ", " . $mos[$mon] . " " . $mday . ", 19" . $year .
|
||||||
|
" @ " . $hour . ":" . sprintf("%02d", $min) . "\n";
|
||||||
|
print OUT "author: " . $login . "\n";
|
||||||
|
|
||||||
|
#
|
||||||
|
#print the stuff on stdin to the logfile
|
||||||
|
#
|
||||||
|
open(IN, "-");
|
||||||
|
while(<IN>) {
|
||||||
|
print OUT $_;
|
||||||
|
}
|
||||||
|
close(IN);
|
||||||
|
|
||||||
|
print OUT "\n";
|
||||||
|
|
||||||
|
#
|
||||||
|
# after log information, do an 'cvs -Qn status' on each file in the arguments.
|
||||||
|
#
|
||||||
|
for $file (@files[1..$#files]) {
|
||||||
|
if ($file eq "-") {
|
||||||
|
last;
|
||||||
|
}
|
||||||
|
open(RCS,"-|") || exec 'cvs', '-Qn', 'status', $file;
|
||||||
|
while (<RCS>) {
|
||||||
|
if (substr($_, 0, 7) eq " RCS") {
|
||||||
|
print OUT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close (RCS);
|
||||||
|
}
|
||||||
|
|
||||||
|
close (OUT);
|
||||||
@@ -0,0 +1,331 @@
|
|||||||
|
#!/usr/local/bin/perl -w
|
||||||
|
#
|
||||||
|
# Perl filter to handle the log messages from the checkin of files in
|
||||||
|
# a directory. This script will group the lists of files by log
|
||||||
|
# message, and mail a single consolidated log message at the end of
|
||||||
|
# the commit.
|
||||||
|
#
|
||||||
|
# This file assumes a pre-commit checking program that leaves the
|
||||||
|
# names of the first and last commit directories in a temporary file.
|
||||||
|
#
|
||||||
|
# Contributed by David Hampton <hampton@cisco.com>
|
||||||
|
#
|
||||||
|
|
||||||
|
############################################################
|
||||||
|
#
|
||||||
|
# Configurable options
|
||||||
|
#
|
||||||
|
############################################################
|
||||||
|
#
|
||||||
|
# Do cisco Systems, Inc. specific nonsense.
|
||||||
|
#
|
||||||
|
$cisco_systems = 1;
|
||||||
|
|
||||||
|
#
|
||||||
|
# Recipient of all mail messages
|
||||||
|
#
|
||||||
|
$mailto = "sw-notification@cisco.com";
|
||||||
|
|
||||||
|
############################################################
|
||||||
|
#
|
||||||
|
# Constants
|
||||||
|
#
|
||||||
|
############################################################
|
||||||
|
$STATE_NONE = 0;
|
||||||
|
$STATE_CHANGED = 1;
|
||||||
|
$STATE_ADDED = 2;
|
||||||
|
$STATE_REMOVED = 3;
|
||||||
|
$STATE_LOG = 4;
|
||||||
|
|
||||||
|
$LAST_FILE = "/tmp/#cvs.lastdir";
|
||||||
|
$CHANGED_FILE = "/tmp/#cvs.files.changed";
|
||||||
|
$ADDED_FILE = "/tmp/#cvs.files.added";
|
||||||
|
$REMOVED_FILE = "/tmp/#cvs.files.removed";
|
||||||
|
$LOG_FILE = "/tmp/#cvs.files.log";
|
||||||
|
$FILE_PREFIX = "#cvs.files";
|
||||||
|
|
||||||
|
$VERSION_FILE = "version";
|
||||||
|
$TRUNKREV_FILE = "TrunkRev";
|
||||||
|
$CHANGES_FILE = "Changes";
|
||||||
|
$CHANGES_TEMP = "Changes.tmp";
|
||||||
|
|
||||||
|
############################################################
|
||||||
|
#
|
||||||
|
# Subroutines
|
||||||
|
#
|
||||||
|
############################################################
|
||||||
|
|
||||||
|
sub format_names {
|
||||||
|
local($dir, @files) = @_;
|
||||||
|
local(@lines);
|
||||||
|
$lines[0] = sprintf(" %-08s", $dir);
|
||||||
|
foreach $file (@files) {
|
||||||
|
if (length($lines[$#lines]) + length($file) > 60) {
|
||||||
|
$lines[++$#lines] = sprintf(" %8s", " ");
|
||||||
|
}
|
||||||
|
$lines[$#lines] .= " ".$file;
|
||||||
|
}
|
||||||
|
@lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub cleanup_tmpfiles {
|
||||||
|
local($all) = @_;
|
||||||
|
local($wd, @files);
|
||||||
|
|
||||||
|
$wd = `pwd`;
|
||||||
|
chdir("/tmp");
|
||||||
|
opendir(DIR, ".");
|
||||||
|
if ($all == 1) {
|
||||||
|
push(@files, grep(/$id$/, readdir(DIR)));
|
||||||
|
} else {
|
||||||
|
push(@files, grep(/^$FILE_PREFIX.*$id$/, readdir(DIR)));
|
||||||
|
}
|
||||||
|
closedir(DIR);
|
||||||
|
foreach (@files) {
|
||||||
|
unlink $_;
|
||||||
|
}
|
||||||
|
chdir($wd);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub write_logfile {
|
||||||
|
local($filename, @lines) = @_;
|
||||||
|
open(FILE, ">$filename") || die ("Cannot open log file $filename.\n");
|
||||||
|
print(FILE join("\n", @lines), "\n");
|
||||||
|
close(FILE);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub append_to_file {
|
||||||
|
local($filename, $dir, @files) = @_;
|
||||||
|
if (@files) {
|
||||||
|
local(@lines) = &format_names($dir, @files);
|
||||||
|
open(FILE, ">>$filename") || die ("Cannot open file $filename.\n");
|
||||||
|
print(FILE join("\n", @lines), "\n");
|
||||||
|
close(FILE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub write_line {
|
||||||
|
local($filename, $line) = @_;
|
||||||
|
open(FILE, ">$filename") || die("Cannot open file $filename.\n");
|
||||||
|
print(FILE $line, "\n");
|
||||||
|
close(FILE);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub read_line {
|
||||||
|
local($line);
|
||||||
|
local($filename) = @_;
|
||||||
|
open(FILE, "<$filename") || die("Cannot open file $filename.\n");
|
||||||
|
$line = <FILE>;
|
||||||
|
close(FILE);
|
||||||
|
chop($line);
|
||||||
|
$line;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub read_file {
|
||||||
|
local(@text);
|
||||||
|
local($filename, $leader) = @_;
|
||||||
|
open(FILE, "<$filename") || return ();
|
||||||
|
while (<FILE>) {
|
||||||
|
chop;
|
||||||
|
push(@text, sprintf(" %-10s %s", $leader, $_));
|
||||||
|
$leader = "";
|
||||||
|
}
|
||||||
|
close(FILE);
|
||||||
|
@text;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub read_logfile {
|
||||||
|
local(@text);
|
||||||
|
local($filename, $leader) = @_;
|
||||||
|
open(FILE, "<$filename") || die ("Cannot open log file $filename.\n");
|
||||||
|
while (<FILE>) {
|
||||||
|
chop;
|
||||||
|
push(@text, $leader.$_);
|
||||||
|
}
|
||||||
|
close(FILE);
|
||||||
|
@text;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub bump_version {
|
||||||
|
local($trunkrev, $editnum, $version);
|
||||||
|
|
||||||
|
$trunkrev = &read_line("$ENV{'CVSROOT'}/$repository/$TRUNKREV_FILE");
|
||||||
|
$editnum = &read_line("$ENV{'CVSROOT'}/$repository/$VERSION_FILE");
|
||||||
|
&write_line("$ENV{'CVSROOT'}/$repository/$VERSION_FILE", $editnum+1);
|
||||||
|
$version = $trunkrev . "(" . $editnum . ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
sub build_header {
|
||||||
|
local($version) = @_;
|
||||||
|
local($header);
|
||||||
|
local($sec,$min,$hour,$mday,$mon,$year) = localtime(time);
|
||||||
|
$header = sprintf("%-8s %s %02d/%02d/%02d %02d:%02d:%02d",
|
||||||
|
$login, $version, $year%100, $mon+1, $mday,
|
||||||
|
$hour, $min, $sec);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub do_changes_file {
|
||||||
|
local($changes, $tmpchanges);
|
||||||
|
local(@text) = @_;
|
||||||
|
|
||||||
|
$changes = "$ENV{'CVSROOT'}/$repository/$CHANGES_FILE";
|
||||||
|
$tmpchanges = "$ENV{'CVSROOT'}/$repository/$CHANGES_TEMP";
|
||||||
|
if (rename($changes, $tmpchanges) != 1) {
|
||||||
|
die("Cannot rename $changes to $tmpchanges.\n");
|
||||||
|
}
|
||||||
|
open(CHANGES, ">$changes") || die("Cannot open $changes.\n");
|
||||||
|
open(TMPCHANGES, "<$tmpchanges") || die("Cannot open $tmpchanges.\n");
|
||||||
|
print(CHANGES join("\n", @text), "\n\n");
|
||||||
|
print(CHANGES <TMPCHANGES>);
|
||||||
|
close(CHANGES);
|
||||||
|
close(TMPCHANGES);
|
||||||
|
unlink($tmpchanges);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub mail_notification {
|
||||||
|
local($name, @text) = @_;
|
||||||
|
open(MAIL, "| mail -s \"Source Repository Modification\" $name");
|
||||||
|
print(MAIL join("\n", @text));
|
||||||
|
close(MAIL);
|
||||||
|
}
|
||||||
|
|
||||||
|
#############################################################
|
||||||
|
#
|
||||||
|
# Main Body
|
||||||
|
#
|
||||||
|
############################################################
|
||||||
|
|
||||||
|
#
|
||||||
|
# Initialize basic variables
|
||||||
|
#
|
||||||
|
$id = getpgrp();
|
||||||
|
$state = $STATE_NONE;
|
||||||
|
$login = getlogin || (getpwuid($<))[0] || die("Unknown user $<.\n");
|
||||||
|
@files = split(' ', $ARGV[0]);
|
||||||
|
@path = split('/', $files[0]);
|
||||||
|
$repository = @path[0];
|
||||||
|
if ($#path == 0) {
|
||||||
|
$dir = ".";
|
||||||
|
} else {
|
||||||
|
$dir = join('/', @path[1..$#path]);
|
||||||
|
}
|
||||||
|
#print("ARGV - ", join(":", @ARGV), "\n");
|
||||||
|
#print("files - ", join(":", @files), "\n");
|
||||||
|
#print("path - ", join(":", @path), "\n");
|
||||||
|
#print("dir - ", $dir, "\n");
|
||||||
|
#print("id - ", $id, "\n");
|
||||||
|
|
||||||
|
#
|
||||||
|
# Check for a new directory first. This will always appear as a
|
||||||
|
# single item in the argument list, and an empty log message.
|
||||||
|
#
|
||||||
|
if ($ARGV[0] =~ /New directory/) {
|
||||||
|
$version = &bump_version if ($cisco_systems != 0);
|
||||||
|
$header = &build_header($version);
|
||||||
|
@text = ();
|
||||||
|
push(@text, $header);
|
||||||
|
push(@text, "");
|
||||||
|
push(@text, " ".$ARGV[0]);
|
||||||
|
&do_changes_file(@text) if ($cisco_systems != 0);
|
||||||
|
&mail_notification($mailto, @text);
|
||||||
|
exit 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Iterate over the body of the message collecting information.
|
||||||
|
#
|
||||||
|
while (<STDIN>) {
|
||||||
|
chop; # Drop the newline
|
||||||
|
if (/^Modified Files/) { $state = $STATE_CHANGED; next; }
|
||||||
|
if (/^Added Files/) { $state = $STATE_ADDED; next; }
|
||||||
|
if (/^Removed Files/) { $state = $STATE_REMOVED; next; }
|
||||||
|
if (/^Log Message/) { $state = $STATE_LOG; next; }
|
||||||
|
s/^[ \t\n]+//; # delete leading space
|
||||||
|
s/[ \t\n]+$//; # delete trailing space
|
||||||
|
|
||||||
|
push (@changed_files, split) if ($state == $STATE_CHANGED);
|
||||||
|
push (@added_files, split) if ($state == $STATE_ADDED);
|
||||||
|
push (@removed_files, split) if ($state == $STATE_REMOVED);
|
||||||
|
push (@log_lines, $_) if ($state == $STATE_LOG);
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Strip leading and trailing blank lines from the log message. Also
|
||||||
|
# compress multiple blank lines in the body of the message down to a
|
||||||
|
# single blank line.
|
||||||
|
#
|
||||||
|
while ($#log_lines > -1) {
|
||||||
|
last if ($log_lines[0] ne "");
|
||||||
|
shift(@log_lines);
|
||||||
|
}
|
||||||
|
while ($#log_lines > -1) {
|
||||||
|
last if ($log_lines[$#log_lines] ne "");
|
||||||
|
pop(@log_lines);
|
||||||
|
}
|
||||||
|
for ($i = $#log_lines; $i > 0; $i--) {
|
||||||
|
if (($log_lines[$i - 1] eq "") && ($log_lines[$i] eq "")) {
|
||||||
|
splice(@log_lines, $i, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Find the log file that matches this log message
|
||||||
|
#
|
||||||
|
for ($i = 0; ; $i++) {
|
||||||
|
last if (! -e "$LOG_FILE.$i.$id");
|
||||||
|
@text = &read_logfile("$LOG_FILE.$i.$id", "");
|
||||||
|
last if ($#text == -1);
|
||||||
|
last if (join(" ", @log_lines) eq join(" ", @text));
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Spit out the information gathered in this pass.
|
||||||
|
#
|
||||||
|
&write_logfile("$LOG_FILE.$i.$id", @log_lines);
|
||||||
|
&append_to_file("$ADDED_FILE.$i.$id", $dir, @added_files);
|
||||||
|
&append_to_file("$CHANGED_FILE.$i.$id", $dir, @changed_files);
|
||||||
|
&append_to_file("$REMOVED_FILE.$i.$id", $dir, @removed_files);
|
||||||
|
|
||||||
|
#
|
||||||
|
# Check whether this is the last directory. If not, quit.
|
||||||
|
#
|
||||||
|
$_ = &read_line("$LAST_FILE.$id");
|
||||||
|
exit 0 if (! grep(/$files[0]$/, $_));
|
||||||
|
|
||||||
|
#
|
||||||
|
# This is it. The commits are all finished. Lump everything together
|
||||||
|
# into a single message, fire a copy off to the mailing list, and drop
|
||||||
|
# it on the end of the Changes file.
|
||||||
|
#
|
||||||
|
# Get the full version number
|
||||||
|
#
|
||||||
|
$version = &bump_version if ($cisco_systems != 0);
|
||||||
|
$header = &build_header($version);
|
||||||
|
|
||||||
|
#
|
||||||
|
# Produce the final compilation of the log messages
|
||||||
|
#
|
||||||
|
@text = ();
|
||||||
|
push(@text, $header);
|
||||||
|
push(@text, "");
|
||||||
|
for ($i = 0; ; $i++) {
|
||||||
|
last if (! -e "$LOG_FILE.$i.$id");
|
||||||
|
push(@text, &read_file("$CHANGED_FILE.$i.$id", "Modified:"));
|
||||||
|
push(@text, &read_file("$ADDED_FILE.$i.$id", "Added:"));
|
||||||
|
push(@text, &read_file("$REMOVED_FILE.$i.$id", "Removed:"));
|
||||||
|
push(@text, " Log:");
|
||||||
|
push(@text, &read_logfile("$LOG_FILE.$i.$id", " "));
|
||||||
|
push(@text, "");
|
||||||
|
}
|
||||||
|
if ($cisco_systems != 0) {
|
||||||
|
@ddts = grep(/^CSCdi/, split(' ', join(" ", @text)));
|
||||||
|
$text[0] .= " " . join(" ", @ddts);
|
||||||
|
}
|
||||||
|
#
|
||||||
|
# Put the log message at the beginning of the Changes file and mail
|
||||||
|
# out the notification.
|
||||||
|
#
|
||||||
|
&do_changes_file(@text) if ($cisco_systems != 0);
|
||||||
|
&mail_notification($mailto, @text);
|
||||||
|
&cleanup_tmpfiles(1);
|
||||||
|
exit 0;
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
#!/usr/bin/perl
|
||||||
|
#
|
||||||
|
# From: clyne@niwot.scd.ucar.EDU (John Clyne)
|
||||||
|
# Date: Fri, 28 Feb 92 09:54:21 MST
|
||||||
|
#
|
||||||
|
# BTW, i wrote a perl script that is similar to 'nfpipe' except that in
|
||||||
|
# addition to logging to a file it provides a command line option for mailing
|
||||||
|
# change notices to a group of users. Obviously you probably wouldn't want
|
||||||
|
# to mail every change. But there may be certain directories that are commonly
|
||||||
|
# accessed by a group of users who would benefit from an email notice.
|
||||||
|
# Especially if they regularly beat on the same directory. Anyway if you
|
||||||
|
# think anyone would be interested here it is.
|
||||||
|
#
|
||||||
|
# mfpipe.pl,v 1.1 1992/03/02 01:22:41 berliner Exp
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# File: mfpipe
|
||||||
|
#
|
||||||
|
# Author: John Clyne
|
||||||
|
# National Center for Atmospheric Research
|
||||||
|
# PO 3000, Boulder, Colorado
|
||||||
|
#
|
||||||
|
# Date: Wed Feb 26 18:34:53 MST 1992
|
||||||
|
#
|
||||||
|
# Description: Tee standard input to mail a list of users and to
|
||||||
|
# a file. Used by CVS logging.
|
||||||
|
#
|
||||||
|
# Usage: mfpipe [-f file] [user@host...]
|
||||||
|
#
|
||||||
|
# Environment: CVSROOT
|
||||||
|
# Path to CVS root.
|
||||||
|
#
|
||||||
|
# Files:
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# Options: -f file
|
||||||
|
# Capture output to 'file'
|
||||||
|
#
|
||||||
|
|
||||||
|
$header = "Log Message:\n";
|
||||||
|
|
||||||
|
$mailcmd = "| mail -s 'CVS update notice'";
|
||||||
|
$whoami = `whoami`;
|
||||||
|
chop $whoami;
|
||||||
|
$date = `date`;
|
||||||
|
chop $date;
|
||||||
|
|
||||||
|
$cvsroot = $ENV{'CVSROOT'};
|
||||||
|
|
||||||
|
while (@ARGV) {
|
||||||
|
$arg = shift @ARGV;
|
||||||
|
|
||||||
|
if ($arg eq '-f') {
|
||||||
|
$file = shift @ARGV;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$users = "$users $arg";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($users) {
|
||||||
|
$mailcmd = "$mailcmd $users";
|
||||||
|
open(MAIL, $mailcmd) || die "Execing $mail: $!\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($file) {
|
||||||
|
$logfile = "$cvsroot/LOG/$file";
|
||||||
|
open(FILE, ">> $logfile") || die "Opening $logfile: $!\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
print FILE "$whoami $date--------BEGIN LOG ENTRY-------------\n" if ($logfile);
|
||||||
|
|
||||||
|
while (<>) {
|
||||||
|
print FILE $log if ($log && $logfile);
|
||||||
|
|
||||||
|
print FILE $_ if ($logfile);
|
||||||
|
print MAIL $_ if ($users);
|
||||||
|
|
||||||
|
$log = "log: " if ($_ eq $header);
|
||||||
|
}
|
||||||
|
|
||||||
|
close FILE;
|
||||||
|
die "Write failed" if $?;
|
||||||
|
close MAIL;
|
||||||
|
die "Mail failed" if $?;
|
||||||
|
|
||||||
|
exit 0;
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
Tue Apr 7 09:11:27 1992 Per Cederqvist (ceder@leopold)
|
||||||
|
|
||||||
|
* Release 1.02.
|
||||||
|
|
||||||
|
* pcl-cvs.el (cvs-diff-backup, cvs-edit-done, cvs-status): Call
|
||||||
|
save-some-buffers.
|
||||||
|
|
||||||
|
* pcl-cvs.el (cvs-diff-backup-extractor): Fixed syntax error.
|
||||||
|
|
||||||
|
* Makefile, README, compile-all.el, dist-makefile, pcl-cvs.el,
|
||||||
|
pcl-cvs.texinfo (XXRELEASEXX): A magic string that is substituted
|
||||||
|
for the current release number when a distribution is made.
|
||||||
|
(Release 1.01 says that it is release 1.00).
|
||||||
|
|
||||||
|
* pcl-cvs.el (cvs-find-file): Added missing pair of parenthesis.
|
||||||
|
|
||||||
|
Mon Mar 30 14:25:26 1992 Per Cederqvist (ceder@leopold)
|
||||||
|
|
||||||
|
* Release 1.01.
|
||||||
|
|
||||||
|
* pcl-cvs.el (cvs-parse-buffer): The message when waiting for a
|
||||||
|
lock has been changed.
|
||||||
|
|
||||||
|
Sun Mar 29 05:29:57 1992 Per Cederqvist (ceder@leopold)
|
||||||
|
|
||||||
|
* Release 1.00.
|
||||||
|
|
||||||
|
* pcl-cvs.el (cvs-do-update, cvs-sentinel, cvs-parse-buffer):
|
||||||
|
Major rewrite of buffer and window selection and handling.
|
||||||
|
The *cvs* buffer is now killed whenever a new "cvs update" is
|
||||||
|
initiated. The -update buffer is replaced with the *cvs*
|
||||||
|
buffer when the update is completed.
|
||||||
|
|
||||||
|
Sat Mar 28 21:03:05 1992 Per Cederqvist (ceder@robin)
|
||||||
|
|
||||||
|
* pcl-cvs.el (cvs-delete-unused-temporary-buffers): Fixed it.
|
||||||
|
|
||||||
|
* pcl-cvs.el (cvs-auto-remove-handled): New variable.
|
||||||
|
* pcl-cvs.el (cvs-edit-done): Use it.
|
||||||
|
* pcl-cvs.texinfo (Customization, Removing handled entries):
|
||||||
|
Document it.
|
||||||
|
|
||||||
|
* pcl-cvs.el (cvs-mode): Turn of the undo feature. It really
|
||||||
|
isn't useful in a cookie buffer...
|
||||||
|
|
||||||
|
* pcl-cvs.el (cvs-edit-done): Committing a file now looks more
|
||||||
|
like diffing a file. The window handling is better.
|
||||||
|
* pcl-cvs.el (cvs-use-temp-buffer): The &optional switch is no
|
||||||
|
longer needed.
|
||||||
|
|
||||||
|
Mon Mar 23 00:20:33 1992 Per Cederqvist (ceder@robin)
|
||||||
|
|
||||||
|
* Release 0.97.
|
||||||
|
|
||||||
|
* pcl-cvs.el (default-directory): Make sure it always ends in a
|
||||||
|
slash. fileinfo->dir does NOT end in a slash, and I had forgotten
|
||||||
|
to call file-name-as-directory in various places.
|
||||||
|
|
||||||
|
* pcl-cvs.el (cvs-diff-backup-extractor): Signal an error if a
|
||||||
|
fileinfo without backup file is given.
|
||||||
|
|
||||||
|
* pcl-cvs.el (cvs-mode): Added documentation.
|
||||||
|
|
||||||
|
* pcl-cvs.el (cvs-execute-list): Fix the order of files in the
|
||||||
|
same directory.
|
||||||
|
|
||||||
|
* pcl-cvs.el (cvs-log-flags, cvs-status-flags): New variables.
|
||||||
|
* pcl-cvs.el (cvs-log, cvs-status): Use them.
|
||||||
|
* pcl-cvs.texinfo (Customization): Document them.
|
||||||
|
|
||||||
|
* pcl-cvs.el (cvs-diff-backup): Filter non-backup-diffable files
|
||||||
|
at an earlier stage, like cvs-commit does.
|
||||||
|
|
||||||
|
* pcl-cvs.el (cvs-diff-flags): New variable.
|
||||||
|
* pcl-cvs.el (cvs-diff-backup): Use it.
|
||||||
|
* pcl-cvs.texinfo (Customization): Document it.
|
||||||
|
|
||||||
|
* pcl-cvs.el (cvs-execute-single-file-list): Remove &rest before
|
||||||
|
last argument. No callers needed updating.
|
||||||
|
|
||||||
|
* pcl-cvs.el (cvs-execute-list): Remove the &rest before the last
|
||||||
|
argument (constant-args). Update all callers of cvs-execute-list
|
||||||
|
to use the new calling convention.
|
||||||
|
* pcl-cvs.el (cvs-cvs-diff-flags): Now a list of strings instead
|
||||||
|
of a string.
|
||||||
|
* pcl-cvs.texinfo (Customization): Document the change to
|
||||||
|
cvs-cvs-diff-flags.
|
||||||
|
|
||||||
|
* Release 0.96.
|
||||||
|
|
||||||
|
* pcl-cvs.el (cvs-cvs-diff-flags): New variable.
|
||||||
|
* pcl-cvs.el (cvs-diff-cvs): Use it.
|
||||||
|
* pcl-cvs.texinfo (Customization, Viewing differences): Document it.
|
||||||
|
|
||||||
|
* pcl-cvs.el (cvs-use-temp-buffe): Don't switch to the temporary
|
||||||
|
buffer. Use display-buffer and set-buffer instead. This way
|
||||||
|
cvs-log, cvs-status, cvs-diff-cvs and friends don't select the
|
||||||
|
temporary buffer. The cursor will remain in the *cvs* buffer.
|
||||||
|
|
||||||
|
Sun Mar 22 21:50:18 1992 Per Cederqvist (ceder@robin)
|
||||||
|
|
||||||
|
* pcl-cvs.el (cvs-find-file, cvs-find-file-other-window): Don't
|
||||||
|
prompt when reading in a directory in dired.
|
||||||
|
|
||||||
|
* Makefile (pcl-cvs-$(VER)): Include pcl-cvs-startup.el in the
|
||||||
|
distribution.
|
||||||
|
|
||||||
|
* dist-makefile (pcl-cvs.dvi): Don't fail even if texindex does
|
||||||
|
not exist.
|
||||||
|
|
||||||
|
* pcl-cvs.texinfo (@setchapternewpage): Changed from 'off' to 'on'.
|
||||||
|
* pcl-cvs.texinfo (Variable index): Joined into function index.
|
||||||
|
* pcl-cvs.texinfo (Key index): add a description about the key.
|
||||||
|
* pcl-cvs.texinfo: Many other small changes.
|
||||||
|
|
||||||
|
Wed Mar 18 01:58:38 1992 Per Cederqvist (ceder@leopold)
|
||||||
|
|
||||||
|
* Use GNU General Public License version 2.
|
||||||
|
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
This text is copied from the TeXinfo manual for pcl-cvs.
|
||||||
|
|
||||||
|
Installation of the pcl-cvs program
|
||||||
|
===================================
|
||||||
|
|
||||||
|
1. Edit the file `Makefile' to reflect the situation at your site.
|
||||||
|
The only things you have to change is the definition of
|
||||||
|
`lispdir' and `infodir'. The elisp files will be copied to
|
||||||
|
`lispdir', and the info file to `infodir'.
|
||||||
|
|
||||||
|
2. Configure pcl-cvs.el
|
||||||
|
|
||||||
|
There are a couple of paths that you have to check to make
|
||||||
|
sure that they match you system. They appear early in the file
|
||||||
|
pcl-cvs.el.
|
||||||
|
|
||||||
|
*NOTE:* If your system is running emacs 18.57 or earlier
|
||||||
|
you MUST uncomment the line that says:
|
||||||
|
|
||||||
|
(setq delete-exited-processes nil)
|
||||||
|
|
||||||
|
Setting `delete-exited-processes' to `nil' works around a bug
|
||||||
|
in emacs that causes it to dump core. The bug was fixed in
|
||||||
|
emacs 18.58.
|
||||||
|
|
||||||
|
3. Type `make install' in the source directory. This will
|
||||||
|
byte-compile all `.el' files and copy both the `.el' and the
|
||||||
|
`.elc' into the directory you specified in step 1.
|
||||||
|
|
||||||
|
If you don't want to install the `.el' files but only the
|
||||||
|
`.elc' files (the byte-compiled files), you can type ``make
|
||||||
|
install_elc'' instead of ``make install''.
|
||||||
|
|
||||||
|
If you only want to create the compiled elisp files, but
|
||||||
|
don't want to install them, you can type `make elcfiles'
|
||||||
|
instead. This is what happens if you only type `make' without
|
||||||
|
parameters.
|
||||||
|
|
||||||
|
4. Edit the file `default.el' in your emacs lisp directory (usually
|
||||||
|
`/usr/gnu/emacs/lisp' or something similar) and enter the
|
||||||
|
contents of the file `pcl-cvs-startup.el' into it. It contains
|
||||||
|
a couple of `auto-load's that facilitates the use of pcl-cvs.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Installation of the on-line manual.
|
||||||
|
===================================
|
||||||
|
|
||||||
|
1. Create the info file `pcl-cvs' from `pcl-cvs.texinfo' by typing
|
||||||
|
`make info'. If you don't have the program `makeinfo' you can
|
||||||
|
get it by anonymous ftp from e.g. `ftp.gnu.ai.mit.edu' as
|
||||||
|
`pub/gnu/texinfo-2.14.tar.Z' (there might be a newer version
|
||||||
|
there when you read this), or you could use the preformatted
|
||||||
|
info file `pcl-cvs.info' that is included in the distribution
|
||||||
|
(type `cp pcl-cvs.info pcl-cvs').
|
||||||
|
|
||||||
|
2. Move the info file `pcl-cvs' to your standard info directory.
|
||||||
|
This might be called something like `/usr/gnu/emacs/info'.
|
||||||
|
|
||||||
|
3. Edit the file `dir' in the info directory and enter one line to
|
||||||
|
contain a pointer to the info file `pcl-cvs'. The line can, for
|
||||||
|
instance, look like this:
|
||||||
|
|
||||||
|
* Pcl-cvs: (pcl-cvs). An Emacs front-end to CVS.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
How to make typeset documentation from pcl-cvs.texinfo
|
||||||
|
======================================================
|
||||||
|
|
||||||
|
If you have TeX installed at your site, you can make a typeset
|
||||||
|
manual from `pcl-cvs.texinfo'.
|
||||||
|
|
||||||
|
1. Run TeX by typing ``make pcl-cvs.dvi''. You will not get the
|
||||||
|
indices unless you have the `texindex' program.
|
||||||
|
|
||||||
|
2. Convert the resulting device independent file `pcl-cvs.dvi' to a
|
||||||
|
form which your printer can output and print it. If you have a
|
||||||
|
postscript printer there is a program, `dvi2ps', which does.
|
||||||
|
There is also a program which comes together with TeX, `dvips',
|
||||||
|
which you can use.
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
# Makefile,v 1.2 1992/04/07 20:49:07 berliner Exp
|
||||||
|
# Makefile for pcl-cvs release 1.02.
|
||||||
|
# Copyright (C) 1992 Per Cederqvist
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
|
||||||
|
# This is the directory in which the ELFILES and ELCFILES will be
|
||||||
|
# installed.
|
||||||
|
|
||||||
|
lispdir = /usr/local/lib/elisp
|
||||||
|
|
||||||
|
# Where to install the info file.
|
||||||
|
|
||||||
|
prefix=/usr/local
|
||||||
|
infodir = $(prefix)/info
|
||||||
|
|
||||||
|
#
|
||||||
|
# The rest of this file should not need to be modified.
|
||||||
|
#
|
||||||
|
|
||||||
|
# Just in case...
|
||||||
|
SHELL = /bin/sh
|
||||||
|
|
||||||
|
ELFILES = pcl-cvs.el cookie.el elib-dll.el elib-node.el
|
||||||
|
ELCFILES = pcl-cvs.elc cookie.elc elib-dll.elc elib-node.elc
|
||||||
|
INFOFILES = pcl-cvs
|
||||||
|
TEXTMPS = pcl-cvs.aux pcl-cvs.log pcl-cvs.toc pcl-cvs.dvi pcl-cvs.cp \
|
||||||
|
pcl-cvs.fn pcl-cvs.vr pcl-cvs.tp pcl-cvs.ky pcl-cvs.pg \
|
||||||
|
pcl-cvs.cps pcl-cvs.fns pcl-cvs.kys pcl-cvs.pgs pcl-cvs.tps \
|
||||||
|
pcl-cvs.vrs
|
||||||
|
|
||||||
|
INSTALL = install
|
||||||
|
INSTALL_DATA = $(INSTALL)
|
||||||
|
|
||||||
|
elcfiles:
|
||||||
|
emacs -batch -l ./compile-all.el -f compile-pcl-cvs
|
||||||
|
|
||||||
|
all: elcfiles info
|
||||||
|
|
||||||
|
# Don't install the info file yet, since it requires makeinfo
|
||||||
|
# version 2.something (and version 1.something is distributed with emacs).
|
||||||
|
#
|
||||||
|
# install: install_elc install_info
|
||||||
|
install: install_elc
|
||||||
|
for i in $(ELFILES); do $(INSTALL_DATA) $$i $(lispdir)/$$i; done
|
||||||
|
|
||||||
|
install_elc: elcfiles
|
||||||
|
for i in $(ELCFILES); do $(INSTALL_DATA) $$i $(lispdir)/$$i; done
|
||||||
|
|
||||||
|
install_info: pcl-cvs
|
||||||
|
$(INSTALL_DATA) pcl-cvs $(infodir)/pcl-cvs
|
||||||
|
|
||||||
|
info pcl-cvs: pcl-cvs.texinfo
|
||||||
|
makeinfo +fill-column=70 pcl-cvs.texinfo
|
||||||
|
|
||||||
|
pcl-cvs.dvi: pcl-cvs.texinfo
|
||||||
|
tex pcl-cvs.texinfo
|
||||||
|
-texindex pcl-cvs.cp pcl-cvs.fn pcl-cvs.vr pcl-cvs.tp pcl-cvs.ky \
|
||||||
|
pcl-cvs.pg
|
||||||
|
tex pcl-cvs.texinfo
|
||||||
|
|
||||||
|
mostlyclean clean realclean:
|
||||||
|
rm -f *~ core $(ELCFILES) $(INFOFILES) $(TEXTMPS)
|
||||||
|
|
||||||
|
tags TAGS:
|
||||||
|
etags *.el
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
README,v 1.2 1992/04/07 20:49:09 berliner Exp
|
||||||
|
|
||||||
|
This is the readme file for pcl-cvs, release 1.02.
|
||||||
|
|
||||||
|
Pcl-cvs is a front-end to CVS version 1.3. It integrates the most
|
||||||
|
frequently used CVS commands into emacs.
|
||||||
|
|
||||||
|
There is some configuration that needs to be done in pcl-cvs.el to get
|
||||||
|
it to work. See the instructions in file INSTALL.
|
||||||
|
|
||||||
|
Full documentation is in pcl-cvs.texinfo. Since it requires makeinfo
|
||||||
|
2.14 a preformatted info file is also included (pcl-cvs.info).
|
||||||
|
|
||||||
|
ceder@lysator.liu.se
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
;;;; compile-all.el,v 1.2 1992/04/07 20:49:10 berliner Exp
|
||||||
|
;;;; This file byte-compiles all .el files in pcl-cvs release 1.02.
|
||||||
|
;;;;
|
||||||
|
;;;; Copyright (C) 1991 Inge Wallin
|
||||||
|
;;;;
|
||||||
|
;;;; This file is part of the GNU Emacs lisp library, Elib.
|
||||||
|
;;;;
|
||||||
|
;;;; GNU Elib is free software; you can redistribute it and/or modify
|
||||||
|
;;;; it under the terms of the GNU General Public License as published by
|
||||||
|
;;;; the Free Software Foundation; either version 1, or (at your option)
|
||||||
|
;;;; any later version.
|
||||||
|
;;;;
|
||||||
|
;;;; GNU Elib is distributed in the hope that it will be useful,
|
||||||
|
;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
;;;; GNU General Public License for more details.
|
||||||
|
;;;;
|
||||||
|
;;;; You should have received a copy of the GNU General Public License
|
||||||
|
;;;; along with GNU Emacs; see the file COPYING. If not, write to
|
||||||
|
;;;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
;;;;
|
||||||
|
|
||||||
|
|
||||||
|
(setq elib-files '("elib-node"
|
||||||
|
"elib-dll"
|
||||||
|
"cookie"
|
||||||
|
"pcl-cvs"))
|
||||||
|
|
||||||
|
|
||||||
|
(defun compile-file-if-necessary (file)
|
||||||
|
"Compile the Elib file FILE if necessary.
|
||||||
|
|
||||||
|
This is done if FILE.el is newer than FILE.elc or if FILE.elc doesn't exist."
|
||||||
|
(let ((el-name (concat file ".el"))
|
||||||
|
(elc-name (concat file ".elc")))
|
||||||
|
(if (or (not (file-exists-p elc-name))
|
||||||
|
(file-newer-than-file-p el-name elc-name))
|
||||||
|
(progn
|
||||||
|
(message (format "Byte-compiling %s..." el-name))
|
||||||
|
(byte-compile-file el-name)))))
|
||||||
|
|
||||||
|
|
||||||
|
(defun compile-pcl-cvs ()
|
||||||
|
"Byte-compile all uncompiled files of elib.
|
||||||
|
Be sure to have . in load-path since a number of files in elib
|
||||||
|
depend on other files and we always want the newer one even if
|
||||||
|
a previous version of elib exists."
|
||||||
|
|
||||||
|
(interactive)
|
||||||
|
(setq load-path (append '(".") load-path))
|
||||||
|
(mapcar (function compile-file-if-necessary)
|
||||||
|
elib-files))
|
||||||
@@ -0,0 +1,884 @@
|
|||||||
|
;;; cookie.el,v 1.2 1992/04/07 20:49:12 berliner Exp
|
||||||
|
;;; cookie.el -- Utility to display cookies in buffers
|
||||||
|
;;; Copyright (C) 1991, 1992 Per Cederqvist
|
||||||
|
;;;
|
||||||
|
;;; This program is free software; you can redistribute it and/or modify
|
||||||
|
;;; it under the terms of the GNU General Public License as published by
|
||||||
|
;;; the Free Software Foundation; either version 2 of the License, or
|
||||||
|
;;; (at your option) any later version.
|
||||||
|
;;;
|
||||||
|
;;; This program is distributed in the hope that it will be useful,
|
||||||
|
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
;;; GNU General Public License for more details.
|
||||||
|
;;;
|
||||||
|
;;; You should have received a copy of the GNU General Public License
|
||||||
|
;;; along with this program; if not, write to the Free Software
|
||||||
|
;;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
|
||||||
|
;;;; TO-DO: Byt namn! tin -> wrapper (eller n}got b{ttre).
|
||||||
|
|
||||||
|
;;; Note that this file is still under development. Comments,
|
||||||
|
;;; enhancements and bug fixes are welcome.
|
||||||
|
;;; Send them to ceder@lysator.liu.se.
|
||||||
|
|
||||||
|
(defun impl nil (error "Not yet implemented!"))
|
||||||
|
|
||||||
|
;;; Cookie is a package that imlements a connection between an
|
||||||
|
;;; elib-dll and the contents of a buffer. Possible uses are dired
|
||||||
|
;;; (have all files in a list, and show them), buffer-list,
|
||||||
|
;;; kom-prioritize (in the LysKOM elisp client) and others. pcl-cvs.el
|
||||||
|
;;; uses cookie.el.
|
||||||
|
;;;
|
||||||
|
;;; A cookie buffer contains a header, any number of cookies, and a
|
||||||
|
;;; footer. The header and footer are constant strings that are given
|
||||||
|
;;; to cookie-create when the buffer is placed under cookie. Each cookie
|
||||||
|
;;; is displayed in the buffer by calling a user-supplied function
|
||||||
|
;;; that takes a cookie and returns a string. The string may be
|
||||||
|
;;; empty, or contain any number of lines. An extra newline is always
|
||||||
|
;;; appended unless the string is empty.
|
||||||
|
;;;
|
||||||
|
;;; Cookie does not affect the mode of the buffer in any way. It
|
||||||
|
;;; merely makes it easy to connect an underlying data representation
|
||||||
|
;;; to the buffer contents.
|
||||||
|
;;;
|
||||||
|
;;; The cookie-node data type:
|
||||||
|
;;; start-marker
|
||||||
|
;;; ;; end-marker This field is no longer present.
|
||||||
|
;;; cookie The user-supplied element.
|
||||||
|
;;;
|
||||||
|
;;; A dll of cookie-nodes are held in the buffer local variable
|
||||||
|
;;; cake-tin.
|
||||||
|
;;;
|
||||||
|
;;; A tin is an object that contains one cookie. You can get the next
|
||||||
|
;;; and previous tin.
|
||||||
|
;;;
|
||||||
|
|
||||||
|
(require 'elib-dll)
|
||||||
|
(provide 'cookie)
|
||||||
|
|
||||||
|
(defvar cookies nil
|
||||||
|
"A doubly linked list that contains the underlying data representation
|
||||||
|
for the contents of a cookie buffer. The package elib-dll is used to
|
||||||
|
manipulate this list.")
|
||||||
|
|
||||||
|
(defvar cookie-pretty-printer nil
|
||||||
|
"The function that is used to pretty-print a cookie in this buffer.")
|
||||||
|
|
||||||
|
(defvar cookie-header nil
|
||||||
|
"The tin that holds the header cookie.")
|
||||||
|
|
||||||
|
(defvar cookie-footer nil
|
||||||
|
"The tin that holds the footer cookie.")
|
||||||
|
|
||||||
|
(defvar cookie-last-tin nil
|
||||||
|
"The tin the cursor was positioned at, the last time the cookie
|
||||||
|
package checked the cursor position. Buffer local in all buffers
|
||||||
|
the cookie package works on. You may set this if your package
|
||||||
|
thinks it knows where the cursor will be the next time this
|
||||||
|
package is called. It can speed things up.
|
||||||
|
|
||||||
|
It must never be set to a tin that has been deleted.")
|
||||||
|
|
||||||
|
;;; ================================================================
|
||||||
|
;;; Internal functions for use in the cookie package
|
||||||
|
|
||||||
|
(put 'cookie-set-buffer 'lisp-indent-hook 1)
|
||||||
|
|
||||||
|
(defmacro cookie-set-buffer (buffer &rest forms)
|
||||||
|
|
||||||
|
;; Execute FORMS with BUFFER selected as current buffer.
|
||||||
|
;; Return value of last form in FORMS. INTERNAL USE ONLY.
|
||||||
|
|
||||||
|
(let ((old-buffer (make-symbol "old-buffer")))
|
||||||
|
(` (let (((, old-buffer) (current-buffer)))
|
||||||
|
(set-buffer (get-buffer-create (, buffer)))
|
||||||
|
(unwind-protect
|
||||||
|
(progn (,@ forms))
|
||||||
|
(set-buffer (, old-buffer)))))))
|
||||||
|
|
||||||
|
|
||||||
|
(defmacro cookie-filter-hf (tin)
|
||||||
|
|
||||||
|
;; Evaluate TIN once and return it. BUT if it is
|
||||||
|
;; equal to cookie-header or cookie-footer return nil instead.
|
||||||
|
;; INTERNAL USE ONLY.
|
||||||
|
|
||||||
|
(let ((tempvar (make-symbol "tin")))
|
||||||
|
(` (let (((, tempvar) (, tin)))
|
||||||
|
(if (or (eq (, tempvar) cookie-header)
|
||||||
|
(eq (, tempvar) cookie-footer))
|
||||||
|
nil
|
||||||
|
(, tempvar))))))
|
||||||
|
|
||||||
|
|
||||||
|
;;; cookie-tin
|
||||||
|
;;; Constructor:
|
||||||
|
|
||||||
|
(defun cookie-create-tin (start-marker
|
||||||
|
cookie)
|
||||||
|
;; Create a tin. INTERNAL USE ONLY.
|
||||||
|
(cons 'COOKIE-TIN (vector start-marker nil cookie)))
|
||||||
|
|
||||||
|
|
||||||
|
;;; Selectors:
|
||||||
|
|
||||||
|
(defun cookie-tin-start-marker (cookie-tin)
|
||||||
|
;; Get start-marker from cookie-tin. INTERNAL USE ONLY.
|
||||||
|
(elt (cdr cookie-tin) 0))
|
||||||
|
|
||||||
|
;(defun cookie-tin-end-marker (cookie-tin)
|
||||||
|
; ;;Get end-marker from cookie-tin. INTERNAL USE ONLY.
|
||||||
|
; (elt (cdr cookie-tin) 1))
|
||||||
|
|
||||||
|
(defun cookie-tin-cookie-safe (cookie-tin)
|
||||||
|
;; Get cookie from cookie-tin. INTERNAL USE ONLY.
|
||||||
|
;; Returns nil if given nil as input.
|
||||||
|
;; This is the same as cookie-tin-cookie in version 18.57
|
||||||
|
;; of emacs, but elt should signal an error when given nil
|
||||||
|
;; as input (according to the info files).
|
||||||
|
(elt (cdr cookie-tin) 2))
|
||||||
|
|
||||||
|
(defun cookie-tin-cookie (cookie-tin)
|
||||||
|
;; Get cookie from cookie-tin. INTERNAL USE ONLY.
|
||||||
|
(elt (cdr cookie-tin) 2))
|
||||||
|
|
||||||
|
|
||||||
|
;;; Modifiers:
|
||||||
|
|
||||||
|
(defun set-cookie-tin-start-marker (cookie-tin newval)
|
||||||
|
;; Set start-marker in cookie-tin to NEWVAL. INTERNAL USE ONLY.
|
||||||
|
(aset (cdr cookie-tin) 0 newval))
|
||||||
|
|
||||||
|
;(defun set-cookie-tin-end-marker (cookie-tin newval)
|
||||||
|
; ;; Set end-marker in cookie-tin to NEWVAL. INTERNAL USE ONLY.
|
||||||
|
; (aset (cdr cookie-tin) 1 newval))
|
||||||
|
|
||||||
|
(defun set-cookie-tin-cookie (cookie-tin newval)
|
||||||
|
;; Set cookie in cookie-tin to NEWVAL. INTERNAL USE ONLY.
|
||||||
|
(aset (cdr cookie-tin) 2 newval))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
;;; Predicate:
|
||||||
|
|
||||||
|
(defun cookie-tin-p (object)
|
||||||
|
;; Return t if OBJECT is a tin. INTERNAL USE ONLY.
|
||||||
|
(eq (car-safe object) 'COOKIE-TIN))
|
||||||
|
|
||||||
|
;;; end of cookie-tin data type.
|
||||||
|
|
||||||
|
|
||||||
|
(defun cookie-create-tin-and-insert (cookie string pos)
|
||||||
|
;; Insert STRING at POS in current buffer. Remember start
|
||||||
|
;; position. Create a tin containing them and the COOKIE.
|
||||||
|
;; INTERNAL USE ONLY.
|
||||||
|
|
||||||
|
(save-excursion
|
||||||
|
(goto-char pos)
|
||||||
|
;; Remember the position as a number so that it doesn't move
|
||||||
|
;; when we insert the string.
|
||||||
|
(let ((start (if (markerp pos)
|
||||||
|
(marker-position pos)
|
||||||
|
pos)))
|
||||||
|
;; Use insert-before-markers so that the marker for the
|
||||||
|
;; next cookie is updated.
|
||||||
|
(insert-before-markers string)
|
||||||
|
(insert-before-markers ?\n)
|
||||||
|
(cookie-create-tin (copy-marker start) cookie))))
|
||||||
|
|
||||||
|
|
||||||
|
(defun cookie-delete-tin-internal (tin)
|
||||||
|
;; Delete a cookie from the buffer. INTERNAL USE ONLY.
|
||||||
|
;; Can not be used on the footer.
|
||||||
|
(delete-region (cookie-tin-start-marker (dll-element cookies tin))
|
||||||
|
(cookie-tin-start-marker
|
||||||
|
(dll-element cookies
|
||||||
|
(dll-next cookies tin)))))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(defun cookie-refresh-tin (tin)
|
||||||
|
;; Redisplay the cookie represented by TIN. INTERNAL USE ONLY.
|
||||||
|
;; Can not be used on the footer.
|
||||||
|
|
||||||
|
(save-excursion
|
||||||
|
;; First, remove the string:
|
||||||
|
(delete-region (cookie-tin-start-marker (dll-element cookies tin))
|
||||||
|
(1- (marker-position
|
||||||
|
(cookie-tin-start-marker
|
||||||
|
(dll-element cookies
|
||||||
|
(dll-next cookies tin))))))
|
||||||
|
|
||||||
|
;; Calculate and insert the string.
|
||||||
|
|
||||||
|
(goto-char (cookie-tin-start-marker (dll-element cookies tin)))
|
||||||
|
(insert
|
||||||
|
(funcall cookie-pretty-printer
|
||||||
|
(cookie-tin-cookie (dll-element cookies tin))))))
|
||||||
|
|
||||||
|
|
||||||
|
;;; ================================================================
|
||||||
|
;;; The public members of the cookie package
|
||||||
|
|
||||||
|
|
||||||
|
(defun cookie-cookie (buffer tin)
|
||||||
|
"Get the cookie from a TIN. Args: BUFFER TIN."
|
||||||
|
(cookie-set-buffer buffer
|
||||||
|
(cookie-tin-cookie (dll-element cookies tin))))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(defun cookie-create (buffer pretty-printer &optional header footer)
|
||||||
|
|
||||||
|
"Start to use the cookie package in BUFFER.
|
||||||
|
BUFFER may be a buffer or a buffer name. It is created if it does not exist.
|
||||||
|
Beware that the entire contents of the buffer will be erased.
|
||||||
|
PRETTY-PRINTER is a function that takes one cookie and returns a string
|
||||||
|
to be displayed in the buffer. The string may be empty. If it is not
|
||||||
|
empty a newline will be added automatically. It may span several lines.
|
||||||
|
Optional third argument HEADER is a string that will always be present
|
||||||
|
at the top of the buffer. HEADER should end with a newline. Optionaly
|
||||||
|
fourth argument FOOTER is similar, and will always be inserted at the
|
||||||
|
bottom of the buffer."
|
||||||
|
|
||||||
|
(cookie-set-buffer buffer
|
||||||
|
|
||||||
|
(erase-buffer)
|
||||||
|
|
||||||
|
(make-local-variable 'cookie-last-tin)
|
||||||
|
(make-local-variable 'cookie-pretty-printer)
|
||||||
|
(make-local-variable 'cookie-header)
|
||||||
|
(make-local-variable 'cookie-footer)
|
||||||
|
(make-local-variable 'cookies)
|
||||||
|
|
||||||
|
(setq cookie-last-tin nil)
|
||||||
|
(setq cookie-pretty-printer pretty-printer)
|
||||||
|
(setq cookies (dll-create))
|
||||||
|
|
||||||
|
(dll-enter-first cookies
|
||||||
|
(cookie-create-tin-and-insert
|
||||||
|
header header 0))
|
||||||
|
(setq cookie-header (dll-nth cookies 0))
|
||||||
|
|
||||||
|
(dll-enter-last cookies
|
||||||
|
(cookie-create-tin-and-insert
|
||||||
|
footer footer (point-max)))
|
||||||
|
(setq cookie-footer (dll-nth cookies -1))
|
||||||
|
|
||||||
|
(goto-char (point-min))
|
||||||
|
(forward-line 1)))
|
||||||
|
|
||||||
|
|
||||||
|
(defun cookie-set-header (buffer header)
|
||||||
|
"Change the header. Args: BUFFER HEADER."
|
||||||
|
(impl))
|
||||||
|
|
||||||
|
|
||||||
|
(defun cookie-set-footer (buffer header)
|
||||||
|
"Change the footer. Args: BUFFER FOOTER."
|
||||||
|
(impl))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(defun cookie-enter-first (buffer cookie)
|
||||||
|
"Enter a COOKIE first in BUFFER.
|
||||||
|
Args: BUFFER COOKIE."
|
||||||
|
|
||||||
|
(cookie-set-buffer buffer
|
||||||
|
|
||||||
|
;; It is always safe to insert an element after the first element,
|
||||||
|
;; because the header is always present. (dll-nth cookies 0) should
|
||||||
|
;; never return nil.
|
||||||
|
|
||||||
|
(dll-enter-after
|
||||||
|
cookies
|
||||||
|
(dll-nth cookies 0)
|
||||||
|
(cookie-create-tin-and-insert
|
||||||
|
cookie
|
||||||
|
(funcall cookie-pretty-printer cookie)
|
||||||
|
(cookie-tin-start-marker
|
||||||
|
(dll-element cookies (dll-nth cookies 1)))))))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(defun cookie-enter-last (buffer cookie)
|
||||||
|
"Enter a COOKIE last in BUFFER.
|
||||||
|
Args: BUFFER COOKIE."
|
||||||
|
|
||||||
|
(cookie-set-buffer buffer
|
||||||
|
|
||||||
|
;; Remember that the header and footer are always present. There
|
||||||
|
;; is no need to check if (dll-nth cookies -2) returns nil.
|
||||||
|
|
||||||
|
(dll-enter-before
|
||||||
|
cookies
|
||||||
|
(dll-nth cookies -1)
|
||||||
|
(cookie-create-tin-and-insert
|
||||||
|
cookie
|
||||||
|
(funcall cookie-pretty-printer cookie)
|
||||||
|
(cookie-tin-start-marker (dll-last cookies))))))
|
||||||
|
|
||||||
|
|
||||||
|
(defun cookie-enter-after (buffer node cookie)
|
||||||
|
(impl))
|
||||||
|
|
||||||
|
|
||||||
|
(defun cookie-enter-before (buffer node cookie)
|
||||||
|
(impl))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(defun cookie-next (buffer tin)
|
||||||
|
"Get the next tin. Args: BUFFER TIN.
|
||||||
|
Returns nil if TIN is nil or the last cookie."
|
||||||
|
(if tin
|
||||||
|
(cookie-set-buffer buffer
|
||||||
|
(cookie-filter-hf (dll-next cookies tin)))))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(defun cookie-previous (buffer tin)
|
||||||
|
"Get the previous tin. Args: BUFFER TIN.
|
||||||
|
Returns nil if TIN is nil or the first cookie."
|
||||||
|
(if tin
|
||||||
|
(cookie-set-buffer buffer
|
||||||
|
(cookie-filter-hf (dll-previous cookies tin)))))
|
||||||
|
|
||||||
|
|
||||||
|
(defun cookie-nth (buffer n)
|
||||||
|
|
||||||
|
"Return the Nth tin. Args: BUFFER N.
|
||||||
|
N counts from zero. Nil is returned if there is less than N cookies.
|
||||||
|
If N is negative, return the -(N+1)th last element.
|
||||||
|
Thus, (cookie-nth dll 0) returns the first node,
|
||||||
|
and (cookie-nth dll -1) returns the last node.
|
||||||
|
|
||||||
|
Use cookie-cookie to extract the cookie from the tin."
|
||||||
|
|
||||||
|
(cookie-set-buffer buffer
|
||||||
|
|
||||||
|
;; Skip the header (or footer, if n is negative).
|
||||||
|
(if (< n 0)
|
||||||
|
(setq n (1- n))
|
||||||
|
(setq n (1+ n)))
|
||||||
|
|
||||||
|
(cookie-filter-hf (dll-nth cookies n))))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(defun cookie-delete (buffer tin)
|
||||||
|
"Delete a cookie. Args: BUFFER TIN."
|
||||||
|
|
||||||
|
(cookie-set-buffer buffer
|
||||||
|
(if (eq cookie-last-tin tin)
|
||||||
|
(setq cookie-last-tin nil))
|
||||||
|
|
||||||
|
(cookie-delete-tin-internal tin)
|
||||||
|
(dll-delete cookies tin)))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(defun cookie-delete-first (buffer)
|
||||||
|
"Delete first cookie and return it. Args: BUFFER.
|
||||||
|
Returns nil if there is no cookie left."
|
||||||
|
|
||||||
|
(cookie-set-buffer buffer
|
||||||
|
|
||||||
|
;; We have to check that we do not try to delete the footer.
|
||||||
|
|
||||||
|
(let ((tin (dll-nth cookies 1))) ;Skip the header.
|
||||||
|
(if (eq tin cookie-footer)
|
||||||
|
nil
|
||||||
|
(cookie-delete-tin-internal tin)
|
||||||
|
(cookie-tin-cookie (dll-delete cookies tin))))))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(defun cookie-delete-last (buffer)
|
||||||
|
"Delete last cookie and return it. Args: BUFFER.
|
||||||
|
Returns nil if there is no cookie left."
|
||||||
|
|
||||||
|
(cookie-set-buffer buffer
|
||||||
|
|
||||||
|
;; We have to check that we do not try to delete the header.
|
||||||
|
|
||||||
|
(let ((tin (dll-nth cookies -2))) ;Skip the footer.
|
||||||
|
(if (eq tin cookie-header)
|
||||||
|
nil
|
||||||
|
(cookie-delete-tin-internal tin)
|
||||||
|
(cookie-tin-cookie (dll-delete cookies tin))))))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(defun cookie-first (buffer)
|
||||||
|
|
||||||
|
"Return the first cookie in BUFFER. The cookie is not removed."
|
||||||
|
|
||||||
|
(cookie-set-buffer buffer
|
||||||
|
(let ((tin (cookie-filter-hf (dll-nth cookies -1))))
|
||||||
|
(if tin
|
||||||
|
(cookie-tin-cookie-safe
|
||||||
|
(dll-element cookies tin))))))
|
||||||
|
|
||||||
|
|
||||||
|
(defun cookie-last (buffer)
|
||||||
|
|
||||||
|
"Return the last cookie in BUFFER. The cookie is not removed."
|
||||||
|
|
||||||
|
(cookie-set-buffer buffer
|
||||||
|
(let ((tin (cookie-filter-hf (dll-nth cookies -2))))
|
||||||
|
(if tin
|
||||||
|
(cookie-tin-cookie-safe
|
||||||
|
(dll-element cookies tin))))))
|
||||||
|
|
||||||
|
|
||||||
|
(defun cookie-empty (buffer)
|
||||||
|
|
||||||
|
"Return true if there are no cookies in BUFFER."
|
||||||
|
|
||||||
|
(cookie-set-buffer buffer
|
||||||
|
(eq (dll-nth cookies 1) cookie-footer)))
|
||||||
|
|
||||||
|
|
||||||
|
(defun cookie-length (buffer)
|
||||||
|
|
||||||
|
"Return number of cookies in BUFFER."
|
||||||
|
|
||||||
|
;; Don't count the footer and header.
|
||||||
|
|
||||||
|
(cookie-set-buffer buffer
|
||||||
|
(- (dll-length cookies) 2)))
|
||||||
|
|
||||||
|
|
||||||
|
(defun cookie-all (buffer)
|
||||||
|
|
||||||
|
"Return a list of all cookies in BUFFER."
|
||||||
|
|
||||||
|
(cookie-set-buffer buffer
|
||||||
|
(let (result
|
||||||
|
(tin (dll-nth cookies -2)))
|
||||||
|
(while (not (eq tin cookie-header))
|
||||||
|
(setq result (cons (cookie-tin-cookie (dll-element cookies tin))
|
||||||
|
result))
|
||||||
|
(setq tin (dll-previous cookies tin)))
|
||||||
|
result)))
|
||||||
|
|
||||||
|
(defun cookie-clear (buffer)
|
||||||
|
|
||||||
|
"Remove all cookies in buffer."
|
||||||
|
|
||||||
|
(cookie-set-buffer buffer
|
||||||
|
(cookie-create buffer cookie-pretty-printer
|
||||||
|
(cookie-tin-cookie (dll-element cookies cookie-header))
|
||||||
|
(cookie-tin-cookie (dll-element cookies cookie-footer)))))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(defun cookie-map (map-function buffer &rest map-args)
|
||||||
|
|
||||||
|
"Apply MAP-FUNCTION to all cookies in BUFFER.
|
||||||
|
MAP-FUNCTION is applied to the first element first.
|
||||||
|
If MAP-FUNCTION returns non-nil the cookie will be refreshed.
|
||||||
|
|
||||||
|
Note that BUFFER will be current buffer when MAP-FUNCTION is called.
|
||||||
|
|
||||||
|
If more than two arguments are given to cookie-map, remaining
|
||||||
|
arguments will be passed to MAP-FUNCTION."
|
||||||
|
|
||||||
|
(cookie-set-buffer buffer
|
||||||
|
(let ((tin (dll-nth cookies 1))
|
||||||
|
result)
|
||||||
|
|
||||||
|
(while (not (eq tin cookie-footer))
|
||||||
|
|
||||||
|
(if (apply map-function
|
||||||
|
(cookie-tin-cookie (dll-element cookies tin))
|
||||||
|
map-args)
|
||||||
|
(cookie-refresh-tin tin))
|
||||||
|
|
||||||
|
(setq tin (dll-next cookies tin))))))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(defun cookie-map-reverse (map-function buffer &rest map-args)
|
||||||
|
|
||||||
|
"Apply MAP-FUNCTION to all cookies in BUFFER.
|
||||||
|
MAP-FUNCTION is applied to the last cookie first.
|
||||||
|
If MAP-FUNCTION returns non-nil the cookie will be refreshed.
|
||||||
|
|
||||||
|
Note that BUFFER will be current buffer when MAP-FUNCTION is called.
|
||||||
|
|
||||||
|
If more than two arguments are given to cookie-map, remaining
|
||||||
|
arguments will be passed to MAP-FUNCTION."
|
||||||
|
|
||||||
|
(cookie-set-buffer buffer
|
||||||
|
(let ((tin (dll-nth cookies -2))
|
||||||
|
result)
|
||||||
|
|
||||||
|
(while (not (eq tin cookie-header))
|
||||||
|
|
||||||
|
(if (apply map-function
|
||||||
|
(cookie-tin-cookie (dll-element cookies tin))
|
||||||
|
map-args)
|
||||||
|
(cookie-refresh-tin tin))
|
||||||
|
|
||||||
|
(setq tin (dll-previous cookies tin))))))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(defun cookie-enter-cookies (buffer cookie-list)
|
||||||
|
|
||||||
|
"Insert all cookies in the list COOKIE-LIST last in BUFFER.
|
||||||
|
Args: BUFFER COOKIE-LIST."
|
||||||
|
|
||||||
|
(while cookie-list
|
||||||
|
(cookie-enter-last buffer (car cookie-list))
|
||||||
|
(setq cookie-list (cdr cookie-list))))
|
||||||
|
|
||||||
|
|
||||||
|
(defun cookie-filter (buffer predicate)
|
||||||
|
|
||||||
|
"Remove all cookies in BUFFER for which PREDICATE returns nil.
|
||||||
|
Note that BUFFER will be current-buffer when PREDICATE is called.
|
||||||
|
|
||||||
|
The PREDICATE is called with one argument, the cookie."
|
||||||
|
|
||||||
|
(cookie-set-buffer buffer
|
||||||
|
(let ((tin (dll-nth cookies 1))
|
||||||
|
next)
|
||||||
|
(while (not (eq tin cookie-footer))
|
||||||
|
(setq next (dll-next cookies tin))
|
||||||
|
(if (funcall predicate (cookie-tin-cookie (dll-element cookies tin)))
|
||||||
|
nil
|
||||||
|
(cookie-delete-tin-internal tin)
|
||||||
|
(dll-delete cookies tin))
|
||||||
|
(setq tin next)))))
|
||||||
|
|
||||||
|
|
||||||
|
(defun cookie-filter-tins (buffer predicate)
|
||||||
|
|
||||||
|
"Remove all cookies in BUFFER for which PREDICATE returns nil.
|
||||||
|
Note that BUFFER will be current-buffer when PREDICATE is called.
|
||||||
|
|
||||||
|
The PREDICATE is called with one argument, the tin."
|
||||||
|
|
||||||
|
(cookie-set-buffer buffer
|
||||||
|
(let ((tin (dll-nth cookies 1))
|
||||||
|
next)
|
||||||
|
(while (not (eq tin cookie-footer))
|
||||||
|
(setq next (dll-next cookies tin))
|
||||||
|
(if (funcall predicate tin)
|
||||||
|
nil
|
||||||
|
(cookie-delete-tin-internal tin)
|
||||||
|
(dll-delete cookies tin))
|
||||||
|
(setq tin next)))))
|
||||||
|
|
||||||
|
(defun cookie-pos-before-middle-p (pos tin1 tin2)
|
||||||
|
|
||||||
|
"Return true if POS is in the first half of the region defined by TIN1 and
|
||||||
|
TIN2."
|
||||||
|
|
||||||
|
(< pos (/ (+ (cookie-tin-start-marker (dll-element cookeis tin1))
|
||||||
|
(cookie-tin-start-marker (dll-element cookeis tin2)))
|
||||||
|
2)))
|
||||||
|
|
||||||
|
|
||||||
|
(defun cookie-get-selection (buffer pos &optional guess force-guess)
|
||||||
|
|
||||||
|
"Return the tin the POS is within.
|
||||||
|
Args: BUFFER POS &optional GUESS FORCE-GUESS.
|
||||||
|
GUESS should be a tin that it is likely that POS is near. If FORCE-GUESS
|
||||||
|
is non-nil GUESS is always used as a first guess, otherwise the first
|
||||||
|
guess is the first tin, last tin, or GUESS, whichever is nearest to
|
||||||
|
pos in the BUFFER.
|
||||||
|
|
||||||
|
If pos points within the header, the first cookie is returned.
|
||||||
|
If pos points within the footer, the last cookie is returned.
|
||||||
|
Nil is returned if there is no cookie.
|
||||||
|
|
||||||
|
It is often good to specify cookie-last-tin as GUESS, but remember
|
||||||
|
that cookie-last-tin is buffer local in all buffers that cookie
|
||||||
|
operates on."
|
||||||
|
|
||||||
|
(cookie-set-buffer buffer
|
||||||
|
|
||||||
|
(cond
|
||||||
|
; No cookies present?
|
||||||
|
((eq (dll-nth cookies 1) (dll-nth cookies -1))
|
||||||
|
nil)
|
||||||
|
|
||||||
|
; Before first cookie?
|
||||||
|
((< pos (cookie-tin-start-marker
|
||||||
|
(dll-element cookies (dll-nth cookies 1))))
|
||||||
|
(dll-nth cookies 1))
|
||||||
|
|
||||||
|
; After last cookie?
|
||||||
|
((>= pos (cookie-tin-start-marker (dll-last cookies)))
|
||||||
|
(dll-nth cookies -2))
|
||||||
|
|
||||||
|
; We now now that pos is within a cookie.
|
||||||
|
(t
|
||||||
|
; Make an educated guess about which of the three known
|
||||||
|
; cookies (the first, the last, or GUESS) is nearest.
|
||||||
|
(setq
|
||||||
|
guess
|
||||||
|
(cond
|
||||||
|
(force-guess guess)
|
||||||
|
(guess
|
||||||
|
(cond
|
||||||
|
;; Closest to first cookie?
|
||||||
|
((cookie-pos-before-middle-p
|
||||||
|
pos guess
|
||||||
|
(dll-nth cookies 1))
|
||||||
|
(dll-nth cookies 1))
|
||||||
|
;; Closest to GUESS?
|
||||||
|
((cookie-pos-before-middle-p
|
||||||
|
pos guess
|
||||||
|
cookie-footer)
|
||||||
|
guess)
|
||||||
|
;; Closest to last cookie.
|
||||||
|
(t (dll-previous cookies cookie-footer))))
|
||||||
|
(t
|
||||||
|
;; No guess given.
|
||||||
|
(cond
|
||||||
|
;; First half?
|
||||||
|
((cookie-pos-before-middle-p
|
||||||
|
pos (dll-nth cookies 1)
|
||||||
|
cookie-footer)
|
||||||
|
(dll-nth cookies 1))
|
||||||
|
(t (dll-previous cookies cookie-footer))))))
|
||||||
|
|
||||||
|
;; GUESS is now a "best guess".
|
||||||
|
|
||||||
|
;; Find the correct cookie. First determine in which direction
|
||||||
|
;; it lies, and then move in that direction until it is found.
|
||||||
|
|
||||||
|
(cond
|
||||||
|
;; Is pos after the guess?
|
||||||
|
((>= pos (cookie-tin-start-marker (dll-element cookiess guess)))
|
||||||
|
|
||||||
|
;; Loop until we are exactly one cookie too far down...
|
||||||
|
(while (>= pos (cookie-tin-start-marker (dll-element cookiess guess)))
|
||||||
|
(setq guess (dll-next cookies guess)))
|
||||||
|
|
||||||
|
;; ...and return the previous cookie.
|
||||||
|
(dll-previous cookies guess))
|
||||||
|
|
||||||
|
;; Pos is before guess
|
||||||
|
(t
|
||||||
|
|
||||||
|
(while (< pos (cookie-tin-start-marker (dll-element cookiess guess)))
|
||||||
|
(setq guess (dll-previous cookies guess)))
|
||||||
|
|
||||||
|
guess))))))
|
||||||
|
|
||||||
|
|
||||||
|
(defun cookie-start-marker (buffer tin)
|
||||||
|
|
||||||
|
"Return start-position of a cookie in BUFFER.
|
||||||
|
Args: BUFFER TIN.
|
||||||
|
The marker that is returned should not be modified in any way,
|
||||||
|
and is only valid until the contents of the cookie buffer changes."
|
||||||
|
|
||||||
|
(cookie-set-buffer buffer
|
||||||
|
(cookie-tin-start-marker (dll-element cookies tin))))
|
||||||
|
|
||||||
|
|
||||||
|
(defun cookie-end-marker (buffer tin)
|
||||||
|
|
||||||
|
"Return end-position of a cookie in BUFFER.
|
||||||
|
Args: BUFFER TIN.
|
||||||
|
The marker that is returned should not be modified in any way,
|
||||||
|
and is only valid until the contents of the cookie buffer changes."
|
||||||
|
|
||||||
|
(cookie-set-buffer buffer
|
||||||
|
(cookie-tin-start-marker
|
||||||
|
(dll-element cookies (dll-next cookies tin)))))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(defun cookie-refresh (buffer)
|
||||||
|
|
||||||
|
"Refresh all cookies in BUFFER.
|
||||||
|
Cookie-pretty-printer will be called for all cookies and the new result
|
||||||
|
displayed.
|
||||||
|
|
||||||
|
See also cookie-invalidate-tins."
|
||||||
|
|
||||||
|
(cookie-set-buffer buffer
|
||||||
|
|
||||||
|
(erase-buffer)
|
||||||
|
|
||||||
|
(set-marker (cookie-tin-start-marker (dll-element cookies cookie-header))
|
||||||
|
(point) buffer)
|
||||||
|
(insert (cookie-tin-cookie (dll-element cookies cookie-header)))
|
||||||
|
(insert "\n")
|
||||||
|
|
||||||
|
(let ((tin (dll-nth cookies 1)))
|
||||||
|
(while (not (eq tin cookie-footer))
|
||||||
|
|
||||||
|
(set-marker (cookie-tin-start-marker (dll-element cookies tin))
|
||||||
|
(point) buffer)
|
||||||
|
(insert
|
||||||
|
(funcall cookie-pretty-printer
|
||||||
|
(cookie-tin-cookie (dll-element cookies tin))))
|
||||||
|
(insert "\n")
|
||||||
|
(setq tin (dll-next cookies tin))))
|
||||||
|
|
||||||
|
(set-marker (cookie-tin-start-marker (dll-element cookies cookie-footer))
|
||||||
|
(point) buffer)
|
||||||
|
(insert (cookie-tin-cookie (dll-element cookies cookie-footer)))
|
||||||
|
(insert "\n")))
|
||||||
|
|
||||||
|
|
||||||
|
(defun cookie-invalidate-tins (buffer &rest tins)
|
||||||
|
|
||||||
|
"Refresh some cookies.
|
||||||
|
Args: BUFFER &rest TINS."
|
||||||
|
|
||||||
|
(cookie-set-buffer buffer
|
||||||
|
|
||||||
|
(while tins
|
||||||
|
(cookie-refresh-tin (car tins))
|
||||||
|
(setq tins (cdr tins)))))
|
||||||
|
|
||||||
|
|
||||||
|
;;; Cookie movement commands.
|
||||||
|
|
||||||
|
(defun cookie-set-goal-column (buffer goal)
|
||||||
|
"Set goal-column for BUFFER.
|
||||||
|
Args: BUFFER GOAL.
|
||||||
|
goal-column is made buffer-local."
|
||||||
|
(cookie-set-buffer buffer
|
||||||
|
(make-local-variable 'goal-column)
|
||||||
|
(setq goal-column goal)))
|
||||||
|
|
||||||
|
|
||||||
|
(defun cookie-previous-cookie (buffer pos arg)
|
||||||
|
"Move point to the ARGth previous cookie.
|
||||||
|
Don't move if we are at the first cookie.
|
||||||
|
ARG is the prefix argument when called interactively.
|
||||||
|
Args: BUFFER POS ARG.
|
||||||
|
Sets cookie-last-tin to the cookie we move to."
|
||||||
|
|
||||||
|
(interactive (list (current-buffer) (point)
|
||||||
|
(prefix-numeric-value current-prefix-arg)))
|
||||||
|
|
||||||
|
(cookie-set-buffer buffer
|
||||||
|
(setq cookie-last-tin
|
||||||
|
(cookie-get-selection buffer pos cookie-last-tin))
|
||||||
|
|
||||||
|
(while (and cookie-last-tin (> arg 0))
|
||||||
|
(setq arg (1- arg))
|
||||||
|
(setq cookie-last-tin
|
||||||
|
(dll-previous cookies cookie-last-tin)))
|
||||||
|
|
||||||
|
;; Never step above the first cookie.
|
||||||
|
|
||||||
|
(if (null (cookie-filter-hf cookie-last-tin))
|
||||||
|
(setq cookie-last-tin (dll-nth cookies 1)))
|
||||||
|
|
||||||
|
(goto-char
|
||||||
|
(cookie-tin-start-marker
|
||||||
|
(dll-element cookies cookie-last-tin)))
|
||||||
|
|
||||||
|
(if goal-column
|
||||||
|
(move-to-column goal-column))))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(defun cookie-next-cookie (buffer pos arg)
|
||||||
|
"Move point to the ARGth next cookie.
|
||||||
|
Don't move if we are at the last cookie.
|
||||||
|
ARG is the prefix argument when called interactively.
|
||||||
|
Args: BUFFER POS ARG.
|
||||||
|
Sets cookie-last-tin to the cookie we move to."
|
||||||
|
|
||||||
|
(interactive (list (current-buffer) (point)
|
||||||
|
(prefix-numeric-value current-prefix-arg)))
|
||||||
|
|
||||||
|
(cookie-set-buffer buffer
|
||||||
|
(setq cookie-last-tin
|
||||||
|
(cookie-get-selection buffer pos cookie-last-tin))
|
||||||
|
|
||||||
|
(while (and cookie-last-tin (> arg 0))
|
||||||
|
(setq arg (1- arg))
|
||||||
|
(setq cookie-last-tin
|
||||||
|
(dll-next cookies cookie-last-tin)))
|
||||||
|
|
||||||
|
(if (null (cookie-filter-hf cookie-last-tin))
|
||||||
|
(setq cookie-last-tin (dll-nth cookies -2)))
|
||||||
|
|
||||||
|
(goto-char
|
||||||
|
(cookie-tin-start-marker
|
||||||
|
(dll-element cookies cookie-last-tin)))
|
||||||
|
|
||||||
|
(if goal-column
|
||||||
|
(move-to-column goal-column))))
|
||||||
|
|
||||||
|
|
||||||
|
(defun cookie-collect-tins (buffer predicate &rest predicate-args)
|
||||||
|
|
||||||
|
"Return a list of all tins in BUFFER whose cookie PREDICATE
|
||||||
|
returns true for.
|
||||||
|
PREDICATE is a function that takes a cookie as its argument.
|
||||||
|
The tins on the returned list will appear in the same order
|
||||||
|
as in the buffer. You should not rely on in which order PREDICATE
|
||||||
|
is called. Note that BUFFER is current-buffer when PREDICATE
|
||||||
|
is called. (If you call cookie-collect with another buffer set
|
||||||
|
as current-buffer and need to access buffer-local variables
|
||||||
|
from that buffer within PREDICATE you must send them via
|
||||||
|
PREDICATE-ARGS).
|
||||||
|
|
||||||
|
If more than two arguments are given to cookie-collect the remaining
|
||||||
|
arguments will be passed to PREDICATE.
|
||||||
|
|
||||||
|
Use cookie-cookie to get the cookie from the tin."
|
||||||
|
|
||||||
|
(cookie-set-buffer buffer
|
||||||
|
(let ((tin (dll-nth cookies -2))
|
||||||
|
result)
|
||||||
|
|
||||||
|
(while (not (eq tin cookie-header))
|
||||||
|
|
||||||
|
(if (apply predicate
|
||||||
|
(cookie-tin-cookie (dll-element cookies tin))
|
||||||
|
predicate-args)
|
||||||
|
(setq result (cons tin result)))
|
||||||
|
|
||||||
|
(setq tin (dll-previous cookies tin)))
|
||||||
|
result)))
|
||||||
|
|
||||||
|
|
||||||
|
(defun cookie-collect-cookies (buffer predicate &rest predicate-args)
|
||||||
|
|
||||||
|
"Return a list of all cookies in BUFFER that PREDICATE
|
||||||
|
returns true for.
|
||||||
|
PREDICATE is a function that takes a cookie as its argument.
|
||||||
|
The cookie on the returned list will appear in the same order
|
||||||
|
as in the buffer. You should not rely on in which order PREDICATE
|
||||||
|
is called. Note that BUFFER is current-buffer when PREDICATE
|
||||||
|
is called. (If you call cookie-collect with another buffer set
|
||||||
|
as current-buffer and need to access buffer-local variables
|
||||||
|
from that buffer within PREDICATE you must send them via
|
||||||
|
PREDICATE-ARGS).
|
||||||
|
|
||||||
|
If more than two arguments are given to cookie-collect the remaining
|
||||||
|
arguments will be passed to PREDICATE."
|
||||||
|
|
||||||
|
(cookie-set-buffer buffer
|
||||||
|
(let ((tin (dll-nth cookies -2))
|
||||||
|
result)
|
||||||
|
|
||||||
|
(while (not (eq tin cookie-header))
|
||||||
|
|
||||||
|
(if (apply predicate
|
||||||
|
(cookie-tin-cookie (dll-element cookies tin))
|
||||||
|
predicate-args)
|
||||||
|
(setq result (cons (cookie-tin-cookie (dll-element cookies tin))
|
||||||
|
result)))
|
||||||
|
|
||||||
|
(setq tin (dll-previous cookies tin)))
|
||||||
|
result)))
|
||||||
@@ -0,0 +1,298 @@
|
|||||||
|
;;; elib-dll-debug -- A slow implementation of elib-dll for debugging.
|
||||||
|
;;; elib-dll-debug.el,v 1.2 1992/04/07 20:49:13 berliner Exp
|
||||||
|
;;; Copyright (C) 1991,1992 Per Cederqvist
|
||||||
|
;;;
|
||||||
|
;;; This program is free software; you can redistribute it and/or modify
|
||||||
|
;;; it under the terms of the GNU General Public License as published by
|
||||||
|
;;; the Free Software Foundation; either version 2 of the License, or
|
||||||
|
;;; (at your option) any later version.
|
||||||
|
;;;
|
||||||
|
;;; This program is distributed in the hope that it will be useful,
|
||||||
|
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
;;; GNU General Public License for more details.
|
||||||
|
;;;
|
||||||
|
;;; You should have received a copy of the GNU General Public License
|
||||||
|
;;; along with this program; if not, write to the Free Software
|
||||||
|
;;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
|
||||||
|
|
||||||
|
;;; This is a plug-in replacement for elib-dll.el. It is dreadfully
|
||||||
|
;;; slow, but it facilitates debugging. Don't trust the comments in
|
||||||
|
;;; this file too much.
|
||||||
|
(provide 'elib-dll)
|
||||||
|
|
||||||
|
;;;
|
||||||
|
;;; A doubly linked list consists of one cons cell which holds the tag
|
||||||
|
;;; 'DL-LIST in the car cell and the list in the cdr
|
||||||
|
;;; cell. The doubly linked list is implemented as a normal list. You
|
||||||
|
;;; should use elib-dll.el and not this package in debugged code. This
|
||||||
|
;;; package is not written for speed...
|
||||||
|
;;;
|
||||||
|
|
||||||
|
;;; ================================================================
|
||||||
|
;;; Internal functions for use in the doubly linked list package
|
||||||
|
|
||||||
|
(defun dll-get-dummy-node (dll)
|
||||||
|
|
||||||
|
;; Return the dummy node. INTERNAL USE ONLY.
|
||||||
|
dll)
|
||||||
|
|
||||||
|
(defun dll-list-nodes (dll)
|
||||||
|
|
||||||
|
;; Return a list of all nodes in DLL. INTERNAL USE ONLY.
|
||||||
|
|
||||||
|
(cdr dll))
|
||||||
|
|
||||||
|
(defun dll-set-from-node-list (dll list)
|
||||||
|
|
||||||
|
;; Set the contents of DLL to the nodes in LIST.
|
||||||
|
;; INTERNAL USE ONLY.
|
||||||
|
|
||||||
|
(setcdr dll list))
|
||||||
|
|
||||||
|
(defun dll-get-node-before (dll node)
|
||||||
|
;; Return the node in DLL that points to NODE. Use
|
||||||
|
;; (dll-get-node-before some-list nil) to get the last node.
|
||||||
|
;; INTERNAL USE ONLY.
|
||||||
|
(while (and dll (not (eq (cdr dll) node)))
|
||||||
|
(setq dll (cdr dll)))
|
||||||
|
(if (not dll)
|
||||||
|
(error "Node not on list"))
|
||||||
|
dll)
|
||||||
|
|
||||||
|
(defmacro dll-insert-after (node element)
|
||||||
|
(let ((node-v (make-symbol "node"))
|
||||||
|
(element-v (make-symbol "element")))
|
||||||
|
(` (let (((, node-v) (, node))
|
||||||
|
((, element-v) (, element)))
|
||||||
|
(setcdr (, node-v) (cons (, element-v) (cdr (, node-v))))))))
|
||||||
|
|
||||||
|
;;; ===================================================================
|
||||||
|
;;; The public functions which operate on doubly linked lists.
|
||||||
|
|
||||||
|
(defmacro dll-element (dll node)
|
||||||
|
|
||||||
|
"Get the element of a NODE in a doubly linked list DLL.
|
||||||
|
Args: DLL NODE."
|
||||||
|
|
||||||
|
(` (car (, node))))
|
||||||
|
|
||||||
|
|
||||||
|
(defun dll-create ()
|
||||||
|
"Create an empty doubly linked list."
|
||||||
|
(cons 'DL-LIST nil))
|
||||||
|
|
||||||
|
|
||||||
|
(defun dll-p (object)
|
||||||
|
"Return t if OBJECT is a doubly linked list, otherwise return nil."
|
||||||
|
(eq (car-safe object) 'DL-LIST))
|
||||||
|
|
||||||
|
|
||||||
|
(defun dll-enter-first (dll element)
|
||||||
|
"Add an element first on a doubly linked list.
|
||||||
|
Args: DLL ELEMENT."
|
||||||
|
(setcdr dll (cons element (cdr dll))))
|
||||||
|
|
||||||
|
|
||||||
|
(defun dll-enter-last (dll element)
|
||||||
|
"Add an element last on a doubly linked list.
|
||||||
|
Args: DLL ELEMENT."
|
||||||
|
(dll-insert-after (dll-get-node-before dll nil) element))
|
||||||
|
|
||||||
|
|
||||||
|
(defun dll-enter-after (dll node element)
|
||||||
|
"In the doubly linked list DLL, insert a node containing ELEMENT after NODE.
|
||||||
|
Args: DLL NODE ELEMENT."
|
||||||
|
|
||||||
|
(dll-get-node-before dll node)
|
||||||
|
(dll-insert-after node element))
|
||||||
|
|
||||||
|
|
||||||
|
(defun dll-enter-before (dll node element)
|
||||||
|
"In the doubly linked list DLL, insert a node containing ELEMENT before NODE.
|
||||||
|
Args: DLL NODE ELEMENT."
|
||||||
|
|
||||||
|
(dll-insert-after (dll-get-node-before dll node) element))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(defun dll-next (dll node)
|
||||||
|
"Return the node after NODE, or nil if NODE is the last node.
|
||||||
|
Args: DLL NODE."
|
||||||
|
|
||||||
|
(dll-get-node-before dll node)
|
||||||
|
(cdr node))
|
||||||
|
|
||||||
|
|
||||||
|
(defun dll-previous (dll node)
|
||||||
|
"Return the node before NODE, or nil if NODE is the first node.
|
||||||
|
Args: DLL NODE."
|
||||||
|
|
||||||
|
(dll-get-node-before dll node))
|
||||||
|
|
||||||
|
|
||||||
|
(defun dll-delete (dll node)
|
||||||
|
|
||||||
|
"Delete NODE from the doubly linked list DLL.
|
||||||
|
Args: DLL NODE. Return the element of node."
|
||||||
|
|
||||||
|
;; This is a no-op when applied to the dummy node. This will return
|
||||||
|
;; nil if applied to the dummy node since it always contains nil.
|
||||||
|
|
||||||
|
(setcdr (dll-get-node-before dll node) (cdr node)))
|
||||||
|
|
||||||
|
|
||||||
|
(defun dll-delete-first (dll)
|
||||||
|
|
||||||
|
"Delete the first NODE from the doubly linked list DLL.
|
||||||
|
Return the element. Args: DLL. Returns nil if the DLL was empty."
|
||||||
|
|
||||||
|
;; Relies on the fact that dll-delete does nothing and
|
||||||
|
;; returns nil if given the dummy node.
|
||||||
|
|
||||||
|
(setcdr dll (cdr (cdr dll))))
|
||||||
|
|
||||||
|
|
||||||
|
(defun dll-delete-last (dll)
|
||||||
|
|
||||||
|
"Delete the last NODE from the doubly linked list DLL.
|
||||||
|
Return the element. Args: DLL. Returns nil if the DLL was empty."
|
||||||
|
|
||||||
|
;; Relies on the fact that dll-delete does nothing and
|
||||||
|
;; returns nil if given the dummy node.
|
||||||
|
|
||||||
|
(setcdr dll (dll-get-node-before dll nil) nil))
|
||||||
|
|
||||||
|
|
||||||
|
(defun dll-first (dll)
|
||||||
|
|
||||||
|
"Return the first element on the doubly linked list DLL.
|
||||||
|
Return nil if the list is empty. The element is not removed."
|
||||||
|
|
||||||
|
(car (cdr dll)))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(defun dll-last (dll)
|
||||||
|
|
||||||
|
"Return the last element on the doubly linked list DLL.
|
||||||
|
Return nil if the list is empty. The element is not removed."
|
||||||
|
|
||||||
|
(car (dll-get-node-before dll nil)))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(defun dll-nth (dll n)
|
||||||
|
|
||||||
|
"Return the Nth node from the doubly linked list DLL.
|
||||||
|
Args: DLL N
|
||||||
|
N counts from zero. If DLL is not that long, nil is returned.
|
||||||
|
If N is negative, return the -(N+1)th last element.
|
||||||
|
Thus, (dll-nth dll 0) returns the first node,
|
||||||
|
and (dll-nth dll -1) returns the last node."
|
||||||
|
|
||||||
|
;; Branch 0 ("follow left pointer") is used when n is negative.
|
||||||
|
;; Branch 1 ("follow right pointer") is used otherwise.
|
||||||
|
|
||||||
|
(if (>= n 0)
|
||||||
|
(nthcdr n (cdr dll))
|
||||||
|
(unwind-protect
|
||||||
|
(progn (setcdr dll (nreverse (cdr dll)))
|
||||||
|
(nthcdr (- n) dll))
|
||||||
|
(setcdr dll (nreverse (cdr dll))))))
|
||||||
|
|
||||||
|
(defun dll-empty (dll)
|
||||||
|
|
||||||
|
"Return t if the doubly linked list DLL is empty, nil otherwise"
|
||||||
|
|
||||||
|
(not (cdr dll)))
|
||||||
|
|
||||||
|
(defun dll-length (dll)
|
||||||
|
|
||||||
|
"Returns the number of elements in the doubly linked list DLL."
|
||||||
|
|
||||||
|
(length (cdr dll)))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(defun dll-copy (dll &optional element-copy-fnc)
|
||||||
|
|
||||||
|
"Return a copy of the doubly linked list DLL.
|
||||||
|
If optional second argument ELEMENT-COPY-FNC is non-nil it should be
|
||||||
|
a function that takes one argument, an element, and returns a copy of it.
|
||||||
|
If ELEMENT-COPY-FNC is not given the elements are not copied."
|
||||||
|
|
||||||
|
(if element-copy-fnc
|
||||||
|
(cons 'DL-LIST (mapcar element-copy-fnc (cdr dll)))
|
||||||
|
(copy-sequence dll)))
|
||||||
|
|
||||||
|
|
||||||
|
(defun dll-all (dll)
|
||||||
|
|
||||||
|
"Return all elements on the double linked list DLL as an ordinary list."
|
||||||
|
|
||||||
|
(cdr dll))
|
||||||
|
|
||||||
|
|
||||||
|
(defun dll-clear (dll)
|
||||||
|
|
||||||
|
"Clear the doubly linked list DLL, i.e. make it completely empty."
|
||||||
|
|
||||||
|
(setcdr dll nil))
|
||||||
|
|
||||||
|
|
||||||
|
(defun dll-map (map-function dll)
|
||||||
|
|
||||||
|
"Apply MAP-FUNCTION to all elements in the doubly linked list DLL.
|
||||||
|
The function is applied to the first element first."
|
||||||
|
|
||||||
|
(mapcar map-function (cdr dll)))
|
||||||
|
|
||||||
|
|
||||||
|
(defun dll-map-reverse (map-function dll)
|
||||||
|
|
||||||
|
"Apply MAP-FUNCTION to all elements in the doubly linked list DLL.
|
||||||
|
The function is applied to the last element first."
|
||||||
|
|
||||||
|
(unwind-protect
|
||||||
|
(setcdr dll (nreverse (cdr dll)))
|
||||||
|
(mapcar map-function (cdr dll))
|
||||||
|
(setcdr dll (nreverse (cdr dll)))))
|
||||||
|
|
||||||
|
|
||||||
|
(defun dll-create-from-list (list)
|
||||||
|
|
||||||
|
"Given an elisp LIST create a doubly linked list with the same elements."
|
||||||
|
|
||||||
|
(cons 'DL-LIST list))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(defun dll-sort (dll predicate)
|
||||||
|
|
||||||
|
"Sort the doubly linked list DLL, stably, comparing elements using PREDICATE.
|
||||||
|
Returns the sorted list. DLL is modified by side effects.
|
||||||
|
PREDICATE is called with two elements of DLL, and should return T
|
||||||
|
if the first element is \"less\" than the second."
|
||||||
|
|
||||||
|
(setcdr dll (sort (cdr dll) predicate))
|
||||||
|
dll)
|
||||||
|
|
||||||
|
|
||||||
|
(defun dll-filter (dll predicate)
|
||||||
|
|
||||||
|
"Remove all elements in the doubly linked list DLL for which PREDICATE
|
||||||
|
return nil."
|
||||||
|
|
||||||
|
(let* ((prev dll)
|
||||||
|
(node (cdr dll)))
|
||||||
|
|
||||||
|
(while node
|
||||||
|
(cond
|
||||||
|
((funcall predicate (car node))
|
||||||
|
(setq prev node))
|
||||||
|
(t
|
||||||
|
(setcdr prev (cdr node))))
|
||||||
|
(setq node (cdr node)))))
|
||||||
@@ -0,0 +1,386 @@
|
|||||||
|
;;; elib-dll.el,v 1.2 1992/04/07 20:49:15 berliner Exp
|
||||||
|
;;; elib-dll.el -- Some primitives for Doubly linked lists.
|
||||||
|
;;; Copyright (C) 1991, 1992 Per Cederqvist
|
||||||
|
;;;
|
||||||
|
;;; This program is free software; you can redistribute it and/or modify
|
||||||
|
;;; it under the terms of the GNU General Public License as published by
|
||||||
|
;;; the Free Software Foundation; either version 2 of the License, or
|
||||||
|
;;; (at your option) any later version.
|
||||||
|
;;;
|
||||||
|
;;; This program is distributed in the hope that it will be useful,
|
||||||
|
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
;;; GNU General Public License for more details.
|
||||||
|
;;;
|
||||||
|
;;; You should have received a copy of the GNU General Public License
|
||||||
|
;;; along with this program; if not, write to the Free Software
|
||||||
|
;;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
|
||||||
|
;;; Mail bug reports to ceder@lysator.liu.se.
|
||||||
|
|
||||||
|
(require 'elib-node)
|
||||||
|
(provide 'elib-dll)
|
||||||
|
|
||||||
|
;;;
|
||||||
|
;;; A doubly linked list consists of one cons cell which holds the tag
|
||||||
|
;;; 'DL-LIST in the car cell and a pointer to a dummy node in the cdr
|
||||||
|
;;; cell. The doubly linked list is implemented as a circular list
|
||||||
|
;;; with the dummy node first and last. The dummy node is recognized
|
||||||
|
;;; by comparing it to the node which the cdr of the cons cell points
|
||||||
|
;;; to.
|
||||||
|
;;;
|
||||||
|
|
||||||
|
;;; ================================================================
|
||||||
|
;;; Internal functions for use in the doubly linked list package
|
||||||
|
|
||||||
|
(defun dll-get-dummy-node (dll)
|
||||||
|
|
||||||
|
;; Return the dummy node. INTERNAL USE ONLY.
|
||||||
|
(cdr dll))
|
||||||
|
|
||||||
|
(defun dll-list-nodes (dll)
|
||||||
|
|
||||||
|
;; Return a list of all nodes in DLL. INTERNAL USE ONLY.
|
||||||
|
|
||||||
|
(let* ((result nil)
|
||||||
|
(dummy (dll-get-dummy-node dll))
|
||||||
|
(node (elib-node-left dummy)))
|
||||||
|
|
||||||
|
(while (not (eq node dummy))
|
||||||
|
(setq result (cons node result))
|
||||||
|
(setq node (elib-node-left node)))
|
||||||
|
|
||||||
|
result))
|
||||||
|
|
||||||
|
(defun dll-set-from-node-list (dll list)
|
||||||
|
|
||||||
|
;; Set the contents of DLL to the nodes in LIST.
|
||||||
|
;; INTERNAL USE ONLY.
|
||||||
|
|
||||||
|
(dll-clear dll)
|
||||||
|
(let* ((dummy (dll-get-dummy-node dll))
|
||||||
|
(left dummy))
|
||||||
|
(while list
|
||||||
|
(elib-node-set-left (car list) left)
|
||||||
|
(elib-node-set-right left (car list))
|
||||||
|
(setq left (car list))
|
||||||
|
(setq list (cdr list)))
|
||||||
|
|
||||||
|
(elib-node-set-right left dummy)
|
||||||
|
(elib-node-set-left dummy left)))
|
||||||
|
|
||||||
|
|
||||||
|
;;; ===================================================================
|
||||||
|
;;; The public functions which operate on doubly linked lists.
|
||||||
|
|
||||||
|
(defmacro dll-element (dll node)
|
||||||
|
|
||||||
|
"Get the element of a NODE in a doubly linked list DLL.
|
||||||
|
Args: DLL NODE."
|
||||||
|
|
||||||
|
(` (elib-node-data (, node))))
|
||||||
|
|
||||||
|
|
||||||
|
(defun dll-create ()
|
||||||
|
"Create an empty doubly linked list."
|
||||||
|
(let ((dummy-node (elib-node-create nil nil nil)))
|
||||||
|
(elib-node-set-right dummy-node dummy-node)
|
||||||
|
(elib-node-set-left dummy-node dummy-node)
|
||||||
|
(cons 'DL-LIST dummy-node)))
|
||||||
|
|
||||||
|
(defun dll-p (object)
|
||||||
|
"Return t if OBJECT is a doubly linked list, otherwise return nil."
|
||||||
|
(eq (car-safe object) 'DL-LIST))
|
||||||
|
|
||||||
|
(defun dll-enter-first (dll element)
|
||||||
|
"Add an element first on a doubly linked list.
|
||||||
|
Args: DLL ELEMENT."
|
||||||
|
(dll-enter-after
|
||||||
|
dll
|
||||||
|
(dll-get-dummy-node dll)
|
||||||
|
element))
|
||||||
|
|
||||||
|
|
||||||
|
(defun dll-enter-last (dll element)
|
||||||
|
"Add an element last on a doubly linked list.
|
||||||
|
Args: DLL ELEMENT."
|
||||||
|
(dll-enter-before
|
||||||
|
dll
|
||||||
|
(dll-get-dummy-node dll)
|
||||||
|
element))
|
||||||
|
|
||||||
|
|
||||||
|
(defun dll-enter-after (dll node element)
|
||||||
|
"In the doubly linked list DLL, insert a node containing ELEMENT after NODE.
|
||||||
|
Args: DLL NODE ELEMENT."
|
||||||
|
|
||||||
|
(let ((new-node (elib-node-create
|
||||||
|
node (elib-node-right node)
|
||||||
|
element)))
|
||||||
|
(elib-node-set-left (elib-node-right node) new-node)
|
||||||
|
(elib-node-set-right node new-node)))
|
||||||
|
|
||||||
|
|
||||||
|
(defun dll-enter-before (dll node element)
|
||||||
|
"In the doubly linked list DLL, insert a node containing ELEMENT before NODE.
|
||||||
|
Args: DLL NODE ELEMENT."
|
||||||
|
|
||||||
|
(let ((new-node (elib-node-create
|
||||||
|
(elib-node-left node) node
|
||||||
|
element)))
|
||||||
|
(elib-node-set-right (elib-node-left node) new-node)
|
||||||
|
(elib-node-set-left node new-node)))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(defun dll-next (dll node)
|
||||||
|
"Return the node after NODE, or nil if NODE is the last node.
|
||||||
|
Args: DLL NODE."
|
||||||
|
|
||||||
|
(if (eq (elib-node-right node) (dll-get-dummy-node dll))
|
||||||
|
nil
|
||||||
|
(elib-node-right node)))
|
||||||
|
|
||||||
|
|
||||||
|
(defun dll-previous (dll node)
|
||||||
|
"Return the node before NODE, or nil if NODE is the first node.
|
||||||
|
Args: DLL NODE."
|
||||||
|
|
||||||
|
(if (eq (elib-node-left node) (dll-get-dummy-node dll))
|
||||||
|
nil
|
||||||
|
(elib-node-left node)))
|
||||||
|
|
||||||
|
|
||||||
|
(defun dll-delete (dll node)
|
||||||
|
|
||||||
|
"Delete NODE from the doubly linked list DLL.
|
||||||
|
Args: DLL NODE. Return the element of node."
|
||||||
|
|
||||||
|
;; This is a no-op when applied to the dummy node. This will return
|
||||||
|
;; nil if applied to the dummy node since it always contains nil.
|
||||||
|
|
||||||
|
(elib-node-set-right (elib-node-left node) (elib-node-right node))
|
||||||
|
(elib-node-set-left (elib-node-right node) (elib-node-left node))
|
||||||
|
(dll-element dll node))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(defun dll-delete-first (dll)
|
||||||
|
|
||||||
|
"Delete the first NODE from the doubly linked list DLL.
|
||||||
|
Return the element. Args: DLL. Returns nil if the DLL was empty."
|
||||||
|
|
||||||
|
;; Relies on the fact that dll-delete does nothing and
|
||||||
|
;; returns nil if given the dummy node.
|
||||||
|
|
||||||
|
(dll-delete dll (elib-node-right (dll-get-dummy-node dll))))
|
||||||
|
|
||||||
|
|
||||||
|
(defun dll-delete-last (dll)
|
||||||
|
|
||||||
|
"Delete the last NODE from the doubly linked list DLL.
|
||||||
|
Return the element. Args: DLL. Returns nil if the DLL was empty."
|
||||||
|
|
||||||
|
;; Relies on the fact that dll-delete does nothing and
|
||||||
|
;; returns nil if given the dummy node.
|
||||||
|
|
||||||
|
(dll-delete dll (elib-node-left (dll-get-dummy-node dll))))
|
||||||
|
|
||||||
|
|
||||||
|
(defun dll-first (dll)
|
||||||
|
|
||||||
|
"Return the first element on the doubly linked list DLL.
|
||||||
|
Return nil if the list is empty. The element is not removed."
|
||||||
|
|
||||||
|
(if (eq (elib-node-right (dll-get-dummy-node dll))
|
||||||
|
(dll-get-dummy-node dll))
|
||||||
|
nil
|
||||||
|
(elib-node-data (elib-node-right (dll-get-dummy-node dll)))))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(defun dll-last (dll)
|
||||||
|
|
||||||
|
"Return the last element on the doubly linked list DLL.
|
||||||
|
Return nil if the list is empty. The element is not removed."
|
||||||
|
|
||||||
|
(if (eq (elib-node-left (dll-get-dummy-node dll))
|
||||||
|
(dll-get-dummy-node dll))
|
||||||
|
nil
|
||||||
|
(elib-node-data (elib-node-left (dll-get-dummy-node dll)))))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(defun dll-nth (dll n)
|
||||||
|
|
||||||
|
"Return the Nth node from the doubly linked list DLL.
|
||||||
|
Args: DLL N
|
||||||
|
N counts from zero. If DLL is not that long, nil is returned.
|
||||||
|
If N is negative, return the -(N+1)th last element.
|
||||||
|
Thus, (dll-nth dll 0) returns the first node,
|
||||||
|
and (dll-nth dll -1) returns the last node."
|
||||||
|
|
||||||
|
;; Branch 0 ("follow left pointer") is used when n is negative.
|
||||||
|
;; Branch 1 ("follow right pointer") is used otherwise.
|
||||||
|
|
||||||
|
(let* ((dummy (dll-get-dummy-node dll))
|
||||||
|
(branch (if (< n 0) 0 1))
|
||||||
|
(node (elib-node-branch dummy branch)))
|
||||||
|
|
||||||
|
(if (< n 0)
|
||||||
|
(setq n (- -1 n)))
|
||||||
|
|
||||||
|
(while (and (not (eq dummy node))
|
||||||
|
(> n 0))
|
||||||
|
(setq node (elib-node-branch node branch))
|
||||||
|
(setq n (1- n)))
|
||||||
|
|
||||||
|
(if (eq dummy node)
|
||||||
|
nil
|
||||||
|
node)))
|
||||||
|
|
||||||
|
|
||||||
|
(defun dll-empty (dll)
|
||||||
|
|
||||||
|
"Return t if the doubly linked list DLL is empty, nil otherwise"
|
||||||
|
|
||||||
|
(eq (elib-node-left (dll-get-dummy-node dll))
|
||||||
|
(dll-get-dummy-node dll)))
|
||||||
|
|
||||||
|
(defun dll-length (dll)
|
||||||
|
|
||||||
|
"Returns the number of elements in the doubly linked list DLL."
|
||||||
|
|
||||||
|
(let* ((dummy (dll-get-dummy-node dll))
|
||||||
|
(node (elib-node-right dummy))
|
||||||
|
(n 0))
|
||||||
|
|
||||||
|
(while (not (eq node dummy))
|
||||||
|
(setq node (elib-node-right node))
|
||||||
|
(setq n (1+ n)))
|
||||||
|
|
||||||
|
n))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(defun dll-copy (dll &optional element-copy-fnc)
|
||||||
|
|
||||||
|
"Return a copy of the doubly linked list DLL.
|
||||||
|
If optional second argument ELEMENT-COPY-FNC is non-nil it should be
|
||||||
|
a function that takes one argument, an element, and returns a copy of it.
|
||||||
|
If ELEMENT-COPY-FNC is not given the elements are not copied."
|
||||||
|
|
||||||
|
(let ((result (dll-create))
|
||||||
|
(node (dll-nth dll 0)))
|
||||||
|
(if element-copy-fnc
|
||||||
|
|
||||||
|
;; Copy the elements with the user-supplied function.
|
||||||
|
(while node
|
||||||
|
(dll-enter-last result
|
||||||
|
(funcall element-copy-fnc
|
||||||
|
(dll-element dll node)))
|
||||||
|
(setq node (dll-next dll node)))
|
||||||
|
|
||||||
|
;; Don't try to copy the elements - they might be
|
||||||
|
;; circular lists, or anything at all...
|
||||||
|
(while node
|
||||||
|
(dll-enter-last result (dll-element dll node))
|
||||||
|
(setq node (dll-next dll node))))
|
||||||
|
|
||||||
|
result))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(defun dll-all (dll)
|
||||||
|
|
||||||
|
"Return all elements on the double linked list DLL as an ordinary list."
|
||||||
|
|
||||||
|
(let* ((result nil)
|
||||||
|
(dummy (dll-get-dummy-node dll))
|
||||||
|
(node (elib-node-left dummy)))
|
||||||
|
|
||||||
|
(while (not (eq node dummy))
|
||||||
|
(setq result (cons (dll-element dll node) result))
|
||||||
|
(setq node (elib-node-left node)))
|
||||||
|
|
||||||
|
result))
|
||||||
|
|
||||||
|
|
||||||
|
(defun dll-clear (dll)
|
||||||
|
|
||||||
|
"Clear the doubly linked list DLL, i.e. make it completely empty."
|
||||||
|
|
||||||
|
(elib-node-set-left (dll-get-dummy-node dll) (dll-get-dummy-node dll))
|
||||||
|
(elib-node-set-right (dll-get-dummy-node dll) (dll-get-dummy-node dll)))
|
||||||
|
|
||||||
|
|
||||||
|
(defun dll-map (map-function dll)
|
||||||
|
|
||||||
|
"Apply MAP-FUNCTION to all elements in the doubly linked list DLL.
|
||||||
|
The function is applied to the first element first."
|
||||||
|
|
||||||
|
(let* ((dummy (dll-get-dummy-node dll))
|
||||||
|
(node (elib-node-right dummy)))
|
||||||
|
|
||||||
|
(while (not (eq node dummy))
|
||||||
|
(funcall map-function (dll-element dll node))
|
||||||
|
(setq node (elib-node-right node)))))
|
||||||
|
|
||||||
|
|
||||||
|
(defun dll-map-reverse (map-function dll)
|
||||||
|
|
||||||
|
"Apply MAP-FUNCTION to all elements in the doubly linked list DLL.
|
||||||
|
The function is applied to the last element first."
|
||||||
|
|
||||||
|
(let* ((dummy (dll-get-dummy-node dll))
|
||||||
|
(node (elib-node-left dummy)))
|
||||||
|
|
||||||
|
(while (not (eq node dummy))
|
||||||
|
(funcall map-function (dll-element dll node))
|
||||||
|
(setq node (elib-node-left node)))))
|
||||||
|
|
||||||
|
|
||||||
|
(defun dll-create-from-list (list)
|
||||||
|
|
||||||
|
"Given an elisp LIST create a doubly linked list with the same elements."
|
||||||
|
|
||||||
|
(let ((dll (dll-create)))
|
||||||
|
(while list
|
||||||
|
(dll-enter-last dll (car list))
|
||||||
|
(setq list (cdr list)))
|
||||||
|
dll))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(defun dll-sort (dll predicate)
|
||||||
|
|
||||||
|
"Sort the doubly linked list DLL, stably, comparing elements using PREDICATE.
|
||||||
|
Returns the sorted list. DLL is modified by side effects.
|
||||||
|
PREDICATE is called with two elements of DLL, and should return T
|
||||||
|
if the first element is \"less\" than the second."
|
||||||
|
|
||||||
|
(dll-set-from-node-list
|
||||||
|
dll (sort (dll-list-nodes dll)
|
||||||
|
(function (lambda (x1 x2)
|
||||||
|
(funcall predicate
|
||||||
|
(dll-element dll x1)
|
||||||
|
(dll-element dll x2))))))
|
||||||
|
dll)
|
||||||
|
|
||||||
|
|
||||||
|
(defun dll-filter (dll predicate)
|
||||||
|
|
||||||
|
"Remove all elements in the doubly linked list DLL for which PREDICATE
|
||||||
|
return nil."
|
||||||
|
|
||||||
|
(let* ((dummy (dll-get-dummy-node dll))
|
||||||
|
(node (elib-node-right dummy))
|
||||||
|
next)
|
||||||
|
|
||||||
|
(while (not (eq node dummy))
|
||||||
|
(setq next (elib-node-right node))
|
||||||
|
(if (funcall predicate (dll-element dll node))
|
||||||
|
nil
|
||||||
|
(dll-delete dll node))
|
||||||
|
(setq node next))))
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
;;;; elib-node.el,v 1.2 1992/04/07 20:49:16 berliner Exp
|
||||||
|
;;;; This file implements the nodes used in binary trees and
|
||||||
|
;;;; doubly linked lists
|
||||||
|
;;;;
|
||||||
|
;;;; Copyright (C) 1991 Inge Wallin
|
||||||
|
;;;;
|
||||||
|
;;;; This file is part of the GNU Emacs lisp library, Elib.
|
||||||
|
;;;;
|
||||||
|
;;;; GNU Elib is free software; you can redistribute it and/or modify
|
||||||
|
;;;; it under the terms of the GNU General Public License as published by
|
||||||
|
;;;; the Free Software Foundation; either version 1, or (at your option)
|
||||||
|
;;;; any later version.
|
||||||
|
;;;;
|
||||||
|
;;;; GNU Elib is distributed in the hope that it will be useful,
|
||||||
|
;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
;;;; GNU General Public License for more details.
|
||||||
|
;;;;
|
||||||
|
;;;; You should have received a copy of the GNU General Public License
|
||||||
|
;;;; along with GNU Emacs; see the file COPYING. If not, write to
|
||||||
|
;;;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
;;;;
|
||||||
|
;;;; Author: Inge Wallin
|
||||||
|
;;;;
|
||||||
|
|
||||||
|
;;;
|
||||||
|
;;; A node is implemented as an array with three elements, using
|
||||||
|
;;; (elt node 0) as the left pointer
|
||||||
|
;;; (elt node 1) as the right pointer
|
||||||
|
;;; (elt node 2) as the data
|
||||||
|
;;;
|
||||||
|
;;; Some types of trees, e.g. AVL trees, need bigger nodes, but
|
||||||
|
;;; as long as the first three parts are the left pointer, the
|
||||||
|
;;; right pointer and the data field, these macros can be used.
|
||||||
|
;;;
|
||||||
|
|
||||||
|
|
||||||
|
(provide 'elib-node)
|
||||||
|
|
||||||
|
|
||||||
|
(defmacro elib-node-create (left right data)
|
||||||
|
"Create a tree node from LEFT, RIGHT and DATA."
|
||||||
|
(` (vector (, left) (, right) (, data))))
|
||||||
|
|
||||||
|
|
||||||
|
(defmacro elib-node-left (node)
|
||||||
|
"Return the left pointer of NODE."
|
||||||
|
(` (aref (, node) 0)))
|
||||||
|
|
||||||
|
|
||||||
|
(defmacro elib-node-right (node)
|
||||||
|
"Return the right pointer of NODE."
|
||||||
|
(` (aref (, node) 1)))
|
||||||
|
|
||||||
|
|
||||||
|
(defmacro elib-node-data (node)
|
||||||
|
"Return the data of NODE."
|
||||||
|
(` (aref (, node) 2)))
|
||||||
|
|
||||||
|
|
||||||
|
(defmacro elib-node-set-left (node newleft)
|
||||||
|
"Set the left pointer of NODE to NEWLEFT."
|
||||||
|
(` (aset (, node) 0 (, newleft))))
|
||||||
|
|
||||||
|
|
||||||
|
(defmacro elib-node-set-right (node newright)
|
||||||
|
"Set the right pointer of NODE to NEWRIGHT."
|
||||||
|
(` (aset (, node) 1 (, newright))))
|
||||||
|
|
||||||
|
|
||||||
|
(defmacro elib-node-set-data (node newdata)
|
||||||
|
"Set the data of NODE to NEWDATA."
|
||||||
|
(` (aset (, node) 2 (, newdata))))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(defmacro elib-node-branch (node branch)
|
||||||
|
"Get value of a branch of a node.
|
||||||
|
NODE is the node, and BRANCH is the branch.
|
||||||
|
0 for left pointer, 1 for right pointer and 2 for the data."
|
||||||
|
(` (aref (, node) (, branch))))
|
||||||
|
|
||||||
|
|
||||||
|
(defmacro elib-node-set-branch (node branch newval)
|
||||||
|
"Set value of a branch of a node.
|
||||||
|
NODE is the node, and BRANCH is the branch.
|
||||||
|
0 for left pointer, 1 for the right pointer and 2 for the data.
|
||||||
|
NEWVAL is new value of the branch."
|
||||||
|
(` (aset (, node) (, branch) (, newval))))
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
;;; pcl-cvs-startup.el,v 1.2 1992/04/07 20:49:17 berliner Exp
|
||||||
|
(autoload 'cvs-update "pcl-cvs"
|
||||||
|
"Run a 'cvs update' in the current working directory. Feed the
|
||||||
|
output to a *cvs* buffer and run cvs-mode on it.
|
||||||
|
If optional prefix argument LOCAL is non-nil, 'cvs update -l' is run."
|
||||||
|
t)
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,208 @@
|
|||||||
|
#!/bin/csh
|
||||||
|
#
|
||||||
|
# rcs-to-cvs,v 1.3 1992/04/10 03:04:25 berliner Exp
|
||||||
|
# Contributed by Per Cederqvist <ceder@lysator.liu.se>.
|
||||||
|
#
|
||||||
|
# Copyright (c) 1989, Brian Berliner
|
||||||
|
#
|
||||||
|
# You may distribute under the terms of the GNU General Public License
|
||||||
|
# as specified in the README file that comes with the CVS 1.0 kit.
|
||||||
|
#
|
||||||
|
#############################################################################
|
||||||
|
# #
|
||||||
|
# This script is used to check in sources that previously was under RCS or #
|
||||||
|
# no source control system. #
|
||||||
|
# #
|
||||||
|
# Usage: rcs-to-cvs repository #
|
||||||
|
# #
|
||||||
|
# The repository is the directory where the sources should #
|
||||||
|
# be deposited.
|
||||||
|
# #
|
||||||
|
# checkin traverses the current directory, ensuring that an #
|
||||||
|
# identical directory structure exists in the repository directory. It #
|
||||||
|
# then checks the files in in the following manner: #
|
||||||
|
# #
|
||||||
|
# 1) If the file doesn't yet exist, check it in #
|
||||||
|
# as revision 0.1 #
|
||||||
|
# #
|
||||||
|
# The script also is somewhat verbose in letting the user know what is #
|
||||||
|
# going on. It prints a diagnostic when it creates a new file, or updates #
|
||||||
|
# a file that has been modified on the trunk. #
|
||||||
|
# #
|
||||||
|
#############################################################################
|
||||||
|
|
||||||
|
set vbose = 0
|
||||||
|
set message = ""
|
||||||
|
set cvsbin = /usr/gnu/bin
|
||||||
|
set rcsbin = /usr/gnu/bin
|
||||||
|
set grep = /bin/grep
|
||||||
|
set message_file = /usr/tmp/checkin.$$
|
||||||
|
set got_one = 0
|
||||||
|
|
||||||
|
if ( $#argv < 1 ) then
|
||||||
|
echo "Usage: rcs-to-cvs [-v] [-m message] [-f message_file] repository"
|
||||||
|
exit 1
|
||||||
|
endif
|
||||||
|
while ( $#argv )
|
||||||
|
switch ( $argv[1] )
|
||||||
|
case -v:
|
||||||
|
set vbose = 1
|
||||||
|
breaksw
|
||||||
|
case -m:
|
||||||
|
shift
|
||||||
|
echo $argv[1] > $message_file
|
||||||
|
set got_one = 1
|
||||||
|
breaksw
|
||||||
|
case -f:
|
||||||
|
shift
|
||||||
|
set message_file = $argv[1]
|
||||||
|
set got_one = 2
|
||||||
|
breaksw
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
endsw
|
||||||
|
shift
|
||||||
|
end
|
||||||
|
if ( $#argv < 1 ) then
|
||||||
|
echo "Usage: rcs-to-cvs [-v] [-m message] [-f message_file] repository"
|
||||||
|
exit 1
|
||||||
|
endif
|
||||||
|
set repository = $argv[1]
|
||||||
|
shift
|
||||||
|
|
||||||
|
if ( ! $?CVSROOT ) then
|
||||||
|
echo "Please set the environmental variable CVSROOT to the root"
|
||||||
|
echo " of the tree you wish to update"
|
||||||
|
exit 1
|
||||||
|
endif
|
||||||
|
|
||||||
|
if ( $got_one == 0 ) then
|
||||||
|
echo "Please Edit this file to contain the RCS log information" >$message_file
|
||||||
|
echo "to be associated with this file (please remove these lines)">>$message_file
|
||||||
|
if ( $?EDITOR ) then
|
||||||
|
$EDITOR $message_file > /dev/tty
|
||||||
|
else
|
||||||
|
/usr/ucb/vi $message_file > /dev/tty
|
||||||
|
endif
|
||||||
|
set got_one = 1
|
||||||
|
endif
|
||||||
|
|
||||||
|
umask 22
|
||||||
|
|
||||||
|
set update_dir = ${CVSROOT}/${repository}
|
||||||
|
if ( -d SCCS ) then
|
||||||
|
echo SCCS files detected!
|
||||||
|
exit 1
|
||||||
|
endif
|
||||||
|
if ( -d RCS ) then
|
||||||
|
$rcsbin/co RCS/* >& /dev/null
|
||||||
|
endif
|
||||||
|
foreach name ( * .[a-zA-Z0-9]* )
|
||||||
|
echo $name
|
||||||
|
if ( "$name" == SCCS ) then
|
||||||
|
continue
|
||||||
|
endif
|
||||||
|
if ( "$name" == RCS ) then
|
||||||
|
continue
|
||||||
|
endif
|
||||||
|
if ( $vbose ) then
|
||||||
|
echo "Updating ${repository}/${name}"
|
||||||
|
endif
|
||||||
|
if ( -d "$name" ) then
|
||||||
|
if ( ! -d "${update_dir}/${name}" ) then
|
||||||
|
echo "WARNING: Creating new directory ${repository}/${name}"
|
||||||
|
mkdir "${update_dir}/${name}"
|
||||||
|
if ( $status ) then
|
||||||
|
echo "ERROR: mkdir failed - aborting"
|
||||||
|
exit 1
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
chdir "$name"
|
||||||
|
if ( $status ) then
|
||||||
|
echo "ERROR: Couldn\'t chdir to "$name" - aborting"
|
||||||
|
exit 1
|
||||||
|
endif
|
||||||
|
if ( $vbose ) then
|
||||||
|
rcs-to-cvs -v -f $message_file "${repository}/${name}"
|
||||||
|
else
|
||||||
|
rcs-to-cvs -f $message_file "${repository}/${name}"
|
||||||
|
endif
|
||||||
|
if ( $status ) then
|
||||||
|
exit 1
|
||||||
|
endif
|
||||||
|
chdir ..
|
||||||
|
else # if not directory
|
||||||
|
if ( ! -f "$name" ) then
|
||||||
|
echo "WARNING: "$name" is neither a regular file"
|
||||||
|
echo " nor a directory - ignored"
|
||||||
|
continue
|
||||||
|
endif
|
||||||
|
set file = "${update_dir}/${name},v"
|
||||||
|
set new = 0
|
||||||
|
set comment = ""
|
||||||
|
grep -s '\$Log.*\$' "${name}"
|
||||||
|
if ( $status == 0 ) then # If $Log keyword
|
||||||
|
set myext = ${name:e}
|
||||||
|
set knownext = 0
|
||||||
|
foreach xx ( "c" "csh" "e" "f" "h" "l" "mac" "me" "mm" "ms" "p" "r" "red" "s" "sh" "sl" "cl" "ml" "el" "tex" "y" "ye" "yr" "" )
|
||||||
|
if ( "${myext}" == "${xx}" ) then
|
||||||
|
set knownext = 1
|
||||||
|
break
|
||||||
|
endif
|
||||||
|
end
|
||||||
|
if ( $knownext == 0 ) then
|
||||||
|
echo For file ${file}:
|
||||||
|
grep '\$Log.*\$' "${name}"
|
||||||
|
echo -n "Please insert a comment leader for file ${name} > "
|
||||||
|
set comment = $<
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
if ( ! -f "$file" ) then # If not exists in repository
|
||||||
|
if ( ! -f "${update_dir}/Attic/${name},v" ) then
|
||||||
|
echo "WARNING: Creating new file ${repository}/${name}"
|
||||||
|
if ( -f RCS/"${name}",v ) then
|
||||||
|
echo "MSG: Copying old rcs file."
|
||||||
|
cp RCS/"${name}",v "$file"
|
||||||
|
else
|
||||||
|
if ( "${comment}" != "" ) then
|
||||||
|
$rcsbin/rcs -q -i -c"${comment}" -t${message_file} -m'.' "$file"
|
||||||
|
endif
|
||||||
|
$rcsbin/ci -q -u0.1 -t${message_file} -m'.' "$file"
|
||||||
|
if ( $status ) then
|
||||||
|
echo "ERROR: Initial check-in of $file failed - aborting"
|
||||||
|
exit 1
|
||||||
|
endif
|
||||||
|
set new = 1
|
||||||
|
endif
|
||||||
|
else
|
||||||
|
set file = "${update_dir}/Attic/${name},v"
|
||||||
|
echo "WARNING: IGNORED: ${repository}/Attic/${name}"
|
||||||
|
continue
|
||||||
|
endif
|
||||||
|
else # File existed
|
||||||
|
echo ERROR: File exists: Ignored: "$file"
|
||||||
|
continue
|
||||||
|
# set headbranch = `sed -n '/^head/p; /^branch/p; 2q' $file`
|
||||||
|
# if ( $#headbranch != 2 && $#headbranch != 4 ) then
|
||||||
|
# echo "ERROR: corrupted RCS file $file - aborting"
|
||||||
|
# endif
|
||||||
|
# set head = "$headbranch[2]"
|
||||||
|
# set branch = ""
|
||||||
|
# if ( $#headbranch == 4 ) then
|
||||||
|
# set branch = "$headbranch[4]"
|
||||||
|
# endif
|
||||||
|
# if ( "$head" == "1.1;" && "$branch" != "1.1.1;" ) then
|
||||||
|
# ${rcsbin}/rcsdiff -q -r1.1 $file > /dev/null
|
||||||
|
# if ( ! $status ) then
|
||||||
|
# set new = 1
|
||||||
|
# endif
|
||||||
|
# else
|
||||||
|
# if ( "$branch" != "1.1.1;" ) then
|
||||||
|
# echo -n "WARNING: Updating locally modified file "
|
||||||
|
# echo "${repository}/${name}"
|
||||||
|
# endif
|
||||||
|
# endif
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
end
|
||||||
|
if ( $got_one == 1 ) rm $message_file
|
||||||
@@ -0,0 +1,234 @@
|
|||||||
|
#!/usr/bin/perl
|
||||||
|
|
||||||
|
# Author: John Rouillard (rouilj@cs.umb.edu)
|
||||||
|
# Supported: Yeah right. (Well what do you expect for 2 hours work?)
|
||||||
|
# Blame-to: rouilj@cs.umb.edu
|
||||||
|
# Complaints to: Anybody except Brian Berliner, he's blameless for
|
||||||
|
# this script.
|
||||||
|
# Acknowlegements: The base code for this script has been acquired
|
||||||
|
# from the log.pl script.
|
||||||
|
|
||||||
|
# rcslock.pl - A program to prevent commits when a file to be ckecked
|
||||||
|
# in is locked in the repository.
|
||||||
|
|
||||||
|
# There are times when you need exclusive access to a file. This
|
||||||
|
# often occurs when binaries are checked into the repository, since
|
||||||
|
# cvs's (actually rcs's) text based merging mechanism won't work. This
|
||||||
|
# script allows you to use the rcs lock mechanism (rcs -l) to make
|
||||||
|
# sure that no changes to a repository are able to be committed if
|
||||||
|
# those changes would result in a locked file being changed.
|
||||||
|
|
||||||
|
# WARNING:
|
||||||
|
# This script will work only if locking is set to strict.
|
||||||
|
#
|
||||||
|
|
||||||
|
# Setup:
|
||||||
|
# Add the following line to the commitinfo file:
|
||||||
|
|
||||||
|
# ALL /local/location/for/script/lockcheck [options]
|
||||||
|
|
||||||
|
# Where ALL is replaced by any suitable regular expression.
|
||||||
|
# Options are -v for verbose info, or -d for debugging info.
|
||||||
|
# The %s will provide the repository directory name and the names of
|
||||||
|
# all changed files.
|
||||||
|
|
||||||
|
# Use:
|
||||||
|
# When a developer needs exclusive access to a version of a file, s/he
|
||||||
|
# should use "rcs -l" in the repository tree to lock the version they
|
||||||
|
# are working on. CVS will automagically release the lock when the
|
||||||
|
# commit is performed.
|
||||||
|
|
||||||
|
# Method:
|
||||||
|
# An "rlog -h" is exec'ed to give info on all about to be
|
||||||
|
# committed files. This (header) information is parsed to determine
|
||||||
|
# if any locks are outstanding and what versions of the file are
|
||||||
|
# locked. This filename, version number info is used to index an
|
||||||
|
# associative array. All of the files to be committed are checked to
|
||||||
|
# see if any locks are outstanding. If locks are outstanding, the
|
||||||
|
# version number of the current file (taken from the CVS/Entries
|
||||||
|
# subdirectory) is used in the key to determine if that version is
|
||||||
|
# locked. If the file being checked in is locked by the person doing
|
||||||
|
# the checkin, the commit is allowed, but if the lock is held on that
|
||||||
|
# version of a file by another person, the commit is not allowed.
|
||||||
|
|
||||||
|
$ext = ",v"; # The extension on your rcs files.
|
||||||
|
|
||||||
|
$\="\n"; # I hate having to put \n's at the end of my print statements
|
||||||
|
$,=' '; # Spaces should occur between arguments to print when printed
|
||||||
|
|
||||||
|
# turn off setgid
|
||||||
|
#
|
||||||
|
$) = $(;
|
||||||
|
|
||||||
|
#
|
||||||
|
# parse command line arguments
|
||||||
|
#
|
||||||
|
require 'getopts.pl';
|
||||||
|
|
||||||
|
&Getopts("vd"); # verbose or debugging
|
||||||
|
|
||||||
|
# Verbose is useful when debugging
|
||||||
|
$opt_v = $opt_d if defined $opt_d;
|
||||||
|
|
||||||
|
# $files[0] is really the name of the subdirectory.
|
||||||
|
# @files = split(/ /,$ARGV[0]);
|
||||||
|
@files = @ARGV[0..$#ARGV];
|
||||||
|
$cvsroot = $ENV{'CVSROOT'};
|
||||||
|
|
||||||
|
#
|
||||||
|
# get login name
|
||||||
|
#
|
||||||
|
$login = getlogin || (getpwuid($<))[0] || "nobody";
|
||||||
|
|
||||||
|
#
|
||||||
|
# save the current directory since we have to return here to parse the
|
||||||
|
# CVS/Entries file if a lock is found.
|
||||||
|
#
|
||||||
|
$pwd = `/bin/pwd`;
|
||||||
|
chop $pwd;
|
||||||
|
|
||||||
|
print "Starting directory is $pwd" if defined $opt_d ;
|
||||||
|
|
||||||
|
#
|
||||||
|
# cd to the repository directory and check on the files.
|
||||||
|
#
|
||||||
|
print "Checking directory ", $files[0] if defined $opt_v ;
|
||||||
|
|
||||||
|
if ( $files[0] =~ /^\// )
|
||||||
|
{
|
||||||
|
print "Directory path is $files[0]" if defined $opt_d ;
|
||||||
|
chdir $files[0] || die "Can't change to repository directory $files[0]" ;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
print "Directory path is $cvsroot/$files[0]" if defined $opt_d ;
|
||||||
|
chdir ($cvsroot . "/" . $files[0]) ||
|
||||||
|
die "Can't change to repository directory $files[0] in $cvsroot" ;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Open the rlog process and apss all of the file names to that one
|
||||||
|
# process to cut down on exec overhead. This may backfire if there
|
||||||
|
# are too many files for the system buffer to handle, but if there are
|
||||||
|
# that many files, chances are that the cvs repository is not set up
|
||||||
|
# cleanly.
|
||||||
|
|
||||||
|
print "opening rlog -h @files[1..$#files] |" if defined $opt_d;
|
||||||
|
|
||||||
|
open( RLOG, "rlog -h @files[1..$#files] |") || die "Can't run rlog command" ;
|
||||||
|
|
||||||
|
# Create the locks associative array. The elements in the array are
|
||||||
|
# of two types:
|
||||||
|
#
|
||||||
|
# The name of the RCS file with a value of the total number of locks found
|
||||||
|
# for that file,
|
||||||
|
# or
|
||||||
|
#
|
||||||
|
# The name of the rcs file concatenated with the version number of the lock.
|
||||||
|
# The value of this element is the name of the locker.
|
||||||
|
|
||||||
|
# The regular expressions used to split the rcs info may have to be changed.
|
||||||
|
# The current ones work for rcs 5.6.
|
||||||
|
|
||||||
|
$lock = 0;
|
||||||
|
|
||||||
|
while (<RLOG>)
|
||||||
|
{
|
||||||
|
chop;
|
||||||
|
next if /^$/; # ditch blank lines
|
||||||
|
|
||||||
|
if ( $_ =~ /^RCS file: (.*)$/ )
|
||||||
|
{
|
||||||
|
$curfile = $1;
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $_ =~ /^locks: strict$/ )
|
||||||
|
{
|
||||||
|
$lock = 1 ;
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $lock )
|
||||||
|
{
|
||||||
|
# access list: is the line immediately following the list of locks.
|
||||||
|
if ( /^access list:/ )
|
||||||
|
{ # we are done getting lock info for this file.
|
||||||
|
$lock = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{ # We are accumulating lock info.
|
||||||
|
|
||||||
|
# increment the lock count
|
||||||
|
$locks{$curfile}++;
|
||||||
|
# save the info on the version that is locked. $2 is the
|
||||||
|
# version number $1 is the name of the locker.
|
||||||
|
$locks{"$curfile" . "$2"} = $1
|
||||||
|
if /[ ]*([a-zA-Z._]*): ([0-9.]*)$/;
|
||||||
|
|
||||||
|
print "lock by $1 found on $curfile version $2" if defined $opt_d;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Lets go back to the starting directory and see if any locked files
|
||||||
|
# are ones we are interested in.
|
||||||
|
|
||||||
|
chdir $pwd;
|
||||||
|
|
||||||
|
# fo all of the file names (remember $files[0] is the directory name
|
||||||
|
foreach $i (@files[1..$#files])
|
||||||
|
{
|
||||||
|
if ( defined $locks{$i . $ext} )
|
||||||
|
{ # well the file has at least one lock outstanding
|
||||||
|
|
||||||
|
# find the base version number of our file
|
||||||
|
&parse_cvs_entry($i,*entry);
|
||||||
|
|
||||||
|
# is our version of this file locked?
|
||||||
|
if ( defined $locks{$i . $ext . $entry{"version"}} )
|
||||||
|
{ # if so, it is by us?
|
||||||
|
if ( $login ne ($by = $locks{$i . $ext . $entry{"version"}}) )
|
||||||
|
{# crud somebody else has it locked.
|
||||||
|
$outstanding_lock++ ;
|
||||||
|
print "$by has file $i locked for version " , $entry{"version"};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{ # yeah I have it locked.
|
||||||
|
print "You have a lock on file $i for version " , $entry{"version"}
|
||||||
|
if defined $opt_v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exit $outstanding_lock;
|
||||||
|
|
||||||
|
|
||||||
|
### End of main program
|
||||||
|
|
||||||
|
sub parse_cvs_entry
|
||||||
|
{ # a very simple minded hack at parsing an entries file.
|
||||||
|
local ( $file, *entry ) = @_;
|
||||||
|
local ( @pp );
|
||||||
|
|
||||||
|
|
||||||
|
open(ENTRIES, "< CVS/Entries") || die "Can't open entries file";
|
||||||
|
|
||||||
|
while (<ENTRIES>)
|
||||||
|
{
|
||||||
|
if ( $_ =~ /^\/$file\// )
|
||||||
|
{
|
||||||
|
@pp = split('/');
|
||||||
|
|
||||||
|
$entry{"name"} = $pp[1];
|
||||||
|
$entry{"version"} = $pp[2];
|
||||||
|
$entry{"dates"} = $pp[3];
|
||||||
|
$entry{"name"} = $pp[4];
|
||||||
|
$entry{"name"} = $pp[5];
|
||||||
|
$entry{"sticky"} = $pp[6];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,277 @@
|
|||||||
|
#!/bin/csh -f
|
||||||
|
#
|
||||||
|
# Sccs2rcs is a script to convert an existing SCCS
|
||||||
|
# history into an RCS history without losing any of
|
||||||
|
# the information contained therein.
|
||||||
|
# It has been tested under the following OS's:
|
||||||
|
# SunOS 3.5, 4.0.3, 4.1
|
||||||
|
# Ultrix-32 2.0, 3.1
|
||||||
|
#
|
||||||
|
# Things to note:
|
||||||
|
# + It will NOT delete or alter your ./SCCS history under any circumstances.
|
||||||
|
#
|
||||||
|
# + Run in a directory where ./SCCS exists and where you can
|
||||||
|
# create ./RCS
|
||||||
|
#
|
||||||
|
# + /usr/local/bin is put in front of the default path.
|
||||||
|
# (SCCS under Ultrix is set-uid sccs, bad bad bad, so
|
||||||
|
# /usr/local/bin/sccs here fixes that)
|
||||||
|
#
|
||||||
|
# + Date, time, author, comments, branches, are all preserved.
|
||||||
|
#
|
||||||
|
# + If a command fails somewhere in the middle, it bombs with
|
||||||
|
# a message -- remove what it's done so far and try again.
|
||||||
|
# "rm -rf RCS; sccs unedit `sccs tell`; sccs clean"
|
||||||
|
# There is no recovery and exit is far from graceful.
|
||||||
|
# If a particular module is hanging you up, consider
|
||||||
|
# doing it separately; move it from the current area so that
|
||||||
|
# the next run will have a better chance or working.
|
||||||
|
# Also (for the brave only) you might consider hacking
|
||||||
|
# the s-file for simpler problems: I've successfully changed
|
||||||
|
# the date of a delta to be in sync, then run "sccs admin -z"
|
||||||
|
# on the thing.
|
||||||
|
#
|
||||||
|
# + After everything finishes, ./SCCS will be moved to ./old-SCCS.
|
||||||
|
#
|
||||||
|
# This file may be copied, processed, hacked, mutilated, and
|
||||||
|
# even destroyed as long as you don't tell anyone you wrote it.
|
||||||
|
#
|
||||||
|
# Ken Cox
|
||||||
|
# Viewlogic Systems, Inc.
|
||||||
|
# kenstir@viewlogic.com
|
||||||
|
# ...!harvard!cg-atla!viewlog!kenstir
|
||||||
|
#
|
||||||
|
# Various hacks made by Brian Berliner before inclusion in CVS contrib area.
|
||||||
|
#
|
||||||
|
# sccs2rcs,v 1.1 1992/04/10 03:04:26 berliner Exp
|
||||||
|
|
||||||
|
|
||||||
|
#we'll assume the user set up the path correctly
|
||||||
|
# for the Pmax, /usr/ucb/sccs is suid sccs, what a pain
|
||||||
|
# /usr/local/bin/sccs should override /usr/ucb/sccs there
|
||||||
|
set path = (/usr/local/bin $path)
|
||||||
|
|
||||||
|
|
||||||
|
############################################################
|
||||||
|
# Error checking
|
||||||
|
#
|
||||||
|
if (! -w .) then
|
||||||
|
echo "Error: ./ not writeable by you."
|
||||||
|
exit 1
|
||||||
|
endif
|
||||||
|
if (! -d SCCS) then
|
||||||
|
echo "Error: ./SCCS directory not found."
|
||||||
|
exit 1
|
||||||
|
endif
|
||||||
|
set edits = (`sccs tell`)
|
||||||
|
if ($#edits) then
|
||||||
|
echo "Error: $#edits file(s) out for edit...clean up before converting."
|
||||||
|
exit 1
|
||||||
|
endif
|
||||||
|
if (-d RCS) then
|
||||||
|
echo "Warning: RCS directory exists"
|
||||||
|
if (`ls -a RCS | wc -l` > 2) then
|
||||||
|
echo "Error: RCS directory not empty
|
||||||
|
exit 1
|
||||||
|
endif
|
||||||
|
else
|
||||||
|
mkdir RCS
|
||||||
|
endif
|
||||||
|
|
||||||
|
sccs clean
|
||||||
|
|
||||||
|
set logfile = /tmp/sccs2rcs_$$_log
|
||||||
|
rm -f $logfile
|
||||||
|
set tmpfile = /tmp/sccs2rcs_$$_tmp
|
||||||
|
rm -f $tmpfile
|
||||||
|
set emptyfile = /tmp/sccs2rcs_$$_empty
|
||||||
|
echo -n "" > $emptyfile
|
||||||
|
set initialfile = /tmp/sccs2rcs_$$_init
|
||||||
|
echo "Initial revision" > $initialfile
|
||||||
|
set sedfile = /tmp/sccs2rcs_$$_sed
|
||||||
|
rm -f $sedfile
|
||||||
|
set revfile = /tmp/sccs2rcs_$$_rev
|
||||||
|
rm -f $revfile
|
||||||
|
|
||||||
|
# the quotes surround the dollar signs to fool RCS when I check in this script
|
||||||
|
set sccs_keywords = (\
|
||||||
|
'%W%[ ]*%G%'\
|
||||||
|
'%W%[ ]*%E%'\
|
||||||
|
'%W%'\
|
||||||
|
'%Z%%M%[ ]*%I%[ ]*%G%'\
|
||||||
|
'%Z%%M%[ ]*%I%[ ]*%E%'\
|
||||||
|
'%M%[ ]*%I%[ ]*%G%'\
|
||||||
|
'%M%[ ]*%I%[ ]*%E%'\
|
||||||
|
'%M%'\
|
||||||
|
'%I%'\
|
||||||
|
'%G%'\
|
||||||
|
'%E%'\
|
||||||
|
'%U%')
|
||||||
|
set rcs_keywords = (\
|
||||||
|
'$'Id'$'\
|
||||||
|
'$'Id'$'\
|
||||||
|
'$'Id'$'\
|
||||||
|
'$'SunId'$'\
|
||||||
|
'$'SunId'$'\
|
||||||
|
'$'Id'$'\
|
||||||
|
'$'Id'$'\
|
||||||
|
'$'RCSfile'$'\
|
||||||
|
'$'Revision'$'\
|
||||||
|
'$'Date'$'\
|
||||||
|
'$'Date'$'\
|
||||||
|
'')
|
||||||
|
|
||||||
|
|
||||||
|
############################################################
|
||||||
|
# Get some answers from user
|
||||||
|
#
|
||||||
|
echo ""
|
||||||
|
echo "Do you want to be prompted for a description of each"
|
||||||
|
echo "file as it is checked in to RCS initially?"
|
||||||
|
echo -n "(y=prompt for description, n=null description) [y] ?"
|
||||||
|
set ans = $<
|
||||||
|
if ((_$ans == _) || (_$ans == _y) || (_$ans == _Y)) then
|
||||||
|
set nodesc = 0
|
||||||
|
else
|
||||||
|
set nodesc = 1
|
||||||
|
endif
|
||||||
|
echo ""
|
||||||
|
echo "The default keyword substitutions are as follows and are"
|
||||||
|
echo "applied in the order specified:"
|
||||||
|
set i = 1
|
||||||
|
while ($i <= $#sccs_keywords)
|
||||||
|
# echo ' '\"$sccs_keywords[$i]\"' ==> '\"$rcs_keywords[$i]\"
|
||||||
|
echo " $sccs_keywords[$i] ==> $rcs_keywords[$i]"
|
||||||
|
@ i = $i + 1
|
||||||
|
end
|
||||||
|
echo ""
|
||||||
|
echo -n "Do you want to change them [n] ?"
|
||||||
|
set ans = $<
|
||||||
|
if ((_$ans != _) && (_$ans != _n) && (_$ans != _N)) then
|
||||||
|
echo "You can't always get what you want."
|
||||||
|
echo "Edit this script file and change the variables:"
|
||||||
|
echo ' $sccs_keywords'
|
||||||
|
echo ' $rcs_keywords'
|
||||||
|
else
|
||||||
|
echo "good idea."
|
||||||
|
endif
|
||||||
|
|
||||||
|
# create the sed script
|
||||||
|
set i = 1
|
||||||
|
while ($i <= $#sccs_keywords)
|
||||||
|
echo "s,$sccs_keywords[$i],$rcs_keywords[$i],g" >> $sedfile
|
||||||
|
@ i = $i + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
onintr ERROR
|
||||||
|
|
||||||
|
############################################################
|
||||||
|
# Loop over every s-file in SCCS dir
|
||||||
|
#
|
||||||
|
foreach sfile (SCCS/s.*)
|
||||||
|
# get rid of the "s." at the beginning of the name
|
||||||
|
set file = `echo $sfile:t | sed -e "s/^..//"`
|
||||||
|
|
||||||
|
# work on each rev of that file in ascending order
|
||||||
|
set firsttime = 1
|
||||||
|
sccs prs $file | grep "^D " | awk '{print $2}' | sed -e 's/\./ /g' | sort -n -u +0 +1 +2 +3 +4 +5 +6 +7 +8 | sed -e 's/ /./g' > $revfile
|
||||||
|
foreach rev (`cat $revfile`)
|
||||||
|
if ($status != 0) goto ERROR
|
||||||
|
|
||||||
|
# get file into current dir and get stats
|
||||||
|
set date = `sccs prs -r$rev $file | grep "^D " | awk '{printf("19%s %s", $3, $4); exit}'`
|
||||||
|
set author = `sccs prs -r$rev $file | grep "^D " | awk '{print $5; exit}'`
|
||||||
|
echo ""
|
||||||
|
echo "==> file $file, rev=$rev, date=$date, author=$author"
|
||||||
|
sccs edit -r$rev $file >>& $logfile
|
||||||
|
if ($status != 0) goto ERROR
|
||||||
|
echo checked out of SCCS
|
||||||
|
|
||||||
|
# add RCS keywords in place of SCCS keywords
|
||||||
|
sed -f $sedfile $file > $tmpfile
|
||||||
|
if ($status != 0) goto ERROR
|
||||||
|
echo performed keyword substitutions
|
||||||
|
cp $tmpfile $file
|
||||||
|
|
||||||
|
# check file into RCS
|
||||||
|
if ($firsttime) then
|
||||||
|
set firsttime = 0
|
||||||
|
if ($nodesc) then
|
||||||
|
echo about to do ci
|
||||||
|
echo ci -f -r$rev -d"$date" -w$author -t$emptyfile $file
|
||||||
|
ci -f -r$rev -d"$date" -w$author -t$emptyfile $file < $initialfile >>& $logfile
|
||||||
|
if ($status != 0) goto ERROR
|
||||||
|
echo initial rev checked into RCS without description
|
||||||
|
else
|
||||||
|
echo ""
|
||||||
|
echo Enter a brief description of the file $file \(end w/ Ctrl-D\):
|
||||||
|
cat > $tmpfile
|
||||||
|
ci -f -r$rev -d"$date" -w$author -t$tmpfile $file < $initialfile >>& $logfile
|
||||||
|
if ($status != 0) goto ERROR
|
||||||
|
echo initial rev checked into RCS
|
||||||
|
endif
|
||||||
|
else
|
||||||
|
# get RCS lock
|
||||||
|
set lckrev = `echo $rev | sed -e 's/\.[0-9]*$//'`
|
||||||
|
if ("$lckrev" =~ [0-9]*.*) then
|
||||||
|
# need to lock the brach -- it is OK if the lock fails
|
||||||
|
rcs -l$lckrev $file >>& $logfile
|
||||||
|
else
|
||||||
|
# need to lock the trunk -- must succeed
|
||||||
|
rcs -l $file >>& $logfile
|
||||||
|
if ($status != 0) goto ERROR
|
||||||
|
endif
|
||||||
|
echo got lock
|
||||||
|
sccs prs -r$rev $file | grep "." > $tmpfile
|
||||||
|
# it's OK if grep fails here and gives status == 1
|
||||||
|
# put the delta message in $tmpfile
|
||||||
|
ed $tmpfile >>& $logfile <<EOF
|
||||||
|
/COMMENTS
|
||||||
|
1,.d
|
||||||
|
w
|
||||||
|
q
|
||||||
|
EOF
|
||||||
|
ci -f -r$rev -d"$date" -w$author $file < $tmpfile >>& $logfile
|
||||||
|
if ($status != 0) goto ERROR
|
||||||
|
echo checked into RCS
|
||||||
|
endif
|
||||||
|
sccs unedit $file >>& $logfile
|
||||||
|
if ($status != 0) goto ERROR
|
||||||
|
end
|
||||||
|
rm -f $file
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
############################################################
|
||||||
|
# Clean up
|
||||||
|
#
|
||||||
|
echo cleaning up...
|
||||||
|
mv SCCS old-SCCS
|
||||||
|
rm -f $tmpfile $emptyfile $initialfile $sedfile
|
||||||
|
echo ===================================================
|
||||||
|
echo " Conversion Completed Successfully"
|
||||||
|
echo ""
|
||||||
|
echo " SCCS history now in old-SCCS/"
|
||||||
|
echo ===================================================
|
||||||
|
set exitval = 0
|
||||||
|
goto cleanup
|
||||||
|
|
||||||
|
ERROR:
|
||||||
|
foreach f (`sccs tell`)
|
||||||
|
sccs unedit $f
|
||||||
|
end
|
||||||
|
echo ""
|
||||||
|
echo ""
|
||||||
|
echo Danger\! Danger\!
|
||||||
|
echo Some command exited with a non-zero exit status.
|
||||||
|
echo Log file exists in $logfile.
|
||||||
|
echo ""
|
||||||
|
echo Incomplete history in ./RCS -- remove it
|
||||||
|
echo Original unchanged history in ./SCCS
|
||||||
|
set exitval = 1
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
# leave log file
|
||||||
|
rm -f $tmpfile $emptyfile $initialfile $sedfile $revfile
|
||||||
|
|
||||||
|
exit $exitval
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
PROG = cvs
|
||||||
|
CFLAGS += -I${.CURDIR}/../lib \
|
||||||
|
-DDIRENT -DSTDC_HEADERS -DPOSIX -DBROKEN_SIGISMEMBER \
|
||||||
|
-DFTIME_MISSING -DHAVE_TIMEZONE -DUTIME_NULL_MISSING
|
||||||
|
|
||||||
|
LDADD= -L${.CURDIR}/../lib/obj -lcvs
|
||||||
|
|
||||||
|
SRCS = add.c admin.c checkin.c checkout.c classify.c commit.c \
|
||||||
|
create_adm.c diff.c entries.c find_names.c history.c ignore.c \
|
||||||
|
import.c lock.c log.c logmsg.c main.c rcs.c modules.c \
|
||||||
|
no_diff.c parseinfo.c patch.c recurse.c release.c remove.c repos.c rtag.c \
|
||||||
|
status.c tag.c update.c vers_ts.c version.c
|
||||||
|
|
||||||
|
MAN1= cvs.0
|
||||||
|
MAN5= cvs.0
|
||||||
|
|
||||||
|
.include <bsd.prog.mk>
|
||||||
|
.include "../../Makefile.inc"
|
||||||
@@ -0,0 +1,447 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 1992, Brian Berliner and Jeff Polk
|
||||||
|
* Copyright (c) 1989-1992, Brian Berliner
|
||||||
|
*
|
||||||
|
* You may distribute under the terms of the GNU General Public License as
|
||||||
|
* specified in the README file that comes with the CVS 1.3 kit.
|
||||||
|
*
|
||||||
|
* Add
|
||||||
|
*
|
||||||
|
* Adds a file or directory to the RCS source repository. For a file,
|
||||||
|
* the entry is marked as "needing to be added" in the user's own CVS
|
||||||
|
* directory, and really added to the repository when it is committed.
|
||||||
|
* For a directory, it is added at the appropriate place in the source
|
||||||
|
* repository and a CVS directory is generated within the directory.
|
||||||
|
*
|
||||||
|
* The -m option is currently the only supported option. Some may wish to
|
||||||
|
* supply standard "rcs" options here, but I've found that this causes more
|
||||||
|
* trouble than anything else.
|
||||||
|
*
|
||||||
|
* The user files or directories must already exist. For a directory, it must
|
||||||
|
* not already have a CVS file in it.
|
||||||
|
*
|
||||||
|
* An "add" on a file that has been "remove"d but not committed will cause the
|
||||||
|
* file to be resurrected.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "cvs.h"
|
||||||
|
|
||||||
|
#ifndef lint
|
||||||
|
static char rcsid[] = "@(#)add.c 1.46 92/04/03";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __STDC__
|
||||||
|
static int add_directory (char *repository, char *dir);
|
||||||
|
static int build_entry (char *repository, char *user, char *options,
|
||||||
|
char *message, List * entries);
|
||||||
|
#else
|
||||||
|
static int add_directory ();
|
||||||
|
static int build_entry ();
|
||||||
|
#endif /* __STDC__ */
|
||||||
|
|
||||||
|
static char *add_usage[] =
|
||||||
|
{
|
||||||
|
"Usage: %s %s [-k rcs-kflag] [-m message] files...\n",
|
||||||
|
"\t-k\tUse \"rcs-kflag\" to add the file with the specified kflag.\n",
|
||||||
|
"\t-m\tUse \"message\" for the creation log.\n",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
int
|
||||||
|
add (argc, argv)
|
||||||
|
int argc;
|
||||||
|
char *argv[];
|
||||||
|
{
|
||||||
|
char message[MAXMESGLEN];
|
||||||
|
char *user;
|
||||||
|
int i;
|
||||||
|
char *repository;
|
||||||
|
int c;
|
||||||
|
int err = 0;
|
||||||
|
int added_files = 0;
|
||||||
|
char *options = NULL;
|
||||||
|
List *entries;
|
||||||
|
Vers_TS *vers;
|
||||||
|
|
||||||
|
if (argc == 1 || argc == -1)
|
||||||
|
usage (add_usage);
|
||||||
|
|
||||||
|
/* parse args */
|
||||||
|
message[0] = '\0';
|
||||||
|
optind = 1;
|
||||||
|
while ((c = gnu_getopt (argc, argv, "k:m:")) != -1)
|
||||||
|
{
|
||||||
|
switch (c)
|
||||||
|
{
|
||||||
|
case 'k':
|
||||||
|
if (options)
|
||||||
|
free (options);
|
||||||
|
options = RCS_check_kflag (optarg);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'm':
|
||||||
|
if (strlen (optarg) >= sizeof (message))
|
||||||
|
{
|
||||||
|
error (0, 0, "warning: message too long; truncated!");
|
||||||
|
(void) strncpy (message, optarg, sizeof (message));
|
||||||
|
message[sizeof (message) - 1] = '\0';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
(void) strcpy (message, optarg);
|
||||||
|
break;
|
||||||
|
case '?':
|
||||||
|
default:
|
||||||
|
usage (add_usage);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
argc -= optind;
|
||||||
|
argv += optind;
|
||||||
|
|
||||||
|
if (argc <= 0)
|
||||||
|
usage (add_usage);
|
||||||
|
|
||||||
|
/* find the repository associated with our current dir */
|
||||||
|
repository = Name_Repository ((char *) NULL, (char *) NULL);
|
||||||
|
entries = ParseEntries (0);
|
||||||
|
|
||||||
|
/* walk the arg list adding files/dirs */
|
||||||
|
for (i = 0; i < argc; i++)
|
||||||
|
{
|
||||||
|
int begin_err = err;
|
||||||
|
|
||||||
|
user = argv[i];
|
||||||
|
if (index (user, '/') != NULL)
|
||||||
|
{
|
||||||
|
error (0, 0,
|
||||||
|
"cannot add files with '/' in their name; %s not added", user);
|
||||||
|
err++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
vers = Version_TS (repository, options, (char *) NULL, (char *) NULL,
|
||||||
|
user, 0, 0, entries, (List *) NULL);
|
||||||
|
if (vers->vn_user == NULL)
|
||||||
|
{
|
||||||
|
/* No entry available, ts_rcs is invalid */
|
||||||
|
if (vers->vn_rcs == NULL)
|
||||||
|
{
|
||||||
|
/* There is no RCS file either */
|
||||||
|
if (vers->ts_user == NULL)
|
||||||
|
{
|
||||||
|
/* There is no user file either */
|
||||||
|
error (0, 0, "nothing known about %s", user);
|
||||||
|
err++;
|
||||||
|
}
|
||||||
|
else if (!isdir (user))
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* See if a directory exists in the repository with
|
||||||
|
* the same name. If so, blow this request off.
|
||||||
|
*/
|
||||||
|
char dname[PATH_MAX];
|
||||||
|
(void) sprintf (dname, "%s/%s", repository, user);
|
||||||
|
if (isdir (dname))
|
||||||
|
{
|
||||||
|
error (0, 0,
|
||||||
|
"cannot add file `%s' since the directory",
|
||||||
|
user);
|
||||||
|
error (0, 0, "`%s' already exists in the repository",
|
||||||
|
dname);
|
||||||
|
error (1, 0, "illegal filename overlap");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* There is a user file, so build the entry for it */
|
||||||
|
if (build_entry (repository, user, vers->options,
|
||||||
|
message, entries) != 0)
|
||||||
|
err++;
|
||||||
|
else if (!quiet)
|
||||||
|
{
|
||||||
|
added_files++;
|
||||||
|
error (0, 0, "scheduling file `%s' for addition",
|
||||||
|
user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
/*
|
||||||
|
* There is an RCS file already, so somebody else must've
|
||||||
|
* added it
|
||||||
|
*/
|
||||||
|
error (0, 0, "%s added independently by second party", user);
|
||||||
|
err++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (vers->vn_user[0] == '0' && vers->vn_user[1] == '\0')
|
||||||
|
{
|
||||||
|
|
||||||
|
/*
|
||||||
|
* An entry for a new-born file, ts_rcs is dummy, but that is
|
||||||
|
* inappropriate here
|
||||||
|
*/
|
||||||
|
error (0, 0, "%s has already been entered", user);
|
||||||
|
err++;
|
||||||
|
}
|
||||||
|
else if (vers->vn_user[0] == '-')
|
||||||
|
{
|
||||||
|
/* An entry for a removed file, ts_rcs is invalid */
|
||||||
|
if (vers->ts_user == NULL)
|
||||||
|
{
|
||||||
|
/* There is no user file (as it should be) */
|
||||||
|
if (vers->vn_rcs == NULL)
|
||||||
|
{
|
||||||
|
|
||||||
|
/*
|
||||||
|
* There is no RCS file, so somebody else must've removed
|
||||||
|
* it from under us
|
||||||
|
*/
|
||||||
|
error (0, 0,
|
||||||
|
"cannot resurrect %s; RCS file removed by second party", user);
|
||||||
|
err++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
/*
|
||||||
|
* There is an RCS file, so remove the "-" from the
|
||||||
|
* version number and restore the file
|
||||||
|
*/
|
||||||
|
char *tmp = xmalloc (strlen (user) + 50);
|
||||||
|
|
||||||
|
(void) strcpy (tmp, vers->vn_user + 1);
|
||||||
|
(void) strcpy (vers->vn_user, tmp);
|
||||||
|
(void) sprintf (tmp, "Resurrected %s", user);
|
||||||
|
Register (entries, user, vers->vn_user, tmp, vers->options,
|
||||||
|
vers->tag, vers->date);
|
||||||
|
free (tmp);
|
||||||
|
|
||||||
|
/* XXX - bugs here; this really resurrect the head */
|
||||||
|
if (update (2, argv + i - 1) == 0)
|
||||||
|
{
|
||||||
|
error (0, 0, "%s, version %s, resurrected", user,
|
||||||
|
vers->vn_user);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
error (0, 0, "could not resurrect %s", user);
|
||||||
|
err++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* The user file shouldn't be there */
|
||||||
|
error (0, 0, "%s should be removed and is still there (or is back again)", user);
|
||||||
|
err++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* A normal entry, ts_rcs is valid, so it must already be there */
|
||||||
|
error (0, 0, "%s already exists, with version number %s", user,
|
||||||
|
vers->vn_user);
|
||||||
|
err++;
|
||||||
|
}
|
||||||
|
freevers_ts (&vers);
|
||||||
|
|
||||||
|
/* passed all the checks. Go ahead and add it if its a directory */
|
||||||
|
if (begin_err == err && isdir (user))
|
||||||
|
{
|
||||||
|
err += add_directory (repository, user);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (added_files)
|
||||||
|
error (0, 0, "use 'cvs commit' to add %s permanently",
|
||||||
|
(added_files == 1) ? "this file" : "these files");
|
||||||
|
dellist (&entries);
|
||||||
|
return (err);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The specified user file is really a directory. So, let's make sure that
|
||||||
|
* it is created in the RCS source repository, and that the user's directory
|
||||||
|
* is updated to include a CVS directory.
|
||||||
|
*
|
||||||
|
* Returns 1 on failure, 0 on success.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
add_directory (repository, dir)
|
||||||
|
char *repository;
|
||||||
|
char *dir;
|
||||||
|
{
|
||||||
|
char cwd[PATH_MAX], rcsdir[PATH_MAX];
|
||||||
|
char message[PATH_MAX + 100];
|
||||||
|
char *tag, *date;
|
||||||
|
|
||||||
|
if (index (dir, '/') != NULL)
|
||||||
|
{
|
||||||
|
error (0, 0,
|
||||||
|
"directory %s not added; must be a direct sub-directory", dir);
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
if (strcmp (dir, CVSADM) == 0 || strcmp (dir, OCVSADM) == 0)
|
||||||
|
{
|
||||||
|
error (0, 0, "cannot add a `%s' or a `%s' directory", CVSADM, OCVSADM);
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* before we do anything else, see if we have any per-directory tags */
|
||||||
|
ParseTag (&tag, &date);
|
||||||
|
|
||||||
|
/* now, remember where we were, so we can get back */
|
||||||
|
if (getwd (cwd) == NULL)
|
||||||
|
{
|
||||||
|
error (0, 0, "cannot get working directory: %s", cwd);
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
if (chdir (dir) < 0)
|
||||||
|
{
|
||||||
|
error (0, errno, "cannot chdir to %s", dir);
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
if (isfile (CVSADM) || isfile (OCVSADM))
|
||||||
|
{
|
||||||
|
error (0, 0,
|
||||||
|
"%s/%s (or %s/%s) already exists", dir, CVSADM, dir, OCVSADM);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
(void) sprintf (rcsdir, "%s/%s", repository, dir);
|
||||||
|
if (isfile (rcsdir) && !isdir (rcsdir))
|
||||||
|
{
|
||||||
|
error (0, 0, "%s is not a directory; %s not added", rcsdir, dir);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* setup the log message */
|
||||||
|
(void) sprintf (message, "Directory %s added to the repository\n", rcsdir);
|
||||||
|
if (tag)
|
||||||
|
{
|
||||||
|
(void) strcat (message, "--> Using per-directory sticky tag `");
|
||||||
|
(void) strcat (message, tag);
|
||||||
|
(void) strcat (message, "'\n");
|
||||||
|
}
|
||||||
|
if (date)
|
||||||
|
{
|
||||||
|
(void) strcat (message, "--> Using per-directory sticky date `");
|
||||||
|
(void) strcat (message, date);
|
||||||
|
(void) strcat (message, "'\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isdir (rcsdir))
|
||||||
|
{
|
||||||
|
mode_t omask;
|
||||||
|
char line[MAXLINELEN];
|
||||||
|
Node *p;
|
||||||
|
List *ulist;
|
||||||
|
|
||||||
|
(void) printf ("Add directory %s to the repository (y/n) [n] ? ",
|
||||||
|
rcsdir);
|
||||||
|
(void) fflush (stdout);
|
||||||
|
clearerr (stdin);
|
||||||
|
if (fgets (line, sizeof (line), stdin) == NULL ||
|
||||||
|
(line[0] != 'y' && line[0] != 'Y'))
|
||||||
|
{
|
||||||
|
error (0, 0, "directory %s not added", rcsdir);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
omask = umask (2);
|
||||||
|
if (mkdir (rcsdir, 0777) < 0)
|
||||||
|
{
|
||||||
|
error (0, errno, "cannot mkdir %s", rcsdir);
|
||||||
|
(void) umask ((int) omask);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
(void) umask ((int) omask);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set up an update list with a single title node for Update_Logfile
|
||||||
|
*/
|
||||||
|
ulist = getlist ();
|
||||||
|
p = getnode ();
|
||||||
|
p->type = UPDATE;
|
||||||
|
p->delproc = update_delproc;
|
||||||
|
p->key = xstrdup ("- New directory");
|
||||||
|
p->data = (char *) T_TITLE;
|
||||||
|
(void) addnode (ulist, p);
|
||||||
|
Update_Logfile (rcsdir, message, (char *) NULL, (FILE *) NULL, ulist);
|
||||||
|
dellist (&ulist);
|
||||||
|
}
|
||||||
|
|
||||||
|
Create_Admin (".", rcsdir, tag, date);
|
||||||
|
if (tag)
|
||||||
|
free (tag);
|
||||||
|
if (date)
|
||||||
|
free (date);
|
||||||
|
|
||||||
|
(void) printf ("%s", message);
|
||||||
|
out:
|
||||||
|
if (chdir (cwd) < 0)
|
||||||
|
error (1, errno, "cannot chdir to %s", cwd);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Builds an entry for a new file and sets up "CVS/file",[pt] by
|
||||||
|
* interrogating the user. Returns non-zero on error.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
build_entry (repository, user, options, message, entries)
|
||||||
|
char *repository;
|
||||||
|
char *user;
|
||||||
|
char *options;
|
||||||
|
char *message;
|
||||||
|
List *entries;
|
||||||
|
{
|
||||||
|
char fname[PATH_MAX];
|
||||||
|
char line[MAXLINELEN];
|
||||||
|
FILE *fp;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* There may be an old file with the same name in the Attic! This is,
|
||||||
|
* perhaps, an awkward place to check for this, but other places are
|
||||||
|
* equally awkward.
|
||||||
|
*/
|
||||||
|
(void) sprintf (fname, "%s/%s/%s%s", repository, CVSATTIC, user, RCSEXT);
|
||||||
|
if (isreadable (fname))
|
||||||
|
{
|
||||||
|
error (0, 0, "there is an old file %s already in %s/%s", user,
|
||||||
|
repository, CVSATTIC);
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (noexec)
|
||||||
|
return (0);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The options for the "add" command are store in the file CVS/user,p
|
||||||
|
*/
|
||||||
|
(void) sprintf (fname, "%s/%s%s", CVSADM, user, CVSEXT_OPT);
|
||||||
|
fp = open_file (fname, "w+");
|
||||||
|
if (fclose (fp) == EOF)
|
||||||
|
error(1, errno, "cannot close %s", fname);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* And the requested log is read directly from the user and stored in the
|
||||||
|
* file user,t. If the "message" argument is set, use it as the
|
||||||
|
* initial creation log (which typically describes the file).
|
||||||
|
*/
|
||||||
|
(void) sprintf (fname, "%s/%s%s", CVSADM, user, CVSEXT_LOG);
|
||||||
|
fp = open_file (fname, "w+");
|
||||||
|
if (*message && fputs (message, fp) == EOF)
|
||||||
|
error (1, errno, "cannot write to %s", fname);
|
||||||
|
if (fclose(fp) == EOF)
|
||||||
|
error(1, errno, "cannot close %s", fname);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create the entry now, since this allows the user to interrupt us above
|
||||||
|
* without needing to clean anything up (well, we could clean up the ,p
|
||||||
|
* and ,t files, but who cares).
|
||||||
|
*/
|
||||||
|
(void) sprintf (line, "Initial %s", user);
|
||||||
|
Register (entries, user, "0", line, options, (char *) 0, (char *) 0);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 1992, Brian Berliner and Jeff Polk
|
||||||
|
* Copyright (c) 1989-1992, Brian Berliner
|
||||||
|
*
|
||||||
|
* You may distribute under the terms of the GNU General Public License as
|
||||||
|
* specified in the README file that comes with the CVS 1.3 kit.
|
||||||
|
*
|
||||||
|
* Administration
|
||||||
|
*
|
||||||
|
* For now, this is basically a front end for rcs. All options are passed
|
||||||
|
* directly on.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "cvs.h"
|
||||||
|
|
||||||
|
#ifndef lint
|
||||||
|
static char rcsid[] = "@(#)admin.c 1.17 92/03/31";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __STDC__
|
||||||
|
static Dtype admin_dirproc (char *dir, char *repos, char *update_dir);
|
||||||
|
static int admin_fileproc (char *file, char *update_dir,
|
||||||
|
char *repository, List *entries,
|
||||||
|
List *srcfiles);
|
||||||
|
#else
|
||||||
|
static int admin_fileproc ();
|
||||||
|
static Dtype admin_dirproc ();
|
||||||
|
#endif /* __STDC__ */
|
||||||
|
|
||||||
|
static char *admin_usage[] =
|
||||||
|
{
|
||||||
|
"Usage: %s %s rcs-options files...\n",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
static int ac;
|
||||||
|
static char **av;
|
||||||
|
|
||||||
|
int
|
||||||
|
admin (argc, argv)
|
||||||
|
int argc;
|
||||||
|
char *argv[];
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (argc <= 1)
|
||||||
|
usage (admin_usage);
|
||||||
|
|
||||||
|
/* skip all optional arguments to see if we have any file names */
|
||||||
|
for (ac = 1; ac < argc; ac++)
|
||||||
|
if (argv[ac][0] != '-')
|
||||||
|
break;
|
||||||
|
argc -= ac;
|
||||||
|
av = argv + 1;
|
||||||
|
argv += ac;
|
||||||
|
ac--;
|
||||||
|
if (ac == 0 || argc == 0)
|
||||||
|
usage (admin_usage);
|
||||||
|
|
||||||
|
/* start the recursion processor */
|
||||||
|
err = start_recursion (admin_fileproc, (int (*) ()) NULL, admin_dirproc,
|
||||||
|
(int (*) ()) NULL, argc, argv, 0,
|
||||||
|
W_LOCAL, 0, 1, (char *) NULL, 1);
|
||||||
|
return (err);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Called to run "rcs" on a particular file.
|
||||||
|
*/
|
||||||
|
/* ARGSUSED */
|
||||||
|
static int
|
||||||
|
admin_fileproc (file, update_dir, repository, entries, srcfiles)
|
||||||
|
char *file;
|
||||||
|
char *update_dir;
|
||||||
|
char *repository;
|
||||||
|
List *entries;
|
||||||
|
List *srcfiles;
|
||||||
|
{
|
||||||
|
Vers_TS *vers;
|
||||||
|
char *version;
|
||||||
|
char **argv;
|
||||||
|
int argc;
|
||||||
|
int retcode = 0;
|
||||||
|
|
||||||
|
vers = Version_TS (repository, (char *) NULL, (char *) NULL, (char *) NULL,
|
||||||
|
file, 0, 0, entries, srcfiles);
|
||||||
|
|
||||||
|
version = vers->vn_user;
|
||||||
|
if (version == NULL)
|
||||||
|
return (0);
|
||||||
|
else if (strcmp (version, "0") == 0)
|
||||||
|
{
|
||||||
|
error (0, 0, "cannot admin newly added file `%s'", file);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
run_setup ("%s%s", Rcsbin, RCS);
|
||||||
|
for (argc = ac, argv = av; argc; argc--, argv++)
|
||||||
|
run_arg (*argv);
|
||||||
|
run_arg (vers->srcfile->path);
|
||||||
|
if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL)) != 0)
|
||||||
|
{
|
||||||
|
if (!quiet)
|
||||||
|
error (0, retcode == -1 ? errno : 0,
|
||||||
|
"%s failed for `%s'", RCS, file);
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Print a warm fuzzy message
|
||||||
|
*/
|
||||||
|
/* ARGSUSED */
|
||||||
|
static Dtype
|
||||||
|
admin_dirproc (dir, repos, update_dir)
|
||||||
|
char *dir;
|
||||||
|
char *repos;
|
||||||
|
char *update_dir;
|
||||||
|
{
|
||||||
|
if (!quiet)
|
||||||
|
error (0, 0, "Administrating %s", update_dir);
|
||||||
|
return (R_PROCESS);
|
||||||
|
}
|
||||||
@@ -0,0 +1,135 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 1992, Brian Berliner and Jeff Polk
|
||||||
|
* Copyright (c) 1989-1992, Brian Berliner
|
||||||
|
*
|
||||||
|
* You may distribute under the terms of the GNU General Public License as
|
||||||
|
* specified in the README file that comes with the CVS 1.3 kit.
|
||||||
|
*
|
||||||
|
* Check In
|
||||||
|
*
|
||||||
|
* Does a very careful checkin of the file "user", and tries not to spoil its
|
||||||
|
* modification time (to avoid needless recompilations). When RCS ID keywords
|
||||||
|
* get expanded on checkout, however, the modification time is updated and
|
||||||
|
* there is no good way to get around this.
|
||||||
|
*
|
||||||
|
* Returns non-zero on error.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "cvs.h"
|
||||||
|
|
||||||
|
#ifndef lint
|
||||||
|
static char rcsid[] = "@(#)checkin.c 1.40 92/03/31";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int
|
||||||
|
Checkin (type, file, repository, rcs, rev, tag, message, entries)
|
||||||
|
int type;
|
||||||
|
char *file;
|
||||||
|
char *repository;
|
||||||
|
char *rcs;
|
||||||
|
char *rev;
|
||||||
|
char *tag;
|
||||||
|
char *message;
|
||||||
|
List *entries;
|
||||||
|
{
|
||||||
|
char fname[PATH_MAX];
|
||||||
|
Vers_TS *vers;
|
||||||
|
|
||||||
|
(void) printf ("Checking in %s;\n", file);
|
||||||
|
(void) sprintf (fname, "%s/%s%s", CVSADM, CVSPREFIX, file);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Move the user file to a backup file, so as to preserve its
|
||||||
|
* modification times, then place a copy back in the original file name
|
||||||
|
* for the checkin and checkout.
|
||||||
|
*/
|
||||||
|
if (!noexec)
|
||||||
|
copy_file (file, fname);
|
||||||
|
|
||||||
|
run_setup ("%s%s -f %s%s", Rcsbin, RCS_CI,
|
||||||
|
rev ? "-r" : "", rev ? rev : "");
|
||||||
|
run_args ("-m%s", message);
|
||||||
|
run_arg (rcs);
|
||||||
|
|
||||||
|
switch (run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL))
|
||||||
|
{
|
||||||
|
case 0: /* everything normal */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The checkin succeeded, so now check the new file back out and
|
||||||
|
* see if it matches exactly with the one we checked in. If it
|
||||||
|
* does, just move the original user file back, thus preserving
|
||||||
|
* the modes; otherwise, we have no recourse but to leave the
|
||||||
|
* newly checkout file as the user file and remove the old
|
||||||
|
* original user file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* XXX - make sure -k options are used on the co; and tag/date? */
|
||||||
|
run_setup ("%s%s -q %s%s", Rcsbin, RCS_CO,
|
||||||
|
rev ? "-r" : "", rev ? rev : "");
|
||||||
|
run_arg (rcs);
|
||||||
|
(void) run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL);
|
||||||
|
xchmod (file, 1);
|
||||||
|
if (xcmp (file, fname) == 0)
|
||||||
|
rename_file (fname, file);
|
||||||
|
else
|
||||||
|
(void) unlink_file (fname);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we want read-only files, muck the permissions here, before
|
||||||
|
* getting the file time-stamp.
|
||||||
|
*/
|
||||||
|
if (cvswrite == FALSE)
|
||||||
|
xchmod (file, 0);
|
||||||
|
|
||||||
|
/* for added files with symbolic tags, need to add the tag too */
|
||||||
|
if (type == 'A' && tag && !isdigit (*tag))
|
||||||
|
{
|
||||||
|
run_setup ("%s%s -q -N%s:%s", Rcsbin, RCS, tag, rev);
|
||||||
|
run_arg (rcs);
|
||||||
|
(void) run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* re-register with the new data */
|
||||||
|
vers = Version_TS (repository, (char *) NULL, tag, (char *) NULL,
|
||||||
|
file, 1, 1, entries, (List *) NULL);
|
||||||
|
if (strcmp (vers->options, "-V4") == 0)
|
||||||
|
vers->options[0] = '\0';
|
||||||
|
Register (entries, file, vers->vn_rcs, vers->ts_user, vers->options,
|
||||||
|
vers->tag, vers->date);
|
||||||
|
history_write (type, (char *) 0, vers->vn_rcs, file, repository);
|
||||||
|
freevers_ts (&vers);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case -1: /* fork failed */
|
||||||
|
if (!noexec)
|
||||||
|
error (1, errno, "could not check in %s -- fork failed", file);
|
||||||
|
return (1);
|
||||||
|
|
||||||
|
default: /* ci failed */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The checkin failed, for some unknown reason, so we restore the
|
||||||
|
* original user file, print an error, and return an error
|
||||||
|
*/
|
||||||
|
if (!noexec)
|
||||||
|
{
|
||||||
|
rename_file (fname, file);
|
||||||
|
error (0, 0, "could not check in %s", file);
|
||||||
|
}
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When checking in a specific revision, we may have locked the wrong
|
||||||
|
* branch, so to be sure, we do an extra unlock here before
|
||||||
|
* returning.
|
||||||
|
*/
|
||||||
|
if (rev)
|
||||||
|
{
|
||||||
|
run_setup ("%s%s -q -u", Rcsbin, RCS);
|
||||||
|
run_arg (rcs);
|
||||||
|
(void) run_exec (RUN_TTY, RUN_TTY, DEVNULL, RUN_NORMAL);
|
||||||
|
}
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
@@ -0,0 +1,718 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 1992, Brian Berliner and Jeff Polk
|
||||||
|
* Copyright (c) 1989-1992, Brian Berliner
|
||||||
|
*
|
||||||
|
* You may distribute under the terms of the GNU General Public License as
|
||||||
|
* specified in the README file that comes with the CVS 1.3 kit.
|
||||||
|
*
|
||||||
|
* Create Version
|
||||||
|
*
|
||||||
|
* "checkout" creates a "version" of an RCS repository. This version is owned
|
||||||
|
* totally by the user and is actually an independent copy, to be dealt with
|
||||||
|
* as seen fit. Once "checkout" has been called in a given directory, it
|
||||||
|
* never needs to be called again. The user can keep up-to-date by calling
|
||||||
|
* "update" when he feels like it; this will supply him with a merge of his
|
||||||
|
* own modifications and the changes made in the RCS original. See "update"
|
||||||
|
* for details.
|
||||||
|
*
|
||||||
|
* "checkout" can be given a list of directories or files to be updated and in
|
||||||
|
* the case of a directory, will recursivley create any sub-directories that
|
||||||
|
* exist in the repository.
|
||||||
|
*
|
||||||
|
* When the user is satisfied with his own modifications, the present version
|
||||||
|
* can be committed by "commit"; this keeps the present version in tact,
|
||||||
|
* usually.
|
||||||
|
*
|
||||||
|
* The call is cvs checkout [options] <module-name>...
|
||||||
|
*
|
||||||
|
* "checkout" creates a directory ./CVS, in which it keeps its administration,
|
||||||
|
* in two files, Repository and Entries. The first contains the name of the
|
||||||
|
* repository. The second contains one line for each registered file,
|
||||||
|
* consisting of the version number it derives from, its time stamp at
|
||||||
|
* derivation time and its name. Both files are normal files and can be
|
||||||
|
* edited by the user, if necessary (when the repository is moved, e.g.)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "cvs.h"
|
||||||
|
|
||||||
|
#ifndef lint
|
||||||
|
static char rcsid[] = "@(#)checkout.c 1.67 92/04/10";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __STDC__
|
||||||
|
static char *findslash (char *start, char *p);
|
||||||
|
static int build_dirs_and_chdir (char *dir, char *prepath, char *realdir,
|
||||||
|
int sticky);
|
||||||
|
static int checkout_proc (int *pargc, char *argv[], char *where,
|
||||||
|
char *mwhere, char *mfile, int shorten,
|
||||||
|
int local_specified, char *omodule,
|
||||||
|
char *msg);
|
||||||
|
#else
|
||||||
|
static int checkout_proc ();
|
||||||
|
static char *findslash ();
|
||||||
|
static int build_dirs_and_chdir ();
|
||||||
|
#endif /* __STDC__ */
|
||||||
|
|
||||||
|
static char *checkout_usage[] =
|
||||||
|
{
|
||||||
|
"Usage:\n %s %s [-ANPQcflnpqs] [-r rev | -D date] [-d dir] [-k kopt] modules...\n",
|
||||||
|
"\t-A\tReset any sticky tags/date/kopts.\n",
|
||||||
|
"\t-N\tDon't shorten module paths if -d specified.\n",
|
||||||
|
"\t-P\tPrune empty directories.\n",
|
||||||
|
"\t-Q\tReally quiet.\n",
|
||||||
|
"\t-c\t\"cat\" the module database.\n",
|
||||||
|
"\t-f\tForce a head revision match if tag/date not found.\n",
|
||||||
|
"\t-l\tLocal directory only, not recursive\n",
|
||||||
|
"\t-n\tDo not run module program (if any).\n",
|
||||||
|
"\t-p\tCheck out files to standard output.\n",
|
||||||
|
"\t-q\tSomewhat quiet.\n",
|
||||||
|
"\t-s\tLike -c, but include module status.\n",
|
||||||
|
"\t-r rev\tCheck out revision or tag. (implies -P)\n",
|
||||||
|
"\t-D date\tCheck out revisions as of date. (implies -P)\n",
|
||||||
|
"\t-d dir\tCheck out into dir instead of module name.\n",
|
||||||
|
"\t-k kopt\tUse RCS kopt -k option on checkout.\n",
|
||||||
|
"\t-j rev\tMerge in changes made between current revision and rev.\n",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
static char *export_usage[] =
|
||||||
|
{
|
||||||
|
"Usage: %s %s [-NPQflnq] [-r rev | -D date] [-d dir] module...\n",
|
||||||
|
"\t-N\tDon't shorten module paths if -d specified.\n",
|
||||||
|
"\t-Q\tReally quiet.\n",
|
||||||
|
"\t-f\tForce a head revision match if tag/date not found.\n",
|
||||||
|
"\t-l\tLocal directory only, not recursive\n",
|
||||||
|
"\t-n\tDo not run module program (if any).\n",
|
||||||
|
"\t-q\tSomewhat quiet.\n",
|
||||||
|
"\t-r rev\tCheck out revision or tag. (implies -P)\n",
|
||||||
|
"\t-D date\tCheck out revisions as of date. (implies -P)\n",
|
||||||
|
"\t-d dir\tCheck out into dir instead of module name.\n",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
static int checkout_prune_dirs;
|
||||||
|
static int force_tag_match = 1;
|
||||||
|
static int pipeout;
|
||||||
|
static int aflag;
|
||||||
|
static char *options = NULL;
|
||||||
|
static char *tag = NULL;
|
||||||
|
static char *date = NULL;
|
||||||
|
static char *join_rev1 = NULL;
|
||||||
|
static char *join_rev2 = NULL;
|
||||||
|
static char *preload_update_dir = NULL;
|
||||||
|
|
||||||
|
int
|
||||||
|
checkout (argc, argv)
|
||||||
|
int argc;
|
||||||
|
char *argv[];
|
||||||
|
{
|
||||||
|
register int i;
|
||||||
|
int c;
|
||||||
|
DBM *db;
|
||||||
|
int cat = 0, err = 0, status = 0;
|
||||||
|
int run_module_prog = 1;
|
||||||
|
int local = 0;
|
||||||
|
int shorten = -1;
|
||||||
|
char *where = NULL;
|
||||||
|
char *valid_options, **valid_usage;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A smaller subset of options are allowed for the export command, which
|
||||||
|
* is essentially like checkout, except that it hard-codes certain
|
||||||
|
* options to be on (like -kv) and takes care to remove the CVS directory
|
||||||
|
* when it has done its duty
|
||||||
|
*/
|
||||||
|
if (strcmp (command_name, "export") == 0)
|
||||||
|
{
|
||||||
|
valid_options = "Nnd:flRQqr:D:";
|
||||||
|
valid_usage = export_usage;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
valid_options = "ANnk:d:flRpQqcsr:D:j:P";
|
||||||
|
valid_usage = checkout_usage;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argc == -1)
|
||||||
|
usage (valid_usage);
|
||||||
|
|
||||||
|
ign_setup ();
|
||||||
|
|
||||||
|
optind = 1;
|
||||||
|
while ((c = gnu_getopt (argc, argv, valid_options)) != -1)
|
||||||
|
{
|
||||||
|
switch (c)
|
||||||
|
{
|
||||||
|
case 'A':
|
||||||
|
aflag = 1;
|
||||||
|
break;
|
||||||
|
case 'N':
|
||||||
|
shorten = 0;
|
||||||
|
break;
|
||||||
|
case 'k':
|
||||||
|
if (options)
|
||||||
|
free (options);
|
||||||
|
options = RCS_check_kflag (optarg);
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
run_module_prog = 0;
|
||||||
|
break;
|
||||||
|
case 'Q':
|
||||||
|
really_quiet = 1;
|
||||||
|
/* FALL THROUGH */
|
||||||
|
case 'q':
|
||||||
|
quiet = 1;
|
||||||
|
break;
|
||||||
|
case 'l':
|
||||||
|
local = 1;
|
||||||
|
break;
|
||||||
|
case 'R':
|
||||||
|
local = 0;
|
||||||
|
break;
|
||||||
|
case 'P':
|
||||||
|
checkout_prune_dirs = 1;
|
||||||
|
break;
|
||||||
|
case 'p':
|
||||||
|
pipeout = 1;
|
||||||
|
run_module_prog = 0; /* don't run module prog when piping */
|
||||||
|
noexec = 1; /* so no locks will be created */
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
cat = 1;
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
where = optarg;
|
||||||
|
if (shorten == -1)
|
||||||
|
shorten = 1;
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
status = 1;
|
||||||
|
break;
|
||||||
|
case 'f':
|
||||||
|
force_tag_match = 0;
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
tag = optarg;
|
||||||
|
checkout_prune_dirs = 1;
|
||||||
|
break;
|
||||||
|
case 'D':
|
||||||
|
date = Make_Date (optarg);
|
||||||
|
checkout_prune_dirs = 1;
|
||||||
|
break;
|
||||||
|
case 'j':
|
||||||
|
if (join_rev2)
|
||||||
|
error (1, 0, "only two -j options can be specified");
|
||||||
|
if (join_rev1)
|
||||||
|
join_rev2 = optarg;
|
||||||
|
else
|
||||||
|
join_rev1 = optarg;
|
||||||
|
break;
|
||||||
|
case '?':
|
||||||
|
default:
|
||||||
|
usage (valid_usage);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
argc -= optind;
|
||||||
|
argv += optind;
|
||||||
|
|
||||||
|
if (shorten == -1)
|
||||||
|
shorten = 0;
|
||||||
|
|
||||||
|
if ((!(cat + status) && argc == 0) || ((cat + status) && argc != 0)
|
||||||
|
|| (tag && date))
|
||||||
|
usage (valid_usage);
|
||||||
|
|
||||||
|
if (where && pipeout)
|
||||||
|
error (1, 0, "-d and -p are mutually exclusive");
|
||||||
|
|
||||||
|
if (strcmp (command_name, "export") == 0)
|
||||||
|
{
|
||||||
|
if (!tag && !date)
|
||||||
|
{
|
||||||
|
error (0, 0, "must specify a tag or date");
|
||||||
|
usage (valid_usage);
|
||||||
|
}
|
||||||
|
if (tag && isdigit (tag[0]))
|
||||||
|
error (1, 0, "tag `%s' must be a symbolic tag", tag);
|
||||||
|
options = RCS_check_kflag ("v");/* -kv must be on */
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cat || status)
|
||||||
|
{
|
||||||
|
cat_module (status);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
db = open_module ();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* if we have more than one argument and where was specified, we make the
|
||||||
|
* where, cd into it, and try to shorten names as much as possible.
|
||||||
|
* Otherwise, we pass the where as a single argument to do_module.
|
||||||
|
*/
|
||||||
|
if (argc > 1 && where != NULL)
|
||||||
|
{
|
||||||
|
char repository[PATH_MAX];
|
||||||
|
|
||||||
|
(void) mkdir (where, 0777);
|
||||||
|
if (chdir (where) < 0)
|
||||||
|
error (1, errno, "cannot chdir to %s", where);
|
||||||
|
preload_update_dir = xstrdup (where);
|
||||||
|
where = (char *) NULL;
|
||||||
|
if (!isfile (CVSADM) && !isfile (OCVSADM))
|
||||||
|
{
|
||||||
|
(void) sprintf (repository, "%s/%s", CVSroot, CVSNULLREPOS);
|
||||||
|
if (!isfile (repository))
|
||||||
|
(void) mkdir (repository, 0777);
|
||||||
|
Create_Admin (".", repository, (char *) NULL, (char *) NULL);
|
||||||
|
if (!noexec)
|
||||||
|
{
|
||||||
|
FILE *fp;
|
||||||
|
|
||||||
|
fp = open_file (CVSADM_ENTSTAT, "w+");
|
||||||
|
if (fclose(fp) == EOF)
|
||||||
|
error(1, errno, "cannot close %s", CVSADM_ENTSTAT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* if where was specified (-d) and we have not taken care of it already
|
||||||
|
* with the multiple arg stuff, and it was not a simple directory name
|
||||||
|
* but rather a path, we strip off everything but the last component and
|
||||||
|
* attempt to cd to the indicated place. where then becomes simply the
|
||||||
|
* last component
|
||||||
|
*/
|
||||||
|
if (where != NULL && index (where, '/') != NULL)
|
||||||
|
{
|
||||||
|
char *slash;
|
||||||
|
|
||||||
|
slash = rindex (where, '/');
|
||||||
|
*slash = '\0';
|
||||||
|
|
||||||
|
if (chdir (where) < 0)
|
||||||
|
error (1, errno, "cannot chdir to %s", where);
|
||||||
|
|
||||||
|
preload_update_dir = xstrdup (where);
|
||||||
|
|
||||||
|
where = slash + 1;
|
||||||
|
if (*where == '\0')
|
||||||
|
where = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < argc; i++)
|
||||||
|
err += do_module (db, argv[i], CHECKOUT, "Updating", checkout_proc,
|
||||||
|
where, shorten, local, run_module_prog,
|
||||||
|
(char *) NULL);
|
||||||
|
close_module (db);
|
||||||
|
return (err);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* process_module calls us back here so we do the actual checkout stuff
|
||||||
|
*/
|
||||||
|
/* ARGSUSED */
|
||||||
|
static int
|
||||||
|
checkout_proc (pargc, argv, where, mwhere, mfile, shorten,
|
||||||
|
local_specified, omodule, msg)
|
||||||
|
int *pargc;
|
||||||
|
char *argv[];
|
||||||
|
char *where;
|
||||||
|
char *mwhere;
|
||||||
|
char *mfile;
|
||||||
|
int shorten;
|
||||||
|
int local_specified;
|
||||||
|
char *omodule;
|
||||||
|
char *msg;
|
||||||
|
{
|
||||||
|
int err = 0;
|
||||||
|
int which;
|
||||||
|
char *cp;
|
||||||
|
char *cp2;
|
||||||
|
char repository[PATH_MAX];
|
||||||
|
char xwhere[PATH_MAX];
|
||||||
|
char *oldupdate = NULL;
|
||||||
|
char *prepath;
|
||||||
|
char *realdirs;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* OK, so we're doing the checkout! Our args are as follows:
|
||||||
|
* argc,argv contain either dir or dir followed by a list of files
|
||||||
|
* where contains where to put it (if supplied by checkout)
|
||||||
|
* mwhere contains the module name or -d from module file
|
||||||
|
* mfile says do only that part of the module
|
||||||
|
* shorten = TRUE says shorten as much as possible
|
||||||
|
* omodule is the original arg to do_module()
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* set up the repository (maybe) for the bottom directory */
|
||||||
|
(void) sprintf (repository, "%s/%s", CVSroot, argv[0]);
|
||||||
|
|
||||||
|
/* save the original value of preload_update_dir */
|
||||||
|
if (preload_update_dir != NULL)
|
||||||
|
oldupdate = xstrdup (preload_update_dir);
|
||||||
|
|
||||||
|
/* fix up argv[] for the case of partial modules */
|
||||||
|
if (mfile != NULL)
|
||||||
|
{
|
||||||
|
char file[PATH_MAX];
|
||||||
|
|
||||||
|
/* if mfile is really a path, straighten it out first */
|
||||||
|
if ((cp = rindex (mfile, '/')) != NULL)
|
||||||
|
{
|
||||||
|
*cp = 0;
|
||||||
|
(void) strcat (repository, "/");
|
||||||
|
(void) strcat (repository, mfile);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now we need to fill in the where correctly. if !shorten, tack
|
||||||
|
* the rest of the path onto where if where is filled in
|
||||||
|
* otherwise tack the rest of the path onto mwhere and make that
|
||||||
|
* the where
|
||||||
|
*
|
||||||
|
* If shorten is enabled, we might use mwhere to set where if
|
||||||
|
* nobody set it yet, so we'll need to setup mwhere as the last
|
||||||
|
* component of the path we are tacking onto repository
|
||||||
|
*/
|
||||||
|
if (!shorten)
|
||||||
|
{
|
||||||
|
if (where != NULL)
|
||||||
|
(void) sprintf (xwhere, "%s/%s", where, mfile);
|
||||||
|
else
|
||||||
|
(void) sprintf (xwhere, "%s/%s", mwhere, mfile);
|
||||||
|
where = xwhere;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
char *slash;
|
||||||
|
|
||||||
|
if ((slash = rindex (mfile, '/')) != NULL)
|
||||||
|
mwhere = slash + 1;
|
||||||
|
else
|
||||||
|
mwhere = mfile;
|
||||||
|
}
|
||||||
|
mfile = cp + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
(void) sprintf (file, "%s/%s", repository, mfile);
|
||||||
|
if (isdir (file))
|
||||||
|
{
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The portion of a module was a directory, so kludge up where to
|
||||||
|
* be the subdir, and fix up repository
|
||||||
|
*/
|
||||||
|
(void) strcpy (repository, file);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* At this point, if shorten is not enabled, we make where either
|
||||||
|
* where with mfile concatenated, or if where hadn't been set we
|
||||||
|
* set it to mwhere with mfile concatenated.
|
||||||
|
*
|
||||||
|
* If shorten is enabled and where hasn't been set yet, then where
|
||||||
|
* becomes mfile
|
||||||
|
*/
|
||||||
|
if (!shorten)
|
||||||
|
{
|
||||||
|
if (where != NULL)
|
||||||
|
(void) sprintf (xwhere, "%s/%s", where, mfile);
|
||||||
|
else
|
||||||
|
(void) sprintf (xwhere, "%s/%s", mwhere, mfile);
|
||||||
|
where = xwhere;
|
||||||
|
}
|
||||||
|
else if (where == NULL)
|
||||||
|
where = mfile;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The portion of a module was a file, so kludge up argv to be
|
||||||
|
* correct
|
||||||
|
*/
|
||||||
|
for (i = 1; i < *pargc; i++)/* free the old ones */
|
||||||
|
free (argv[i]);
|
||||||
|
argv[1] = xstrdup (mfile); /* set up the new one */
|
||||||
|
*pargc = 2;
|
||||||
|
|
||||||
|
/* where gets mwhere if where isn't set */
|
||||||
|
if (where == NULL)
|
||||||
|
where = mwhere;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* if shorten is enabled and where isn't specified yet, we pluck the last
|
||||||
|
* directory component of argv[0] and make it the where
|
||||||
|
*/
|
||||||
|
if (shorten && where == NULL)
|
||||||
|
{
|
||||||
|
if ((cp = rindex (argv[0], '/')) != NULL)
|
||||||
|
{
|
||||||
|
(void) strcpy (xwhere, cp + 1);
|
||||||
|
where = xwhere;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if where is still NULL, use mwhere if set or the argv[0] dir */
|
||||||
|
if (where == NULL)
|
||||||
|
{
|
||||||
|
if (mwhere)
|
||||||
|
where = mwhere;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
(void) strcpy (xwhere, argv[0]);
|
||||||
|
where = xwhere;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (preload_update_dir != NULL)
|
||||||
|
{
|
||||||
|
char tmp[PATH_MAX];
|
||||||
|
|
||||||
|
(void) sprintf (tmp, "%s/%s", preload_update_dir, where);
|
||||||
|
free (preload_update_dir);
|
||||||
|
preload_update_dir = xstrdup (tmp);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
preload_update_dir = xstrdup (where);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* At this point, where is the directory we want to build, repository is
|
||||||
|
* the repository for the lowest level of the path.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we are sending everything to stdout, we can skip a whole bunch of
|
||||||
|
* work from here
|
||||||
|
*/
|
||||||
|
if (!pipeout)
|
||||||
|
{
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We need to tell build_dirs not only the path we want it to build,
|
||||||
|
* but also the repositories we want it to populate the path with. To
|
||||||
|
* accomplish this, we pass build_dirs a ``real path'' with valid
|
||||||
|
* repositories and a string to pre-pend based on how many path
|
||||||
|
* elements exist in where. Big Black Magic
|
||||||
|
*/
|
||||||
|
prepath = xstrdup (repository);
|
||||||
|
cp = rindex (where, '/');
|
||||||
|
cp2 = rindex (prepath, '/');
|
||||||
|
while (cp != NULL)
|
||||||
|
{
|
||||||
|
cp = findslash (where, cp - 1);
|
||||||
|
cp2 = findslash (prepath, cp2 - 1);
|
||||||
|
}
|
||||||
|
*cp2 = '\0';
|
||||||
|
realdirs = cp2 + 1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* build dirs on the path if necessary and leave us in the bottom
|
||||||
|
* directory (where if where was specified) doesn't contain a CVS
|
||||||
|
* subdir yet, but all the others contain CVS and Entries.Static
|
||||||
|
* files
|
||||||
|
*/
|
||||||
|
if (build_dirs_and_chdir (where, prepath, realdirs, *pargc <= 1) != 0)
|
||||||
|
{
|
||||||
|
error (0, 0, "ignoring module %s", omodule);
|
||||||
|
free (prepath);
|
||||||
|
free (preload_update_dir);
|
||||||
|
preload_update_dir = oldupdate;
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* clean up */
|
||||||
|
free (prepath);
|
||||||
|
|
||||||
|
/* set up the repository (or make sure the old one matches) */
|
||||||
|
if (!isfile (CVSADM) && !isfile (OCVSADM))
|
||||||
|
{
|
||||||
|
FILE *fp;
|
||||||
|
|
||||||
|
if (!noexec && *pargc > 1)
|
||||||
|
{
|
||||||
|
Create_Admin (".", repository, (char *) NULL, (char *) NULL);
|
||||||
|
fp = open_file (CVSADM_ENTSTAT, "w+");
|
||||||
|
if (fclose(fp) == EOF)
|
||||||
|
error(1, errno, "cannot close %s", CVSADM_ENTSTAT);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
Create_Admin (".", repository, tag, date);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
char *repos;
|
||||||
|
|
||||||
|
/* get the contents of the previously existing repository */
|
||||||
|
repos = Name_Repository ((char *) NULL, preload_update_dir);
|
||||||
|
if (strcmp (repository, repos) != 0)
|
||||||
|
{
|
||||||
|
error (0, 0, "existing repository %s does not match %s",
|
||||||
|
repos, repository);
|
||||||
|
error (0, 0, "ignoring module %s", omodule);
|
||||||
|
free (repos);
|
||||||
|
free (preload_update_dir);
|
||||||
|
preload_update_dir = oldupdate;
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
free (repos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we are going to be updating to stdout, we need to cd to the
|
||||||
|
* repository directory so the recursion processor can use the current
|
||||||
|
* directory as the place to find repository information
|
||||||
|
*/
|
||||||
|
if (pipeout)
|
||||||
|
{
|
||||||
|
if (chdir (repository) < 0)
|
||||||
|
{
|
||||||
|
error (0, errno, "cannot chdir to %s", repository);
|
||||||
|
free (preload_update_dir);
|
||||||
|
preload_update_dir = oldupdate;
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
which = W_REPOS;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
which = W_LOCAL | W_REPOS;
|
||||||
|
|
||||||
|
if (tag != NULL || date != NULL)
|
||||||
|
which |= W_ATTIC;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* if we are going to be recursive (building dirs), go ahead and call the
|
||||||
|
* update recursion processor. We will be recursive unless either local
|
||||||
|
* only was specified, or we were passed arguments
|
||||||
|
*/
|
||||||
|
if (!(local_specified || *pargc > 1))
|
||||||
|
{
|
||||||
|
if (strcmp (command_name, "export") != 0 && !pipeout)
|
||||||
|
history_write ('O', preload_update_dir, tag ? tag : date, where,
|
||||||
|
repository);
|
||||||
|
err += do_update (0, (char **) NULL, options, tag, date,
|
||||||
|
force_tag_match, 0 /* !local */ ,
|
||||||
|
1 /* update -d */ , aflag, checkout_prune_dirs,
|
||||||
|
pipeout, which, join_rev1, join_rev2,
|
||||||
|
preload_update_dir);
|
||||||
|
free (preload_update_dir);
|
||||||
|
preload_update_dir = oldupdate;
|
||||||
|
return (err);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pipeout)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
List *entries;
|
||||||
|
|
||||||
|
/* we are only doing files, so register them */
|
||||||
|
entries = ParseEntries (0);
|
||||||
|
for (i = 1; i < *pargc; i++)
|
||||||
|
{
|
||||||
|
char line[MAXLINELEN];
|
||||||
|
char *user;
|
||||||
|
Vers_TS *vers;
|
||||||
|
|
||||||
|
user = argv[i];
|
||||||
|
vers = Version_TS (repository, options, tag, date, user,
|
||||||
|
force_tag_match, 0, entries, (List *) NULL);
|
||||||
|
if (vers->ts_user == NULL)
|
||||||
|
{
|
||||||
|
(void) sprintf (line, "Initial %s", user);
|
||||||
|
Register (entries, user, vers->vn_rcs, line, vers->options,
|
||||||
|
vers->tag, vers->date);
|
||||||
|
}
|
||||||
|
freevers_ts (&vers);
|
||||||
|
}
|
||||||
|
dellist (&entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Don't log "export", just regular "checkouts" */
|
||||||
|
if (strcmp (command_name, "export") != 0 && !pipeout)
|
||||||
|
history_write ('O', preload_update_dir, (tag ? tag : date), where,
|
||||||
|
repository);
|
||||||
|
|
||||||
|
/* go ahead and call update now that everything is set */
|
||||||
|
err += do_update (*pargc - 1, argv + 1, options, tag, date,
|
||||||
|
force_tag_match, local_specified, 1 /* update -d */,
|
||||||
|
aflag, checkout_prune_dirs, pipeout, which, join_rev1,
|
||||||
|
join_rev2, preload_update_dir);
|
||||||
|
free (preload_update_dir);
|
||||||
|
preload_update_dir = oldupdate;
|
||||||
|
return (err);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
findslash (start, p)
|
||||||
|
char *start;
|
||||||
|
char *p;
|
||||||
|
{
|
||||||
|
while ((int) p >= (int) start && *p != '/')
|
||||||
|
p--;
|
||||||
|
if ((int) p < (int) start)
|
||||||
|
return (NULL);
|
||||||
|
else
|
||||||
|
return (p);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* build all the dirs along the path to dir with CVS subdirs with appropriate
|
||||||
|
* repositories and Entries.Static files
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
build_dirs_and_chdir (dir, prepath, realdir, sticky)
|
||||||
|
char *dir;
|
||||||
|
char *prepath;
|
||||||
|
char *realdir;
|
||||||
|
int sticky;
|
||||||
|
{
|
||||||
|
FILE *fp;
|
||||||
|
char repository[PATH_MAX];
|
||||||
|
char path[PATH_MAX];
|
||||||
|
char path2[PATH_MAX];
|
||||||
|
char *slash;
|
||||||
|
char *slash2;
|
||||||
|
char *cp;
|
||||||
|
char *cp2;
|
||||||
|
|
||||||
|
(void) strcpy (path, dir);
|
||||||
|
(void) strcpy (path2, realdir);
|
||||||
|
for (cp = path, cp2 = path2;
|
||||||
|
(slash = index (cp, '/')) != NULL && (slash2 = index (cp2, '/')) != NULL;
|
||||||
|
cp = slash + 1, cp2 = slash2 + 1)
|
||||||
|
{
|
||||||
|
*slash = '\0';
|
||||||
|
*slash2 = '\0';
|
||||||
|
(void) mkdir (cp, 0777);
|
||||||
|
if (chdir (cp) < 0)
|
||||||
|
{
|
||||||
|
error (0, errno, "cannot chdir to %s", cp);
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
if (!isfile (CVSADM) && !isfile (OCVSADM) &&
|
||||||
|
strcmp (command_name, "export") != 0)
|
||||||
|
{
|
||||||
|
(void) sprintf (repository, "%s/%s", prepath, path2);
|
||||||
|
Create_Admin (".", repository, sticky ? (char *) NULL : tag,
|
||||||
|
sticky ? (char *) NULL : date);
|
||||||
|
if (!noexec)
|
||||||
|
{
|
||||||
|
fp = open_file (CVSADM_ENTSTAT, "w+");
|
||||||
|
if (fclose(fp) == EOF)
|
||||||
|
error(1, errno, "cannot close %s", CVSADM_ENTSTAT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*slash = '/';
|
||||||
|
*slash2 = '/';
|
||||||
|
}
|
||||||
|
(void) mkdir (cp, 0777);
|
||||||
|
if (chdir (cp) < 0)
|
||||||
|
{
|
||||||
|
error (0, errno, "cannot chdir to %s", cp);
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
@@ -0,0 +1,380 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 1992, Brian Berliner and Jeff Polk
|
||||||
|
* Copyright (c) 1989-1992, Brian Berliner
|
||||||
|
*
|
||||||
|
* You may distribute under the terms of the GNU General Public License as
|
||||||
|
* specified in the README file that comes with the CVS 1.3 kit.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "cvs.h"
|
||||||
|
|
||||||
|
#ifndef lint
|
||||||
|
static char rcsid[] = "@(#)classify.c 1.11 92/03/31";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __STDC__
|
||||||
|
static void sticky_ck (char *file, int aflag, Vers_TS * vers, List * entries);
|
||||||
|
#else
|
||||||
|
static void sticky_ck ();
|
||||||
|
#endif /* __STDC__ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Classify the state of a file
|
||||||
|
*/
|
||||||
|
Ctype
|
||||||
|
Classify_File (file, tag, date, options, force_tag_match, aflag, repository,
|
||||||
|
entries, srcfiles, versp)
|
||||||
|
char *file;
|
||||||
|
char *tag;
|
||||||
|
char *date;
|
||||||
|
char *options;
|
||||||
|
int force_tag_match;
|
||||||
|
int aflag;
|
||||||
|
char *repository;
|
||||||
|
List *entries;
|
||||||
|
List *srcfiles;
|
||||||
|
Vers_TS **versp;
|
||||||
|
{
|
||||||
|
Vers_TS *vers;
|
||||||
|
Ctype ret;
|
||||||
|
|
||||||
|
/* get all kinds of good data about the file */
|
||||||
|
vers = Version_TS (repository, options, tag, date, file,
|
||||||
|
force_tag_match, 0, entries, srcfiles);
|
||||||
|
|
||||||
|
if (vers->vn_user == NULL)
|
||||||
|
{
|
||||||
|
/* No entry available, ts_rcs is invalid */
|
||||||
|
if (vers->vn_rcs == NULL)
|
||||||
|
{
|
||||||
|
/* there is no RCS file either */
|
||||||
|
if (vers->ts_user == NULL)
|
||||||
|
{
|
||||||
|
/* there is no user file */
|
||||||
|
if (!force_tag_match || !(vers->tag || vers->date))
|
||||||
|
if (!really_quiet)
|
||||||
|
error (0, 0, "nothing known about %s", file);
|
||||||
|
ret = T_UNKNOWN;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* there is a user file */
|
||||||
|
if (!force_tag_match || !(vers->tag || vers->date))
|
||||||
|
if (!really_quiet)
|
||||||
|
error (0, 0, "use `cvs add' to create an entry for %s",
|
||||||
|
file);
|
||||||
|
ret = T_UNKNOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* there is an rcs file */
|
||||||
|
|
||||||
|
if (vers->ts_user == NULL)
|
||||||
|
{
|
||||||
|
/* There is no user file; needs checkout */
|
||||||
|
ret = T_CHECKOUT;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* There is a user file; print a warning and add it to the
|
||||||
|
* conflict list, only if it is indeed different from what we
|
||||||
|
* plan to extract
|
||||||
|
*/
|
||||||
|
if (No_Difference (file, vers, entries))
|
||||||
|
{
|
||||||
|
/* the files were different so it is a conflict */
|
||||||
|
if (!really_quiet)
|
||||||
|
error (0, 0, "move away %s; it is in the way", file);
|
||||||
|
ret = T_CONFLICT;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
/* since there was no difference, still needs checkout */
|
||||||
|
ret = T_CHECKOUT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (strcmp (vers->vn_user, "0") == 0)
|
||||||
|
{
|
||||||
|
/* An entry for a new-born file; ts_rcs is dummy */
|
||||||
|
|
||||||
|
if (vers->ts_user == NULL)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* There is no user file, but there should be one; remove the
|
||||||
|
* entry
|
||||||
|
*/
|
||||||
|
if (!really_quiet)
|
||||||
|
error (0, 0, "warning: new-born %s has disappeared", file);
|
||||||
|
ret = T_REMOVE_ENTRY;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* There is a user file */
|
||||||
|
|
||||||
|
if (vers->vn_rcs == NULL)
|
||||||
|
/* There is no RCS file, added file */
|
||||||
|
ret = T_ADDED;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* There is an RCS file, so someone else must have checked
|
||||||
|
* one in behind our back; conflict
|
||||||
|
*/
|
||||||
|
if (!really_quiet)
|
||||||
|
error (0, 0,
|
||||||
|
"conflict: %s created independently by second party",
|
||||||
|
file);
|
||||||
|
ret = T_CONFLICT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (vers->vn_user[0] == '-')
|
||||||
|
{
|
||||||
|
/* An entry for a removed file, ts_rcs is invalid */
|
||||||
|
|
||||||
|
if (vers->ts_user == NULL)
|
||||||
|
{
|
||||||
|
char tmp[PATH_MAX];
|
||||||
|
|
||||||
|
/* There is no user file (as it should be) */
|
||||||
|
|
||||||
|
(void) sprintf (tmp, "-%s", vers->vn_rcs ? vers->vn_rcs : "");
|
||||||
|
|
||||||
|
if (vers->vn_rcs == NULL)
|
||||||
|
{
|
||||||
|
|
||||||
|
/*
|
||||||
|
* There is no RCS file; this is all-right, but it has been
|
||||||
|
* removed independently by a second party; remove the entry
|
||||||
|
*/
|
||||||
|
ret = T_REMOVE_ENTRY;
|
||||||
|
}
|
||||||
|
else if (strcmp (tmp, vers->vn_user) == 0)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The RCS file is the same version as the user file was, and
|
||||||
|
* that's OK; remove it
|
||||||
|
*/
|
||||||
|
ret = T_REMOVED;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The RCS file is a newer version than the removed user file
|
||||||
|
* and this is definitely not OK; make it a conflict.
|
||||||
|
*/
|
||||||
|
if (!really_quiet)
|
||||||
|
error (0, 0,
|
||||||
|
"conflict: removed %s was modified by second party",
|
||||||
|
file);
|
||||||
|
ret = T_CONFLICT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* The user file shouldn't be there */
|
||||||
|
if (!really_quiet)
|
||||||
|
error (0, 0, "%s should be removed and is still there", file);
|
||||||
|
ret = T_REMOVED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* A normal entry, TS_Rcs is valid */
|
||||||
|
if (vers->vn_rcs == NULL)
|
||||||
|
{
|
||||||
|
/* There is no RCS file */
|
||||||
|
|
||||||
|
if (vers->ts_user == NULL)
|
||||||
|
{
|
||||||
|
/* There is no user file, so just remove the entry */
|
||||||
|
if (!really_quiet)
|
||||||
|
error (0, 0, "warning: %s is not (any longer) pertinent",
|
||||||
|
file);
|
||||||
|
ret = T_REMOVE_ENTRY;
|
||||||
|
}
|
||||||
|
else if (strcmp (vers->ts_user, vers->ts_rcs) == 0)
|
||||||
|
{
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The user file is still unmodified, so just remove it from
|
||||||
|
* the entry list
|
||||||
|
*/
|
||||||
|
if (!really_quiet)
|
||||||
|
error (0, 0, "%s is no longer in the repository", file);
|
||||||
|
ret = T_REMOVE_ENTRY;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* The user file has been modified and since it is no longer
|
||||||
|
* in the repository, a conflict is raised
|
||||||
|
*/
|
||||||
|
if (No_Difference (file, vers, entries))
|
||||||
|
{
|
||||||
|
/* they are different -> conflict */
|
||||||
|
if (!really_quiet)
|
||||||
|
error (0, 0,
|
||||||
|
"conflict: %s is modified but no longer in the repository",
|
||||||
|
file);
|
||||||
|
ret = T_CONFLICT;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* they weren't really different */
|
||||||
|
if (!really_quiet)
|
||||||
|
error (0, 0,
|
||||||
|
"warning: %s is not (any longer) pertinent",
|
||||||
|
file);
|
||||||
|
ret = T_REMOVE_ENTRY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (strcmp (vers->vn_rcs, vers->vn_user) == 0)
|
||||||
|
{
|
||||||
|
/* The RCS file is the same version as the user file */
|
||||||
|
|
||||||
|
if (vers->ts_user == NULL)
|
||||||
|
{
|
||||||
|
|
||||||
|
/*
|
||||||
|
* There is no user file, so note that it was lost and
|
||||||
|
* extract a new version
|
||||||
|
*/
|
||||||
|
if (strcmp (command_name, "update") == 0)
|
||||||
|
if (!really_quiet)
|
||||||
|
error (0, 0, "warning: %s was lost", file);
|
||||||
|
ret = T_CHECKOUT;
|
||||||
|
}
|
||||||
|
else if (strcmp (vers->ts_user, vers->ts_rcs) == 0)
|
||||||
|
{
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The user file is still unmodified, so nothing special at
|
||||||
|
* all to do -- no lists updated, unless the sticky -k option
|
||||||
|
* has changed. If the sticky tag has changed, we just need
|
||||||
|
* to re-register the entry
|
||||||
|
*/
|
||||||
|
if (vers->entdata->options &&
|
||||||
|
strcmp (vers->entdata->options, vers->options) != 0)
|
||||||
|
ret = T_CHECKOUT;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sticky_ck (file, aflag, vers, entries);
|
||||||
|
ret = T_UPTODATE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The user file appears to have been modified, but we call
|
||||||
|
* No_Difference to verify that it really has been modified
|
||||||
|
*/
|
||||||
|
if (No_Difference (file, vers, entries))
|
||||||
|
{
|
||||||
|
|
||||||
|
/*
|
||||||
|
* they really are different; modified if we aren't
|
||||||
|
* changing any sticky -k options, else needs merge
|
||||||
|
*/
|
||||||
|
#ifdef XXX_FIXME_WHEN_RCSMERGE_IS_FIXED
|
||||||
|
if (strcmp (vers->entdata->options ?
|
||||||
|
vers->entdata->options : "", vers->options) == 0)
|
||||||
|
ret = T_MODIFIED;
|
||||||
|
else
|
||||||
|
ret = T_NEEDS_MERGE;
|
||||||
|
#else
|
||||||
|
ret = T_MODIFIED;
|
||||||
|
sticky_ck (file, aflag, vers, entries);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* file has not changed; check out if -k changed */
|
||||||
|
if (strcmp (vers->entdata->options ?
|
||||||
|
vers->entdata->options : "", vers->options) != 0)
|
||||||
|
{
|
||||||
|
ret = T_CHECKOUT;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
/*
|
||||||
|
* else -> note that No_Difference will Register the
|
||||||
|
* file already for us, using the new tag/date. This
|
||||||
|
* is the desired behaviour
|
||||||
|
*/
|
||||||
|
ret = T_UPTODATE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* The RCS file is a newer version than the user file */
|
||||||
|
|
||||||
|
if (vers->ts_user == NULL)
|
||||||
|
{
|
||||||
|
/* There is no user file, so just get it */
|
||||||
|
|
||||||
|
if (strcmp (command_name, "update") == 0)
|
||||||
|
if (!really_quiet)
|
||||||
|
error (0, 0, "warning: %s was lost", file);
|
||||||
|
ret = T_CHECKOUT;
|
||||||
|
}
|
||||||
|
else if (strcmp (vers->ts_user, vers->ts_rcs) == 0)
|
||||||
|
{
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The user file is still unmodified, so just get it as well
|
||||||
|
*/
|
||||||
|
ret = T_CHECKOUT;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (No_Difference (file, vers, entries))
|
||||||
|
/* really modified, needs to merge */
|
||||||
|
ret = T_NEEDS_MERGE;
|
||||||
|
else
|
||||||
|
/* not really modified, check it out */
|
||||||
|
ret = T_CHECKOUT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* free up the vers struct, or just return it */
|
||||||
|
if (versp != (Vers_TS **) NULL)
|
||||||
|
*versp = vers;
|
||||||
|
else
|
||||||
|
freevers_ts (&vers);
|
||||||
|
|
||||||
|
/* return the status of the file */
|
||||||
|
return (ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
sticky_ck (file, aflag, vers, entries)
|
||||||
|
char *file;
|
||||||
|
int aflag;
|
||||||
|
Vers_TS *vers;
|
||||||
|
List *entries;
|
||||||
|
{
|
||||||
|
if (aflag || vers->tag || vers->date)
|
||||||
|
{
|
||||||
|
char *enttag = vers->entdata->tag;
|
||||||
|
char *entdate = vers->entdata->date;
|
||||||
|
|
||||||
|
if ((enttag && vers->tag && strcmp (enttag, vers->tag)) ||
|
||||||
|
((enttag && !vers->tag) || (!enttag && vers->tag)) ||
|
||||||
|
(entdate && vers->date && strcmp (entdate, vers->date)) ||
|
||||||
|
((entdate && !vers->date) || (!entdate && vers->date)))
|
||||||
|
{
|
||||||
|
Register (entries, file, vers->vn_user, vers->ts_rcs,
|
||||||
|
vers->options, vers->tag, vers->date);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,217 @@
|
|||||||
|
/* @(#)config.h 1.19 92/03/31 */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) 1992, Brian Berliner and Jeff Polk
|
||||||
|
* Copyright (c) 1989-1992, Brian Berliner
|
||||||
|
*
|
||||||
|
* You may distribute under the terms of the GNU General Public License as
|
||||||
|
* specified in the README file that comes with the CVS 1.3 kit.
|
||||||
|
*
|
||||||
|
* This file holds (most of) the configuration tweaks that can be made to
|
||||||
|
* customize CVS for your site. CVS comes configured for a typical SunOS 4.x
|
||||||
|
* environment. The comments for each configurable item are intended to be
|
||||||
|
* self-explanatory. All #defines are tested first to see if an over-riding
|
||||||
|
* option was specified on the "make" command line.
|
||||||
|
*
|
||||||
|
* If special libraries are needed, you will have to edit the Makefile.in file
|
||||||
|
* or the configure script directly. Sorry.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CVS provides the most features when used in conjunction with the Version-5
|
||||||
|
* release of RCS. Thus, it is the default. This also assumes that GNU diff
|
||||||
|
* Version-1.15 is being used as well -- you will have to configure your RCS
|
||||||
|
* V5 release separately to make this the case. If you do not have RCS V5 and
|
||||||
|
* GNU diff V1.15, comment out this define. You should not try mixing and
|
||||||
|
* matching other combinations of these tools.
|
||||||
|
*/
|
||||||
|
#ifndef HAVE_RCS5
|
||||||
|
#define HAVE_RCS5
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If, before installing this version of CVS, you were running RCS V4 AND you
|
||||||
|
* are installing this CVS and RCS V5 and GNU diff 1.15 all at the same time,
|
||||||
|
* you should turn on the following define. It only exists to try to do
|
||||||
|
* reasonable things with your existing checked out files when you upgrade to
|
||||||
|
* RCS V5, since the keyword expansion formats have changed with RCS V5.
|
||||||
|
*
|
||||||
|
* If you already have been running with RCS5, or haven't been running with CVS
|
||||||
|
* yet at all, or are sticking with RCS V4 for now, leave the commented out.
|
||||||
|
*/
|
||||||
|
#ifndef HAD_RCS4
|
||||||
|
/* #define HAD_RCS4 */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For portability and heterogeneity reasons, CVS is shipped by default using
|
||||||
|
* my own text-file version of the ndbm database library in the src/myndbm.c
|
||||||
|
* file. If you want better performance and are not concerned about
|
||||||
|
* heterogeneous hosts accessing your modules file, turn this option off.
|
||||||
|
*/
|
||||||
|
#ifndef MY_NDBM
|
||||||
|
#define MY_NDBM
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The "diff" program to execute when creating patch output. This "diff"
|
||||||
|
* must support the "-c" option for context diffing. Specify a full pathname
|
||||||
|
* if your site wants to use a particular diff. If you are using the GNU
|
||||||
|
* version of diff (version 1.15 or later), this should be "diff -a".
|
||||||
|
*
|
||||||
|
* NOTE: this program is only used for the ``patch'' sub-command. The other
|
||||||
|
* commands use rcsdiff which will use whatever version of diff was specified
|
||||||
|
* when rcsdiff was built on your system.
|
||||||
|
*/
|
||||||
|
#ifndef DIFF
|
||||||
|
#define DIFF "diff"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The "grep" program to execute when checking to see if a merged file had
|
||||||
|
* any conflicts. This "grep" must support the "-s" option and a standard
|
||||||
|
* regular expression as an argument. Specify a full pathname if your site
|
||||||
|
* wants to use a particular grep.
|
||||||
|
*/
|
||||||
|
#ifndef GREP
|
||||||
|
#define GREP "grep"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The "rm" program to execute when pruning directories that are not part of
|
||||||
|
* a release. This "rm" must support the "-fr" options. Specify a full
|
||||||
|
* pathname if your site wants to use a particular rm.
|
||||||
|
*/
|
||||||
|
#ifndef RM
|
||||||
|
#define RM "rm"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The "sort" program to execute when displaying the module database. Specify
|
||||||
|
* a full pathname if your site wants to use a particular sort.
|
||||||
|
*/
|
||||||
|
#ifndef SORT
|
||||||
|
#define SORT "sort"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* By default, RCS programs are executed with the shell or through execlp(),
|
||||||
|
* so the user's PATH environment variable is searched. If you'd like to
|
||||||
|
* bind all RCS programs to a certain directory (perhaps one not in most
|
||||||
|
* people's PATH) then set the default in RCSBIN_DFLT. Note that setting
|
||||||
|
* this here will cause all RCS programs to be executed from this directory,
|
||||||
|
* unless the user overrides the default with the RCSBIN environment variable
|
||||||
|
* or the "-b" option to CVS.
|
||||||
|
*
|
||||||
|
* This define should be either the empty string ("") or a full pathname to the
|
||||||
|
* directory containing all the installed programs from the RCS distribution.
|
||||||
|
*/
|
||||||
|
#ifndef RCSBIN_DFLT
|
||||||
|
#define RCSBIN_DFLT ""
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The default editor to use, if one does not specify the "-e" option to cvs,
|
||||||
|
* or does not have an EDITOR environment variable. I set this to just "vi",
|
||||||
|
* and use the shell to find where "vi" actually is. This allows sites with
|
||||||
|
* /usr/bin/vi or /usr/ucb/vi to work equally well (assuming that your PATH
|
||||||
|
* is reasonable).
|
||||||
|
*/
|
||||||
|
#ifndef EDITOR_DFLT
|
||||||
|
#define EDITOR_DFLT "vi"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The Repository file holds the path to the directory within the source
|
||||||
|
* repository that contains the RCS ,v files for each CVS working directory.
|
||||||
|
* This path is either a full-path or a path relative to CVSROOT.
|
||||||
|
*
|
||||||
|
* The only advantage that I can see to having a relative path is that One can
|
||||||
|
* change the physical location of the master source repository, change one's
|
||||||
|
* CVSROOT environment variable, and CVS will work without problems. I
|
||||||
|
* recommend using full-paths.
|
||||||
|
*/
|
||||||
|
#ifndef RELATIVE_REPOS
|
||||||
|
/* #define RELATIVE_REPOS */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When committing or importing files, you must enter a log message.
|
||||||
|
* Normally, you can do this either via the -m flag on the command line or an
|
||||||
|
* editor will be started for you. If you like to use logging templates (the
|
||||||
|
* rcsinfo file within the $CVSROOT/CVSROOT directory), you might want to
|
||||||
|
* force people to use the editor even if they specify a message with -m.
|
||||||
|
* Enabling FORCE_USE_EDITOR will cause the -m message to be appended to the
|
||||||
|
* temp file when the editor is started.
|
||||||
|
*/
|
||||||
|
#ifndef FORCE_USE_EDITOR
|
||||||
|
/* #define FORCE_USE_EDITOR */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When locking the repository, some sites like to remove locks and assume
|
||||||
|
* the program that created them went away if the lock has existed for a long
|
||||||
|
* time. This used to be the default for previous versions of CVS. CVS now
|
||||||
|
* attempts to be much more robust, so lock files should not be left around
|
||||||
|
* by mistake. The new behaviour will never remove old locks (they must now
|
||||||
|
* be removed by hand). Enabling CVS_FUDGELOCKS will cause CVS to remove
|
||||||
|
* locks that are older than CVSLCKAGE seconds.
|
||||||
|
* Use of this option is NOT recommended.
|
||||||
|
*/
|
||||||
|
#ifndef CVS_FUDGELOCKS
|
||||||
|
/* #define CVS_FUDGELOCKS */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When committing a permanent change, CVS and RCS make a log entry of
|
||||||
|
* who committed the change. If you are committing the change logged in
|
||||||
|
* as "root" (not under "su" or other root-priv giving program), CVS/RCS
|
||||||
|
* cannot determine who is actually making the change.
|
||||||
|
*
|
||||||
|
* As such, by default, CVS disallows changes to be committed by users
|
||||||
|
* logged in as "root". You can disable this option by commenting
|
||||||
|
* out the lines below.
|
||||||
|
*/
|
||||||
|
#ifndef CVS_BADROOT
|
||||||
|
#define CVS_BADROOT
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The "cvs diff" command accepts all the single-character options that GNU
|
||||||
|
* diff (1.15) accepts. Except -D. GNU diff uses -D as a way to put
|
||||||
|
* cpp-style #define's around the output differences. CVS, by default, uses
|
||||||
|
* -D to specify a free-form date (like "cvs diff -D '1 week ago'"). If
|
||||||
|
* you would prefer that the -D option of "cvs diff" work like the GNU diff
|
||||||
|
* option, then comment out this define.
|
||||||
|
*/
|
||||||
|
#ifndef CVS_DIFFDATE
|
||||||
|
#define CVS_DIFFDATE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* End of CVS configuration section */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Externs that are included in libc, but are used frequently enough to
|
||||||
|
* warrant defining here.
|
||||||
|
*/
|
||||||
|
#ifndef STDC_HEADERS
|
||||||
|
extern void exit ();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef getwd
|
||||||
|
extern char *getwd ();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Some UNIX distributions don't include these in their stat.h Defined here
|
||||||
|
* because "config.h" is always included last.
|
||||||
|
*/
|
||||||
|
#ifndef S_IWRITE
|
||||||
|
#define S_IWRITE 0000200 /* write permission, owner */
|
||||||
|
#endif
|
||||||
|
#ifndef S_IWGRP
|
||||||
|
#define S_IWGRP 0000020 /* write permission, grougroup */
|
||||||
|
#endif
|
||||||
|
#ifndef S_IWOTH
|
||||||
|
#define S_IWOTH 0000002 /* write permission, other */
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 1992, Brian Berliner and Jeff Polk
|
||||||
|
* Copyright (c) 1989-1992, Brian Berliner
|
||||||
|
*
|
||||||
|
* You may distribute under the terms of the GNU General Public License as
|
||||||
|
* specified in the README file that comes with the CVS 1.3 kit.
|
||||||
|
*
|
||||||
|
* Create Administration.
|
||||||
|
*
|
||||||
|
* Creates a CVS administration directory based on the argument repository; the
|
||||||
|
* "Entries" file is prefilled from the "initrecord" argument.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "cvs.h"
|
||||||
|
|
||||||
|
#ifndef lint
|
||||||
|
static char rcsid[] = "@(#)create_adm.c 1.24 92/03/31";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void
|
||||||
|
Create_Admin (dir, repository, tag, date)
|
||||||
|
char *dir;
|
||||||
|
char *repository;
|
||||||
|
char *tag;
|
||||||
|
char *date;
|
||||||
|
{
|
||||||
|
FILE *fout;
|
||||||
|
char *cp;
|
||||||
|
char tmp[PATH_MAX];
|
||||||
|
|
||||||
|
if (noexec)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!isdir (repository))
|
||||||
|
error (1, 0, "there is no repository %s", repository);
|
||||||
|
|
||||||
|
if (dir != NULL)
|
||||||
|
(void) sprintf (tmp, "%s/%s", dir, CVSADM);
|
||||||
|
else
|
||||||
|
(void) strcpy (tmp, CVSADM);
|
||||||
|
|
||||||
|
if (isfile (tmp))
|
||||||
|
error (1, 0, "there is a version here already");
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (dir != NULL)
|
||||||
|
(void) sprintf (tmp, "%s/%s", dir, OCVSADM);
|
||||||
|
else
|
||||||
|
(void) strcpy (tmp, OCVSADM);
|
||||||
|
|
||||||
|
if (isfile (tmp))
|
||||||
|
error (1, 0, "there is a version here already");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dir != NULL)
|
||||||
|
(void) sprintf (tmp, "%s/%s", dir, CVSADM);
|
||||||
|
else
|
||||||
|
(void) strcpy (tmp, CVSADM);
|
||||||
|
make_directory (tmp);
|
||||||
|
|
||||||
|
if (dir != NULL)
|
||||||
|
(void) sprintf (tmp, "%s/%s", dir, CVSADM_REP);
|
||||||
|
else
|
||||||
|
(void) strcpy (tmp, CVSADM_REP);
|
||||||
|
fout = open_file (tmp, "w+");
|
||||||
|
cp = repository;
|
||||||
|
strip_path (cp);
|
||||||
|
|
||||||
|
#ifdef RELATIVE_REPOS
|
||||||
|
/*
|
||||||
|
* If the Repository file is to hold a relative path, try to strip off
|
||||||
|
* the leading CVSroot argument.
|
||||||
|
*/
|
||||||
|
if (CVSroot != NULL)
|
||||||
|
{
|
||||||
|
char path[PATH_MAX];
|
||||||
|
|
||||||
|
(void) sprintf (path, "%s/", CVSroot);
|
||||||
|
if (strncmp (repository, path, strlen (path)) == 0)
|
||||||
|
cp = repository + strlen (path);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (fprintf (fout, "%s\n", cp) == EOF)
|
||||||
|
error (1, errno, "write to %s failed", tmp);
|
||||||
|
if (fclose (fout) == EOF)
|
||||||
|
error (1, errno, "cannot close %s", tmp);
|
||||||
|
|
||||||
|
/* now, do the Entries file */
|
||||||
|
if (dir != NULL)
|
||||||
|
(void) sprintf (tmp, "%s/%s", dir, CVSADM_ENT);
|
||||||
|
else
|
||||||
|
(void) strcpy (tmp, CVSADM_ENT);
|
||||||
|
fout = open_file (tmp, "w+");
|
||||||
|
if (fclose (fout) == EOF)
|
||||||
|
error (1, errno, "cannot close %s", tmp);
|
||||||
|
|
||||||
|
/* Create a new CVS/Tag file */
|
||||||
|
WriteTag (dir, tag, date);
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,326 @@
|
|||||||
|
.TH cvs 5 "12 February 1992"
|
||||||
|
.\" Full space in nroff; half space in troff
|
||||||
|
.de SP
|
||||||
|
.if n .sp
|
||||||
|
.if t .sp .5
|
||||||
|
..
|
||||||
|
.SH NAME
|
||||||
|
cvs \- Concurrent Versions System support files
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.hy 0
|
||||||
|
.na
|
||||||
|
.TP
|
||||||
|
.B $CVSROOT/CVSROOT/modules,v
|
||||||
|
.TP
|
||||||
|
.B $CVSROOT/CVSROOT/commitinfo,v
|
||||||
|
.TP
|
||||||
|
.B $CVSROOT/CVSROOT/loginfo,v
|
||||||
|
.TP
|
||||||
|
.B $CVSROOT/CVSROOT/rcsinfo,v
|
||||||
|
.TP
|
||||||
|
.B $CVSROOT/CVSROOT/editinfo,v
|
||||||
|
.TP
|
||||||
|
.B $CVSROOT/CVSROOT/cvsignore,v
|
||||||
|
.TP
|
||||||
|
.B $CVSROOT/CVSROOT/history
|
||||||
|
.ad b
|
||||||
|
.hy 1
|
||||||
|
.SH DESCRIPTION
|
||||||
|
.B cvs
|
||||||
|
is a system for providing source control to hierarchical collections
|
||||||
|
of source directories. Commands and procedures for using \fBcvs\fP
|
||||||
|
are described in
|
||||||
|
.BR cvs ( 1 ).
|
||||||
|
.SP
|
||||||
|
.B cvs
|
||||||
|
manages \fIsource repositories\fP, the directories containing master
|
||||||
|
copies of the revision-controlled files, by copying particular
|
||||||
|
revisions of the files to (and modifications back from) developers'
|
||||||
|
private \fIworking directories\fP. In terms of file structure, each
|
||||||
|
individual source repository is an immediate subdirectory of
|
||||||
|
\fB$CVSROOT\fP.
|
||||||
|
.SP
|
||||||
|
The files described here are supporting files; they do not have to
|
||||||
|
exist for \fBcvs\fP to operate, but they allow you to make \fBcvs\fP
|
||||||
|
operation more flexible.
|
||||||
|
.SP
|
||||||
|
The
|
||||||
|
.BR cvsinit ( 1 )
|
||||||
|
shell script included at the top-level of the
|
||||||
|
.B cvs
|
||||||
|
distribution can be used to setup an initial
|
||||||
|
.B $CVSROOT/CVSROOT
|
||||||
|
area, if you don't have one already.
|
||||||
|
.SP
|
||||||
|
You can use the `\|modules\|' file to define symbolic names for
|
||||||
|
collections of source maintained with \fBcvs\fP. If there is no
|
||||||
|
`\|modules\|' file, developers must specify complete path names
|
||||||
|
(absolute, or relative to \fB$CVSROOT\fP) for the files they wish to
|
||||||
|
manage with \fBcvs\fP commands.
|
||||||
|
.SP
|
||||||
|
You can use the `\|commitinfo\|' file to define programs to execute
|
||||||
|
whenever `\|\fBcvs commit\fP\|' is about to execute.
|
||||||
|
These programs are used for ``pre-commit'' checking to verify that the
|
||||||
|
modified, added, and removed files are really ready to be committed.
|
||||||
|
Some uses for this check might be to turn off a portion (or all) of the
|
||||||
|
source repository from a particular person or group.
|
||||||
|
Or, perhaps, to verify that the changed files conform to the site's
|
||||||
|
standards for coding practice.
|
||||||
|
.SP
|
||||||
|
You can use the `\|loginfo\|' file to define programs to execute after
|
||||||
|
any
|
||||||
|
.BR commit ,
|
||||||
|
which writes a log entry for changes in the repository.
|
||||||
|
These logging programs might be used to append the log message to a file.
|
||||||
|
Or send the log message through electronic mail to a group of developers.
|
||||||
|
Or, perhaps, post the log message to a particular newsgroup.
|
||||||
|
.SP
|
||||||
|
You can use the `\|rcsinfo\|' file to define forms for log messages.
|
||||||
|
.SP
|
||||||
|
You can use the `\|editinfo\|' file to define a program to execute for
|
||||||
|
editing/validating `\|\fBcvs commit\fP\|' log entries.
|
||||||
|
This is most useful when used with a `\|rcsinfo\|' forms specification, as
|
||||||
|
it can verify that the proper fields of the form have been filled in by the
|
||||||
|
user committing the change.
|
||||||
|
.SP
|
||||||
|
You can use the `\|cvsignore\|' file to specify the default list of
|
||||||
|
files to ignore during \fBupdate\fP.
|
||||||
|
.SP
|
||||||
|
You can use the `\|history\|' file to record the \fBcvs\fP commands
|
||||||
|
that affect the repository.
|
||||||
|
The creation of this file enables history logging.
|
||||||
|
.SH FILES
|
||||||
|
.TP
|
||||||
|
.B modules
|
||||||
|
The `\|modules\|' file records your definitions of names for
|
||||||
|
collections of source code. \fBcvs\fP will use these definitions if
|
||||||
|
you create a file with the right format in
|
||||||
|
`\|\fB$CVSROOT/CVSROOT/modules,v\fP\|'.
|
||||||
|
The
|
||||||
|
.BR mkmodules ( 1 )
|
||||||
|
command should be run whenever the modules file changes, so that the
|
||||||
|
appropriate files can be generated (depending on how you have configured
|
||||||
|
.B cvs
|
||||||
|
operation).
|
||||||
|
.SP
|
||||||
|
To allow convenient editing of the `\|modules\|' file itself, the file should
|
||||||
|
include an entry like the following (where \fIlocalbin\fP represents the
|
||||||
|
directory where your site installs programs like
|
||||||
|
.BR mkmodules ( 1 )):
|
||||||
|
.SP
|
||||||
|
.nf
|
||||||
|
\&\fBmodules \-i /\fP\fIlocalbin\fP\fB/mkmodules CVSROOT modules\fP
|
||||||
|
.fi
|
||||||
|
.SP
|
||||||
|
This defines the name `\|\fBmodules\fP\|' as the module name for the
|
||||||
|
file itself, so that you can use
|
||||||
|
.SP
|
||||||
|
.in +1i
|
||||||
|
.ft B
|
||||||
|
.nf
|
||||||
|
example% cvs checkout modules
|
||||||
|
.fi
|
||||||
|
.ft P
|
||||||
|
.in -1i
|
||||||
|
.SP
|
||||||
|
to get an editable copy of the file. You should define similar module
|
||||||
|
entries for the other configuration files described here (except
|
||||||
|
\&`\|history\|').
|
||||||
|
The
|
||||||
|
.BR cvsinit ( 1 )
|
||||||
|
script will setup a smilar `\|modules\|' file for you automatically.
|
||||||
|
.SP
|
||||||
|
The `\|modules\|' file may contain blank lines and comments (lines
|
||||||
|
beginning with `\|\fB#\fP\|') as well as module definitions.
|
||||||
|
Long lines can be continued on the next line by specifying a backslash
|
||||||
|
(``\e'') as the last character on the line.
|
||||||
|
.SP
|
||||||
|
A \fImodule definition\fP is a single line of the `\|modules\|' file,
|
||||||
|
in either of two formats. In both cases, \fImname\fP represents the
|
||||||
|
symbolic module name, and the remainder of the line is its definition.
|
||||||
|
.SP
|
||||||
|
\fImname\fP \fB\-a\fP \fIaliases\fP\|.\|.\|.
|
||||||
|
.br
|
||||||
|
This represents the simplest way of defining a module \fImname\fP.
|
||||||
|
The `\|\fB\-a\fP\|' flags the definition as a simple alias: \fBcvs\fP
|
||||||
|
will treat any use of \fImname\fP (as a command argument) as if the list
|
||||||
|
of names \fIaliases\fP had been specified instead. \fIaliases\fP may
|
||||||
|
contain either other module names or paths. When you use paths in
|
||||||
|
\fIaliases\fP, `\|\fBcvs checkout\fP\|' creates all intermediate
|
||||||
|
directories in the working directory, just as if the path had been
|
||||||
|
specified explicitly in the \fBcvs\fP arguments.
|
||||||
|
.SP
|
||||||
|
.nf
|
||||||
|
\fImname\fP [ \fIoptions\fP ] \fIdir\fP [ \fIfiles\fP\|.\|.\|. ] [ \fB&\fP\fImodule\fP\|.\|.\|. ]
|
||||||
|
.fi
|
||||||
|
.SP
|
||||||
|
In the simplest case, this form of module definition reduces to
|
||||||
|
`\|\fImname dir\fP\|'. This defines all the files in directory
|
||||||
|
\fIdir\fP as module \fImname\fP. \fIdir\fP is a relative path (from
|
||||||
|
\fB$CVSROOT\fP) to a directory of source in one of the source
|
||||||
|
repositories. In this case, on \fBcheckout\fP, a single directory
|
||||||
|
called \fImname\fP is created as a working directory; no intermediate
|
||||||
|
directory levels are used by default, even if \fIdir\fP was a path
|
||||||
|
involving several directory levels.
|
||||||
|
.SP
|
||||||
|
By explicitly specifying \fIfiles\fP in the module definition after
|
||||||
|
\fIdir\fP, you can select particular files from directory
|
||||||
|
\fIdir\fP. The sample definition for \fBmodules\fP is an example of
|
||||||
|
a module defined with a single file from a particular directory. Here
|
||||||
|
is another example:
|
||||||
|
.SP
|
||||||
|
.nf
|
||||||
|
.ft B
|
||||||
|
m4test unsupported/gnu/m4 foreach.m4 forloop.m4
|
||||||
|
.ft P
|
||||||
|
.fi
|
||||||
|
.SP
|
||||||
|
With this definition, executing `\|\fBcvs checkout m4test\fP\|'
|
||||||
|
will create a single working directory `\|m4test\|' containing the two
|
||||||
|
files listed, which both come from a common directory several levels
|
||||||
|
deep in the \fBcvs\fP source repository.
|
||||||
|
.SP
|
||||||
|
A module definition can refer to other modules by including
|
||||||
|
`\|\fB&\fP\fImodule\fP\|' in its definition. \fBcheckout\fP creates
|
||||||
|
a subdirectory for each such \fImodule\fP, in your working directory.
|
||||||
|
.br
|
||||||
|
.I
|
||||||
|
New in \fBcvs\fP 1.3;
|
||||||
|
avoid this feature if sharing module definitions with older versions
|
||||||
|
of \fBcvs\fP.
|
||||||
|
.SP
|
||||||
|
Finally, you can use one or more of the following \fIoptions\fP in
|
||||||
|
module definitions:
|
||||||
|
.SP
|
||||||
|
\&`\|\fB\-d\fP \fIname\fP\|', to name the working directory something
|
||||||
|
other than the module name.
|
||||||
|
.br
|
||||||
|
.I
|
||||||
|
New in \fBcvs\fP 1.3;
|
||||||
|
avoid this feature if sharing module definitions with older versions
|
||||||
|
of \fBcvs\fP.
|
||||||
|
.SP
|
||||||
|
\&`\|\fB\-i\fP \fIprog\fP\|' allows you to specify a program \fIprog\fP
|
||||||
|
to run whenever files in a module are committed. \fIprog\fP runs with a
|
||||||
|
single argument, the full pathname of the affected directory in a
|
||||||
|
source repository. The `\|commitinfo\|', `\|loginfo\|', and
|
||||||
|
`\|editinfo\|' files provide other ways to call a program on \fBcommit\fP.
|
||||||
|
.SP
|
||||||
|
`\|\fB\-o\fP \fIprog\fP\|' allows you to specify a program \fIprog\fP
|
||||||
|
to run whenever files in a module are checked out. \fIprog\fP runs
|
||||||
|
with a single argument, the module name.
|
||||||
|
.SP
|
||||||
|
`\|\fB\-t\fP \fIprog\fP\|' allows you to specify a program \fIprog\fP
|
||||||
|
to run whenever files in a module are tagged. \fIprog\fP runs with two
|
||||||
|
arguments: the module name and the symbolic tag specified to \fBrtag\fP.
|
||||||
|
.SP
|
||||||
|
`\|\fB\-u\fP \fIprog\fP\|' allows you to specify a program \fIprog\fP
|
||||||
|
to run whenever `\|\fBcvs update\fP\|' is executed from the top-level
|
||||||
|
directory of the checked-out module. \fIprog\fP runs with a
|
||||||
|
single argument, the full path to the source repository for this module.
|
||||||
|
.TP
|
||||||
|
\&\fBcommitinfo\fP, \fBloginfo\fP, \fBrcsinfo\fP, \fBeditinfo\fP
|
||||||
|
These files all specify programs to call at different points in the
|
||||||
|
`\|\fBcvs commit\fP\|' process. They have a common structure.
|
||||||
|
Each line is a pair of fields: a regular expression, separated by
|
||||||
|
whitespace from a filename or command-line template.
|
||||||
|
Whenever one of the regular expression matches a directory name in the
|
||||||
|
repository, the rest of the line is used.
|
||||||
|
If the line begins with a \fB#\fP character, the entire line is considered
|
||||||
|
a comment and is ignored.
|
||||||
|
Whitespace between the fields is also ignored.
|
||||||
|
.SP
|
||||||
|
For `\|loginfo\|', the rest of the
|
||||||
|
line is a command-line template to execute.
|
||||||
|
The templates can include not only
|
||||||
|
a program name, but whatever list of arguments you wish. If you write
|
||||||
|
`\|\fB%s\fP\|' somewhere on the argument list, \fBcvs\fP supplies, at
|
||||||
|
that point, the list of files affected by the \fBcommit\fP.
|
||||||
|
The first entry in the list is the relative path within the source
|
||||||
|
repository where the change is being made.
|
||||||
|
The remaining arguments list the files that are being modified, added, or
|
||||||
|
removed by this \fBcommit\fP invocation.
|
||||||
|
.SP
|
||||||
|
For `\|commitinfo\|', the rest of the line is a command-line template to
|
||||||
|
execute.
|
||||||
|
The template can include can include not only a program name, but whatever
|
||||||
|
list of arguments you wish.
|
||||||
|
The full path to the current source repository is appended to the template,
|
||||||
|
followed by the file names of any files involved in the commit (added,
|
||||||
|
removed, and modified files).
|
||||||
|
.SP
|
||||||
|
For `\|rcsinfo\|', the rest of the line is the full path to a file that
|
||||||
|
should be loaded into the log message template.
|
||||||
|
.SP
|
||||||
|
For `\|editinfo\|', the rest of the line is a command-line template to
|
||||||
|
execute.
|
||||||
|
The template can include can include not only a program name, but whatever
|
||||||
|
list of arguments you wish.
|
||||||
|
The full path to the current log message template file is appended to the
|
||||||
|
template.
|
||||||
|
.SP
|
||||||
|
You can use one of two special strings instead of a regular
|
||||||
|
expression: `\|\fBALL\fP\|' specifies a command line template that
|
||||||
|
must always be executed, and `\|\fBDEFAULT\fP\|' specifies a command
|
||||||
|
line template to use if no regular expression is a match.
|
||||||
|
.SP
|
||||||
|
The `\|commitinfo\|' file contains commands to execute \fIbefore\fP any
|
||||||
|
other \fBcommit\fP activity, to allow you to check any conditions that
|
||||||
|
must be satisfied before \fBcommit\fP can proceed. The rest of the
|
||||||
|
\fBcommit\fP will execute only if all selected commands from this file
|
||||||
|
exit with exit status \fB0\fP.
|
||||||
|
.SP
|
||||||
|
The `\|rcsinfo\|' file allows you to specify \fIlog templates\fP for
|
||||||
|
the \fBcommit\fP logging session; you can use this to provide a form
|
||||||
|
to edit when filling out the \fBcommit\fP log. The field after the
|
||||||
|
regular expression, in this file, contains filenames (of files
|
||||||
|
containing the logging forms) rather than command templates.
|
||||||
|
.SP
|
||||||
|
The `\|editinfo\|' file allows you to execute a script \fIbefore the
|
||||||
|
commit starts\fP, but after the log information is recorded. These
|
||||||
|
"edit" scripts can verify information recorded in the log file. If
|
||||||
|
the edit script exits wth a non-zero exit status, the commit is aborted.
|
||||||
|
.SP
|
||||||
|
The `\|loginfo\|' file contains commands to execute \fIat the end\fP
|
||||||
|
of a commit. The text specified as a commit log message is piped
|
||||||
|
through the command; typical uses include sending mail, filing an
|
||||||
|
article in a newsgroup, or appending to a central file.
|
||||||
|
.TP
|
||||||
|
\&\fBcvsignore\fP, \fB.cvsignore\fP
|
||||||
|
The default list of files (or
|
||||||
|
.BR sh ( 1 )
|
||||||
|
file name patterns) to ignore during `\|\fBcvs update\fP\|'.
|
||||||
|
At startup time, \fBcvs\fP loads the compiled in default list of file name
|
||||||
|
patterns (see
|
||||||
|
.BR cvs ( 1 )).
|
||||||
|
Then the per-repository list included in \fB$CVSROOT/CVSROOT/cvsignore\fP
|
||||||
|
is loaded, if it exists.
|
||||||
|
Then the per-user list is loaded from `\|$HOME/.cvsignore\|'.
|
||||||
|
Finally, as \fBcvs\fP traverses through your directories, it will load any
|
||||||
|
per-directory `\|.cvsignore\|' files whenever it finds one.
|
||||||
|
These per-directory files are only valid for exactly the directory that
|
||||||
|
contains them, not for any sub-directories.
|
||||||
|
.TP
|
||||||
|
.B history
|
||||||
|
Create this file in \fB$CVSROOT/CVSROOT\fP to enable history logging
|
||||||
|
(see the description of `\|\fBcvs history\fP\|').
|
||||||
|
.SH "SEE ALSO"
|
||||||
|
.BR cvs ( 1 ),
|
||||||
|
.BR mkmodules ( 1 ).
|
||||||
|
.SH COPYING
|
||||||
|
Copyright \(co 1992 Cygnus Support, Brian Berliner, and Jeff Polk
|
||||||
|
.PP
|
||||||
|
Permission is granted to make and distribute verbatim copies of
|
||||||
|
this manual provided the copyright notice and this permission notice
|
||||||
|
are preserved on all copies.
|
||||||
|
.PP
|
||||||
|
Permission is granted to copy and distribute modified versions of this
|
||||||
|
manual under the conditions for verbatim copying, provided that the
|
||||||
|
entire resulting derived work is distributed under the terms of a
|
||||||
|
permission notice identical to this one.
|
||||||
|
.PP
|
||||||
|
Permission is granted to copy and distribute translations of this
|
||||||
|
manual into another language, under the above conditions for modified
|
||||||
|
versions, except that this permission notice may be included in
|
||||||
|
translations approved by the Free Software Foundation instead of in
|
||||||
|
the original English.
|
||||||
@@ -0,0 +1,438 @@
|
|||||||
|
/* @(#)cvs.h 1.72 92/03/31 */
|
||||||
|
|
||||||
|
#include "system.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <pwd.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include "hash.h"
|
||||||
|
#include "rcs.h"
|
||||||
|
#include "regex.h"
|
||||||
|
#include "fnmatch.h"
|
||||||
|
#include "getopt.h"
|
||||||
|
#include "wait.h"
|
||||||
|
#include "config.h"
|
||||||
|
#ifdef MY_NDBM
|
||||||
|
#include "myndbm.h"
|
||||||
|
#else
|
||||||
|
#include <ndbm.h>
|
||||||
|
#endif /* !MY_NDBM */
|
||||||
|
|
||||||
|
/* XXX - for now this is static */
|
||||||
|
#undef PATH_MAX
|
||||||
|
#ifdef MAXPATHLEN
|
||||||
|
#define PATH_MAX MAXPATHLEN+2
|
||||||
|
#else
|
||||||
|
#define PATH_MAX 1024+2
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* just in case this implementation does not define this */
|
||||||
|
#ifndef L_tmpnam
|
||||||
|
#define L_tmpnam 50
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __STDC__
|
||||||
|
#define CONST const
|
||||||
|
#define PTR void *
|
||||||
|
#else
|
||||||
|
#define CONST
|
||||||
|
#define PTR char *
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) 1992, Brian Berliner and Jeff Polk
|
||||||
|
* Copyright (c) 1989-1992, Brian Berliner
|
||||||
|
*
|
||||||
|
* You may distribute under the terms of the GNU General Public License as
|
||||||
|
* specified in the README file that comes with the CVS 1.3 kit.
|
||||||
|
*
|
||||||
|
* Definitions for the CVS Administrative directory and the files it contains.
|
||||||
|
* Here as #define's to make changing the names a simple task.
|
||||||
|
*/
|
||||||
|
#define CVSADM "CVS"
|
||||||
|
#define CVSADM_ENT "CVS/Entries"
|
||||||
|
#define CVSADM_ENTBAK "CVS/Entries.Backup"
|
||||||
|
#define CVSADM_ENTSTAT "CVS/Entries.Static"
|
||||||
|
#define CVSADM_REP "CVS/Repository"
|
||||||
|
#define CVSADM_CIPROG "CVS/Checkin.prog"
|
||||||
|
#define CVSADM_UPROG "CVS/Update.prog"
|
||||||
|
#define CVSADM_TAG "CVS/Tag"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The following are obsolete and are maintained here only so that they can be
|
||||||
|
* cleaned up during the transition
|
||||||
|
*/
|
||||||
|
#define OCVSADM "CVS.adm" /* for CVS 1.2 and earlier */
|
||||||
|
#define CVSADM_FILE "CVS/Files"
|
||||||
|
#define CVSADM_MOD "CVS/Mod"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Definitions for the CVSROOT Administrative directory and the files it
|
||||||
|
* contains. This directory is created as a sub-directory of the $CVSROOT
|
||||||
|
* environment variable, and holds global administration information for the
|
||||||
|
* entire source repository beginning at $CVSROOT.
|
||||||
|
*/
|
||||||
|
#define CVSROOTADM "CVSROOT"
|
||||||
|
#define CVSROOTADM_MODULES "modules"
|
||||||
|
#define CVSROOTADM_LOGINFO "loginfo"
|
||||||
|
#define CVSROOTADM_RCSINFO "rcsinfo"
|
||||||
|
#define CVSROOTADM_COMMITINFO "commitinfo"
|
||||||
|
#define CVSROOTADM_EDITINFO "editinfo"
|
||||||
|
#define CVSROOTADM_HISTORY "history"
|
||||||
|
#define CVSROOTADM_IGNORE "cvsignore"
|
||||||
|
#define CVSNULLREPOS "Emptydir" /* an empty directory */
|
||||||
|
|
||||||
|
/* support for the modules file (CVSROOTADM_MODULES) */
|
||||||
|
#define CVSMODULE_OPTS "ad:i:lo:s:t:u:"/* options in modules file */
|
||||||
|
#define CVSMODULE_SPEC '&' /* special delimiter */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The following are obsolete and are maintained here only so that they can be
|
||||||
|
* cleaned up during the transition
|
||||||
|
*/
|
||||||
|
#define OCVSROOTADM "CVSROOT.adm" /* for CVS 1.2 and earlier */
|
||||||
|
|
||||||
|
/* Other CVS file names */
|
||||||
|
#define CVSATTIC "Attic"
|
||||||
|
#define CVSLCK "#cvs.lock"
|
||||||
|
#define CVSTFL "#cvs.tfl"
|
||||||
|
#define CVSRFL "#cvs.rfl"
|
||||||
|
#define CVSWFL "#cvs.wfl"
|
||||||
|
#define CVSEXT_OPT ",p"
|
||||||
|
#define CVSEXT_LOG ",t"
|
||||||
|
#define CVSPREFIX ",,"
|
||||||
|
#define CVSDOTIGNORE ".cvsignore"
|
||||||
|
|
||||||
|
/* miscellaneous CVS defines */
|
||||||
|
#define CVSEDITPREFIX "CVS: "
|
||||||
|
#define CVSLCKAGE (60*60) /* 1-hour old lock files cleaned up */
|
||||||
|
#define CVSLCKSLEEP 30 /* wait 30 seconds before retrying */
|
||||||
|
#define CVSBRANCH "1.1.1" /* RCS branch used for vendor srcs */
|
||||||
|
#define BAKPREFIX ".#" /* when rcsmerge'ing */
|
||||||
|
#define DEVNULL "/dev/null"
|
||||||
|
|
||||||
|
#define FALSE 0
|
||||||
|
#define TRUE 1
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Special tags. -rHEAD refers to the head of an RCS file, regardless of any
|
||||||
|
* sticky tags. -rBASE refers to the current revision the user has checked
|
||||||
|
* out This mimics the behaviour of RCS.
|
||||||
|
*/
|
||||||
|
#define TAG_HEAD "HEAD"
|
||||||
|
#define TAG_BASE "BASE"
|
||||||
|
|
||||||
|
/* Environment variable used by CVS */
|
||||||
|
#define CVSREAD_ENV "CVSREAD" /* make files read-only */
|
||||||
|
#define CVSREAD_DFLT FALSE /* writable files by default */
|
||||||
|
|
||||||
|
#define RCSBIN_ENV "RCSBIN" /* RCS binary directory */
|
||||||
|
/* #define RCSBIN_DFLT Set by config.h */
|
||||||
|
|
||||||
|
#define EDITOR_ENV "EDITOR" /* which editor to use */
|
||||||
|
/* #define EDITOR_DFLT Set by config.h */
|
||||||
|
|
||||||
|
#define CVSROOT_ENV "CVSROOT" /* source directory root */
|
||||||
|
#define CVSROOT_DFLT NULL /* No dflt; must set for checkout */
|
||||||
|
|
||||||
|
#define IGNORE_ENV "CVSIGNORE" /* More files to ignore */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the beginning of the Repository matches the following string, strip it
|
||||||
|
* so that the output to the logfile does not contain a full pathname.
|
||||||
|
*
|
||||||
|
* If the CVSROOT environment variable is set, it overrides this define.
|
||||||
|
*/
|
||||||
|
#define REPOS_STRIP "/master/"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The maximum number of files per each CVS directory. This is mainly for
|
||||||
|
* sizing arrays statically rather than dynamically. 3000 seems plenty for
|
||||||
|
* now.
|
||||||
|
*/
|
||||||
|
#define MAXFILEPERDIR 3000
|
||||||
|
#define MAXLINELEN 5000 /* max input line from a file */
|
||||||
|
#define MAXPROGLEN 30000 /* max program length to system() */
|
||||||
|
#define MAXLISTLEN 40000 /* For [A-Z]list holders */
|
||||||
|
#define MAXMESGLEN 10000 /* max RCS log message size */
|
||||||
|
#define MAXDATELEN 50 /* max length for a date */
|
||||||
|
|
||||||
|
/* The type of request that is being done in do_module() */
|
||||||
|
enum mtype
|
||||||
|
{
|
||||||
|
CHECKOUT, TAG, PATCH
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* defines for Classify_File() to determine the current state of a file.
|
||||||
|
* These are also used as types in the data field for the list we make for
|
||||||
|
* Update_Logfile in commit, import, and add.
|
||||||
|
*/
|
||||||
|
enum classify_type
|
||||||
|
{
|
||||||
|
T_UNKNOWN = 1, /* no old-style analog existed */
|
||||||
|
T_CONFLICT, /* C (conflict) list */
|
||||||
|
T_NEEDS_MERGE, /* G (needs merging) list */
|
||||||
|
T_MODIFIED, /* M (needs checked in) list */
|
||||||
|
T_CHECKOUT, /* O (needs checkout) list */
|
||||||
|
T_ADDED, /* A (added file) list */
|
||||||
|
T_REMOVED, /* R (removed file) list */
|
||||||
|
T_REMOVE_ENTRY, /* W (removed entry) list */
|
||||||
|
T_UPTODATE, /* File is up-to-date */
|
||||||
|
T_TITLE /* title for node type */
|
||||||
|
};
|
||||||
|
typedef enum classify_type Ctype;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* a struct vers_ts contains all the information about a file including the
|
||||||
|
* user and rcs file names, and the version checked out and the head.
|
||||||
|
*
|
||||||
|
* this is usually obtained from a call to Version_TS which takes a tag argument
|
||||||
|
* for the RCS file if desired
|
||||||
|
*/
|
||||||
|
struct vers_ts
|
||||||
|
{
|
||||||
|
char *vn_user; /* rcs version user file derives from
|
||||||
|
* it can have the following special
|
||||||
|
* values:
|
||||||
|
* empty = no user file
|
||||||
|
* 0 = user file is new
|
||||||
|
* -vers = user file to be removed */
|
||||||
|
char *vn_rcs; /* the verion for the rcs file
|
||||||
|
* (tag version?) */
|
||||||
|
char *ts_user; /* the timestamp for the user file */
|
||||||
|
char *ts_rcs; /* the user timestamp from entries */
|
||||||
|
char *options; /* opts from Entries file
|
||||||
|
* (keyword expansion) */
|
||||||
|
char *tag; /* tag stored in the Entries file */
|
||||||
|
char *date; /* date stored in the Entries file */
|
||||||
|
Entnode *entdata; /* pointer to entries file node */
|
||||||
|
RCSNode *srcfile; /* pointer to parsed src file info */
|
||||||
|
};
|
||||||
|
typedef struct vers_ts Vers_TS;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* structure used for list-private storage by ParseEntries() and
|
||||||
|
* Version_TS().
|
||||||
|
*/
|
||||||
|
struct stickydirtag
|
||||||
|
{
|
||||||
|
int aflag;
|
||||||
|
char *tag;
|
||||||
|
char *date;
|
||||||
|
char *options;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* flags for run_exec(), the fast system() for CVS */
|
||||||
|
#define RUN_NORMAL 0x0000 /* no special behaviour */
|
||||||
|
#define RUN_COMBINED 0x0001 /* stdout is duped to stderr */
|
||||||
|
#define RUN_REALLY 0x0002 /* do the exec, even if noexec is on */
|
||||||
|
#define RUN_STDOUT_APPEND 0x0004 /* append to stdout, don't truncate */
|
||||||
|
#define RUN_STDERR_APPEND 0x0008 /* append to stderr, don't truncate */
|
||||||
|
#define RUN_SIGIGNORE 0x0010 /* ignore interrupts for command */
|
||||||
|
#define RUN_TTY (char *)0 /* for the benefit of lint */
|
||||||
|
|
||||||
|
/* Flags for find_{names,dirs} routines */
|
||||||
|
#define W_LOCAL 0x01 /* look for files locally */
|
||||||
|
#define W_REPOS 0x02 /* look for files in the repository */
|
||||||
|
#define W_ATTIC 0x04 /* look for files in the attic */
|
||||||
|
|
||||||
|
/* Flags for return values of direnter procs for the recursion processor */
|
||||||
|
enum direnter_type
|
||||||
|
{
|
||||||
|
R_PROCESS = 1, /* process files and maybe dirs */
|
||||||
|
R_SKIP_FILES, /* don't process files in this dir */
|
||||||
|
R_SKIP_DIRS, /* don't process sub-dirs */
|
||||||
|
R_SKIP_ALL /* don't process files or dirs */
|
||||||
|
};
|
||||||
|
typedef enum direnter_type Dtype;
|
||||||
|
|
||||||
|
extern char *program_name, *command_name;
|
||||||
|
extern char *Rcsbin, *Editor, *CVSroot;
|
||||||
|
extern char *CurDir;
|
||||||
|
extern int really_quiet, quiet;
|
||||||
|
extern int use_editor;
|
||||||
|
extern int cvswrite;
|
||||||
|
|
||||||
|
extern int trace; /* Show all commands */
|
||||||
|
extern int noexec; /* Don't modify disk anywhere */
|
||||||
|
extern int logoff; /* Don't write history entry */
|
||||||
|
|
||||||
|
/* Externs that are included directly in the CVS sources */
|
||||||
|
#if __STDC__
|
||||||
|
int Reader_Lock (char *xrepository);
|
||||||
|
DBM *open_module (void);
|
||||||
|
FILE *Fopen (char *name, char *mode);
|
||||||
|
FILE *open_file (char *name, char *mode);
|
||||||
|
List *Find_Dirs (char *repository, int which);
|
||||||
|
List *ParseEntries (int aflag);
|
||||||
|
char *Make_Date (char *rawdate);
|
||||||
|
char *Name_Repository (char *dir, char *update_dir);
|
||||||
|
char *Short_Repository (char *repository);
|
||||||
|
char *getcaller (void);
|
||||||
|
char *time_stamp (char *file);
|
||||||
|
char *xmalloc (int bytes);
|
||||||
|
char *xrealloc (char *ptr, int bytes);
|
||||||
|
char *xstrdup (char *str);
|
||||||
|
int No_Difference (char *file, Vers_TS * vers, List * entries);
|
||||||
|
int Parse_Info (char *infofile, char *repository, int (*callproc) (), int all);
|
||||||
|
int Reader_Lock (char *xrepository);
|
||||||
|
int SIG_register (int sig, SIGTYPE (*fn) ());
|
||||||
|
int Writer_Lock (List * list);
|
||||||
|
int gethostname (char *name, int namelen);
|
||||||
|
int ign_name (char *name);
|
||||||
|
int isdir (char *file);
|
||||||
|
int isfile (char *file);
|
||||||
|
int islink (char *file);
|
||||||
|
int isreadable (char *file);
|
||||||
|
int iswritable (char *file);
|
||||||
|
int link_file (char *from, char *to);
|
||||||
|
int numdots (char *s);
|
||||||
|
int run_exec (char *stin, char *stout, char *sterr, int flags);
|
||||||
|
int unlink_file (char *f);
|
||||||
|
int update (int argc, char *argv[]);
|
||||||
|
int xcmp (char *file1, char *file2);
|
||||||
|
int yesno (void);
|
||||||
|
time_t get_date (char *date, struct timeb *now);
|
||||||
|
void Create_Admin (char *dir, char *repository, char *tag, char *date);
|
||||||
|
void Lock_Cleanup (void);
|
||||||
|
void ParseTag (char **tagp, char **datep);
|
||||||
|
void Scratch_Entry (List * list, char *fname);
|
||||||
|
void WriteTag (char *dir, char *tag, char *date);
|
||||||
|
void cat_module (int status);
|
||||||
|
void check_entries (char *dir);
|
||||||
|
void close_module (DBM * db);
|
||||||
|
void copy_file (char *from, char *to);
|
||||||
|
void error (int status, int errnum, char *message,...);
|
||||||
|
void fperror (FILE * fp, int status, int errnum, char *message,...);
|
||||||
|
void free_names (int *pargc, char *argv[]);
|
||||||
|
void freevers_ts (Vers_TS ** versp);
|
||||||
|
void ign_add (char *ign, int hold);
|
||||||
|
void ign_add_file (char *file, int hold);
|
||||||
|
void ign_setup (void);
|
||||||
|
void line2argv (int *pargc, char *argv[], char *line);
|
||||||
|
void make_directories (char *name);
|
||||||
|
void make_directory (char *name);
|
||||||
|
void rename_file (char *from, char *to);
|
||||||
|
void run_arg (char *s);
|
||||||
|
void run_args (char *fmt,...);
|
||||||
|
void run_print (FILE * fp);
|
||||||
|
void run_setup (char *fmt,...);
|
||||||
|
void strip_path (char *path);
|
||||||
|
void update_delproc (Node * p);
|
||||||
|
void usage (char **cpp);
|
||||||
|
void xchmod (char *fname, int writable);
|
||||||
|
int Checkin (int type, char *file, char *repository, char *rcs, char *rev,
|
||||||
|
char *tag, char *message, List * entries);
|
||||||
|
Ctype Classify_File (char *file, char *tag, char *date, char *options,
|
||||||
|
int force_tag_match, int aflag, char *repository,
|
||||||
|
List *entries, List *srcfiles, Vers_TS **versp);
|
||||||
|
List *Find_Names (char *repository, int which, int aflag,
|
||||||
|
List ** optentries);
|
||||||
|
void Register (List * list, char *fname, char *vn, char *ts,
|
||||||
|
char *options, char *tag, char *date);
|
||||||
|
void Update_Logfile (char *repository, char *xmessage, char *xrevision,
|
||||||
|
FILE * xlogfp, List * xchanges);
|
||||||
|
Vers_TS *Version_TS (char *repository, char *options, char *tag,
|
||||||
|
char *date, char *user, int force_tag_match,
|
||||||
|
int set_time, List * entries, List * xfiles);
|
||||||
|
void do_editor (char *dir, char *message, char *repository,
|
||||||
|
List * changes);
|
||||||
|
int do_module (DBM * db, char *mname, enum mtype m_type, char *msg,
|
||||||
|
int (*callback_proc) (), char *where, int shorten,
|
||||||
|
int local_specified, int run_module_prog, char *extra_arg);
|
||||||
|
int do_recursion (int (*xfileproc) (), int (*xfilesdoneproc) (),
|
||||||
|
Dtype (*xdirentproc) (), int (*xdirleaveproc) (),
|
||||||
|
Dtype xflags, int xwhich, int xaflag, int xreadlock,
|
||||||
|
int xdosrcs);
|
||||||
|
int do_update (int argc, char *argv[], char *xoptions, char *xtag,
|
||||||
|
char *xdate, int xforce, int local, int xbuild,
|
||||||
|
int xaflag, int xprune, int xpipeout, int which,
|
||||||
|
char *xjoin_rev1, char *xjoin_rev2, char *preload_update_dir);
|
||||||
|
void history_write (int type, char *update_dir, char *revs, char *name,
|
||||||
|
char *repository);
|
||||||
|
int start_recursion (int (*fileproc) (), int (*filesdoneproc) (),
|
||||||
|
Dtype (*direntproc) (), int (*dirleaveproc) (),
|
||||||
|
int argc, char *argv[], int local, int which,
|
||||||
|
int aflag, int readlock, char *update_preload,
|
||||||
|
int dosrcs);
|
||||||
|
void SIG_beginCrSect ();
|
||||||
|
void SIG_endCrSect ();
|
||||||
|
#else /* !__STDC__ */
|
||||||
|
DBM *open_module ();
|
||||||
|
FILE *Fopen ();
|
||||||
|
FILE *open_file ();
|
||||||
|
List *Find_Dirs ();
|
||||||
|
List *Find_Names ();
|
||||||
|
List *ParseEntries ();
|
||||||
|
Vers_TS *Version_TS ();
|
||||||
|
char *Make_Date ();
|
||||||
|
char *Name_Repository ();
|
||||||
|
char *Short_Repository ();
|
||||||
|
char *getcaller ();
|
||||||
|
char *time_stamp ();
|
||||||
|
char *xmalloc ();
|
||||||
|
char *xrealloc ();
|
||||||
|
char *xstrdup ();
|
||||||
|
int Checkin ();
|
||||||
|
Ctype Classify_File ();
|
||||||
|
int No_Difference ();
|
||||||
|
int Parse_Info ();
|
||||||
|
int Reader_Lock ();
|
||||||
|
int SIG_register ();
|
||||||
|
int Writer_Lock ();
|
||||||
|
int do_module ();
|
||||||
|
int do_recursion ();
|
||||||
|
int do_update ();
|
||||||
|
int gethostname ();
|
||||||
|
int ign_name ();
|
||||||
|
int isdir ();
|
||||||
|
int isfile ();
|
||||||
|
int islink ();
|
||||||
|
int isreadable ();
|
||||||
|
int iswritable ();
|
||||||
|
int link_file ();
|
||||||
|
int numdots ();
|
||||||
|
int run_exec ();
|
||||||
|
int start_recursion ();
|
||||||
|
int unlink_file ();
|
||||||
|
int update ();
|
||||||
|
int xcmp ();
|
||||||
|
int yesno ();
|
||||||
|
time_t get_date ();
|
||||||
|
void Create_Admin ();
|
||||||
|
void Lock_Cleanup ();
|
||||||
|
void ParseTag ();
|
||||||
|
void ParseTag ();
|
||||||
|
void Register ();
|
||||||
|
void Scratch_Entry ();
|
||||||
|
void Update_Logfile ();
|
||||||
|
void WriteTag ();
|
||||||
|
void cat_module ();
|
||||||
|
void check_entries ();
|
||||||
|
void close_module ();
|
||||||
|
void copy_file ();
|
||||||
|
void do_editor ();
|
||||||
|
void error ();
|
||||||
|
void fperror ();
|
||||||
|
void free_names ();
|
||||||
|
void freevers_ts ();
|
||||||
|
void history_write ();
|
||||||
|
void ign_add ();
|
||||||
|
void ign_add_file ();
|
||||||
|
void ign_setup ();
|
||||||
|
void line2argv ();
|
||||||
|
void make_directories ();
|
||||||
|
void make_directory ();
|
||||||
|
void rename_file ();
|
||||||
|
void run_arg ();
|
||||||
|
void run_args ();
|
||||||
|
void run_print ();
|
||||||
|
void run_setup ();
|
||||||
|
void strip_path ();
|
||||||
|
void update_delproc ();
|
||||||
|
void usage ();
|
||||||
|
void xchmod ();
|
||||||
|
void SIG_beginCrSect ();
|
||||||
|
void SIG_endCrSect ();
|
||||||
|
#endif /* __STDC__ */
|
||||||
@@ -0,0 +1,407 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 1992, Brian Berliner and Jeff Polk
|
||||||
|
* Copyright (c) 1989-1992, Brian Berliner
|
||||||
|
*
|
||||||
|
* You may distribute under the terms of the GNU General Public License as
|
||||||
|
* specified in the README file that comes with the CVS 1.3 kit.
|
||||||
|
*
|
||||||
|
* Difference
|
||||||
|
*
|
||||||
|
* Run diff against versions in the repository. Options that are specified are
|
||||||
|
* passed on directly to "rcsdiff".
|
||||||
|
*
|
||||||
|
* Without any file arguments, runs diff against all the currently modified
|
||||||
|
* files.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "cvs.h"
|
||||||
|
|
||||||
|
#ifndef lint
|
||||||
|
static char rcsid[] = "@(#)diff.c 1.52 92/04/10";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __STDC__
|
||||||
|
static Dtype diff_dirproc (char *dir, char *pos_repos, char *update_dir);
|
||||||
|
static int diff_dirleaveproc (char *dir, int err, char *update_dir);
|
||||||
|
static int diff_file_nodiff (char *file, char *repository, List *entries,
|
||||||
|
List *srcfiles, Vers_TS *vers);
|
||||||
|
static int diff_fileproc (char *file, char *update_dir, char *repository,
|
||||||
|
List * entries, List * srcfiles);
|
||||||
|
static void diff_mark_errors (int err);
|
||||||
|
#else
|
||||||
|
static int diff_fileproc ();
|
||||||
|
static Dtype diff_dirproc ();
|
||||||
|
static int diff_dirleaveproc ();
|
||||||
|
static int diff_file_nodiff ();
|
||||||
|
static void diff_mark_errors ();
|
||||||
|
#endif /* __STDC__ */
|
||||||
|
|
||||||
|
static char *diff_rev1, *diff_rev2;
|
||||||
|
static char *diff_date1, *diff_date2;
|
||||||
|
static char *use_rev1, *use_rev2;
|
||||||
|
static char *options;
|
||||||
|
static char opts[PATH_MAX];
|
||||||
|
static int diff_errors;
|
||||||
|
|
||||||
|
static char *diff_usage[] =
|
||||||
|
{
|
||||||
|
"Usage: %s %s [-l] [rcsdiff-options]\n",
|
||||||
|
#ifdef CVS_DIFFDATE
|
||||||
|
" [[-r rev1 | -D date1] [-r rev2 | -D date2]] [files...] \n",
|
||||||
|
#else
|
||||||
|
" [-r rev1 [-r rev2]] [files...] \n",
|
||||||
|
#endif
|
||||||
|
"\t-l\tLocal directory only, not recursive\n",
|
||||||
|
"\t-D d1\tDiff revision for date against working file.\n",
|
||||||
|
"\t-D d2\tDiff rev1/date1 against date2.\n",
|
||||||
|
"\t-r rev1\tDiff revision for rev1 against working file.\n",
|
||||||
|
"\t-r rev2\tDiff rev1/date1 against rev2.\n",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
int
|
||||||
|
diff (argc, argv)
|
||||||
|
int argc;
|
||||||
|
char *argv[];
|
||||||
|
{
|
||||||
|
char tmp[50];
|
||||||
|
int c, err = 0;
|
||||||
|
int local = 0;
|
||||||
|
|
||||||
|
if (argc == -1)
|
||||||
|
usage (diff_usage);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Note that we catch all the valid arguments here, so that we can
|
||||||
|
* intercept the -r arguments for doing revision diffs; and -l/-R for a
|
||||||
|
* non-recursive/recursive diff.
|
||||||
|
*/
|
||||||
|
optind = 1;
|
||||||
|
while ((c = gnu_getopt (argc, argv,
|
||||||
|
"abcdefhilnpqtuw0123456789BHQRTC:D:F:I:L:V:k:r:")) != -1)
|
||||||
|
{
|
||||||
|
switch (c)
|
||||||
|
{
|
||||||
|
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
|
||||||
|
case 'h': case 'i': case 'n': case 'p': case 't': case 'u':
|
||||||
|
case 'w': case '0': case '1': case '2': case '3': case '4':
|
||||||
|
case '5': case '6': case '7': case '8': case '9': case 'B':
|
||||||
|
case 'H': case 'T': case 'Q':
|
||||||
|
(void) sprintf (tmp, " -%c", (char) c);
|
||||||
|
(void) strcat (opts, tmp);
|
||||||
|
if (c == 'Q')
|
||||||
|
{
|
||||||
|
quiet = 1;
|
||||||
|
really_quiet = 1;
|
||||||
|
c = 'q';
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'C': case 'F': case 'I': case 'L': case 'V':
|
||||||
|
#ifndef CVS_DIFFDATE
|
||||||
|
case 'D':
|
||||||
|
#endif
|
||||||
|
(void) sprintf (tmp, " -%c%s", (char) c, optarg);
|
||||||
|
(void) strcat (opts, tmp);
|
||||||
|
break;
|
||||||
|
case 'R':
|
||||||
|
local = 0;
|
||||||
|
break;
|
||||||
|
case 'l':
|
||||||
|
local = 1;
|
||||||
|
break;
|
||||||
|
case 'q':
|
||||||
|
quiet = 1;
|
||||||
|
break;
|
||||||
|
case 'k':
|
||||||
|
if (options)
|
||||||
|
free (options);
|
||||||
|
options = RCS_check_kflag (optarg);
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
if (diff_rev2 != NULL || diff_date2 != NULL)
|
||||||
|
error (1, 0,
|
||||||
|
"no more than two revisions/dates can be specified");
|
||||||
|
if (diff_rev1 != NULL || diff_date1 != NULL)
|
||||||
|
diff_rev2 = optarg;
|
||||||
|
else
|
||||||
|
diff_rev1 = optarg;
|
||||||
|
break;
|
||||||
|
#ifdef CVS_DIFFDATE
|
||||||
|
case 'D':
|
||||||
|
if (diff_rev2 != NULL || diff_date2 != NULL)
|
||||||
|
error (1, 0,
|
||||||
|
"no more than two revisions/dates can be specified");
|
||||||
|
if (diff_rev1 != NULL || diff_date1 != NULL)
|
||||||
|
diff_date2 = Make_Date (optarg);
|
||||||
|
else
|
||||||
|
diff_date1 = Make_Date (optarg);
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
case '?':
|
||||||
|
default:
|
||||||
|
usage (diff_usage);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
argc -= optind;
|
||||||
|
argv += optind;
|
||||||
|
|
||||||
|
/* make sure options is non-null */
|
||||||
|
if (!options)
|
||||||
|
options = xstrdup ("");
|
||||||
|
|
||||||
|
/* start the recursion processor */
|
||||||
|
err = start_recursion (diff_fileproc, (int (*) ()) NULL, diff_dirproc,
|
||||||
|
diff_dirleaveproc, argc, argv, local,
|
||||||
|
W_LOCAL, 0, 1, (char *) NULL, 1);
|
||||||
|
|
||||||
|
/* clean up */
|
||||||
|
free (options);
|
||||||
|
return (err);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Do a file diff
|
||||||
|
*/
|
||||||
|
/* ARGSUSED */
|
||||||
|
static int
|
||||||
|
diff_fileproc (file, update_dir, repository, entries, srcfiles)
|
||||||
|
char *file;
|
||||||
|
char *update_dir;
|
||||||
|
char *repository;
|
||||||
|
List *entries;
|
||||||
|
List *srcfiles;
|
||||||
|
{
|
||||||
|
int status, err = 2; /* 2 == trouble, like rcsdiff */
|
||||||
|
Vers_TS *vers;
|
||||||
|
|
||||||
|
vers = Version_TS (repository, (char *) NULL, (char *) NULL, (char *) NULL,
|
||||||
|
file, 1, 0, entries, srcfiles);
|
||||||
|
|
||||||
|
if (vers->vn_user == NULL)
|
||||||
|
{
|
||||||
|
error (0, 0, "I know nothing about %s", file);
|
||||||
|
freevers_ts (&vers);
|
||||||
|
diff_mark_errors (err);
|
||||||
|
return (err);
|
||||||
|
}
|
||||||
|
else if (vers->vn_user[0] == '0' && vers->vn_user[1] == '\0')
|
||||||
|
{
|
||||||
|
error (0, 0, "%s is a new entry, no comparison available", file);
|
||||||
|
freevers_ts (&vers);
|
||||||
|
diff_mark_errors (err);
|
||||||
|
return (err);
|
||||||
|
}
|
||||||
|
else if (vers->vn_user[0] == '-')
|
||||||
|
{
|
||||||
|
error (0, 0, "%s was removed, no comparison available", file);
|
||||||
|
freevers_ts (&vers);
|
||||||
|
diff_mark_errors (err);
|
||||||
|
return (err);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (vers->vn_rcs == NULL && vers->srcfile == NULL)
|
||||||
|
{
|
||||||
|
error (0, 0, "cannot find revision control file for %s", file);
|
||||||
|
freevers_ts (&vers);
|
||||||
|
diff_mark_errors (err);
|
||||||
|
return (err);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (vers->ts_user == NULL)
|
||||||
|
{
|
||||||
|
error (0, 0, "cannot find %s", file);
|
||||||
|
freevers_ts (&vers);
|
||||||
|
diff_mark_errors (err);
|
||||||
|
return (err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (diff_file_nodiff (file, repository, entries, srcfiles, vers))
|
||||||
|
{
|
||||||
|
freevers_ts (&vers);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
(void) fflush (stdout);
|
||||||
|
if (use_rev2)
|
||||||
|
{
|
||||||
|
run_setup ("%s%s %s %s -r%s -r%s", Rcsbin, RCS_DIFF,
|
||||||
|
opts, *options ? options : vers->options,
|
||||||
|
use_rev1, use_rev2);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
run_setup ("%s%s %s %s -r%s", Rcsbin, RCS_DIFF, opts,
|
||||||
|
*options ? options : vers->options, use_rev1);
|
||||||
|
}
|
||||||
|
run_arg (vers->srcfile->path);
|
||||||
|
|
||||||
|
switch ((status = run_exec (RUN_TTY, RUN_TTY, RUN_TTY,
|
||||||
|
RUN_REALLY|RUN_COMBINED)))
|
||||||
|
{
|
||||||
|
case -1: /* fork failed */
|
||||||
|
error (1, errno, "fork failed during rcsdiff of %s",
|
||||||
|
vers->srcfile->path);
|
||||||
|
case 0: /* everything ok */
|
||||||
|
err = 0;
|
||||||
|
break;
|
||||||
|
default: /* other error */
|
||||||
|
err = status;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
(void) fflush (stdout);
|
||||||
|
freevers_ts (&vers);
|
||||||
|
diff_mark_errors (err);
|
||||||
|
return (err);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Remember the exit status for each file.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
diff_mark_errors (err)
|
||||||
|
int err;
|
||||||
|
{
|
||||||
|
if (err > diff_errors)
|
||||||
|
diff_errors = err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Print a warm fuzzy message when we enter a dir
|
||||||
|
*/
|
||||||
|
/* ARGSUSED */
|
||||||
|
static Dtype
|
||||||
|
diff_dirproc (dir, pos_repos, update_dir)
|
||||||
|
char *dir;
|
||||||
|
char *pos_repos;
|
||||||
|
char *update_dir;
|
||||||
|
{
|
||||||
|
/* XXX - check for dirs we don't want to process??? */
|
||||||
|
if (!quiet)
|
||||||
|
error (0, 0, "Diffing %s", update_dir);
|
||||||
|
return (R_PROCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Concoct the proper exit status.
|
||||||
|
*/
|
||||||
|
/* ARGSUSED */
|
||||||
|
static int
|
||||||
|
diff_dirleaveproc (dir, err, update_dir)
|
||||||
|
char *dir;
|
||||||
|
int err;
|
||||||
|
char *update_dir;
|
||||||
|
{
|
||||||
|
return (diff_errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* verify that a file is different 0=same 1=different
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
diff_file_nodiff (file, repository, entries, srcfiles, vers)
|
||||||
|
char *file;
|
||||||
|
char *repository;
|
||||||
|
List *entries;
|
||||||
|
List *srcfiles;
|
||||||
|
Vers_TS *vers;
|
||||||
|
{
|
||||||
|
Vers_TS *xvers;
|
||||||
|
char tmp[L_tmpnam+1];
|
||||||
|
|
||||||
|
/* free up any old use_rev* variables and reset 'em */
|
||||||
|
if (use_rev1)
|
||||||
|
free (use_rev1);
|
||||||
|
if (use_rev2)
|
||||||
|
free (use_rev2);
|
||||||
|
use_rev1 = use_rev2 = (char *) NULL;
|
||||||
|
|
||||||
|
if (diff_rev1 || diff_date1)
|
||||||
|
{
|
||||||
|
/* special handling for TAG_HEAD */
|
||||||
|
if (diff_rev1 && strcmp (diff_rev1, TAG_HEAD) == 0)
|
||||||
|
use_rev1 = xstrdup (vers->vn_rcs);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
xvers = Version_TS (repository, (char *) NULL, diff_rev1,
|
||||||
|
diff_date1, file, 1, 0, entries, srcfiles);
|
||||||
|
if (xvers->vn_rcs == NULL)
|
||||||
|
{
|
||||||
|
if (diff_rev1)
|
||||||
|
error (0, 0, "tag %s is not in file %s", diff_rev1, file);
|
||||||
|
else
|
||||||
|
error (0, 0, "no revision for date %s in file %s",
|
||||||
|
diff_date1, file);
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
use_rev1 = xstrdup (xvers->vn_rcs);
|
||||||
|
freevers_ts (&xvers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (diff_rev2 || diff_date2)
|
||||||
|
{
|
||||||
|
/* special handling for TAG_HEAD */
|
||||||
|
if (diff_rev2 && strcmp (diff_rev2, TAG_HEAD) == 0)
|
||||||
|
use_rev2 = xstrdup (vers->vn_rcs);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
xvers = Version_TS (repository, (char *) NULL, diff_rev2,
|
||||||
|
diff_date2, file, 1, 0, entries, srcfiles);
|
||||||
|
if (xvers->vn_rcs == NULL)
|
||||||
|
{
|
||||||
|
if (diff_rev1)
|
||||||
|
error (0, 0, "tag %s is not in file %s", diff_rev2, file);
|
||||||
|
else
|
||||||
|
error (0, 0, "no revision for date %s in file %s",
|
||||||
|
diff_date2, file);
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
use_rev2 = xstrdup (xvers->vn_rcs);
|
||||||
|
freevers_ts (&xvers);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* now, see if we really need to do the diff */
|
||||||
|
return (strcmp (use_rev1, use_rev2) == 0);
|
||||||
|
}
|
||||||
|
if (use_rev1 == NULL || strcmp (use_rev1, vers->vn_user) == 0)
|
||||||
|
{
|
||||||
|
if (strcmp (vers->ts_rcs, vers->ts_user) == 0 &&
|
||||||
|
(!(*options) || strcmp (options, vers->options) == 0))
|
||||||
|
{
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
if (use_rev1 == NULL)
|
||||||
|
use_rev1 = xstrdup (vers->vn_user);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* with 0 or 1 -r option specified, run a quick diff to see if we
|
||||||
|
* should bother with it at all.
|
||||||
|
*/
|
||||||
|
run_setup ("%s%s -p -q %s -r%s", Rcsbin, RCS_CO,
|
||||||
|
*options ? options : vers->options, use_rev1);
|
||||||
|
run_arg (vers->srcfile->path);
|
||||||
|
switch (run_exec (RUN_TTY, tmpnam (tmp), RUN_TTY, RUN_REALLY))
|
||||||
|
{
|
||||||
|
case 0: /* everything ok */
|
||||||
|
if (xcmp (file, tmp) == 0)
|
||||||
|
{
|
||||||
|
(void) unlink (tmp);
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case -1: /* fork failed */
|
||||||
|
(void) unlink (tmp);
|
||||||
|
error (1, errno, "fork failed during checkout of %s",
|
||||||
|
vers->srcfile->path);
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
(void) unlink (tmp);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
@@ -0,0 +1,488 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 1992, Brian Berliner and Jeff Polk
|
||||||
|
* Copyright (c) 1989-1992, Brian Berliner
|
||||||
|
*
|
||||||
|
* You may distribute under the terms of the GNU General Public License as
|
||||||
|
* specified in the README file that comes with the CVS 1.3 kit.
|
||||||
|
*
|
||||||
|
* Entries file to Files file
|
||||||
|
*
|
||||||
|
* Creates the file Files containing the names that comprise the project, from
|
||||||
|
* the Entries file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "cvs.h"
|
||||||
|
|
||||||
|
#ifndef lint
|
||||||
|
static char rcsid[] = "@(#)entries.c 1.37 92/03/31";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __STDC__
|
||||||
|
static Node *AddEntryNode (List * list, char *name, char *version,
|
||||||
|
char *timestamp, char *options, char *tag,
|
||||||
|
char *date);
|
||||||
|
#else
|
||||||
|
static Node *AddEntryNode ();
|
||||||
|
#endif /* __STDC__ */
|
||||||
|
|
||||||
|
static FILE *entfile;
|
||||||
|
static char *entfilename; /* for error messages */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Write out the line associated with a node of an entries file
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
write_ent_proc (node)
|
||||||
|
Node *node;
|
||||||
|
{
|
||||||
|
Entnode *p;
|
||||||
|
|
||||||
|
p = (Entnode *) node->data;
|
||||||
|
if (fprintf (entfile, "/%s/%s/%s/%s/", node->key, p->version,
|
||||||
|
p->timestamp, p->options) == EOF)
|
||||||
|
error (1, errno, "cannot write %s", entfilename);
|
||||||
|
if (p->tag)
|
||||||
|
{
|
||||||
|
if (fprintf (entfile, "T%s\n", p->tag) == EOF)
|
||||||
|
error (1, errno, "cannot write %s", entfilename);
|
||||||
|
}
|
||||||
|
else if (p->date)
|
||||||
|
{
|
||||||
|
if (fprintf (entfile, "D%s\n", p->date) == EOF)
|
||||||
|
error (1, errno, "cannot write %s", entfilename);
|
||||||
|
}
|
||||||
|
else if (fprintf (entfile, "\n") == EOF)
|
||||||
|
error (1, errno, "cannot write %s", entfilename);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* write out the current entries file given a list, making a backup copy
|
||||||
|
* first of course
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
write_entries (list)
|
||||||
|
List *list;
|
||||||
|
{
|
||||||
|
/* open the new one and walk the list writing entries */
|
||||||
|
entfilename = CVSADM_ENTBAK;
|
||||||
|
entfile = open_file (entfilename, "w+");
|
||||||
|
(void) walklist (list, write_ent_proc);
|
||||||
|
if (fclose (entfile) == EOF)
|
||||||
|
error (1, errno, "error closing %s", entfilename);
|
||||||
|
|
||||||
|
/* now, atomically (on systems that support it) rename it */
|
||||||
|
rename_file (entfilename, CVSADM_ENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Removes the argument file from the Entries file if necessary.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
Scratch_Entry (list, fname)
|
||||||
|
List *list;
|
||||||
|
char *fname;
|
||||||
|
{
|
||||||
|
Node *node;
|
||||||
|
|
||||||
|
if (trace)
|
||||||
|
(void) fprintf (stderr, "-> Scratch_Entry(%s)\n", fname);
|
||||||
|
|
||||||
|
/* hashlookup to see if it is there */
|
||||||
|
if ((node = findnode (list, fname)) != NULL)
|
||||||
|
{
|
||||||
|
delnode (node); /* delete the node */
|
||||||
|
if (!noexec)
|
||||||
|
write_entries (list); /* re-write the file */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Enters the given file name/version/time-stamp into the Entries file,
|
||||||
|
* removing the old entry first, if necessary.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
Register (list, fname, vn, ts, options, tag, date)
|
||||||
|
List *list;
|
||||||
|
char *fname;
|
||||||
|
char *vn;
|
||||||
|
char *ts;
|
||||||
|
char *options;
|
||||||
|
char *tag;
|
||||||
|
char *date;
|
||||||
|
{
|
||||||
|
Node *node;
|
||||||
|
|
||||||
|
if (trace)
|
||||||
|
(void) fprintf (stderr, "-> Register(%s, %s, %s, %s, %s %s)\n",
|
||||||
|
fname, vn, ts, options, tag ? tag : "",
|
||||||
|
date ? date : "");
|
||||||
|
/* was it already there? */
|
||||||
|
if ((node = findnode (list, fname)) != NULL)
|
||||||
|
{
|
||||||
|
/* take it out */
|
||||||
|
delnode (node);
|
||||||
|
|
||||||
|
/* add the new one and re-write the file */
|
||||||
|
(void) AddEntryNode (list, fname, vn, ts, options, tag, date);
|
||||||
|
if (!noexec)
|
||||||
|
write_entries (list);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* add the new one */
|
||||||
|
node = AddEntryNode (list, fname, vn, ts, options, tag, date);
|
||||||
|
|
||||||
|
if (!noexec)
|
||||||
|
{
|
||||||
|
/* append it to the end */
|
||||||
|
entfilename = CVSADM_ENT;
|
||||||
|
entfile = open_file (entfilename, "a");
|
||||||
|
(void) write_ent_proc (node);
|
||||||
|
if (fclose (entfile) == EOF)
|
||||||
|
error (1, errno, "error closing %s", entfilename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Node delete procedure for list-private sticky dir tag/date info
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
freesdt (p)
|
||||||
|
Node *p;
|
||||||
|
{
|
||||||
|
struct stickydirtag *sdtp;
|
||||||
|
|
||||||
|
sdtp = (struct stickydirtag *) p->data;
|
||||||
|
if (sdtp->tag)
|
||||||
|
free (sdtp->tag);
|
||||||
|
if (sdtp->date)
|
||||||
|
free (sdtp->date);
|
||||||
|
if (sdtp->options)
|
||||||
|
free (sdtp->options);
|
||||||
|
free ((char *) sdtp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read the entries file into a list, hashing on the file name.
|
||||||
|
*/
|
||||||
|
List *
|
||||||
|
ParseEntries (aflag)
|
||||||
|
int aflag;
|
||||||
|
{
|
||||||
|
List *entries;
|
||||||
|
char line[MAXLINELEN];
|
||||||
|
char *cp, *user, *vn, *ts, *options;
|
||||||
|
char *tag_or_date, *tag, *date;
|
||||||
|
char *dirtag, *dirdate;
|
||||||
|
int lineno = 0;
|
||||||
|
FILE *fpin;
|
||||||
|
|
||||||
|
/* get a fresh list... */
|
||||||
|
entries = getlist ();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parse the CVS/Tag file, to get any default tag/date settings. Use
|
||||||
|
* list-private storage to tuck them away for Version_TS().
|
||||||
|
*/
|
||||||
|
ParseTag (&dirtag, &dirdate);
|
||||||
|
if (aflag || dirtag || dirdate)
|
||||||
|
{
|
||||||
|
struct stickydirtag *sdtp;
|
||||||
|
|
||||||
|
sdtp = (struct stickydirtag *) xmalloc (sizeof (*sdtp));
|
||||||
|
bzero ((char *) sdtp, sizeof (*sdtp));
|
||||||
|
sdtp->aflag = aflag;
|
||||||
|
sdtp->tag = xstrdup (dirtag);
|
||||||
|
sdtp->date = xstrdup (dirdate);
|
||||||
|
|
||||||
|
/* feed it into the list-private area */
|
||||||
|
entries->list->data = (char *) sdtp;
|
||||||
|
entries->list->delproc = freesdt;
|
||||||
|
}
|
||||||
|
|
||||||
|
again:
|
||||||
|
fpin = fopen (CVSADM_ENT, "r");
|
||||||
|
if (fpin == NULL)
|
||||||
|
error (0, errno, "cannot open %s for reading", CVSADM_ENT);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
while (fgets (line, sizeof (line), fpin) != NULL)
|
||||||
|
{
|
||||||
|
lineno++;
|
||||||
|
if (line[0] == '/')
|
||||||
|
{
|
||||||
|
user = line + 1;
|
||||||
|
if ((cp = index (user, '/')) == NULL)
|
||||||
|
continue;
|
||||||
|
*cp++ = '\0';
|
||||||
|
vn = cp;
|
||||||
|
if ((cp = index (vn, '/')) == NULL)
|
||||||
|
continue;
|
||||||
|
*cp++ = '\0';
|
||||||
|
ts = cp;
|
||||||
|
if ((cp = index (ts, '/')) == NULL)
|
||||||
|
continue;
|
||||||
|
*cp++ = '\0';
|
||||||
|
options = cp;
|
||||||
|
if ((cp = index (options, '/')) == NULL)
|
||||||
|
continue;
|
||||||
|
*cp++ = '\0';
|
||||||
|
tag_or_date = cp;
|
||||||
|
if ((cp = index (tag_or_date, '\n')) == NULL)
|
||||||
|
continue;
|
||||||
|
*cp = '\0';
|
||||||
|
tag = (char *) NULL;
|
||||||
|
date = (char *) NULL;
|
||||||
|
if (*tag_or_date == 'T')
|
||||||
|
tag = tag_or_date + 1;
|
||||||
|
else if (*tag_or_date == 'D')
|
||||||
|
date = tag_or_date + 1;
|
||||||
|
(void) AddEntryNode (entries, user, vn, ts, options, tag, date);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* try conversion only on first line */
|
||||||
|
if (lineno == 1)
|
||||||
|
{
|
||||||
|
(void) fclose (fpin);
|
||||||
|
check_entries ((char *) NULL);
|
||||||
|
goto again;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* clean up and return */
|
||||||
|
if (fpin)
|
||||||
|
(void) fclose (fpin);
|
||||||
|
if (dirtag)
|
||||||
|
free (dirtag);
|
||||||
|
if (dirdate)
|
||||||
|
free (dirdate);
|
||||||
|
return (entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Look at the entries file to determine if it is in the old entries format.
|
||||||
|
* If so, convert it to the new format.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
check_entries (dir)
|
||||||
|
char *dir;
|
||||||
|
{
|
||||||
|
FILE *fpin, *fpout;
|
||||||
|
char tmp[MAXLINELEN];
|
||||||
|
char line[MAXLINELEN];
|
||||||
|
char entname[MAXLINELEN];
|
||||||
|
char entbak[MAXLINELEN];
|
||||||
|
char *cp, *user, *rev, *ts, *opt;
|
||||||
|
|
||||||
|
if (dir != NULL)
|
||||||
|
{
|
||||||
|
(void) sprintf (entname, "%s/%s", dir, CVSADM_ENT);
|
||||||
|
(void) sprintf (entbak, "%s/%s", dir, CVSADM_ENTBAK);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
(void) strcpy (entname, CVSADM_ENT);
|
||||||
|
(void) strcpy (entbak, CVSADM_ENTBAK);
|
||||||
|
}
|
||||||
|
|
||||||
|
fpin = open_file (entname, "r");
|
||||||
|
if (fgets (line, sizeof (line), fpin) == NULL)
|
||||||
|
{
|
||||||
|
(void) fclose (fpin);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
(void) fclose (fpin);
|
||||||
|
if (line[0] != '/')
|
||||||
|
{
|
||||||
|
rename_file (entname, entbak);
|
||||||
|
fpin = open_file (entbak, "r");
|
||||||
|
fpout = open_file (entname, "w+");
|
||||||
|
while (fgets (line, sizeof (line), fpin) != NULL)
|
||||||
|
{
|
||||||
|
if (line[0] == '/')
|
||||||
|
{
|
||||||
|
if (fputs (line, fpout) == EOF)
|
||||||
|
error (1, errno, "cannot write %s", CVSADM_ENT);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
rev = line;
|
||||||
|
if ((ts = index (line, '|')) == NULL)
|
||||||
|
continue;
|
||||||
|
*ts++ = '\0';
|
||||||
|
if ((user = rindex (ts, ' ')) == NULL)
|
||||||
|
continue;
|
||||||
|
*user++ = '\0';
|
||||||
|
if ((cp = index (user, '|')) == NULL)
|
||||||
|
continue;
|
||||||
|
*cp = '\0';
|
||||||
|
opt = "";
|
||||||
|
#ifdef HAVE_RCS5
|
||||||
|
#ifdef HAD_RCS4
|
||||||
|
opt = "-V4";
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
if (fprintf (fpout, "/%s/%s/%s/%s/\n", user, rev, ts, opt) == EOF)
|
||||||
|
error (1, errno, "cannot write %s", CVSADM_ENT);
|
||||||
|
}
|
||||||
|
(void) fclose (fpin);
|
||||||
|
if (fclose (fpout) == EOF)
|
||||||
|
error (1, errno, "cannot close %s", entname);
|
||||||
|
|
||||||
|
/* clean up any old Files or Mod files */
|
||||||
|
if (dir != NULL)
|
||||||
|
(void) sprintf (tmp, "%s/%s", dir, CVSADM_FILE);
|
||||||
|
else
|
||||||
|
(void) strcpy (tmp, CVSADM_FILE);
|
||||||
|
if (isfile (tmp))
|
||||||
|
(void) unlink (tmp);
|
||||||
|
|
||||||
|
if (dir != NULL)
|
||||||
|
(void) sprintf (tmp, "%s/%s", dir, CVSADM_MOD);
|
||||||
|
else
|
||||||
|
(void) strcpy (tmp, CVSADM_MOD);
|
||||||
|
if (isfile (tmp))
|
||||||
|
(void) unlink (tmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Free up the memory associated with the data section of an ENTRIES type
|
||||||
|
* node
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
Entries_delproc (node)
|
||||||
|
Node *node;
|
||||||
|
{
|
||||||
|
Entnode *p;
|
||||||
|
|
||||||
|
p = (Entnode *) node->data;
|
||||||
|
free (p->version);
|
||||||
|
free (p->timestamp);
|
||||||
|
free (p->options);
|
||||||
|
if (p->tag)
|
||||||
|
free (p->tag);
|
||||||
|
if (p->date)
|
||||||
|
free (p->date);
|
||||||
|
free ((char *) p);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get an Entries file list node, initialize it, and add it to the specified
|
||||||
|
* list
|
||||||
|
*/
|
||||||
|
static Node *
|
||||||
|
AddEntryNode (list, name, version, timestamp, options, tag, date)
|
||||||
|
List *list;
|
||||||
|
char *name;
|
||||||
|
char *version;
|
||||||
|
char *timestamp;
|
||||||
|
char *options;
|
||||||
|
char *tag;
|
||||||
|
char *date;
|
||||||
|
{
|
||||||
|
Node *p;
|
||||||
|
Entnode *entdata;
|
||||||
|
|
||||||
|
/* get a node and fill in the regular stuff */
|
||||||
|
p = getnode ();
|
||||||
|
p->type = ENTRIES;
|
||||||
|
p->delproc = Entries_delproc;
|
||||||
|
|
||||||
|
/* this one gets a key of the name for hashing */
|
||||||
|
p->key = xstrdup (name);
|
||||||
|
|
||||||
|
/* malloc the data parts and fill them in */
|
||||||
|
p->data = xmalloc (sizeof (Entnode));
|
||||||
|
entdata = (Entnode *) p->data;
|
||||||
|
entdata->version = xstrdup (version);
|
||||||
|
entdata->timestamp = xstrdup (timestamp);
|
||||||
|
entdata->options = xstrdup (options);
|
||||||
|
if (entdata->options == NULL)
|
||||||
|
entdata->options = xstrdup ("");/* must be non-NULL */
|
||||||
|
entdata->tag = xstrdup (tag);
|
||||||
|
entdata->date = xstrdup (date);
|
||||||
|
|
||||||
|
/* put the node into the list */
|
||||||
|
if (addnode (list, p) != 0)
|
||||||
|
error (0, 0, "Duplicate filename in entries file (%s) -- ignored",
|
||||||
|
name);
|
||||||
|
|
||||||
|
return (p);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Write out/Clear the CVS/Tag file.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
WriteTag (dir, tag, date)
|
||||||
|
char *dir;
|
||||||
|
char *tag;
|
||||||
|
char *date;
|
||||||
|
{
|
||||||
|
FILE *fout;
|
||||||
|
char tmp[PATH_MAX];
|
||||||
|
|
||||||
|
if (noexec)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (dir == NULL)
|
||||||
|
(void) strcpy (tmp, CVSADM_TAG);
|
||||||
|
else
|
||||||
|
(void) sprintf (tmp, "%s/%s", dir, CVSADM_TAG);
|
||||||
|
|
||||||
|
if (tag || date)
|
||||||
|
{
|
||||||
|
fout = open_file (tmp, "w+");
|
||||||
|
if (tag)
|
||||||
|
{
|
||||||
|
if (fprintf (fout, "T%s\n", tag) == EOF)
|
||||||
|
error (1, errno, "write to %s failed", tmp);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (fprintf (fout, "D%s\n", date) == EOF)
|
||||||
|
error (1, errno, "write to %s failed", tmp);
|
||||||
|
}
|
||||||
|
if (fclose (fout) == EOF)
|
||||||
|
error (1, errno, "cannot close %s", tmp);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
(void) unlink_file (tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parse the CVS/Tag file for the current directory.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
ParseTag (tagp, datep)
|
||||||
|
char **tagp;
|
||||||
|
char **datep;
|
||||||
|
{
|
||||||
|
FILE *fp;
|
||||||
|
char line[MAXLINELEN];
|
||||||
|
char *cp;
|
||||||
|
|
||||||
|
if (tagp)
|
||||||
|
*tagp = (char *) NULL;
|
||||||
|
if (datep)
|
||||||
|
*datep = (char *) NULL;
|
||||||
|
fp = fopen (CVSADM_TAG, "r");
|
||||||
|
if (fp)
|
||||||
|
{
|
||||||
|
if (fgets (line, sizeof (line), fp) != NULL)
|
||||||
|
{
|
||||||
|
if ((cp = rindex (line, '\n')) != NULL)
|
||||||
|
*cp = '\0';
|
||||||
|
if (*line == 'T' && tagp)
|
||||||
|
*tagp = xstrdup (line + 1);
|
||||||
|
else if (*line == 'D' && datep)
|
||||||
|
*datep = xstrdup (line + 1);
|
||||||
|
}
|
||||||
|
(void) fclose (fp);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,272 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 1992, Brian Berliner and Jeff Polk
|
||||||
|
* Copyright (c) 1989-1992, Brian Berliner
|
||||||
|
*
|
||||||
|
* You may distribute under the terms of the GNU General Public License as
|
||||||
|
* specified in the README file that comes with the CVS 1.3 kit.
|
||||||
|
*
|
||||||
|
* Find Names
|
||||||
|
*
|
||||||
|
* Finds all the pertinent file names, both from the administration and from the
|
||||||
|
* repository
|
||||||
|
*
|
||||||
|
* Find Dirs
|
||||||
|
*
|
||||||
|
* Finds all pertinent sub-directories of the checked out instantiation and the
|
||||||
|
* repository (and optionally the attic)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "cvs.h"
|
||||||
|
|
||||||
|
#ifndef lint
|
||||||
|
static char rcsid[] = "@(#)find_names.c 1.38 92/04/10";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __STDC__
|
||||||
|
static int find_dirs (char *dir, List * list, int checkadm);
|
||||||
|
static int find_rcs (char *dir, List * list);
|
||||||
|
#else
|
||||||
|
static int find_rcs ();
|
||||||
|
static int find_dirs ();
|
||||||
|
#endif /* __STDC__ */
|
||||||
|
|
||||||
|
static List *filelist;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* add the key from entry on entries list to the files list
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
add_entries_proc (node)
|
||||||
|
Node *node;
|
||||||
|
{
|
||||||
|
Node *fnode;
|
||||||
|
|
||||||
|
fnode = getnode ();
|
||||||
|
fnode->type = FILES;
|
||||||
|
fnode->key = xstrdup (node->key);
|
||||||
|
if (addnode (filelist, fnode) != 0)
|
||||||
|
freenode (fnode);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* compare two files list node (for sort)
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
fsortcmp (p, q)
|
||||||
|
Node *p, *q;
|
||||||
|
{
|
||||||
|
return (strcmp (p->key, q->key));
|
||||||
|
}
|
||||||
|
|
||||||
|
List *
|
||||||
|
Find_Names (repository, which, aflag, optentries)
|
||||||
|
char *repository;
|
||||||
|
int which;
|
||||||
|
int aflag;
|
||||||
|
List **optentries;
|
||||||
|
{
|
||||||
|
List *entries;
|
||||||
|
List *files;
|
||||||
|
char dir[PATH_MAX];
|
||||||
|
|
||||||
|
/* make a list for the files */
|
||||||
|
files = filelist = getlist ();
|
||||||
|
|
||||||
|
/* look at entries (if necessary) */
|
||||||
|
if (which & W_LOCAL)
|
||||||
|
{
|
||||||
|
/* parse the entries file (if it exists) */
|
||||||
|
entries = ParseEntries (aflag);
|
||||||
|
|
||||||
|
if (entries != NULL)
|
||||||
|
{
|
||||||
|
/* walk the entries file adding elements to the files list */
|
||||||
|
(void) walklist (entries, add_entries_proc);
|
||||||
|
|
||||||
|
/* if our caller wanted the entries list, return it; else free it */
|
||||||
|
if (optentries != NULL)
|
||||||
|
*optentries = entries;
|
||||||
|
else
|
||||||
|
dellist (&entries);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((which & W_REPOS) && repository && !isreadable (CVSADM_ENTSTAT))
|
||||||
|
{
|
||||||
|
/* search the repository */
|
||||||
|
if (find_rcs (repository, files) != 0)
|
||||||
|
error (1, errno, "cannot open directory %s", repository);
|
||||||
|
|
||||||
|
/* search the attic too */
|
||||||
|
if (which & W_ATTIC)
|
||||||
|
{
|
||||||
|
(void) sprintf (dir, "%s/%s", repository, CVSATTIC);
|
||||||
|
(void) find_rcs (dir, files);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* sort the list into alphabetical order and return it */
|
||||||
|
sortlist (files, fsortcmp);
|
||||||
|
return (files);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* create a list of directories to traverse from the current directory
|
||||||
|
*/
|
||||||
|
List *
|
||||||
|
Find_Dirs (repository, which)
|
||||||
|
char *repository;
|
||||||
|
int which;
|
||||||
|
{
|
||||||
|
List *dirlist;
|
||||||
|
|
||||||
|
/* make a list for the directories */
|
||||||
|
dirlist = getlist ();
|
||||||
|
|
||||||
|
/* find the local ones */
|
||||||
|
if (which & W_LOCAL)
|
||||||
|
{
|
||||||
|
/* look only for CVS controlled sub-directories */
|
||||||
|
if (find_dirs (".", dirlist, 1) != 0)
|
||||||
|
error (1, errno, "cannot open current directory");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* look for sub-dirs in the repository */
|
||||||
|
if ((which & W_REPOS) && repository)
|
||||||
|
{
|
||||||
|
/* search the repository */
|
||||||
|
if (find_dirs (repository, dirlist, 0) != 0)
|
||||||
|
error (1, errno, "cannot open directory %s", repository);
|
||||||
|
|
||||||
|
#ifdef ATTIC_DIR_SUPPORT /* XXX - FIXME */
|
||||||
|
/* search the attic too */
|
||||||
|
if (which & W_ATTIC)
|
||||||
|
{
|
||||||
|
char dir[PATH_MAX];
|
||||||
|
|
||||||
|
(void) sprintf (dir, "%s/%s", repository, CVSATTIC);
|
||||||
|
(void) find_dirs (dir, dirlist, 0);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/* sort the list into alphabetical order and return it */
|
||||||
|
sortlist (dirlist, fsortcmp);
|
||||||
|
return (dirlist);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Finds all the ,v files in the argument directory, and adds them to the
|
||||||
|
* files list. Returns 0 for success and non-zero if the argument directory
|
||||||
|
* cannot be opened.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
find_rcs (dir, list)
|
||||||
|
char *dir;
|
||||||
|
List *list;
|
||||||
|
{
|
||||||
|
Node *p;
|
||||||
|
CONST char *regex_err;
|
||||||
|
char line[50];
|
||||||
|
struct direct *dp;
|
||||||
|
DIR *dirp;
|
||||||
|
|
||||||
|
/* set up to read the dir */
|
||||||
|
if ((dirp = opendir (dir)) == NULL)
|
||||||
|
return (1);
|
||||||
|
|
||||||
|
/* set up a regular expression to find the ,v files */
|
||||||
|
(void) sprintf (line, ".*%s$", RCSEXT);
|
||||||
|
if ((regex_err = re_comp (line)) != NULL)
|
||||||
|
error (1, 0, "%s", regex_err);
|
||||||
|
|
||||||
|
/* read the dir, grabbing the ,v files */
|
||||||
|
while ((dp = readdir (dirp)) != NULL)
|
||||||
|
{
|
||||||
|
if (re_exec (dp->d_name))
|
||||||
|
{
|
||||||
|
char *comma;
|
||||||
|
|
||||||
|
comma = rindex (dp->d_name, ','); /* strip the ,v */
|
||||||
|
*comma = '\0';
|
||||||
|
p = getnode ();
|
||||||
|
p->type = FILES;
|
||||||
|
p->key = xstrdup (dp->d_name);
|
||||||
|
if (addnode (list, p) != 0)
|
||||||
|
freenode (p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(void) closedir (dirp);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Finds all the subdirectories of the argument dir and adds them to the
|
||||||
|
* specified list. Sub-directories without a CVS administration directory
|
||||||
|
* are optionally ignored Returns 0 for success or 1 on error.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
find_dirs (dir, list, checkadm)
|
||||||
|
char *dir;
|
||||||
|
List *list;
|
||||||
|
int checkadm;
|
||||||
|
{
|
||||||
|
Node *p;
|
||||||
|
CONST char *regex_err;
|
||||||
|
char tmp[PATH_MAX];
|
||||||
|
char admdir[PATH_MAX];
|
||||||
|
struct direct *dp;
|
||||||
|
DIR *dirp;
|
||||||
|
|
||||||
|
/* build a regex to blow off ,v files */
|
||||||
|
(void) sprintf (tmp, ".*%s$", RCSEXT);
|
||||||
|
if ((regex_err = re_comp (tmp)) != NULL)
|
||||||
|
error (1, 0, "%s", regex_err);
|
||||||
|
|
||||||
|
/* set up to read the dir */
|
||||||
|
if ((dirp = opendir (dir)) == NULL)
|
||||||
|
return (1);
|
||||||
|
|
||||||
|
/* read the dir, grabbing sub-dirs */
|
||||||
|
while ((dp = readdir (dirp)) != NULL)
|
||||||
|
{
|
||||||
|
if (strcmp (dp->d_name, ".") == 0 ||
|
||||||
|
strcmp (dp->d_name, "..") == 0 ||
|
||||||
|
strcmp (dp->d_name, CVSATTIC) == 0 ||
|
||||||
|
strcmp (dp->d_name, CVSLCK) == 0 ||
|
||||||
|
re_exec (dp->d_name)) /* don't bother stating ,v files */
|
||||||
|
continue;
|
||||||
|
|
||||||
|
(void) sprintf (tmp, "%s/%s", dir, dp->d_name);
|
||||||
|
if (isdir (tmp))
|
||||||
|
{
|
||||||
|
/* check for administration directories (if needed) */
|
||||||
|
if (checkadm)
|
||||||
|
{
|
||||||
|
/* blow off symbolic links to dirs in local dir */
|
||||||
|
if (islink (tmp))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* check for new style */
|
||||||
|
(void) sprintf (admdir, "%s/%s", tmp, CVSADM);
|
||||||
|
if (!isdir (admdir))
|
||||||
|
{
|
||||||
|
/* and old style */
|
||||||
|
(void) sprintf (admdir, "%s/%s", tmp, OCVSADM);
|
||||||
|
if (!isdir (admdir))
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* put it in the list */
|
||||||
|
p = getnode ();
|
||||||
|
p->type = DIRS;
|
||||||
|
p->key = xstrdup (dp->d_name);
|
||||||
|
if (addnode (list, p) != 0)
|
||||||
|
freenode (p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(void) closedir (dirp);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,227 @@
|
|||||||
|
/*
|
||||||
|
* .cvsignore file support contributed by David G. Grubbs <dgg@ksr.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "cvs.h"
|
||||||
|
|
||||||
|
#ifndef lint
|
||||||
|
static char rcsid[] = "@(#)ignore.c 1.13 92/04/03";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ignore file section.
|
||||||
|
*
|
||||||
|
* "!" may be included any time to reset the list (i.e. ignore nothing);
|
||||||
|
* "*" may be specified to ignore everything. It stays as the first
|
||||||
|
* element forever, unless a "!" clears it out.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static char **ign_list; /* List of files to ignore in update
|
||||||
|
* and import */
|
||||||
|
static char **s_ign_list = NULL;
|
||||||
|
static int ign_count; /* Number of active entries */
|
||||||
|
static int s_ign_count = 0;
|
||||||
|
static int ign_size; /* This many slots available (plus
|
||||||
|
* one for a NULL) */
|
||||||
|
static int ign_hold; /* Index where first "temporary" item
|
||||||
|
* is held */
|
||||||
|
|
||||||
|
char *ign_default = ". .. core RCSLOG tags TAGS RCS SCCS .make.state .nse_depinfo #* .#* cvslog.* ,* CVS* .del-* *.a *.o *.so *.Z *~ *.old *.elc *.ln *.bak *.BAK *.orig *.rej";
|
||||||
|
|
||||||
|
#define IGN_GROW 16 /* grow the list by 16 elements at a
|
||||||
|
* time */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* To the "ignore list", add the hard-coded default ignored wildcards above,
|
||||||
|
* the wildcards found in $CVSROOT/CVSROOT/cvsignore, the wildcards found in
|
||||||
|
* ~/.cvsignore and the wildcards found in the CVSIGNORE environment
|
||||||
|
* variable.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
ign_setup ()
|
||||||
|
{
|
||||||
|
extern char *getenv ();
|
||||||
|
struct passwd *pw;
|
||||||
|
char file[PATH_MAX];
|
||||||
|
char *tmp;
|
||||||
|
|
||||||
|
/* Start with default list and special case */
|
||||||
|
tmp = xstrdup (ign_default);
|
||||||
|
ign_add (tmp, 0);
|
||||||
|
free (tmp);
|
||||||
|
|
||||||
|
/* Then add entries found in repository, if it exists */
|
||||||
|
(void) sprintf (file, "%s/%s/%s", CVSroot, CVSROOTADM, CVSROOTADM_IGNORE);
|
||||||
|
if (isfile (file))
|
||||||
|
ign_add_file (file, 0);
|
||||||
|
|
||||||
|
/* Then add entries found in home dir, (if user has one) and file exists */
|
||||||
|
if ((pw = (struct passwd *) getpwuid (getuid ())) && pw->pw_dir)
|
||||||
|
{
|
||||||
|
(void) sprintf (file, "%s/%s", pw->pw_dir, CVSDOTIGNORE);
|
||||||
|
if (isfile (file))
|
||||||
|
ign_add_file (file, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Then add entries found in CVSIGNORE environment variable. */
|
||||||
|
ign_add (getenv (IGNORE_ENV), 0);
|
||||||
|
|
||||||
|
/* Later, add ignore entries found in -I arguments */
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Open a file and read lines, feeding each line to a line parser. Arrange
|
||||||
|
* for keeping a temporary list of wildcards at the end, if the "hold"
|
||||||
|
* argument is set.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
ign_add_file (file, hold)
|
||||||
|
char *file;
|
||||||
|
int hold;
|
||||||
|
{
|
||||||
|
FILE *fp;
|
||||||
|
char line[1024];
|
||||||
|
|
||||||
|
/* restore the saved list (if any) */
|
||||||
|
if (s_ign_list != NULL)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < s_ign_count; i++)
|
||||||
|
ign_list[i] = s_ign_list[i];
|
||||||
|
ign_count = s_ign_count;
|
||||||
|
ign_list[ign_count] = NULL;
|
||||||
|
|
||||||
|
s_ign_count = 0;
|
||||||
|
free (s_ign_list);
|
||||||
|
s_ign_list = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* is this a temporary ignore file? */
|
||||||
|
if (hold)
|
||||||
|
{
|
||||||
|
/* re-set if we had already done a temporary file */
|
||||||
|
if (ign_hold)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = ign_hold; i < ign_count; i++)
|
||||||
|
free (ign_list[i]);
|
||||||
|
ign_count = ign_hold;
|
||||||
|
ign_list[ign_count] = NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ign_hold = ign_count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* load the file */
|
||||||
|
if (!(fp = fopen (file, "r")))
|
||||||
|
return;
|
||||||
|
while (fgets (line, sizeof (line), fp))
|
||||||
|
ign_add (line, hold);
|
||||||
|
(void) fclose (fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parse a line of space-separated wildcards and add them to the list. */
|
||||||
|
void
|
||||||
|
ign_add (ign, hold)
|
||||||
|
char *ign;
|
||||||
|
int hold;
|
||||||
|
{
|
||||||
|
if (!ign || !*ign)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (; *ign; ign++)
|
||||||
|
{
|
||||||
|
char *mark;
|
||||||
|
char save;
|
||||||
|
|
||||||
|
/* ignore whitespace before the token */
|
||||||
|
if (isspace (*ign))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* if we find a single character !, we must re-set the ignore list
|
||||||
|
* (saving it if necessary). We also catch * as a special case in a
|
||||||
|
* global ignore file as an optimization
|
||||||
|
*/
|
||||||
|
if (isspace (*(ign + 1)) && (*ign == '!' || *ign == '*'))
|
||||||
|
{
|
||||||
|
if (!hold)
|
||||||
|
{
|
||||||
|
/* permanently reset the ignore list */
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < ign_count; i++)
|
||||||
|
free (ign_list[i]);
|
||||||
|
ign_count = 0;
|
||||||
|
ign_list[0] = NULL;
|
||||||
|
|
||||||
|
/* if we are doing a '!', continue; otherwise add the '*' */
|
||||||
|
if (*ign == '!')
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (*ign == '!')
|
||||||
|
{
|
||||||
|
/* temporarily reset the ignore list */
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (ign_hold)
|
||||||
|
{
|
||||||
|
for (i = ign_hold; i < ign_count; i++)
|
||||||
|
free (ign_list[i]);
|
||||||
|
ign_hold = 0;
|
||||||
|
}
|
||||||
|
s_ign_list = (char **) xmalloc (ign_count * sizeof (char *));
|
||||||
|
for (i = 0; i < ign_count; i++)
|
||||||
|
s_ign_list[i] = ign_list[i];
|
||||||
|
s_ign_count = ign_count;
|
||||||
|
ign_count = 0;
|
||||||
|
ign_list[0] = NULL;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If we have used up all the space, add some more */
|
||||||
|
if (ign_count >= ign_size)
|
||||||
|
{
|
||||||
|
ign_size += IGN_GROW;
|
||||||
|
ign_list = (char **) xrealloc ((char *) ign_list,
|
||||||
|
(ign_size + 1) * sizeof (char *));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* find the end of this token */
|
||||||
|
for (mark = ign; *mark && !isspace (*mark); mark++)
|
||||||
|
/* do nothing */ ;
|
||||||
|
|
||||||
|
save = *mark;
|
||||||
|
*mark = '\0';
|
||||||
|
|
||||||
|
ign_list[ign_count++] = xstrdup (ign);
|
||||||
|
ign_list[ign_count] = NULL;
|
||||||
|
|
||||||
|
*mark = save;
|
||||||
|
if (save)
|
||||||
|
ign = mark;
|
||||||
|
else
|
||||||
|
ign = mark - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return 1 if the given filename should be ignored by update or import. */
|
||||||
|
int
|
||||||
|
ign_name (name)
|
||||||
|
char *name;
|
||||||
|
{
|
||||||
|
char **cpp = ign_list;
|
||||||
|
|
||||||
|
if (cpp == NULL)
|
||||||
|
return (0);
|
||||||
|
|
||||||
|
while (*cpp)
|
||||||
|
if (fnmatch (*cpp++, name, 0) == 0)
|
||||||
|
return (1);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
@@ -0,0 +1,974 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 1992, Brian Berliner and Jeff Polk
|
||||||
|
* Copyright (c) 1989-1992, Brian Berliner
|
||||||
|
*
|
||||||
|
* You may distribute under the terms of the GNU General Public License as
|
||||||
|
* specified in the README file that comes with the CVS 1.3 kit.
|
||||||
|
*
|
||||||
|
* "import" checks in the vendor release located in the current directory into
|
||||||
|
* the CVS source repository. The CVS vendor branch support is utilized.
|
||||||
|
*
|
||||||
|
* At least three arguments are expected to follow the options:
|
||||||
|
* repository Where the source belongs relative to the CVSROOT
|
||||||
|
* VendorTag Vendor's major tag
|
||||||
|
* VendorReleTag Tag for this particular release
|
||||||
|
*
|
||||||
|
* Additional arguments specify more Vendor Release Tags.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "cvs.h"
|
||||||
|
|
||||||
|
#ifndef lint
|
||||||
|
static char rcsid[] = "@(#)import.c 1.52 92/03/31";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define FILE_HOLDER ".#cvsxxx"
|
||||||
|
|
||||||
|
#if __STDC__
|
||||||
|
static char *get_comment (char *user);
|
||||||
|
static int add_rcs_file (char *message, char *rcs, char *user, char *vtag,
|
||||||
|
int targc, char *targv[]);
|
||||||
|
static int expand_at_signs (char *buf, off_t size, FILE *fp);
|
||||||
|
static int add_rev (char *message, char *rcs, char *vfile, char *vers);
|
||||||
|
static int add_tags (char *rcs, char *vfile, char *vtag, int targc,
|
||||||
|
char *targv[]);
|
||||||
|
static int import_descend (char *message, char *vtag, int targc, char *targv[]);
|
||||||
|
static int import_descend_dir (char *message, char *dir, char *vtag,
|
||||||
|
int targc, char *targv[]);
|
||||||
|
static int process_import_file (char *message, char *vfile, char *vtag,
|
||||||
|
int targc, char *targv[]);
|
||||||
|
static int update_rcs_file (char *message, char *vfile, char *vtag, int targc,
|
||||||
|
char *targv[]);
|
||||||
|
static void add_log (int ch, char *fname);
|
||||||
|
#else
|
||||||
|
static int import_descend ();
|
||||||
|
static int process_import_file ();
|
||||||
|
static int update_rcs_file ();
|
||||||
|
static int add_rev ();
|
||||||
|
static int add_tags ();
|
||||||
|
static char *get_comment ();
|
||||||
|
static int add_rcs_file ();
|
||||||
|
static int expand_at_signs ();
|
||||||
|
static void add_log ();
|
||||||
|
static int import_descend_dir ();
|
||||||
|
#endif /* __STDC__ */
|
||||||
|
|
||||||
|
static int repos_len;
|
||||||
|
static char vhead[50];
|
||||||
|
static char vbranch[50];
|
||||||
|
static FILE *logfp;
|
||||||
|
static char repository[PATH_MAX];
|
||||||
|
static int conflicts;
|
||||||
|
|
||||||
|
static char *import_usage[] =
|
||||||
|
{
|
||||||
|
"Usage: %s %s [-Qq] [-I ign] [-m msg] [-b branch]\n",
|
||||||
|
" repository vendor-tag release-tags...\n",
|
||||||
|
"\t-Q\tReally quiet.\n",
|
||||||
|
"\t-q\tSomewhat quiet.\n",
|
||||||
|
"\t-I ign\tMore files to ignore (! to reset).\n",
|
||||||
|
"\t-b bra\tVendor branch id.\n",
|
||||||
|
"\t-m msg\tLog message.\n",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
int
|
||||||
|
import (argc, argv)
|
||||||
|
int argc;
|
||||||
|
char *argv[];
|
||||||
|
{
|
||||||
|
char message[MAXMESGLEN];
|
||||||
|
char tmpfile[L_tmpnam+1];
|
||||||
|
char *cp;
|
||||||
|
int i, c, msglen, err;
|
||||||
|
List *ulist;
|
||||||
|
Node *p;
|
||||||
|
|
||||||
|
if (argc == -1)
|
||||||
|
usage (import_usage);
|
||||||
|
|
||||||
|
ign_setup ();
|
||||||
|
|
||||||
|
(void) strcpy (vbranch, CVSBRANCH);
|
||||||
|
message[0] = '\0';
|
||||||
|
optind = 1;
|
||||||
|
while ((c = gnu_getopt (argc, argv, "Qqb:m:I:")) != -1)
|
||||||
|
{
|
||||||
|
switch (c)
|
||||||
|
{
|
||||||
|
case 'Q':
|
||||||
|
really_quiet = 1;
|
||||||
|
/* FALL THROUGH */
|
||||||
|
case 'q':
|
||||||
|
quiet = 1;
|
||||||
|
break;
|
||||||
|
case 'b':
|
||||||
|
(void) strcpy (vbranch, optarg);
|
||||||
|
break;
|
||||||
|
case 'm':
|
||||||
|
#ifdef FORCE_USE_EDITOR
|
||||||
|
use_editor = TRUE;
|
||||||
|
#else
|
||||||
|
use_editor = FALSE;
|
||||||
|
#endif
|
||||||
|
if (strlen (optarg) >= (sizeof (message) - 1))
|
||||||
|
{
|
||||||
|
error (0, 0, "warning: message too long; truncated!");
|
||||||
|
(void) strncpy (message, optarg, sizeof (message));
|
||||||
|
message[sizeof (message) - 2] = '\0';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
(void) strcpy (message, optarg);
|
||||||
|
break;
|
||||||
|
case 'I':
|
||||||
|
ign_add (optarg, 0);
|
||||||
|
break;
|
||||||
|
case '?':
|
||||||
|
default:
|
||||||
|
usage (import_usage);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
argc -= optind;
|
||||||
|
argv += optind;
|
||||||
|
if (argc < 3)
|
||||||
|
usage (import_usage);
|
||||||
|
|
||||||
|
for (i = 1; i < argc; i++) /* check the tags for validity */
|
||||||
|
RCS_check_tag (argv[i]);
|
||||||
|
|
||||||
|
/* XXX - this should be a module, not just a pathname */
|
||||||
|
if (argv[0][0] != '/')
|
||||||
|
{
|
||||||
|
if (CVSroot == NULL)
|
||||||
|
{
|
||||||
|
error (0, 0, "missing CVSROOT environment variable\n");
|
||||||
|
error (1, 0, "Set it or specify the '-d' option to %s.",
|
||||||
|
program_name);
|
||||||
|
}
|
||||||
|
(void) sprintf (repository, "%s/%s", CVSroot, argv[0]);
|
||||||
|
repos_len = strlen (CVSroot);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
(void) strcpy (repository, argv[0]);
|
||||||
|
repos_len = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Consistency checks on the specified vendor branch. It must be
|
||||||
|
* composed of only numbers and dots ('.'). Also, for now we only
|
||||||
|
* support branching to a single level, so the specified vendor branch
|
||||||
|
* must only have two dots in it (like "1.1.1").
|
||||||
|
*/
|
||||||
|
for (cp = vbranch; *cp != '\0'; cp++)
|
||||||
|
if (!isdigit (*cp) && *cp != '.')
|
||||||
|
error (1, 0, "%s is not a numeric branch", vbranch);
|
||||||
|
if (numdots (vbranch) != 2)
|
||||||
|
error (1, 0, "Only branches with two dots are supported: %s", vbranch);
|
||||||
|
(void) strcpy (vhead, vbranch);
|
||||||
|
cp = rindex (vhead, '.');
|
||||||
|
*cp = '\0';
|
||||||
|
if (use_editor)
|
||||||
|
do_editor ((char *) NULL, message, repository, (List *) NULL);
|
||||||
|
msglen = strlen (message);
|
||||||
|
if (msglen == 0 || message[msglen - 1] != '\n')
|
||||||
|
{
|
||||||
|
message[msglen] = '\n';
|
||||||
|
message[msglen + 1] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make all newly created directories writable. Should really use a more
|
||||||
|
* sophisticated security mechanism here.
|
||||||
|
*/
|
||||||
|
(void) umask (2);
|
||||||
|
make_directories (repository);
|
||||||
|
|
||||||
|
/* Create the logfile that will be logged upon completion */
|
||||||
|
if ((logfp = fopen (tmpnam (tmpfile), "w+")) == NULL)
|
||||||
|
error (1, errno, "cannot create temporary file `%s'", tmpfile);
|
||||||
|
(void) unlink (tmpfile); /* to be sure it goes away */
|
||||||
|
(void) fprintf (logfp, "\nVendor Tag:\t%s\n", argv[1]);
|
||||||
|
(void) fprintf (logfp, "Release Tags:\t");
|
||||||
|
for (i = 2; i < argc; i++)
|
||||||
|
(void) fprintf (logfp, "%s\n\t\t", argv[i]);
|
||||||
|
(void) fprintf (logfp, "\n");
|
||||||
|
|
||||||
|
/* Just Do It. */
|
||||||
|
err = import_descend (message, argv[1], argc - 2, argv + 2);
|
||||||
|
if (conflicts)
|
||||||
|
{
|
||||||
|
if (!really_quiet)
|
||||||
|
{
|
||||||
|
(void) printf ("\n%d conflicts created by this import.\n",
|
||||||
|
conflicts);
|
||||||
|
(void) printf ("Use the following command to help the merge:\n\n");
|
||||||
|
(void) printf ("\t%s checkout -j%s:yesterday -j%s %s\n\n",
|
||||||
|
program_name, argv[1], argv[1], argv[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
(void) fprintf (logfp, "\n%d conflicts created by this import.\n",
|
||||||
|
conflicts);
|
||||||
|
(void) fprintf (logfp,
|
||||||
|
"Use the following command to help the merge:\n\n");
|
||||||
|
(void) fprintf (logfp, "\t%s checkout -j%s:yesterday -j%s %s\n\n",
|
||||||
|
program_name, argv[1], argv[1], argv[0]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!really_quiet)
|
||||||
|
(void) printf ("\nNo conflicts created by this import\n\n");
|
||||||
|
(void) fprintf (logfp, "\nNo conflicts created by this import\n\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Write out the logfile and clean up.
|
||||||
|
*/
|
||||||
|
ulist = getlist ();
|
||||||
|
p = getnode ();
|
||||||
|
p->type = UPDATE;
|
||||||
|
p->delproc = update_delproc;
|
||||||
|
p->key = xstrdup ("- Imported sources");
|
||||||
|
p->data = (char *) T_TITLE;
|
||||||
|
(void) addnode (ulist, p);
|
||||||
|
Update_Logfile (repository, message, vbranch, logfp, ulist);
|
||||||
|
dellist (&ulist);
|
||||||
|
(void) fclose (logfp);
|
||||||
|
return (err);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* process all the files in ".", then descend into other directories.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
import_descend (message, vtag, targc, targv)
|
||||||
|
char *message;
|
||||||
|
char *vtag;
|
||||||
|
int targc;
|
||||||
|
char *targv[];
|
||||||
|
{
|
||||||
|
DIR *dirp;
|
||||||
|
struct direct *dp;
|
||||||
|
int err = 0;
|
||||||
|
int has_dirs = 0;
|
||||||
|
|
||||||
|
/* first, load up any per-directory ignore lists */
|
||||||
|
ign_add_file (CVSDOTIGNORE, 1);
|
||||||
|
|
||||||
|
if ((dirp = opendir (".")) == NULL)
|
||||||
|
{
|
||||||
|
err++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
while ((dp = readdir (dirp)) != NULL)
|
||||||
|
{
|
||||||
|
if (strcmp (dp->d_name, ".") == 0 || strcmp (dp->d_name, "..") == 0)
|
||||||
|
continue;
|
||||||
|
if (ign_name (dp->d_name))
|
||||||
|
{
|
||||||
|
add_log ('I', dp->d_name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (isdir (dp->d_name))
|
||||||
|
{
|
||||||
|
has_dirs = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (islink (dp->d_name))
|
||||||
|
{
|
||||||
|
add_log ('L', dp->d_name);
|
||||||
|
err++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
err += process_import_file (message, dp->d_name,
|
||||||
|
vtag, targc, targv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(void) closedir (dirp);
|
||||||
|
}
|
||||||
|
if (has_dirs)
|
||||||
|
{
|
||||||
|
if ((dirp = opendir (".")) == NULL)
|
||||||
|
err++;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
while ((dp = readdir (dirp)) != NULL)
|
||||||
|
{
|
||||||
|
if (ign_name (dp->d_name) || !isdir (dp->d_name))
|
||||||
|
continue;
|
||||||
|
err += import_descend_dir (message, dp->d_name,
|
||||||
|
vtag, targc, targv);
|
||||||
|
}
|
||||||
|
(void) closedir (dirp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (err);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Process the argument import file.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
process_import_file (message, vfile, vtag, targc, targv)
|
||||||
|
char *message;
|
||||||
|
char *vfile;
|
||||||
|
char *vtag;
|
||||||
|
int targc;
|
||||||
|
char *targv[];
|
||||||
|
{
|
||||||
|
char attic_name[PATH_MAX];
|
||||||
|
char rcs[PATH_MAX];
|
||||||
|
|
||||||
|
(void) sprintf (rcs, "%s/%s%s", repository, vfile, RCSEXT);
|
||||||
|
if (!isfile (rcs))
|
||||||
|
{
|
||||||
|
(void) sprintf (attic_name, "%s/%s/%s%s", repository, CVSATTIC,
|
||||||
|
vfile, RCSEXT);
|
||||||
|
if (!isfile (attic_name))
|
||||||
|
{
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A new import source file; it doesn't exist as a ,v within the
|
||||||
|
* repository nor in the Attic -- create it anew.
|
||||||
|
*/
|
||||||
|
add_log ('N', vfile);
|
||||||
|
return (add_rcs_file (message, rcs, vfile, vtag, targc, targv));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* an rcs file exists. have to do things the official, slow, way.
|
||||||
|
*/
|
||||||
|
return (update_rcs_file (message, vfile, vtag, targc, targv));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The RCS file exists; update it by adding the new import file to the
|
||||||
|
* (possibly already existing) vendor branch.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
update_rcs_file (message, vfile, vtag, targc, targv)
|
||||||
|
char *message;
|
||||||
|
char *vfile;
|
||||||
|
char *vtag;
|
||||||
|
int targc;
|
||||||
|
char *targv[];
|
||||||
|
{
|
||||||
|
Vers_TS *vers;
|
||||||
|
char letter;
|
||||||
|
int ierrno;
|
||||||
|
|
||||||
|
vers = Version_TS (repository, (char *) NULL, vbranch, (char *) NULL, vfile,
|
||||||
|
1, 0, (List *) NULL, (List *) NULL);
|
||||||
|
if (vers->vn_rcs != NULL)
|
||||||
|
{
|
||||||
|
char xtmpfile[50];
|
||||||
|
int different;
|
||||||
|
int retcode = 0;
|
||||||
|
|
||||||
|
/* XXX - should be more unique */
|
||||||
|
(void) sprintf (xtmpfile, "/tmp/%s", FILE_HOLDER);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The rcs file does have a revision on the vendor branch. Compare
|
||||||
|
* this revision with the import file; if they match exactly, there
|
||||||
|
* is no need to install the new import file as a new revision to the
|
||||||
|
* branch. Just tag the revision with the new import tags.
|
||||||
|
*
|
||||||
|
* This is to try to cut down the number of "C" conflict messages for
|
||||||
|
* locally modified import source files.
|
||||||
|
*/
|
||||||
|
#ifdef HAVE_RCS5
|
||||||
|
run_setup ("%s%s -q -f -r%s -p -ko", Rcsbin, RCS_CO, vers->vn_rcs);
|
||||||
|
#else
|
||||||
|
run_setup ("%s%s -q -f -r%s -p", Rcsbin, RCS_CO, vers->vn_rcs);
|
||||||
|
#endif
|
||||||
|
run_arg (vers->srcfile->path);
|
||||||
|
if ((retcode = run_exec (RUN_TTY, xtmpfile, RUN_TTY,
|
||||||
|
RUN_NORMAL|RUN_REALLY)) != 0)
|
||||||
|
{
|
||||||
|
ierrno = errno;
|
||||||
|
fperror (logfp, 0, retcode == -1 ? ierrno : 0,
|
||||||
|
"ERROR: cannot co revision %s of file %s", vers->vn_rcs,
|
||||||
|
vers->srcfile->path);
|
||||||
|
error (0, retcode == -1 ? ierrno : 0,
|
||||||
|
"ERROR: cannot co revision %s of file %s", vers->vn_rcs,
|
||||||
|
vers->srcfile->path);
|
||||||
|
(void) unlink_file (xtmpfile);
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
different = xcmp (xtmpfile, vfile);
|
||||||
|
(void) unlink_file (xtmpfile);
|
||||||
|
if (!different)
|
||||||
|
{
|
||||||
|
int retval = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The two files are identical. Just update the tags, print the
|
||||||
|
* "U", signifying that the file has changed, but needs no
|
||||||
|
* attention, and we're done.
|
||||||
|
*/
|
||||||
|
if (add_tags (vers->srcfile->path, vfile, vtag, targc, targv))
|
||||||
|
retval = 1;
|
||||||
|
add_log ('U', vfile);
|
||||||
|
freevers_ts (&vers);
|
||||||
|
return (retval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We may have failed to parse the RCS file; check just in case */
|
||||||
|
if (vers->srcfile == NULL || add_rev (message, vers->srcfile->path,
|
||||||
|
vfile, vers->vn_rcs) ||
|
||||||
|
add_tags (vers->srcfile->path, vfile, vtag, targc, targv))
|
||||||
|
{
|
||||||
|
freevers_ts (&vers);
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vers->srcfile->branch == NULL ||
|
||||||
|
strcmp (vers->srcfile->branch, vbranch) != 0)
|
||||||
|
{
|
||||||
|
conflicts++;
|
||||||
|
letter = 'C';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
letter = 'U';
|
||||||
|
add_log (letter, vfile);
|
||||||
|
|
||||||
|
freevers_ts (&vers);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add the revision to the vendor branch
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
add_rev (message, rcs, vfile, vers)
|
||||||
|
char *message;
|
||||||
|
char *rcs;
|
||||||
|
char *vfile;
|
||||||
|
char *vers;
|
||||||
|
{
|
||||||
|
int locked, status, ierrno;
|
||||||
|
int retcode = 0;
|
||||||
|
|
||||||
|
if (noexec)
|
||||||
|
return (0);
|
||||||
|
|
||||||
|
locked = 0;
|
||||||
|
if (vers != NULL)
|
||||||
|
{
|
||||||
|
run_setup ("%s%s -q -l%s", Rcsbin, RCS, vbranch);
|
||||||
|
run_arg (rcs);
|
||||||
|
if ((retcode = run_exec (RUN_TTY, DEVNULL, DEVNULL, RUN_NORMAL)) == 0)
|
||||||
|
locked = 1;
|
||||||
|
else if (retcode == -1)
|
||||||
|
{
|
||||||
|
error (0, errno, "fork failed");
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (link_file (vfile, FILE_HOLDER) < 0)
|
||||||
|
{
|
||||||
|
if (errno == EEXIST)
|
||||||
|
{
|
||||||
|
(void) unlink_file (FILE_HOLDER);
|
||||||
|
(void) link_file (vfile, FILE_HOLDER);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ierrno = errno;
|
||||||
|
fperror (logfp, 0, ierrno, "ERROR: cannot create link to %s", vfile);
|
||||||
|
error (0, ierrno, "ERROR: cannot create link to %s", vfile);
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
run_setup ("%s%s -q -f -r%s", Rcsbin, RCS_CI, vbranch);
|
||||||
|
run_args ("-m%s", message);
|
||||||
|
run_arg (rcs);
|
||||||
|
status = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL);
|
||||||
|
ierrno = errno;
|
||||||
|
rename_file (FILE_HOLDER, vfile);
|
||||||
|
if (status)
|
||||||
|
{
|
||||||
|
if (!noexec)
|
||||||
|
{
|
||||||
|
fperror (logfp, 0, status == -1 ? ierrno : 0, "ERROR: Check-in of %s failed", rcs);
|
||||||
|
error (0, status == -1 ? ierrno : 0, "ERROR: Check-in of %s failed", rcs);
|
||||||
|
}
|
||||||
|
if (locked)
|
||||||
|
{
|
||||||
|
run_setup ("%s%s -q -u%s", Rcsbin, RCS, vbranch);
|
||||||
|
run_arg (rcs);
|
||||||
|
(void) run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL);
|
||||||
|
}
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add the vendor branch tag and all the specified import release tags to the
|
||||||
|
* RCS file. The vendor branch tag goes on the branch root (1.1.1) while the
|
||||||
|
* vendor release tags go on the newly added leaf of the branch (1.1.1.1,
|
||||||
|
* 1.1.1.2, ...).
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
add_tags (rcs, vfile, vtag, targc, targv)
|
||||||
|
char *rcs;
|
||||||
|
char *vfile;
|
||||||
|
char *vtag;
|
||||||
|
int targc;
|
||||||
|
char *targv[];
|
||||||
|
{
|
||||||
|
int i, ierrno;
|
||||||
|
Vers_TS *vers;
|
||||||
|
int retcode = 0;
|
||||||
|
|
||||||
|
if (noexec)
|
||||||
|
return (0);
|
||||||
|
|
||||||
|
run_setup ("%s%s -q -N%s:%s", Rcsbin, RCS, vtag, vbranch);
|
||||||
|
run_arg (rcs);
|
||||||
|
if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL)) != 0)
|
||||||
|
{
|
||||||
|
ierrno = errno;
|
||||||
|
fperror (logfp, 0, retcode == -1 ? ierrno : 0,
|
||||||
|
"ERROR: Failed to set tag %s in %s", vtag, rcs);
|
||||||
|
error (0, retcode == -1 ? ierrno : 0,
|
||||||
|
"ERROR: Failed to set tag %s in %s", vtag, rcs);
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
vers = Version_TS (repository, (char *) NULL, vtag, (char *) NULL, vfile,
|
||||||
|
1, 0, (List *) NULL, (List *) NULL);
|
||||||
|
for (i = 0; i < targc; i++)
|
||||||
|
{
|
||||||
|
run_setup ("%s%s -q -N%s:%s", Rcsbin, RCS, targv[i], vers->vn_rcs);
|
||||||
|
run_arg (rcs);
|
||||||
|
if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL)) != 0)
|
||||||
|
{
|
||||||
|
ierrno = errno;
|
||||||
|
fperror (logfp, 0, retcode == -1 ? ierrno : 0,
|
||||||
|
"WARNING: Couldn't add tag %s to %s", targv[i], rcs);
|
||||||
|
error (0, retcode == -1 ? ierrno : 0,
|
||||||
|
"WARNING: Couldn't add tag %s to %s", targv[i], rcs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
freevers_ts (&vers);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Stolen from rcs/src/rcsfnms.c, and adapted/extended.
|
||||||
|
*/
|
||||||
|
struct compair
|
||||||
|
{
|
||||||
|
char *suffix, *comlead;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct compair comtable[] =
|
||||||
|
{
|
||||||
|
|
||||||
|
/*
|
||||||
|
* comtable pairs each filename suffix with a comment leader. The comment
|
||||||
|
* leader is placed before each line generated by the $Log keyword. This
|
||||||
|
* table is used to guess the proper comment leader from the working file's
|
||||||
|
* suffix during initial ci (see InitAdmin()). Comment leaders are needed for
|
||||||
|
* languages without multiline comments; for others they are optional.
|
||||||
|
*/
|
||||||
|
"a", "-- ", /* Ada */
|
||||||
|
"ada", "-- ",
|
||||||
|
"asm", ";; ", /* assembler (MS-DOS) */
|
||||||
|
"bat", ":: ", /* batch (MS-DOS) */
|
||||||
|
"c", " * ", /* C */
|
||||||
|
"c++", "// ", /* C++ in all its infinite guises */
|
||||||
|
"cc", "// ",
|
||||||
|
"cpp", "// ",
|
||||||
|
"cxx", "// ",
|
||||||
|
"cl", ";;; ", /* Common Lisp */
|
||||||
|
"cmd", ":: ", /* command (OS/2) */
|
||||||
|
"cmf", "c ", /* CM Fortran */
|
||||||
|
"cs", " * ", /* C* */
|
||||||
|
"csh", "# ", /* shell */
|
||||||
|
"e", "# ", /* efl */
|
||||||
|
"el", "; ", /* Emacs Lisp */
|
||||||
|
"f", "c ", /* Fortran */
|
||||||
|
"for", "c ",
|
||||||
|
"h", " * ", /* C-header */
|
||||||
|
"hh", "// ", /* C++ header */
|
||||||
|
"hpp", "// ",
|
||||||
|
"hxx", "// ",
|
||||||
|
"in", "# ", /* for Makefile.in */
|
||||||
|
"l", " * ", /* lex (conflict between lex and
|
||||||
|
* franzlisp) */
|
||||||
|
"mac", ";; ", /* macro (DEC-10, MS-DOS, PDP-11,
|
||||||
|
* VMS, etc) */
|
||||||
|
"me", ".\\\" ", /* me-macros t/nroff */
|
||||||
|
"ml", "; ", /* mocklisp */
|
||||||
|
"mm", ".\\\" ", /* mm-macros t/nroff */
|
||||||
|
"ms", ".\\\" ", /* ms-macros t/nroff */
|
||||||
|
"man", ".\\\" ", /* man-macros t/nroff */
|
||||||
|
"1", ".\\\" ", /* feeble attempt at man pages... */
|
||||||
|
"2", ".\\\" ",
|
||||||
|
"3", ".\\\" ",
|
||||||
|
"4", ".\\\" ",
|
||||||
|
"5", ".\\\" ",
|
||||||
|
"6", ".\\\" ",
|
||||||
|
"7", ".\\\" ",
|
||||||
|
"8", ".\\\" ",
|
||||||
|
"9", ".\\\" ",
|
||||||
|
"p", " * ", /* pascal */
|
||||||
|
"pas", " * ",
|
||||||
|
"pl", "# ", /* perl (conflict with Prolog) */
|
||||||
|
"ps", "% ", /* postscript */
|
||||||
|
"r", "# ", /* ratfor */
|
||||||
|
"red", "% ", /* psl/rlisp */
|
||||||
|
#ifdef sparc
|
||||||
|
"s", "! ", /* assembler */
|
||||||
|
#endif
|
||||||
|
#ifdef mc68000
|
||||||
|
"s", "| ", /* assembler */
|
||||||
|
#endif
|
||||||
|
#ifdef pdp11
|
||||||
|
"s", "/ ", /* assembler */
|
||||||
|
#endif
|
||||||
|
#ifdef vax
|
||||||
|
"s", "# ", /* assembler */
|
||||||
|
#endif
|
||||||
|
#ifdef __ksr__
|
||||||
|
"s", "# ", /* assembler */
|
||||||
|
"S", "# ", /* Macro assembler */
|
||||||
|
#endif
|
||||||
|
"sh", "# ", /* shell */
|
||||||
|
"sl", "% ", /* psl */
|
||||||
|
"tex", "% ", /* tex */
|
||||||
|
"y", " * ", /* yacc */
|
||||||
|
"ye", " * ", /* yacc-efl */
|
||||||
|
"yr", " * ", /* yacc-ratfor */
|
||||||
|
"", "# ", /* default for empty suffix */
|
||||||
|
NULL, "# " /* default for unknown suffix; */
|
||||||
|
/* must always be last */
|
||||||
|
};
|
||||||
|
|
||||||
|
static char *
|
||||||
|
get_comment (user)
|
||||||
|
char *user;
|
||||||
|
{
|
||||||
|
char *cp, *suffix;
|
||||||
|
char suffix_path[PATH_MAX];
|
||||||
|
int i;
|
||||||
|
|
||||||
|
cp = rindex (user, '.');
|
||||||
|
if (cp != NULL)
|
||||||
|
{
|
||||||
|
cp++;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Convert to lower-case, since we are not concerned about the
|
||||||
|
* case-ness of the suffix.
|
||||||
|
*/
|
||||||
|
(void) strcpy (suffix_path, cp);
|
||||||
|
for (cp = suffix_path; *cp; cp++)
|
||||||
|
if (isupper (*cp))
|
||||||
|
*cp = tolower (*cp);
|
||||||
|
suffix = suffix_path;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
suffix = ""; /* will use the default */
|
||||||
|
for (i = 0;; i++)
|
||||||
|
{
|
||||||
|
if (comtable[i].suffix == NULL) /* default */
|
||||||
|
return (comtable[i].comlead);
|
||||||
|
if (strcmp (suffix, comtable[i].suffix) == 0)
|
||||||
|
return (comtable[i].comlead);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
add_rcs_file (message, rcs, user, vtag, targc, targv)
|
||||||
|
char *message;
|
||||||
|
char *rcs;
|
||||||
|
char *user;
|
||||||
|
char *vtag;
|
||||||
|
int targc;
|
||||||
|
char *targv[];
|
||||||
|
{
|
||||||
|
FILE *fprcs, *fpuser;
|
||||||
|
struct stat sb;
|
||||||
|
struct tm *ftm;
|
||||||
|
time_t now;
|
||||||
|
char altdate1[50], altdate2[50];
|
||||||
|
char *author, *buf;
|
||||||
|
int i, mode, ierrno, err = 0;
|
||||||
|
|
||||||
|
if (noexec)
|
||||||
|
return (0);
|
||||||
|
|
||||||
|
fprcs = open_file (rcs, "w+");
|
||||||
|
fpuser = open_file (user, "r");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* putadmin()
|
||||||
|
*/
|
||||||
|
if (fprintf (fprcs, "head %s;\n", vhead) == EOF ||
|
||||||
|
fprintf (fprcs, "branch %s;\n", vbranch) == EOF ||
|
||||||
|
fprintf (fprcs, "access ;\n") == EOF ||
|
||||||
|
fprintf (fprcs, "symbols ") == EOF)
|
||||||
|
{
|
||||||
|
goto write_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = targc - 1; i >= 0; i--) /* RCS writes the symbols backwards */
|
||||||
|
if (fprintf (fprcs, "%s:%s.1 ", targv[i], vbranch) == EOF)
|
||||||
|
goto write_error;
|
||||||
|
|
||||||
|
if (fprintf (fprcs, "%s:%s;\n", vtag, vbranch) == EOF ||
|
||||||
|
fprintf (fprcs, "locks ; strict;\n") == EOF ||
|
||||||
|
/* XXX - make sure @@ processing works in the RCS file */
|
||||||
|
fprintf (fprcs, "comment @%s@;\n\n", get_comment (user)) == EOF)
|
||||||
|
{
|
||||||
|
goto write_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* puttree()
|
||||||
|
*/
|
||||||
|
(void) time (&now);
|
||||||
|
#ifdef HAVE_RCS5
|
||||||
|
ftm = gmtime (&now);
|
||||||
|
#else
|
||||||
|
ftm = localtime (&now);
|
||||||
|
#endif
|
||||||
|
(void) sprintf (altdate1, DATEFORM,
|
||||||
|
ftm->tm_year + (ftm->tm_year < 100 ? 0 : 1900),
|
||||||
|
ftm->tm_mon + 1, ftm->tm_mday, ftm->tm_hour,
|
||||||
|
ftm->tm_min, ftm->tm_sec);
|
||||||
|
now++;
|
||||||
|
#ifdef HAVE_RCS5
|
||||||
|
ftm = gmtime (&now);
|
||||||
|
#else
|
||||||
|
ftm = localtime (&now);
|
||||||
|
#endif
|
||||||
|
(void) sprintf (altdate2, DATEFORM,
|
||||||
|
ftm->tm_year + (ftm->tm_year < 100 ? 0 : 1900),
|
||||||
|
ftm->tm_mon + 1, ftm->tm_mday, ftm->tm_hour,
|
||||||
|
ftm->tm_min, ftm->tm_sec);
|
||||||
|
author = getcaller ();
|
||||||
|
|
||||||
|
if (fprintf (fprcs, "\n%s\n", vhead) == EOF ||
|
||||||
|
fprintf (fprcs, "date %s; author %s; state Exp;\n",
|
||||||
|
altdate1, author) == EOF ||
|
||||||
|
fprintf (fprcs, "branches %s.1;\n", vbranch) == EOF ||
|
||||||
|
fprintf (fprcs, "next ;\n") == EOF ||
|
||||||
|
fprintf (fprcs, "\n%s.1\n", vbranch) == EOF ||
|
||||||
|
fprintf (fprcs, "date %s; author %s; state Exp;\n",
|
||||||
|
altdate2, author) == EOF ||
|
||||||
|
fprintf (fprcs, "branches ;\n") == EOF ||
|
||||||
|
fprintf (fprcs, "next ;\n\n") == EOF ||
|
||||||
|
/*
|
||||||
|
* putdesc()
|
||||||
|
*/
|
||||||
|
fprintf (fprcs, "\ndesc\n") == EOF ||
|
||||||
|
fprintf (fprcs, "@@\n\n\n") == EOF ||
|
||||||
|
/*
|
||||||
|
* putdelta()
|
||||||
|
*/
|
||||||
|
fprintf (fprcs, "\n%s\n", vhead) == EOF ||
|
||||||
|
fprintf (fprcs, "log\n") == EOF ||
|
||||||
|
fprintf (fprcs, "@Initial revision\n@\n") == EOF ||
|
||||||
|
fprintf (fprcs, "text\n@") == EOF)
|
||||||
|
{
|
||||||
|
goto write_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fstat (fileno (fpuser), &sb) < 0)
|
||||||
|
error (1, errno, "cannot fstat %s", user);
|
||||||
|
if (sb.st_size > 0)
|
||||||
|
{
|
||||||
|
off_t size;
|
||||||
|
|
||||||
|
size = sb.st_size;
|
||||||
|
buf = xmalloc ((int) size);
|
||||||
|
if (fread (buf, (int) size, 1, fpuser) != 1)
|
||||||
|
error (1, errno, "cannot read file %s for copying", user);
|
||||||
|
if (expand_at_signs (buf, size, fprcs) == EOF)
|
||||||
|
goto write_error;
|
||||||
|
free (buf);
|
||||||
|
}
|
||||||
|
if (fprintf (fprcs, "@\n\n") == EOF ||
|
||||||
|
fprintf (fprcs, "\n%s.1\n", vbranch) == EOF ||
|
||||||
|
fprintf (fprcs, "log\n@") == EOF ||
|
||||||
|
expand_at_signs (message, (off_t) strlen (message), fprcs) == EOF ||
|
||||||
|
fprintf (fprcs, "@\ntext\n") == EOF ||
|
||||||
|
fprintf (fprcs, "@@\n") == EOF)
|
||||||
|
{
|
||||||
|
goto write_error;
|
||||||
|
}
|
||||||
|
if (fclose (fprcs) == EOF)
|
||||||
|
{
|
||||||
|
ierrno = errno;
|
||||||
|
goto write_error_noclose;
|
||||||
|
}
|
||||||
|
(void) fclose (fpuser);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fix the modes on the RCS files. They must maintain the same modes as
|
||||||
|
* the original user file, except that all write permissions must be
|
||||||
|
* turned off.
|
||||||
|
*/
|
||||||
|
mode = sb.st_mode & ~(S_IWRITE | S_IWGRP | S_IWOTH);
|
||||||
|
if (chmod (rcs, mode) < 0)
|
||||||
|
{
|
||||||
|
ierrno = errno;
|
||||||
|
fperror (logfp, 0, ierrno,
|
||||||
|
"WARNING: cannot change mode of file %s", rcs);
|
||||||
|
error (0, ierrno, "WARNING: cannot change mode of file %s", rcs);
|
||||||
|
err++;
|
||||||
|
}
|
||||||
|
return (err);
|
||||||
|
|
||||||
|
write_error:
|
||||||
|
ierrno = errno;
|
||||||
|
(void) fclose (fprcs);
|
||||||
|
write_error_noclose:
|
||||||
|
(void) fclose (fpuser);
|
||||||
|
fperror (logfp, 0, ierrno, "ERROR: cannot write file %s", rcs);
|
||||||
|
error (0, ierrno, "ERROR: cannot write file %s", rcs);
|
||||||
|
if (ierrno == ENOSPC)
|
||||||
|
{
|
||||||
|
(void) unlink (rcs);
|
||||||
|
fperror (logfp, 0, 0, "ERROR: out of space - aborting");
|
||||||
|
error (1, 0, "ERROR: out of space - aborting");
|
||||||
|
}
|
||||||
|
return (err + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sigh.. need to expand @ signs into double @ signs
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
expand_at_signs (buf, size, fp)
|
||||||
|
char *buf;
|
||||||
|
off_t size;
|
||||||
|
FILE *fp;
|
||||||
|
{
|
||||||
|
char *cp, *end;
|
||||||
|
|
||||||
|
for (cp = buf, end = buf + size; cp < end; cp++)
|
||||||
|
{
|
||||||
|
if (*cp == '@')
|
||||||
|
(void) putc ('@', fp);
|
||||||
|
if (putc (*cp, fp) == EOF)
|
||||||
|
return (EOF);
|
||||||
|
}
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Write an update message to (potentially) the screen and the log file.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
add_log (ch, fname)
|
||||||
|
char ch;
|
||||||
|
char *fname;
|
||||||
|
{
|
||||||
|
if (!really_quiet) /* write to terminal */
|
||||||
|
{
|
||||||
|
if (repos_len)
|
||||||
|
(void) printf ("%c %s/%s\n", ch, repository + repos_len + 1, fname);
|
||||||
|
else if (repository[0])
|
||||||
|
(void) printf ("%c %s/%s\n", ch, repository, fname);
|
||||||
|
else
|
||||||
|
(void) printf ("%c %s\n", ch, fname);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (repos_len) /* write to logfile */
|
||||||
|
(void) fprintf (logfp, "%c %s/%s\n", ch,
|
||||||
|
repository + repos_len + 1, fname);
|
||||||
|
else if (repository[0])
|
||||||
|
(void) fprintf (logfp, "%c %s/%s\n", ch, repository, fname);
|
||||||
|
else
|
||||||
|
(void) fprintf (logfp, "%c %s\n", ch, fname);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is the recursive function that walks the argument directory looking
|
||||||
|
* for sub-directories that have CVS administration files in them and updates
|
||||||
|
* them recursively.
|
||||||
|
*
|
||||||
|
* Note that we do not follow symbolic links here, which is a feature!
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
import_descend_dir (message, dir, vtag, targc, targv)
|
||||||
|
char *message;
|
||||||
|
char *dir;
|
||||||
|
char *vtag;
|
||||||
|
int targc;
|
||||||
|
char *targv[];
|
||||||
|
{
|
||||||
|
char cwd[PATH_MAX];
|
||||||
|
char *cp;
|
||||||
|
int ierrno, err;
|
||||||
|
|
||||||
|
if (islink (dir))
|
||||||
|
return (0);
|
||||||
|
if (getwd (cwd) == NULL)
|
||||||
|
{
|
||||||
|
fperror (logfp, 0, 0, "ERROR: cannot get working directory: %s", cwd);
|
||||||
|
error (0, 0, "ERROR: cannot get working directory: %s", cwd);
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
if (repository[0] == '\0')
|
||||||
|
(void) strcpy (repository, dir);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
(void) strcat (repository, "/");
|
||||||
|
(void) strcat (repository, dir);
|
||||||
|
}
|
||||||
|
if (!quiet)
|
||||||
|
error (0, 0, "Importing %s", repository);
|
||||||
|
if (chdir (dir) < 0)
|
||||||
|
{
|
||||||
|
ierrno = errno;
|
||||||
|
fperror (logfp, 0, ierrno, "ERROR: cannot chdir to %s", repository);
|
||||||
|
error (0, ierrno, "ERROR: cannot chdir to %s", repository);
|
||||||
|
err = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (!isdir (repository))
|
||||||
|
{
|
||||||
|
if (isfile (repository))
|
||||||
|
{
|
||||||
|
fperror (logfp, 0, 0, "ERROR: %s is a file, should be a directory!",
|
||||||
|
repository);
|
||||||
|
error (0, 0, "ERROR: %s is a file, should be a directory!",
|
||||||
|
repository);
|
||||||
|
err = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (noexec == 0 && mkdir (repository, 0777) < 0)
|
||||||
|
{
|
||||||
|
ierrno = errno;
|
||||||
|
fperror (logfp, 0, ierrno,
|
||||||
|
"ERROR: cannot mkdir %s -- not added", repository);
|
||||||
|
error (0, ierrno,
|
||||||
|
"ERROR: cannot mkdir %s -- not added", repository);
|
||||||
|
err = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = import_descend (message, vtag, targc, targv);
|
||||||
|
out:
|
||||||
|
if ((cp = rindex (repository, '/')) != NULL)
|
||||||
|
*cp = '\0';
|
||||||
|
else
|
||||||
|
repository[0] = '\0';
|
||||||
|
if (chdir (cwd) < 0)
|
||||||
|
error (1, errno, "cannot chdir to %s", cwd);
|
||||||
|
return (err);
|
||||||
|
}
|
||||||
@@ -0,0 +1,522 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 1992, Brian Berliner and Jeff Polk
|
||||||
|
* Copyright (c) 1989-1992, Brian Berliner
|
||||||
|
*
|
||||||
|
* You may distribute under the terms of the GNU General Public License as
|
||||||
|
* specified in the README file that comes with the CVS 1.3 kit.
|
||||||
|
*
|
||||||
|
* Set Lock
|
||||||
|
*
|
||||||
|
* Lock file support for CVS.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "cvs.h"
|
||||||
|
|
||||||
|
#ifndef lint
|
||||||
|
static char rcsid[] = "@(#)lock.c 1.42 92/04/10";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern char *ctime ();
|
||||||
|
|
||||||
|
#if __STDC__
|
||||||
|
static int readers_exist (char *repository);
|
||||||
|
static int set_lock (char *lockdir, int will_wait, char *repository);
|
||||||
|
static void set_lockers_name (struct stat *statp);
|
||||||
|
static int set_writelock_proc (Node * p);
|
||||||
|
static int unlock_proc (Node * p);
|
||||||
|
static int write_lock (char *repository);
|
||||||
|
static void unlock (char *repository);
|
||||||
|
static void lock_wait ();
|
||||||
|
#else
|
||||||
|
static int unlock_proc ();
|
||||||
|
static void unlock ();
|
||||||
|
static int set_writelock_proc ();
|
||||||
|
static int write_lock ();
|
||||||
|
static int readers_exist ();
|
||||||
|
static int set_lock ();
|
||||||
|
static void set_lockers_name ();
|
||||||
|
static void lock_wait ();
|
||||||
|
#endif /* __STDC__ */
|
||||||
|
|
||||||
|
static char lockers_name[20];
|
||||||
|
static char *repository;
|
||||||
|
static char readlock[PATH_MAX], writelock[PATH_MAX];
|
||||||
|
static int cleanup_lckdir;
|
||||||
|
static List *locklist;
|
||||||
|
|
||||||
|
#define L_OK 0 /* success */
|
||||||
|
#define L_ERROR 1 /* error condition */
|
||||||
|
#define L_LOCK_OWNED 2 /* lock already owned by us */
|
||||||
|
#define L_LOCKED 3 /* lock owned by someone else */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Clean up all outstanding locks
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
Lock_Cleanup ()
|
||||||
|
{
|
||||||
|
/* clean up simple locks (if any) */
|
||||||
|
if (repository != NULL)
|
||||||
|
{
|
||||||
|
unlock (repository);
|
||||||
|
repository = (char *) NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* clean up multiple locks (if any) */
|
||||||
|
if (locklist != (List *) NULL)
|
||||||
|
{
|
||||||
|
(void) walklist (locklist, unlock_proc);
|
||||||
|
locklist = (List *) NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* walklist proc for removing a list of locks
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
unlock_proc (p)
|
||||||
|
Node *p;
|
||||||
|
{
|
||||||
|
unlock (p->key);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Remove the lock files (without complaining if they are not there),
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
unlock (repository)
|
||||||
|
char *repository;
|
||||||
|
{
|
||||||
|
char tmp[PATH_MAX];
|
||||||
|
struct stat sb;
|
||||||
|
|
||||||
|
if (readlock[0] != '\0')
|
||||||
|
{
|
||||||
|
(void) sprintf (tmp, "%s/%s", repository, readlock);
|
||||||
|
(void) unlink (tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (writelock[0] != '\0')
|
||||||
|
{
|
||||||
|
(void) sprintf (tmp, "%s/%s", repository, writelock);
|
||||||
|
(void) unlink (tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Only remove the lock directory if it is ours, note that this does
|
||||||
|
* lead to the limitation that one user ID should not be committing
|
||||||
|
* files into the same Repository directory at the same time. Oh well.
|
||||||
|
*/
|
||||||
|
(void) sprintf (tmp, "%s/%s", repository, CVSLCK);
|
||||||
|
if (stat (tmp, &sb) != -1 && sb.st_uid == geteuid () &&
|
||||||
|
(writelock[0] != '\0' || (readlock[0] != '\0' && cleanup_lckdir)))
|
||||||
|
{
|
||||||
|
(void) rmdir (tmp);
|
||||||
|
}
|
||||||
|
cleanup_lckdir = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create a lock file for readers
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
Reader_Lock (xrepository)
|
||||||
|
char *xrepository;
|
||||||
|
{
|
||||||
|
int err = 0;
|
||||||
|
FILE *fp;
|
||||||
|
char tmp[PATH_MAX];
|
||||||
|
|
||||||
|
if (noexec)
|
||||||
|
return (0);
|
||||||
|
|
||||||
|
/* we only do one directory at a time for read locks! */
|
||||||
|
if (repository != NULL)
|
||||||
|
{
|
||||||
|
error (0, 0, "Reader_Lock called while read locks set - Help!");
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (readlock[0] == '\0')
|
||||||
|
(void) sprintf (readlock, "%s.%d", CVSRFL, getpid ());
|
||||||
|
|
||||||
|
/* remember what we're locking (for lock_cleanup) */
|
||||||
|
repository = xrepository;
|
||||||
|
|
||||||
|
/* make sure we clean up on error */
|
||||||
|
(void) SIG_register (SIGHUP, Lock_Cleanup);
|
||||||
|
(void) SIG_register (SIGINT, Lock_Cleanup);
|
||||||
|
(void) SIG_register (SIGQUIT, Lock_Cleanup);
|
||||||
|
(void) SIG_register (SIGPIPE, Lock_Cleanup);
|
||||||
|
(void) SIG_register (SIGTERM, Lock_Cleanup);
|
||||||
|
|
||||||
|
/* make sure we can write the repository */
|
||||||
|
(void) sprintf (tmp, "%s/%s.%d", xrepository, CVSTFL, getpid ());
|
||||||
|
if ((fp = fopen (tmp, "w+")) == NULL || fclose (fp) == EOF)
|
||||||
|
{
|
||||||
|
error (0, errno, "cannot create read lock in repository `%s'",
|
||||||
|
xrepository);
|
||||||
|
readlock[0] = '\0';
|
||||||
|
(void) unlink (tmp);
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
(void) unlink (tmp);
|
||||||
|
|
||||||
|
/* get the lock dir for our own */
|
||||||
|
(void) sprintf (tmp, "%s/%s", xrepository, CVSLCK);
|
||||||
|
if (set_lock (tmp, 1, xrepository) != L_OK)
|
||||||
|
{
|
||||||
|
error (0, 0, "failed to obtain dir lock in repository `%s'",
|
||||||
|
xrepository);
|
||||||
|
readlock[0] = '\0';
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* write a read-lock */
|
||||||
|
(void) sprintf (tmp, "%s/%s", xrepository, readlock);
|
||||||
|
if ((fp = fopen (tmp, "w+")) == NULL || fclose (fp) == EOF)
|
||||||
|
{
|
||||||
|
error (0, errno, "cannot create read lock in repository `%s'",
|
||||||
|
xrepository);
|
||||||
|
readlock[0] = '\0';
|
||||||
|
err = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* free the lock dir */
|
||||||
|
(void) sprintf (tmp, "%s/%s", xrepository, CVSLCK);
|
||||||
|
if (rmdir (tmp) < 0)
|
||||||
|
error (0, errno, "failed to remove lock dir `%s'", tmp);
|
||||||
|
|
||||||
|
return (err);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Lock a list of directories for writing
|
||||||
|
*/
|
||||||
|
static char *lock_error_repos;
|
||||||
|
static int lock_error;
|
||||||
|
int
|
||||||
|
Writer_Lock (list)
|
||||||
|
List *list;
|
||||||
|
{
|
||||||
|
if (noexec)
|
||||||
|
return (0);
|
||||||
|
|
||||||
|
/* We only know how to do one list at a time */
|
||||||
|
if (locklist != (List *) NULL)
|
||||||
|
{
|
||||||
|
error (0, 0, "Writer_Lock called while write locks set - Help!");
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
/* try to lock everything on the list */
|
||||||
|
lock_error = L_OK; /* init for set_writelock_proc */
|
||||||
|
lock_error_repos = (char *) NULL; /* init for set_writelock_proc */
|
||||||
|
locklist = list; /* init for Lock_Cleanup */
|
||||||
|
(void) strcpy (lockers_name, "unknown");
|
||||||
|
|
||||||
|
(void) walklist (list, set_writelock_proc);
|
||||||
|
|
||||||
|
switch (lock_error)
|
||||||
|
{
|
||||||
|
case L_ERROR: /* Real Error */
|
||||||
|
Lock_Cleanup (); /* clean up any locks we set */
|
||||||
|
error (0, 0, "lock failed - giving up");
|
||||||
|
return (1);
|
||||||
|
|
||||||
|
case L_LOCKED: /* Someone already had a lock */
|
||||||
|
Lock_Cleanup (); /* clean up any locks we set */
|
||||||
|
lock_wait (lock_error_repos); /* sleep a while and try again */
|
||||||
|
continue;
|
||||||
|
|
||||||
|
case L_OK: /* we got the locks set */
|
||||||
|
return (0);
|
||||||
|
|
||||||
|
default:
|
||||||
|
error (0, 0, "unknown lock status %d in Writer_Lock",
|
||||||
|
lock_error);
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* walklist proc for setting write locks
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
set_writelock_proc (p)
|
||||||
|
Node *p;
|
||||||
|
{
|
||||||
|
/* if some lock was not OK, just skip this one */
|
||||||
|
if (lock_error != L_OK)
|
||||||
|
return (0);
|
||||||
|
|
||||||
|
/* apply the write lock */
|
||||||
|
lock_error_repos = p->key;
|
||||||
|
lock_error = write_lock (p->key);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create a lock file for writers returns L_OK if lock set ok, L_LOCKED if
|
||||||
|
* lock held by someone else or L_ERROR if an error occurred
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
write_lock (repository)
|
||||||
|
char *repository;
|
||||||
|
{
|
||||||
|
int status;
|
||||||
|
FILE *fp;
|
||||||
|
char tmp[PATH_MAX];
|
||||||
|
|
||||||
|
if (writelock[0] == '\0')
|
||||||
|
(void) sprintf (writelock, "%s.%d", CVSWFL, getpid ());
|
||||||
|
|
||||||
|
/* make sure we clean up on error */
|
||||||
|
(void) SIG_register (SIGHUP, Lock_Cleanup);
|
||||||
|
(void) SIG_register (SIGINT, Lock_Cleanup);
|
||||||
|
(void) SIG_register (SIGQUIT, Lock_Cleanup);
|
||||||
|
(void) SIG_register (SIGPIPE, Lock_Cleanup);
|
||||||
|
(void) SIG_register (SIGTERM, Lock_Cleanup);
|
||||||
|
|
||||||
|
/* make sure we can write the repository */
|
||||||
|
(void) sprintf (tmp, "%s/%s.%d", repository, CVSTFL, getpid ());
|
||||||
|
if ((fp = fopen (tmp, "w+")) == NULL || fclose (fp) == EOF)
|
||||||
|
{
|
||||||
|
error (0, errno, "cannot create write lock in repository `%s'",
|
||||||
|
repository);
|
||||||
|
(void) unlink (tmp);
|
||||||
|
return (L_ERROR);
|
||||||
|
}
|
||||||
|
(void) unlink (tmp);
|
||||||
|
|
||||||
|
/* make sure the lock dir is ours (not necessarily unique to us!) */
|
||||||
|
(void) sprintf (tmp, "%s/%s", repository, CVSLCK);
|
||||||
|
status = set_lock (tmp, 0, repository);
|
||||||
|
if (status == L_OK || status == L_LOCK_OWNED)
|
||||||
|
{
|
||||||
|
/* we now own a writer - make sure there are no readers */
|
||||||
|
if (readers_exist (repository))
|
||||||
|
{
|
||||||
|
/* clean up the lock dir if we created it */
|
||||||
|
if (status == L_OK)
|
||||||
|
{
|
||||||
|
if (rmdir (tmp) < 0)
|
||||||
|
error (0, errno, "failed to remove lock dir `%s'", tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* indicate we failed due to read locks instead of error */
|
||||||
|
return (L_LOCKED);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* write the write-lock file */
|
||||||
|
(void) sprintf (tmp, "%s/%s", repository, writelock);
|
||||||
|
if ((fp = fopen (tmp, "w+")) == NULL || fclose (fp) == EOF)
|
||||||
|
{
|
||||||
|
int xerrno = errno;
|
||||||
|
|
||||||
|
(void) unlink (tmp);
|
||||||
|
/* free the lock dir if we created it */
|
||||||
|
if (status == L_OK)
|
||||||
|
{
|
||||||
|
(void) sprintf (tmp, "%s/%s", repository, CVSLCK);
|
||||||
|
if (rmdir (tmp) < 0)
|
||||||
|
error (0, errno, "failed to remove lock dir `%s'", tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* return the error */
|
||||||
|
error (0, xerrno, "cannot create write lock in repository `%s'",
|
||||||
|
repository);
|
||||||
|
return (L_ERROR);
|
||||||
|
}
|
||||||
|
return (L_OK);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return (status);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* readers_exist() returns 0 if there are no reader lock files remaining in
|
||||||
|
* the repository; else 1 is returned, to indicate that the caller should
|
||||||
|
* sleep a while and try again.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
readers_exist (repository)
|
||||||
|
char *repository;
|
||||||
|
{
|
||||||
|
char line[MAXLINELEN];
|
||||||
|
DIR *dirp;
|
||||||
|
struct direct *dp;
|
||||||
|
struct stat sb;
|
||||||
|
CONST char *regex_err;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
#ifdef CVS_FUDGELOCKS
|
||||||
|
again:
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if ((dirp = opendir (repository)) == NULL)
|
||||||
|
error (1, 0, "cannot open directory %s", repository);
|
||||||
|
|
||||||
|
(void) sprintf (line, "^%s.*", CVSRFL);
|
||||||
|
if ((regex_err = re_comp (line)) != NULL)
|
||||||
|
error (1, 0, "%s", regex_err);
|
||||||
|
|
||||||
|
while ((dp = readdir (dirp)) != NULL)
|
||||||
|
{
|
||||||
|
(void) sprintf (line, "%s/%s", repository, dp->d_name);
|
||||||
|
if (re_exec (dp->d_name))
|
||||||
|
{
|
||||||
|
#ifdef CVS_FUDGELOCKS
|
||||||
|
time_t now;
|
||||||
|
|
||||||
|
(void) time (&now);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the create time of the file is more than CVSLCKAGE seconds
|
||||||
|
* ago, try to clean-up the lock file, and if successful, re-open
|
||||||
|
* the directory and try again.
|
||||||
|
*/
|
||||||
|
if (stat (line, &sb) != -1)
|
||||||
|
{
|
||||||
|
if (now >= (sb.st_ctime + CVSLCKAGE) && unlink (line) != -1)
|
||||||
|
{
|
||||||
|
(void) closedir (dirp);
|
||||||
|
goto again;
|
||||||
|
}
|
||||||
|
set_lockers_name (&sb);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (stat (line, &sb) != -1)
|
||||||
|
set_lockers_name (&sb);
|
||||||
|
#endif
|
||||||
|
ret = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(void) closedir (dirp);
|
||||||
|
return (ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set the static variable lockers_name appropriately, based on the stat
|
||||||
|
* structure passed in.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
set_lockers_name (statp)
|
||||||
|
struct stat *statp;
|
||||||
|
{
|
||||||
|
struct passwd *pw;
|
||||||
|
|
||||||
|
if ((pw = (struct passwd *) getpwuid (statp->st_uid)) !=
|
||||||
|
(struct passwd *) NULL)
|
||||||
|
{
|
||||||
|
(void) strcpy (lockers_name, pw->pw_name);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
(void) sprintf (lockers_name, "uid%d", statp->st_uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Persistently tries to make the directory "lckdir",, which serves as a
|
||||||
|
* lock. If the create time on the directory is greater than CVSLCKAGE
|
||||||
|
* seconds old, just try to remove the directory.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
set_lock (lockdir, will_wait, repository)
|
||||||
|
char *lockdir;
|
||||||
|
int will_wait;
|
||||||
|
char *repository;
|
||||||
|
{
|
||||||
|
struct stat sb;
|
||||||
|
#ifdef CVS_FUDGELOCKS
|
||||||
|
time_t now;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Note that it is up to the callers of set_lock() to arrange for signal
|
||||||
|
* handlers that do the appropriate things, like remove the lock
|
||||||
|
* directory before they exit.
|
||||||
|
*/
|
||||||
|
cleanup_lckdir = 0;
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
SIG_beginCrSect ();
|
||||||
|
if (mkdir (lockdir, 0777) == 0)
|
||||||
|
{
|
||||||
|
cleanup_lckdir = 1;
|
||||||
|
SIG_endCrSect ();
|
||||||
|
return (L_OK);
|
||||||
|
}
|
||||||
|
SIG_endCrSect ();
|
||||||
|
|
||||||
|
if (errno != EEXIST)
|
||||||
|
{
|
||||||
|
error (0, errno,
|
||||||
|
"failed to create lock directory in repository `%s'",
|
||||||
|
repository);
|
||||||
|
return (L_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* stat the dir - if it is non-existent, re-try the loop since
|
||||||
|
* someone probably just removed it (thus releasing the lock)
|
||||||
|
*/
|
||||||
|
if (stat (lockdir, &sb) < 0)
|
||||||
|
{
|
||||||
|
if (errno == ENOENT)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
error (0, errno, "couldn't stat lock directory `%s'", lockdir);
|
||||||
|
return (L_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* if we already own the lock, go ahead and return 1 which means it
|
||||||
|
* existed but we owned it
|
||||||
|
*/
|
||||||
|
if (sb.st_uid == geteuid () && !will_wait)
|
||||||
|
return (L_LOCK_OWNED);
|
||||||
|
|
||||||
|
#ifdef CVS_FUDGELOCKS
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the create time of the directory is more than CVSLCKAGE seconds
|
||||||
|
* ago, try to clean-up the lock directory, and if successful, just
|
||||||
|
* quietly retry to make it.
|
||||||
|
*/
|
||||||
|
(void) time (&now);
|
||||||
|
if (now >= (sb.st_ctime + CVSLCKAGE))
|
||||||
|
{
|
||||||
|
if (rmdir (lockdir) >= 0)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* set the lockers name */
|
||||||
|
set_lockers_name (&sb);
|
||||||
|
|
||||||
|
/* if he wasn't willing to wait, return an error */
|
||||||
|
if (!will_wait)
|
||||||
|
return (L_LOCKED);
|
||||||
|
lock_wait (repository);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Print out a message that the lock is still held, then sleep a while.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
lock_wait (repos)
|
||||||
|
char *repos;
|
||||||
|
{
|
||||||
|
time_t now;
|
||||||
|
|
||||||
|
(void) time (&now);
|
||||||
|
error (0, 0, "[%8.8s] waiting for %s's lock in %s", ctime (&now) + 11,
|
||||||
|
lockers_name, repos);
|
||||||
|
(void) sleep (CVSLCKSLEEP);
|
||||||
|
}
|
||||||
@@ -0,0 +1,132 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 1992, Brian Berliner and Jeff Polk
|
||||||
|
* Copyright (c) 1989-1992, Brian Berliner
|
||||||
|
*
|
||||||
|
* You may distribute under the terms of the GNU General Public License as
|
||||||
|
* specified in the README file that comes with the CVS 1.3 kit.
|
||||||
|
*
|
||||||
|
* Print Log Information
|
||||||
|
*
|
||||||
|
* Prints the RCS "log" (rlog) information for the specified files. With no
|
||||||
|
* argument, prints the log information for all the files in the directory
|
||||||
|
* (recursive by default).
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "cvs.h"
|
||||||
|
|
||||||
|
#ifndef lint
|
||||||
|
static char rcsid[] = "@(#)log.c 1.39 92/03/31";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __STDC__
|
||||||
|
static Dtype log_dirproc (char *dir, char *repository, char *update_dir);
|
||||||
|
static int log_fileproc (char *file, char *update_dir, char *repository,
|
||||||
|
List * entries, List * srcfiles);
|
||||||
|
#else
|
||||||
|
static int log_fileproc ();
|
||||||
|
static Dtype log_dirproc ();
|
||||||
|
#endif /* __STDC__ */
|
||||||
|
|
||||||
|
static char options[PATH_MAX];
|
||||||
|
|
||||||
|
static char *log_usage[] =
|
||||||
|
{
|
||||||
|
"Usage: %s %s [-l] [rlog-options] [files...]\n",
|
||||||
|
"\t-l\tLocal directory only, no recursion.\n",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
int
|
||||||
|
cvslog (argc, argv)
|
||||||
|
int argc;
|
||||||
|
char *argv[];
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int numopt = 1;
|
||||||
|
int err = 0;
|
||||||
|
int local = 0;
|
||||||
|
|
||||||
|
if (argc == -1)
|
||||||
|
usage (log_usage);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* All 'log' command options except -l are passed directly on to 'rlog'
|
||||||
|
*/
|
||||||
|
options[0] = '\0'; /* Assume none */
|
||||||
|
for (i = 1; i < argc; i++)
|
||||||
|
{
|
||||||
|
if (argv[i][0] == '-' || argv[i][0] == '\0')
|
||||||
|
{
|
||||||
|
numopt++;
|
||||||
|
switch (argv[i][1])
|
||||||
|
{
|
||||||
|
case 'l':
|
||||||
|
local = 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
(void) strcat (options, " ");
|
||||||
|
(void) strcat (options, argv[i]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
argc -= numopt;
|
||||||
|
argv += numopt;
|
||||||
|
|
||||||
|
err = start_recursion (log_fileproc, (int (*) ()) NULL, log_dirproc,
|
||||||
|
(int (*) ()) NULL, argc, argv, local,
|
||||||
|
W_LOCAL | W_REPOS | W_ATTIC, 0, 1,
|
||||||
|
(char *) NULL, 1);
|
||||||
|
return (err);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Do an rlog on a file
|
||||||
|
*/
|
||||||
|
/* ARGSUSED */
|
||||||
|
static int
|
||||||
|
log_fileproc (file, update_dir, repository, entries, srcfiles)
|
||||||
|
char *file;
|
||||||
|
char *update_dir;
|
||||||
|
char *repository;
|
||||||
|
List *entries;
|
||||||
|
List *srcfiles;
|
||||||
|
{
|
||||||
|
Node *p;
|
||||||
|
RCSNode *rcsfile;
|
||||||
|
int retcode = 0;
|
||||||
|
|
||||||
|
p = findnode (srcfiles, file);
|
||||||
|
if (p == NULL || (rcsfile = (RCSNode *) p->data) == NULL)
|
||||||
|
{
|
||||||
|
if (!really_quiet)
|
||||||
|
error (0, 0, "nothing known about %s", file);
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
run_setup ("%s%s %s", Rcsbin, RCS_RLOG, options);
|
||||||
|
run_arg (rcsfile->path);
|
||||||
|
if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_REALLY)) == -1)
|
||||||
|
{
|
||||||
|
error (1, errno, "fork failed for rlog on %s", file);
|
||||||
|
}
|
||||||
|
return (retcode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Print a warm fuzzy message
|
||||||
|
*/
|
||||||
|
/* ARGSUSED */
|
||||||
|
static Dtype
|
||||||
|
log_dirproc (dir, repository, update_dir)
|
||||||
|
char *dir;
|
||||||
|
char *repository;
|
||||||
|
char *update_dir;
|
||||||
|
{
|
||||||
|
if (!isdir (dir))
|
||||||
|
return (R_SKIP_ALL);
|
||||||
|
|
||||||
|
if (!quiet)
|
||||||
|
error (0, 0, "Logging %s", update_dir);
|
||||||
|
return (R_PROCESS);
|
||||||
|
}
|
||||||
@@ -0,0 +1,449 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 1992, Brian Berliner and Jeff Polk
|
||||||
|
* Copyright (c) 1989-1992, Brian Berliner
|
||||||
|
*
|
||||||
|
* You may distribute under the terms of the GNU General Public License as
|
||||||
|
* specified in the README file that comes with the CVS 1.3 kit.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "cvs.h"
|
||||||
|
|
||||||
|
#ifndef lint
|
||||||
|
static char rcsid[] = "@(#)logmsg.c 1.40 92/04/10";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __STDC__
|
||||||
|
static int find_type (Node * p);
|
||||||
|
static int fmt_proc (Node * p);
|
||||||
|
static int logfile_write (char *repository, char *filter, char *title,
|
||||||
|
char *message, char *revision, FILE * logfp,
|
||||||
|
List * changes);
|
||||||
|
static int rcsinfo_proc (char *repository, char *template);
|
||||||
|
static int title_proc (Node * p);
|
||||||
|
static int update_logfile_proc (char *repository, char *filter);
|
||||||
|
static void setup_tmpfile (FILE * xfp, char *xprefix, List * changes);
|
||||||
|
static int editinfo_proc (char *repository, char *template);
|
||||||
|
#else
|
||||||
|
static void setup_tmpfile ();
|
||||||
|
static int find_type ();
|
||||||
|
static int fmt_proc ();
|
||||||
|
static int rcsinfo_proc ();
|
||||||
|
static int update_logfile_proc ();
|
||||||
|
static int title_proc ();
|
||||||
|
static int logfile_write ();
|
||||||
|
static int editinfo_proc ();
|
||||||
|
#endif /* __STDC__ */
|
||||||
|
|
||||||
|
static FILE *fp;
|
||||||
|
static char *strlist;
|
||||||
|
static char *editinfo_editor;
|
||||||
|
static Ctype type;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Puts a standard header on the output which is either being prepared for an
|
||||||
|
* editor session, or being sent to a logfile program. The modified, added,
|
||||||
|
* and removed files are included (if any) and formatted to look pretty.
|
||||||
|
*/
|
||||||
|
static char *prefix;
|
||||||
|
static int col;
|
||||||
|
static void
|
||||||
|
setup_tmpfile (xfp, xprefix, changes)
|
||||||
|
FILE *xfp;
|
||||||
|
char *xprefix;
|
||||||
|
List *changes;
|
||||||
|
{
|
||||||
|
/* set up statics */
|
||||||
|
fp = xfp;
|
||||||
|
prefix = xprefix;
|
||||||
|
|
||||||
|
type = T_MODIFIED;
|
||||||
|
if (walklist (changes, find_type) != 0)
|
||||||
|
{
|
||||||
|
(void) fprintf (fp, "%sModified Files:\n", prefix);
|
||||||
|
(void) fprintf (fp, "%s\t", prefix);
|
||||||
|
col = 8;
|
||||||
|
(void) walklist (changes, fmt_proc);
|
||||||
|
(void) fprintf (fp, "\n");
|
||||||
|
}
|
||||||
|
type = T_ADDED;
|
||||||
|
if (walklist (changes, find_type) != 0)
|
||||||
|
{
|
||||||
|
(void) fprintf (fp, "%sAdded Files:\n", prefix);
|
||||||
|
(void) fprintf (fp, "%s\t", prefix);
|
||||||
|
col = 8;
|
||||||
|
(void) walklist (changes, fmt_proc);
|
||||||
|
(void) fprintf (fp, "\n");
|
||||||
|
}
|
||||||
|
type = T_REMOVED;
|
||||||
|
if (walklist (changes, find_type) != 0)
|
||||||
|
{
|
||||||
|
(void) fprintf (fp, "%sRemoved Files:\n", prefix);
|
||||||
|
(void) fprintf (fp, "%s\t", prefix);
|
||||||
|
col = 8;
|
||||||
|
(void) walklist (changes, fmt_proc);
|
||||||
|
(void) fprintf (fp, "\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Looks for nodes of a specified type and returns 1 if found
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
find_type (p)
|
||||||
|
Node *p;
|
||||||
|
{
|
||||||
|
if (p->data == (char *) type)
|
||||||
|
return (1);
|
||||||
|
else
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Breaks the files list into reasonable sized lines to avoid line wrap...
|
||||||
|
* all in the name of pretty output. It only works on nodes whose types
|
||||||
|
* match the one we're looking for
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
fmt_proc (p)
|
||||||
|
Node *p;
|
||||||
|
{
|
||||||
|
if (p->data == (char *) type)
|
||||||
|
{
|
||||||
|
if ((col + (int) strlen (p->key)) > 70)
|
||||||
|
{
|
||||||
|
(void) fprintf (fp, "\n%s\t", prefix);
|
||||||
|
col = 8;
|
||||||
|
}
|
||||||
|
(void) fprintf (fp, "%s ", p->key);
|
||||||
|
col += strlen (p->key) + 1;
|
||||||
|
}
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Builds a temporary file using setup_tmpfile() and invokes the user's
|
||||||
|
* editor on the file. The header garbage in the resultant file is then
|
||||||
|
* stripped and the log message is stored in the "message" argument.
|
||||||
|
*
|
||||||
|
* rcsinfo - is the name of a file containing lines tacked onto the end of the
|
||||||
|
* RCS info offered to the user for editing. If specified, the '-m' flag to
|
||||||
|
* "commit" is disabled -- users are forced to run the editor.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
do_editor (dir, message, repository, changes)
|
||||||
|
char *dir;
|
||||||
|
char *message;
|
||||||
|
char *repository;
|
||||||
|
List *changes;
|
||||||
|
{
|
||||||
|
static int reuse_log_message = 0;
|
||||||
|
char line[MAXLINELEN], fname[L_tmpnam+1];
|
||||||
|
char *orig_message;
|
||||||
|
struct stat pre_stbuf, post_stbuf;
|
||||||
|
int retcode = 0;
|
||||||
|
|
||||||
|
if (noexec || reuse_log_message)
|
||||||
|
return;
|
||||||
|
|
||||||
|
orig_message = xstrdup (message); /* save it for later */
|
||||||
|
|
||||||
|
/* Create a temporary file */
|
||||||
|
(void) tmpnam (fname);
|
||||||
|
again:
|
||||||
|
if ((fp = fopen (fname, "w+")) == NULL)
|
||||||
|
error (1, 0, "cannot create temporary file %s", fname);
|
||||||
|
|
||||||
|
/* set up the file so that the first line is blank if no msg specified */
|
||||||
|
if (*orig_message)
|
||||||
|
{
|
||||||
|
(void) fprintf (fp, "%s", orig_message);
|
||||||
|
if (orig_message[strlen (orig_message) - 1] != '\n')
|
||||||
|
(void) fprintf (fp, "\n");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
(void) fprintf (fp, "\n");
|
||||||
|
|
||||||
|
/* tack templates on if necessary */
|
||||||
|
(void) Parse_Info (CVSROOTADM_RCSINFO, repository, rcsinfo_proc, 1);
|
||||||
|
|
||||||
|
(void) fprintf (fp,
|
||||||
|
"%s----------------------------------------------------------------------\n",
|
||||||
|
CVSEDITPREFIX);
|
||||||
|
(void) fprintf (fp,
|
||||||
|
"%sEnter Log. Lines beginning with `%s' are removed automatically\n%s\n",
|
||||||
|
CVSEDITPREFIX, CVSEDITPREFIX, CVSEDITPREFIX);
|
||||||
|
if (dir != NULL)
|
||||||
|
(void) fprintf (fp, "%sCommitting in %s\n%s\n", CVSEDITPREFIX,
|
||||||
|
dir, CVSEDITPREFIX);
|
||||||
|
setup_tmpfile (fp, CVSEDITPREFIX, changes);
|
||||||
|
(void) fprintf (fp,
|
||||||
|
"%s----------------------------------------------------------------------\n",
|
||||||
|
CVSEDITPREFIX);
|
||||||
|
|
||||||
|
/* finish off the temp file */
|
||||||
|
(void) fclose (fp);
|
||||||
|
if (stat (fname, &pre_stbuf) == -1)
|
||||||
|
pre_stbuf.st_mtime = 0;
|
||||||
|
|
||||||
|
if (editinfo_editor)
|
||||||
|
free (editinfo_editor);
|
||||||
|
editinfo_editor = (char *) NULL;
|
||||||
|
(void) Parse_Info (CVSROOTADM_EDITINFO, repository, editinfo_proc, 0);
|
||||||
|
|
||||||
|
/* run the editor */
|
||||||
|
run_setup ("%s", editinfo_editor ? editinfo_editor : Editor);
|
||||||
|
run_arg (fname);
|
||||||
|
if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY,
|
||||||
|
RUN_NORMAL | RUN_SIGIGNORE)) != 0)
|
||||||
|
error (editinfo_editor ? 1 : 0, retcode == -1 ? errno : 0,
|
||||||
|
editinfo_editor ? "Logfile verification failed" :
|
||||||
|
"warning: editor session failed");
|
||||||
|
|
||||||
|
/* put the entire message back into the message variable */
|
||||||
|
fp = open_file (fname, "r");
|
||||||
|
*message = '\0';
|
||||||
|
while (fgets (line, sizeof (line), fp) != NULL)
|
||||||
|
{
|
||||||
|
if (strncmp (line, CVSEDITPREFIX, sizeof (CVSEDITPREFIX) - 1) == 0)
|
||||||
|
continue;
|
||||||
|
if (((int) strlen (message) + (int) strlen (line)) >= MAXMESGLEN)
|
||||||
|
{
|
||||||
|
error (0, 0, "warning: log message truncated!");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
(void) strcat (message, line);
|
||||||
|
}
|
||||||
|
(void) fclose (fp);
|
||||||
|
if ((stat (fname, &post_stbuf) == 0 &&
|
||||||
|
pre_stbuf.st_mtime == post_stbuf.st_mtime) ||
|
||||||
|
(*message == '\0' || strcmp (message, "\n") == 0))
|
||||||
|
{
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
(void) printf ("\nLog message unchanged or not specified\n");
|
||||||
|
(void) printf ("a)bort, c)continue, e)dit, !)reuse this message unchanged for remaining dirs\n");
|
||||||
|
(void) printf ("Action: (continue) ");
|
||||||
|
(void) fflush (stdout);
|
||||||
|
*line = '\0';
|
||||||
|
(void) fgets (line, sizeof (line), stdin);
|
||||||
|
if (*line == '\0' || *line == '\n' || *line == 'c' || *line == 'C')
|
||||||
|
break;
|
||||||
|
if (*line == 'a' || *line == 'A')
|
||||||
|
error (1, 0, "aborted by user");
|
||||||
|
if (*line == 'e' || *line == 'E')
|
||||||
|
goto again;
|
||||||
|
if (*line == '!')
|
||||||
|
{
|
||||||
|
reuse_log_message = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
(void) printf ("Unknown input\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free (orig_message);
|
||||||
|
(void) unlink_file (fname);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* callback proc for Parse_Info for rcsinfo templates this routine basically
|
||||||
|
* copies the matching template onto the end of the tempfile we are setting
|
||||||
|
* up
|
||||||
|
*/
|
||||||
|
/* ARGSUSED */
|
||||||
|
static int
|
||||||
|
rcsinfo_proc (repository, template)
|
||||||
|
char *repository;
|
||||||
|
char *template;
|
||||||
|
{
|
||||||
|
static char *last_template;
|
||||||
|
FILE *tfp;
|
||||||
|
char line[MAXLINELEN];
|
||||||
|
|
||||||
|
/* nothing to do if the last one included is the same as this one */
|
||||||
|
if (last_template && strcmp (last_template, template) == 0)
|
||||||
|
return (0);
|
||||||
|
if (last_template)
|
||||||
|
free (last_template);
|
||||||
|
last_template = xstrdup (template);
|
||||||
|
|
||||||
|
if ((tfp = fopen (template, "r")) != NULL)
|
||||||
|
{
|
||||||
|
while (fgets (line, sizeof (line), tfp) != NULL)
|
||||||
|
(void) fputs (line, fp);
|
||||||
|
(void) fclose (tfp);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
error (0, 0, "Couldn't open rcsinfo template file %s", template);
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Uses setup_tmpfile() to pass the updated message on directly to any
|
||||||
|
* logfile programs that have a regular expression match for the checked in
|
||||||
|
* directory in the source repository. The log information is fed into the
|
||||||
|
* specified program as standard input.
|
||||||
|
*/
|
||||||
|
static char *title;
|
||||||
|
static FILE *logfp;
|
||||||
|
static char *message;
|
||||||
|
static char *revision;
|
||||||
|
static List *changes;
|
||||||
|
|
||||||
|
void
|
||||||
|
Update_Logfile (repository, xmessage, xrevision, xlogfp, xchanges)
|
||||||
|
char *repository;
|
||||||
|
char *xmessage;
|
||||||
|
char *xrevision;
|
||||||
|
FILE *xlogfp;
|
||||||
|
List *xchanges;
|
||||||
|
{
|
||||||
|
char *srepos;
|
||||||
|
|
||||||
|
/* set up static vars for update_logfile_proc */
|
||||||
|
message = xmessage;
|
||||||
|
revision = xrevision;
|
||||||
|
logfp = xlogfp;
|
||||||
|
changes = xchanges;
|
||||||
|
|
||||||
|
/* figure out a good title string */
|
||||||
|
srepos = Short_Repository (repository);
|
||||||
|
|
||||||
|
/* allocate a chunk of memory to hold the title string */
|
||||||
|
if (!strlist)
|
||||||
|
strlist = xmalloc (MAXLISTLEN);
|
||||||
|
strlist[0] = '\0';
|
||||||
|
|
||||||
|
type = T_TITLE;
|
||||||
|
(void) walklist (changes, title_proc);
|
||||||
|
type = T_ADDED;
|
||||||
|
(void) walklist (changes, title_proc);
|
||||||
|
type = T_MODIFIED;
|
||||||
|
(void) walklist (changes, title_proc);
|
||||||
|
type = T_REMOVED;
|
||||||
|
(void) walklist (changes, title_proc);
|
||||||
|
title = xmalloc (strlen (srepos) + strlen (strlist) + 1 + 2); /* for 's */
|
||||||
|
(void) sprintf (title, "'%s%s'", srepos, strlist);
|
||||||
|
|
||||||
|
/* to be nice, free up this chunk of memory */
|
||||||
|
free (strlist);
|
||||||
|
strlist = (char *) NULL;
|
||||||
|
|
||||||
|
/* call Parse_Info to do the actual logfile updates */
|
||||||
|
(void) Parse_Info (CVSROOTADM_LOGINFO, repository, update_logfile_proc, 1);
|
||||||
|
|
||||||
|
/* clean up */
|
||||||
|
free (title);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* callback proc to actually do the logfile write from Update_Logfile
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
update_logfile_proc (repository, filter)
|
||||||
|
char *repository;
|
||||||
|
char *filter;
|
||||||
|
{
|
||||||
|
return (logfile_write (repository, filter, title, message, revision,
|
||||||
|
logfp, changes));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* concatenate each name onto strlist
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
title_proc (p)
|
||||||
|
Node *p;
|
||||||
|
{
|
||||||
|
if (p->data == (char *) type)
|
||||||
|
{
|
||||||
|
(void) strcat (strlist, " ");
|
||||||
|
(void) strcat (strlist, p->key);
|
||||||
|
}
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Since some systems don't define this...
|
||||||
|
*/
|
||||||
|
#ifndef MAXHOSTNAMELEN
|
||||||
|
#define MAXHOSTNAMELEN 256
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Writes some stuff to the logfile "filter" and returns the status of the
|
||||||
|
* filter program.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
logfile_write (repository, filter, title, message, revision, logfp, changes)
|
||||||
|
char *repository;
|
||||||
|
char *filter;
|
||||||
|
char *title;
|
||||||
|
char *message;
|
||||||
|
char *revision;
|
||||||
|
FILE *logfp;
|
||||||
|
List *changes;
|
||||||
|
{
|
||||||
|
char cwd[PATH_MAX], host[MAXHOSTNAMELEN];
|
||||||
|
FILE *pipefp, *Popen ();
|
||||||
|
char *prog = xmalloc (MAXPROGLEN);
|
||||||
|
char *cp;
|
||||||
|
int c;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A maximum of 6 %s arguments are supported in the filter
|
||||||
|
*/
|
||||||
|
(void) sprintf (prog, filter, title, title, title, title, title, title);
|
||||||
|
if ((pipefp = Popen (prog, "w")) == NULL)
|
||||||
|
{
|
||||||
|
if (!noexec)
|
||||||
|
error (0, 0, "cannot write entry to log filter: %s", prog);
|
||||||
|
free (prog);
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
if (gethostname (host, sizeof (host)) < 0)
|
||||||
|
(void) strcpy (host, "(unknown)");
|
||||||
|
(void) fprintf (pipefp, "Update of %s\n", repository);
|
||||||
|
(void) fprintf (pipefp, "In directory %s:%s\n\n", host,
|
||||||
|
((cp = getwd (cwd)) != NULL) ? cp : cwd);
|
||||||
|
if (revision && *revision)
|
||||||
|
(void) fprintf (pipefp, "Revision/Branch: %s\n\n", revision);
|
||||||
|
setup_tmpfile (pipefp, "", changes);
|
||||||
|
(void) fprintf (pipefp, "Log Message:\n%s\n", message);
|
||||||
|
if (logfp != (FILE *) 0)
|
||||||
|
{
|
||||||
|
(void) fprintf (pipefp, "Status:\n");
|
||||||
|
(void) rewind (logfp);
|
||||||
|
while ((c = getc (logfp)) != EOF)
|
||||||
|
(void) putc ((char) c, pipefp);
|
||||||
|
}
|
||||||
|
free (prog);
|
||||||
|
return (pclose (pipefp));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We choose to use the *last* match within the editinfo file for this
|
||||||
|
* repository. This allows us to have a global editinfo program for the
|
||||||
|
* root of some hierarchy, for example, and different ones within different
|
||||||
|
* sub-directories of the root (like a special checker for changes made to
|
||||||
|
* the "src" directory versus changes made to the "doc" or "test"
|
||||||
|
* directories.
|
||||||
|
*/
|
||||||
|
/* ARGSUSED */
|
||||||
|
static int
|
||||||
|
editinfo_proc(repository, editor)
|
||||||
|
char *repository;
|
||||||
|
char *editor;
|
||||||
|
{
|
||||||
|
/* nothing to do if the last match is the same as this one */
|
||||||
|
if (editinfo_editor && strcmp (editinfo_editor, editor) == 0)
|
||||||
|
return (0);
|
||||||
|
if (editinfo_editor)
|
||||||
|
free (editinfo_editor);
|
||||||
|
|
||||||
|
editinfo_editor = xstrdup (editor);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
@@ -0,0 +1,444 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 1992, Brian Berliner and Jeff Polk
|
||||||
|
* Copyright (c) 1989-1992, Brian Berliner
|
||||||
|
*
|
||||||
|
* You may distribute under the terms of the GNU General Public License
|
||||||
|
* as specified in the README file that comes with the CVS 1.3 kit.
|
||||||
|
*
|
||||||
|
* This is the main C driver for the CVS system.
|
||||||
|
*
|
||||||
|
* Credit to Dick Grune, Vrije Universiteit, Amsterdam, for writing
|
||||||
|
* the shell-script CVS system that this is based on.
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* cvs [options] command [options] [files/modules...]
|
||||||
|
*
|
||||||
|
* Where "command" is composed of:
|
||||||
|
* admin RCS command
|
||||||
|
* checkout Check out a module/dir/file
|
||||||
|
* export Like checkout, but used for exporting sources
|
||||||
|
* update Brings work tree in sync with repository
|
||||||
|
* commit Checks files into the repository
|
||||||
|
* diff Runs diffs between revisions
|
||||||
|
* log Prints "rlog" information for files
|
||||||
|
* add Adds an entry to the repository
|
||||||
|
* remove Removes an entry from the repository
|
||||||
|
* status Status info on the revisions
|
||||||
|
* rdiff "patch" format diff listing between releases
|
||||||
|
* tag Add/delete a symbolic tag to the RCS file
|
||||||
|
* rtag Add/delete a symbolic tag to the RCS file
|
||||||
|
* import Import sources into CVS, using vendor branches
|
||||||
|
* release Indicate that Module is no longer in use.
|
||||||
|
* history Display history of Users and Modules.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "cvs.h"
|
||||||
|
#include "patchlevel.h"
|
||||||
|
|
||||||
|
char rcsid[] = "@(#)main.c 1.64 92/03/31\n";
|
||||||
|
|
||||||
|
extern char *getenv ();
|
||||||
|
|
||||||
|
char *program_name;
|
||||||
|
char *command_name = "";
|
||||||
|
|
||||||
|
int use_editor = TRUE;
|
||||||
|
int cvswrite = !CVSREAD_DFLT;
|
||||||
|
int really_quiet = FALSE;
|
||||||
|
int quiet = FALSE;
|
||||||
|
int trace = FALSE;
|
||||||
|
int noexec = FALSE;
|
||||||
|
int logoff = FALSE;
|
||||||
|
|
||||||
|
char *CurDir;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Defaults, for the environment variables that are not set
|
||||||
|
*/
|
||||||
|
char *Rcsbin = RCSBIN_DFLT;
|
||||||
|
char *Editor = EDITOR_DFLT;
|
||||||
|
char *CVSroot = CVSROOT_DFLT;
|
||||||
|
|
||||||
|
#if __STDC__
|
||||||
|
int add (int argc, char **argv);
|
||||||
|
int admin (int argc, char **argv);
|
||||||
|
int checkout (int argc, char **argv);
|
||||||
|
int commit (int argc, char **argv);
|
||||||
|
int diff (int argc, char **argv);
|
||||||
|
int history (int argc, char **argv);
|
||||||
|
int import (int argc, char **argv);
|
||||||
|
int cvslog (int argc, char **argv);
|
||||||
|
int patch (int argc, char **argv);
|
||||||
|
int release (int argc, char **argv);
|
||||||
|
int cvsremove (int argc, char **argv);
|
||||||
|
int rtag (int argc, char **argv);
|
||||||
|
int status (int argc, char **argv);
|
||||||
|
int tag (int argc, char **argv);
|
||||||
|
int update (int argc, char **argv);
|
||||||
|
#else
|
||||||
|
int add ();
|
||||||
|
int admin ();
|
||||||
|
int checkout ();
|
||||||
|
int commit ();
|
||||||
|
int diff ();
|
||||||
|
int history ();
|
||||||
|
int import ();
|
||||||
|
int cvslog ();
|
||||||
|
int patch ();
|
||||||
|
int release ();
|
||||||
|
int cvsremove ();
|
||||||
|
int rtag ();
|
||||||
|
int status ();
|
||||||
|
int tag ();
|
||||||
|
int update ();
|
||||||
|
#endif /* __STDC__ */
|
||||||
|
|
||||||
|
struct cmd
|
||||||
|
{
|
||||||
|
char *fullname; /* Full name of the function (e.g. "commit") */
|
||||||
|
char *nick1; /* alternate name (e.g. "ci") */
|
||||||
|
char *nick2; /* another alternate names (e.g. "ci") */
|
||||||
|
int (*func) (); /* Function takes (argc, argv) arguments. */
|
||||||
|
} cmds[] =
|
||||||
|
|
||||||
|
{
|
||||||
|
{ "add", "ad", "new", add },
|
||||||
|
{ "admin", "adm", "rcs", admin },
|
||||||
|
{ "checkout", "co", "get", checkout },
|
||||||
|
{ "commit", "ci", "com", commit },
|
||||||
|
{ "diff", "di", "dif", diff },
|
||||||
|
{ "export", "exp", "ex", checkout },
|
||||||
|
{ "history", "hi", "his", history },
|
||||||
|
{ "import", "im", "imp", import },
|
||||||
|
{ "log", "lo", "rlog", cvslog },
|
||||||
|
{ "rdiff", "patch", "pa", patch },
|
||||||
|
{ "release", "re", "rel", release },
|
||||||
|
{ "remove", "rm", "delete", cvsremove },
|
||||||
|
{ "status", "st", "stat", status },
|
||||||
|
{ "rtag", "rt", "rfreeze", rtag },
|
||||||
|
{ "tag", "ta", "freeze", tag },
|
||||||
|
{ "update", "up", "upd", update },
|
||||||
|
{ NULL, NULL, NULL, NULL },
|
||||||
|
};
|
||||||
|
|
||||||
|
static char *usg[] =
|
||||||
|
{
|
||||||
|
"Usage: %s [cvs-options] command [command-options] [files...]\n",
|
||||||
|
" Where 'cvs-options' are:\n",
|
||||||
|
" -H Displays Usage information for command\n",
|
||||||
|
" -Q Cause CVS to be really quiet.\n",
|
||||||
|
" -q Cause CVS to be somewhat quiet.\n",
|
||||||
|
" -r Make checked-out files read-only\n",
|
||||||
|
" -w Make checked-out files read-write (default)\n",
|
||||||
|
" -l Turn History logging off\n",
|
||||||
|
" -n Do not execute anything that will change the disk\n",
|
||||||
|
" -t Show trace of program execution -- Try with -n\n",
|
||||||
|
" -v CVS version and copyright\n",
|
||||||
|
" -b bindir Find RCS programs in 'bindir'\n",
|
||||||
|
" -e editor Use 'editor' for editing log information\n",
|
||||||
|
" -d CVS_root Overrides $CVSROOT as the root of the CVS tree\n",
|
||||||
|
"\n",
|
||||||
|
" and where 'command' is:\n",
|
||||||
|
" add Adds a new file/directory to the repository\n",
|
||||||
|
" admin Administration front end for rcs\n",
|
||||||
|
" checkout Checkout sources for editing\n",
|
||||||
|
" commit Checks files into the repository\n",
|
||||||
|
" diff Runs diffs between revisions\n",
|
||||||
|
" history Shows status of files and users\n",
|
||||||
|
" import Import sources into CVS, using vendor branches\n",
|
||||||
|
" export Export sources from CVS, similar to checkout\n",
|
||||||
|
" log Prints out 'rlog' information for files\n",
|
||||||
|
" rdiff 'patch' format diffs between releases\n",
|
||||||
|
" release Indicate that a Module is no longer in use\n",
|
||||||
|
" remove Removes an entry from the repository\n",
|
||||||
|
" status Status info on the revisions\n",
|
||||||
|
" tag Add a symbolic tag to checked out version of RCS file\n",
|
||||||
|
" rtag Add a symbolic tag to the RCS file\n",
|
||||||
|
" update Brings work tree in sync with repository\n",
|
||||||
|
NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
static SIGTYPE
|
||||||
|
main_cleanup ()
|
||||||
|
{
|
||||||
|
exit (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main (argc, argv)
|
||||||
|
int argc;
|
||||||
|
char *argv[];
|
||||||
|
{
|
||||||
|
extern char *version_string;
|
||||||
|
char *cp;
|
||||||
|
struct cmd *cm;
|
||||||
|
int c, help = FALSE, err = 0;
|
||||||
|
int rcsbin_update_env, cvs_update_env;
|
||||||
|
char tmp[PATH_MAX];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Just save the last component of the path for error messages
|
||||||
|
*/
|
||||||
|
if ((program_name = rindex (argv[0], '/')) == NULL)
|
||||||
|
program_name = argv[0];
|
||||||
|
else
|
||||||
|
program_name++;
|
||||||
|
|
||||||
|
CurDir = xmalloc (PATH_MAX);
|
||||||
|
if (!getwd (CurDir))
|
||||||
|
error (1, 0, "cannot get working directory: %s", CurDir);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Query the environment variables up-front, so that
|
||||||
|
* they can be overridden by command line arguments
|
||||||
|
*/
|
||||||
|
rcsbin_update_env = *Rcsbin; /* RCSBIN_DFLT must be set */
|
||||||
|
if ((cp = getenv (RCSBIN_ENV)) != NULL)
|
||||||
|
{
|
||||||
|
Rcsbin = cp;
|
||||||
|
rcsbin_update_env = 0; /* it's already there */
|
||||||
|
}
|
||||||
|
if ((cp = getenv (EDITOR_ENV)) != NULL)
|
||||||
|
Editor = cp;
|
||||||
|
if ((cp = getenv (CVSROOT_ENV)) != NULL)
|
||||||
|
{
|
||||||
|
CVSroot = cp;
|
||||||
|
cvs_update_env = 0; /* it's already there */
|
||||||
|
}
|
||||||
|
if (getenv (CVSREAD_ENV) != NULL)
|
||||||
|
cvswrite = FALSE;
|
||||||
|
|
||||||
|
optind = 1;
|
||||||
|
while ((c = gnu_getopt (argc, argv, "Qqrwtnlvb:e:d:H")) != -1)
|
||||||
|
{
|
||||||
|
switch (c)
|
||||||
|
{
|
||||||
|
case 'Q':
|
||||||
|
really_quiet = TRUE;
|
||||||
|
/* FALL THROUGH */
|
||||||
|
case 'q':
|
||||||
|
quiet = TRUE;
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
cvswrite = FALSE;
|
||||||
|
break;
|
||||||
|
case 'w':
|
||||||
|
cvswrite = TRUE;
|
||||||
|
break;
|
||||||
|
case 't':
|
||||||
|
trace = TRUE;
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
noexec = TRUE;
|
||||||
|
case 'l': /* Fall through */
|
||||||
|
logoff = TRUE;
|
||||||
|
break;
|
||||||
|
case 'v':
|
||||||
|
(void) fputs (rcsid, stdout);
|
||||||
|
(void) fputs (version_string, stdout);
|
||||||
|
(void) sprintf (tmp, "Patch Level: %d\n", PATCHLEVEL);
|
||||||
|
(void) fputs (tmp, stdout);
|
||||||
|
(void) fputs ("\nCopyright (c) 1992, Brian Berliner and Jeff Polk\nCopyright (c) 1989-1992, Brian Berliner\n\nCVS may be copied only under the terms of the GNU General Public License,\na copy of which can be found with the CVS 1.3 distribution kit.\n", stdout);
|
||||||
|
exit (0);
|
||||||
|
break;
|
||||||
|
case 'b':
|
||||||
|
Rcsbin = optarg;
|
||||||
|
rcsbin_update_env = 1; /* need to update environment */
|
||||||
|
break;
|
||||||
|
case 'e':
|
||||||
|
Editor = optarg;
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
CVSroot = optarg;
|
||||||
|
cvs_update_env = 1; /* need to update environment */
|
||||||
|
break;
|
||||||
|
case 'H':
|
||||||
|
help = TRUE;
|
||||||
|
break;
|
||||||
|
case '?':
|
||||||
|
default:
|
||||||
|
usage (usg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
argc -= optind;
|
||||||
|
argv += optind;
|
||||||
|
if (argc < 1)
|
||||||
|
usage (usg);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* XXX - Compatibility. This can be removed in the release after CVS 1.3.
|
||||||
|
* Try to rename the CVSROOT.adm file to CVSROOT, unless there already is
|
||||||
|
* a CVSROOT directory.
|
||||||
|
*/
|
||||||
|
if (CVSroot != NULL)
|
||||||
|
{
|
||||||
|
char rootadm[PATH_MAX];
|
||||||
|
char orootadm[PATH_MAX];
|
||||||
|
|
||||||
|
(void) sprintf (rootadm, "%s/%s", CVSroot, CVSROOTADM);
|
||||||
|
if (!isdir (rootadm))
|
||||||
|
{
|
||||||
|
(void) sprintf (orootadm, "%s/%s", CVSroot, OCVSROOTADM);
|
||||||
|
if (isdir (orootadm))
|
||||||
|
(void) rename (orootadm, rootadm);
|
||||||
|
}
|
||||||
|
strip_path (CVSroot);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Specifying just the '-H' flag to the sub-command causes a Usage
|
||||||
|
* message to be displayed.
|
||||||
|
*/
|
||||||
|
command_name = cp = argv[0];
|
||||||
|
if (help == TRUE || (argc > 1 && strcmp (argv[1], "-H") == 0))
|
||||||
|
argc = -1;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Check to see if we can write into the history file. If not,
|
||||||
|
* we assume that we can't work in the repository.
|
||||||
|
* BUT, only if the history file exists.
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
char path[PATH_MAX];
|
||||||
|
int save_errno;
|
||||||
|
|
||||||
|
if (!CVSroot || !*CVSroot)
|
||||||
|
error (1, 0, "You don't have a %s environment variable",
|
||||||
|
CVSROOT_ENV);
|
||||||
|
(void) sprintf (path, "%s/%s", CVSroot, CVSROOTADM);
|
||||||
|
if (access (path, R_OK | X_OK))
|
||||||
|
{
|
||||||
|
save_errno = errno;
|
||||||
|
error (0, 0,
|
||||||
|
"Sorry, you don't have sufficient access to %s", CVSroot);
|
||||||
|
error (1, save_errno, "%s", path);
|
||||||
|
}
|
||||||
|
(void) strcat (path, "/");
|
||||||
|
(void) strcat (path, CVSROOTADM_HISTORY);
|
||||||
|
if (isfile (path) && access (path, R_OK | W_OK))
|
||||||
|
{
|
||||||
|
save_errno = errno;
|
||||||
|
error (0, 0,
|
||||||
|
"Sorry, you don't have read/write access to the history file");
|
||||||
|
error (1, save_errno, "%s", path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef PUTENV_MISSING
|
||||||
|
/* Now, see if we should update the environment with the Rcsbin value */
|
||||||
|
if (cvs_update_env)
|
||||||
|
{
|
||||||
|
char *env;
|
||||||
|
|
||||||
|
env = xmalloc (strlen (CVSROOT_ENV) + strlen (CVSroot) + 1 + 1);
|
||||||
|
(void) sprintf (env, "%s=%s", CVSROOT_ENV, CVSroot);
|
||||||
|
(void) putenv (env);
|
||||||
|
/* do not free env, as putenv has control of it */
|
||||||
|
}
|
||||||
|
if (rcsbin_update_env)
|
||||||
|
{
|
||||||
|
char *env;
|
||||||
|
|
||||||
|
env = xmalloc (strlen (RCSBIN_ENV) + strlen (Rcsbin) + 1 + 1);
|
||||||
|
(void) sprintf (env, "%s=%s", RCSBIN_ENV, Rcsbin);
|
||||||
|
(void) putenv (env);
|
||||||
|
/* do not free env, as putenv has control of it */
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If Rcsbin is set to something, make sure it is terminated with
|
||||||
|
* a slash character. If not, add one.
|
||||||
|
*/
|
||||||
|
if (*Rcsbin)
|
||||||
|
{
|
||||||
|
int len = strlen (Rcsbin);
|
||||||
|
char *rcsbin;
|
||||||
|
|
||||||
|
if (Rcsbin[len - 1] != '/')
|
||||||
|
{
|
||||||
|
rcsbin = Rcsbin;
|
||||||
|
Rcsbin = xmalloc (len + 2); /* one for '/', one for NULL */
|
||||||
|
(void) strcpy (Rcsbin, rcsbin);
|
||||||
|
(void) strcat (Rcsbin, "/");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (cm = cmds; cm->fullname; cm++)
|
||||||
|
{
|
||||||
|
if (cm->nick1 && !strcmp (cp, cm->nick1))
|
||||||
|
break;
|
||||||
|
if (cm->nick2 && !strcmp (cp, cm->nick2))
|
||||||
|
break;
|
||||||
|
if (!strcmp (cp, cm->fullname))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!cm->fullname)
|
||||||
|
usage (usg); /* no match */
|
||||||
|
else
|
||||||
|
{
|
||||||
|
command_name = cm->fullname; /* Global pointer for later use */
|
||||||
|
(void) SIG_register (SIGHUP, main_cleanup);
|
||||||
|
(void) SIG_register (SIGINT, main_cleanup);
|
||||||
|
(void) SIG_register (SIGQUIT, main_cleanup);
|
||||||
|
(void) SIG_register (SIGPIPE, main_cleanup);
|
||||||
|
(void) SIG_register (SIGTERM, main_cleanup);
|
||||||
|
|
||||||
|
#ifndef SETVBUF_MISSING
|
||||||
|
/*
|
||||||
|
* Make stdout line buffered, so 'tail -f' can monitor progress.
|
||||||
|
* Patch creates too much output to monitor and it runs slowly.
|
||||||
|
*/
|
||||||
|
if (strcmp (cm->fullname, "patch"))
|
||||||
|
(void) setvbuf (stdout, (char *) NULL, _IOLBF, 0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
err = (*(cm->func)) (argc, argv);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* If the command's error count is modulo 256, we need to change it
|
||||||
|
* so that we don't overflow the 8-bits we get to report exit status
|
||||||
|
*/
|
||||||
|
if (err && (err % 256) == 0)
|
||||||
|
err = 1;
|
||||||
|
Lock_Cleanup ();
|
||||||
|
return (err);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
Make_Date (rawdate)
|
||||||
|
char *rawdate;
|
||||||
|
{
|
||||||
|
struct tm *ftm;
|
||||||
|
time_t unixtime;
|
||||||
|
char date[256]; /* XXX bigger than we'll ever need? */
|
||||||
|
char *ret;
|
||||||
|
|
||||||
|
unixtime = get_date (rawdate, (struct timeb *) NULL);
|
||||||
|
if (unixtime == (time_t) - 1)
|
||||||
|
error (1, 0, "Can't parse date/time: %s", rawdate);
|
||||||
|
#ifdef HAVE_RCS5
|
||||||
|
ftm = gmtime (&unixtime);
|
||||||
|
#else
|
||||||
|
ftm = localtime (&unixtime);
|
||||||
|
#endif
|
||||||
|
(void) sprintf (date, DATEFORM,
|
||||||
|
ftm->tm_year + (ftm->tm_year < 100 ? 0 : 1900),
|
||||||
|
ftm->tm_mon + 1, ftm->tm_mday, ftm->tm_hour,
|
||||||
|
ftm->tm_min, ftm->tm_sec);
|
||||||
|
ret = xstrdup (date);
|
||||||
|
return (ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
usage (cpp)
|
||||||
|
register char **cpp;
|
||||||
|
{
|
||||||
|
(void) fprintf (stderr, *cpp++, program_name, command_name);
|
||||||
|
for (; *cpp; cpp++)
|
||||||
|
(void) fprintf (stderr, *cpp);
|
||||||
|
exit (1);
|
||||||
|
}
|
||||||
@@ -0,0 +1,810 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 1992, Brian Berliner and Jeff Polk
|
||||||
|
* Copyright (c) 1989-1992, Brian Berliner
|
||||||
|
*
|
||||||
|
* You may distribute under the terms of the GNU General Public License
|
||||||
|
* as specified in the README file that comes with the CVS 1.3 kit.
|
||||||
|
*
|
||||||
|
* Modules
|
||||||
|
*
|
||||||
|
* Functions for accessing the modules file.
|
||||||
|
*
|
||||||
|
* The modules file supports basically three formats of lines:
|
||||||
|
* key [options] directory files... [ -x directory [files] ] ...
|
||||||
|
* key [options] directory [ -x directory [files] ] ...
|
||||||
|
* key -a aliases...
|
||||||
|
*
|
||||||
|
* The -a option allows an aliasing step in the parsing of the modules
|
||||||
|
* file. The "aliases" listed on a line following the -a are
|
||||||
|
* processed one-by-one, as if they were specified as arguments on the
|
||||||
|
* command line.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "cvs.h"
|
||||||
|
|
||||||
|
#ifndef lint
|
||||||
|
static char rcsid[] = "@(#)modules.c 1.57 92/04/10";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct sortrec
|
||||||
|
{
|
||||||
|
char *modname;
|
||||||
|
char *status;
|
||||||
|
char *rest;
|
||||||
|
char *comment;
|
||||||
|
};
|
||||||
|
|
||||||
|
#if __STDC__
|
||||||
|
static int sort_order (CONST PTR l, CONST PTR r);
|
||||||
|
static void save_d (char *k, int ks, char *d, int ds);
|
||||||
|
#else
|
||||||
|
static int sort_order ();
|
||||||
|
static void save_d ();
|
||||||
|
#endif /* __STDC__ */
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Open the modules file, and die if the CVSROOT environment variable
|
||||||
|
* was not set. If the modules file does not exist, that's fine, and
|
||||||
|
* a warning message is displayed and a NULL is returned.
|
||||||
|
*/
|
||||||
|
DBM *
|
||||||
|
open_module ()
|
||||||
|
{
|
||||||
|
char mfile[PATH_MAX];
|
||||||
|
|
||||||
|
if (CVSroot == NULL)
|
||||||
|
{
|
||||||
|
(void) fprintf (stderr,
|
||||||
|
"%s: must set the CVSROOT environment variable\n",
|
||||||
|
program_name);
|
||||||
|
error (1, 0, "or specify the '-d' option to %s", program_name);
|
||||||
|
}
|
||||||
|
(void) sprintf (mfile, "%s/%s/%s", CVSroot, CVSROOTADM, CVSROOTADM_MODULES);
|
||||||
|
return (dbm_open (mfile, O_RDONLY, 0666));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Close the modules file, if the open succeeded, that is
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
close_module (db)
|
||||||
|
DBM *db;
|
||||||
|
{
|
||||||
|
if (db != NULL)
|
||||||
|
dbm_close (db);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is the recursive function that processes a module name.
|
||||||
|
* It calls back the passed routine for each directory of a module
|
||||||
|
* It runs the post checkout or post tag proc from the modules file
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
do_module (db, mname, m_type, msg, callback_proc, where,
|
||||||
|
shorten, local_specified, run_module_prog, extra_arg)
|
||||||
|
DBM *db;
|
||||||
|
char *mname;
|
||||||
|
enum mtype m_type;
|
||||||
|
char *msg;
|
||||||
|
int (*callback_proc) ();
|
||||||
|
char *where;
|
||||||
|
int shorten;
|
||||||
|
int local_specified;
|
||||||
|
int run_module_prog;
|
||||||
|
char *extra_arg;
|
||||||
|
{
|
||||||
|
char *checkin_prog = NULL;
|
||||||
|
char *checkout_prog = NULL;
|
||||||
|
char *tag_prog = NULL;
|
||||||
|
char *update_prog = NULL;
|
||||||
|
char cwd[PATH_MAX];
|
||||||
|
char line[MAXLINELEN];
|
||||||
|
char *xmodargv[MAXFILEPERDIR];
|
||||||
|
char **modargv;
|
||||||
|
char *value;
|
||||||
|
char *zvalue;
|
||||||
|
char *mwhere = NULL;
|
||||||
|
char *mfile = NULL;
|
||||||
|
char *spec_opt = NULL;
|
||||||
|
char xvalue[PATH_MAX];
|
||||||
|
int modargc, alias = 0;
|
||||||
|
datum key, val;
|
||||||
|
char *cp;
|
||||||
|
int c, err = 0;
|
||||||
|
|
||||||
|
/* remember where we start */
|
||||||
|
if (getwd (cwd) == NULL)
|
||||||
|
error (1, 0, "cannot get current working directory: %s", cwd);
|
||||||
|
|
||||||
|
/* strip extra stuff from the module name */
|
||||||
|
strip_path (mname);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Look up the module using the following scheme:
|
||||||
|
* 1) look for mname as a module name
|
||||||
|
* 2) look for mname as a directory
|
||||||
|
* 3) look for mname as a file
|
||||||
|
* 4) take mname up to the first slash and look it up as a module name
|
||||||
|
* (this is for checking out only part of a module)
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* look it up as a module name */
|
||||||
|
key.dptr = mname;
|
||||||
|
key.dsize = strlen (key.dptr);
|
||||||
|
if (db != NULL)
|
||||||
|
val = dbm_fetch (db, key);
|
||||||
|
else
|
||||||
|
val.dptr = NULL;
|
||||||
|
if (val.dptr != NULL)
|
||||||
|
{
|
||||||
|
/* null terminate the value XXX - is this space ours? */
|
||||||
|
val.dptr[val.dsize] = '\0';
|
||||||
|
|
||||||
|
/* If the line ends in a comment, strip it off */
|
||||||
|
if ((cp = index (val.dptr, '#')) != NULL)
|
||||||
|
{
|
||||||
|
do
|
||||||
|
*cp-- = '\0';
|
||||||
|
while (isspace (*cp));
|
||||||
|
}
|
||||||
|
value = val.dptr;
|
||||||
|
mwhere = xstrdup (mname);
|
||||||
|
goto found;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
char file[PATH_MAX];
|
||||||
|
char attic_file[PATH_MAX];
|
||||||
|
char *acp;
|
||||||
|
|
||||||
|
/* check to see if mname is a directory or file */
|
||||||
|
|
||||||
|
(void) sprintf (file, "%s/%s", CVSroot, mname);
|
||||||
|
if ((acp = rindex (mname, '/')) != NULL)
|
||||||
|
{
|
||||||
|
*acp = '\0';
|
||||||
|
(void) sprintf (attic_file, "%s/%s/%s/%s%s", CVSroot, mname,
|
||||||
|
CVSATTIC, acp + 1, RCSEXT);
|
||||||
|
*acp = '/';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
(void) sprintf (attic_file, "%s/%s/%s%s", CVSroot, CVSATTIC,
|
||||||
|
mname, RCSEXT);
|
||||||
|
|
||||||
|
if (isdir (file))
|
||||||
|
{
|
||||||
|
value = mname;
|
||||||
|
goto found;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
(void) strcat (file, RCSEXT);
|
||||||
|
if (isfile (file) || isfile (attic_file))
|
||||||
|
{
|
||||||
|
/* if mname was a file, we have to split it into "dir file" */
|
||||||
|
if ((cp = rindex (mname, '/')) != NULL && cp != mname)
|
||||||
|
{
|
||||||
|
char *slashp;
|
||||||
|
|
||||||
|
/* put the ' ' in a copy so we don't mess up the original */
|
||||||
|
value = strcpy (xvalue, mname);
|
||||||
|
slashp = rindex (value, '/');
|
||||||
|
*slashp = ' ';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* the only '/' at the beginning or no '/' at all
|
||||||
|
* means the file we are interested in is in CVSROOT
|
||||||
|
* itself so the directory should be '.'
|
||||||
|
*/
|
||||||
|
if (cp == mname)
|
||||||
|
{
|
||||||
|
/* drop the leading / if specified */
|
||||||
|
value = strcpy (xvalue, ". ");
|
||||||
|
(void) strcat (xvalue, mname + 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* otherwise just copy it */
|
||||||
|
value = strcpy (xvalue, ". ");
|
||||||
|
(void) strcat (xvalue, mname);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
goto found;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* look up everything to the first / as a module */
|
||||||
|
if (mname[0] != '/' && (cp = index (mname, '/')) != NULL)
|
||||||
|
{
|
||||||
|
/* Make the slash the new end of the string temporarily */
|
||||||
|
*cp = '\0';
|
||||||
|
key.dptr = mname;
|
||||||
|
key.dsize = strlen (key.dptr);
|
||||||
|
|
||||||
|
/* do the lookup */
|
||||||
|
if (db != NULL)
|
||||||
|
val = dbm_fetch (db, key);
|
||||||
|
else
|
||||||
|
val.dptr = NULL;
|
||||||
|
|
||||||
|
/* if we found it, clean up the value and life is good */
|
||||||
|
if (val.dptr != NULL)
|
||||||
|
{
|
||||||
|
char *cp2;
|
||||||
|
|
||||||
|
/* null terminate the value XXX - is this space ours? */
|
||||||
|
val.dptr[val.dsize] = '\0';
|
||||||
|
|
||||||
|
/* If the line ends in a comment, strip it off */
|
||||||
|
if ((cp2 = index (val.dptr, '#')) != NULL)
|
||||||
|
{
|
||||||
|
do
|
||||||
|
*cp2-- = '\0';
|
||||||
|
while (isspace (*cp2));
|
||||||
|
}
|
||||||
|
value = val.dptr;
|
||||||
|
|
||||||
|
/* mwhere gets just the module name */
|
||||||
|
mwhere = xstrdup (mname);
|
||||||
|
mfile = cp + 1;
|
||||||
|
|
||||||
|
/* put the / back in mname */
|
||||||
|
*cp = '/';
|
||||||
|
|
||||||
|
goto found;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* put the / back in mname */
|
||||||
|
*cp = '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if we got here, we couldn't find it using our search, so give up */
|
||||||
|
error (0, 0, "cannot find module `%s' - ignored", mname);
|
||||||
|
err++;
|
||||||
|
if (mwhere)
|
||||||
|
free (mwhere);
|
||||||
|
return (err);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* At this point, we found what we were looking for in one
|
||||||
|
* of the many different forms.
|
||||||
|
*/
|
||||||
|
found:
|
||||||
|
|
||||||
|
/* copy value to our own string since if we go recursive we'll be
|
||||||
|
really screwed if we do another dbm lookup */
|
||||||
|
zvalue = xstrdup (value);
|
||||||
|
value = zvalue;
|
||||||
|
|
||||||
|
/* search the value for the special delimiter and save for later */
|
||||||
|
if ((cp = index (value, CVSMODULE_SPEC)) != NULL)
|
||||||
|
{
|
||||||
|
*cp = '\0'; /* null out the special char */
|
||||||
|
spec_opt = cp + 1; /* save the options for later */
|
||||||
|
|
||||||
|
if (cp != value) /* strip whitespace if necessary */
|
||||||
|
while (isspace (*--cp))
|
||||||
|
*cp = '\0';
|
||||||
|
|
||||||
|
if (cp == value)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* we had nothing but special options, so skip arg
|
||||||
|
* parsing and regular stuff entirely
|
||||||
|
*
|
||||||
|
* If there were only special ones though, we must
|
||||||
|
* make the appropriate directory and cd to it
|
||||||
|
*/
|
||||||
|
char *dir;
|
||||||
|
|
||||||
|
/* XXX - XXX - MAJOR HACK - DO NOT SHIP - this needs to
|
||||||
|
be !pipeout, but we don't know that here yet */
|
||||||
|
if (!run_module_prog)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
dir = where ? where : mname;
|
||||||
|
/* XXX - think about making null repositories at each dir here
|
||||||
|
instead of just at the bottom */
|
||||||
|
make_directories (dir);
|
||||||
|
if (chdir (dir) < 0)
|
||||||
|
{
|
||||||
|
error (0, errno, "cannot chdir to %s", dir);
|
||||||
|
spec_opt = NULL;
|
||||||
|
err++;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (!isfile (CVSADM) && !isfile (OCVSADM))
|
||||||
|
{
|
||||||
|
char nullrepos[PATH_MAX];
|
||||||
|
|
||||||
|
(void) sprintf (nullrepos, "%s/%s/%s", CVSroot,
|
||||||
|
CVSROOTADM, CVSNULLREPOS);
|
||||||
|
if (!isfile (nullrepos))
|
||||||
|
(void) mkdir (nullrepos, 0777);
|
||||||
|
Create_Admin (".", nullrepos, (char *) NULL, (char *) NULL);
|
||||||
|
if (!noexec)
|
||||||
|
{
|
||||||
|
FILE *fp;
|
||||||
|
|
||||||
|
fp = open_file (CVSADM_ENTSTAT, "w+");
|
||||||
|
if (fclose (fp) == EOF)
|
||||||
|
error (1, errno, "cannot close %s", CVSADM_ENTSTAT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out:
|
||||||
|
goto do_special;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* don't do special options only part of a module was specified */
|
||||||
|
if (mfile != NULL)
|
||||||
|
spec_opt = NULL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* value now contains one of the following:
|
||||||
|
* 1) dir
|
||||||
|
* 2) dir file
|
||||||
|
* 3) the value from modules without any special args
|
||||||
|
* [ args ] dir [file] [file] ...
|
||||||
|
* or -a module [ module ] ...
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Put the value on a line with XXX prepended for getopt to eat */
|
||||||
|
(void) sprintf (line, "%s %s", "XXX", value);
|
||||||
|
|
||||||
|
/* turn the line into an argv[] array */
|
||||||
|
line2argv (&modargc, xmodargv, line);
|
||||||
|
modargv = xmodargv;
|
||||||
|
|
||||||
|
/* parse the args */
|
||||||
|
optind = 1;
|
||||||
|
while ((c = gnu_getopt (modargc, modargv, CVSMODULE_OPTS)) != -1)
|
||||||
|
{
|
||||||
|
switch (c)
|
||||||
|
{
|
||||||
|
case 'a':
|
||||||
|
alias = 1;
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
if (mwhere)
|
||||||
|
free (mwhere);
|
||||||
|
mwhere = xstrdup (optarg);
|
||||||
|
break;
|
||||||
|
case 'i':
|
||||||
|
checkin_prog = optarg;
|
||||||
|
break;
|
||||||
|
case 'l':
|
||||||
|
local_specified = 1;
|
||||||
|
case 'o':
|
||||||
|
checkout_prog = optarg;
|
||||||
|
break;
|
||||||
|
case 't':
|
||||||
|
tag_prog = optarg;
|
||||||
|
break;
|
||||||
|
case 'u':
|
||||||
|
update_prog = optarg;
|
||||||
|
break;
|
||||||
|
case '?':
|
||||||
|
error (0, 0,
|
||||||
|
"modules file has invalid option for key %s value %s",
|
||||||
|
key.dptr, val.dptr);
|
||||||
|
err++;
|
||||||
|
if (mwhere)
|
||||||
|
free (mwhere);
|
||||||
|
free (zvalue);
|
||||||
|
return (err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
modargc -= optind;
|
||||||
|
modargv += optind;
|
||||||
|
if (modargc == 0)
|
||||||
|
{
|
||||||
|
error (0, 0, "modules file missing directory for module %s", mname);
|
||||||
|
if (mwhere)
|
||||||
|
free (mwhere);
|
||||||
|
free (zvalue);
|
||||||
|
return (++err);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if this was an alias, call ourselves recursively for each module */
|
||||||
|
if (alias)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < modargc; i++)
|
||||||
|
err += do_module (db, modargv[i], m_type, msg, callback_proc,
|
||||||
|
where, shorten, local_specified,
|
||||||
|
run_module_prog, extra_arg);
|
||||||
|
if (mwhere)
|
||||||
|
free (mwhere);
|
||||||
|
free (zvalue);
|
||||||
|
return (err);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* otherwise, process this module */
|
||||||
|
err += callback_proc (&modargc, modargv, where, mwhere, mfile, shorten,
|
||||||
|
local_specified, mname, msg);
|
||||||
|
|
||||||
|
/* clean up */
|
||||||
|
free_names (&modargc, modargv);
|
||||||
|
|
||||||
|
/* if there were special include args, process them now */
|
||||||
|
|
||||||
|
do_special:
|
||||||
|
|
||||||
|
/* blow off special options if -l was specified */
|
||||||
|
if (local_specified)
|
||||||
|
spec_opt = NULL;
|
||||||
|
|
||||||
|
while (spec_opt != NULL)
|
||||||
|
{
|
||||||
|
char *next_opt;
|
||||||
|
|
||||||
|
cp = index (spec_opt, CVSMODULE_SPEC);
|
||||||
|
if (cp != NULL)
|
||||||
|
{
|
||||||
|
/* save the beginning of the next arg */
|
||||||
|
next_opt = cp + 1;
|
||||||
|
|
||||||
|
/* strip whitespace off the end */
|
||||||
|
do
|
||||||
|
*cp = '\0';
|
||||||
|
while (isspace (*--cp));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
next_opt = NULL;
|
||||||
|
|
||||||
|
/* strip whitespace from front */
|
||||||
|
while (isspace (*spec_opt))
|
||||||
|
spec_opt++;
|
||||||
|
|
||||||
|
if (*spec_opt == '\0')
|
||||||
|
error (0, 0, "Mal-formed %c option for module %s - ignored",
|
||||||
|
CVSMODULE_SPEC, mname);
|
||||||
|
else
|
||||||
|
err += do_module (db, spec_opt, m_type, msg, callback_proc,
|
||||||
|
(char *) NULL, 0, local_specified,
|
||||||
|
run_module_prog, extra_arg);
|
||||||
|
spec_opt = next_opt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* write out the checkin/update prog files if necessary */
|
||||||
|
if (err == 0 && !noexec && m_type == CHECKOUT && run_module_prog)
|
||||||
|
{
|
||||||
|
FILE *fp;
|
||||||
|
|
||||||
|
if (checkin_prog != NULL)
|
||||||
|
{
|
||||||
|
fp = open_file (CVSADM_CIPROG, "w+");
|
||||||
|
(void) fprintf (fp, "%s\n", checkin_prog);
|
||||||
|
if (fclose (fp) == EOF)
|
||||||
|
error (1, errno, "cannot close %s", CVSADM_CIPROG);
|
||||||
|
}
|
||||||
|
if (update_prog != NULL)
|
||||||
|
{
|
||||||
|
fp = open_file (CVSADM_UPROG, "w+");
|
||||||
|
(void) fprintf (fp, "%s\n", update_prog);
|
||||||
|
if (fclose (fp) == EOF)
|
||||||
|
error (1, errno, "cannot close %s", CVSADM_UPROG);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* cd back to where we started */
|
||||||
|
if (chdir (cwd) < 0)
|
||||||
|
error (1, errno, "failed chdir to %s!", cwd);
|
||||||
|
|
||||||
|
/* run checkout or tag prog if appropriate */
|
||||||
|
if (err == 0 && run_module_prog)
|
||||||
|
{
|
||||||
|
if ((m_type == TAG && tag_prog != NULL) ||
|
||||||
|
(m_type == CHECKOUT && checkout_prog != NULL))
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* If a relative pathname is specified as the checkout or
|
||||||
|
* tag proc, try to tack on the current "where" value.
|
||||||
|
* if we can't find a matching program, just punt and use
|
||||||
|
* whatever is specified in the modules file.
|
||||||
|
*/
|
||||||
|
char real_prog[PATH_MAX];
|
||||||
|
char *prog = (m_type == TAG ? tag_prog : checkout_prog);
|
||||||
|
char *real_where = (where != NULL ? where : mwhere);
|
||||||
|
|
||||||
|
if ((*prog != '/') && (*prog != '.'))
|
||||||
|
{
|
||||||
|
(void) sprintf (real_prog, "%s/%s", real_where, prog);
|
||||||
|
if (isfile (real_prog))
|
||||||
|
prog = real_prog;
|
||||||
|
}
|
||||||
|
|
||||||
|
run_setup ("%s %s", prog, real_where);
|
||||||
|
if (extra_arg)
|
||||||
|
run_arg (extra_arg);
|
||||||
|
|
||||||
|
if (!quiet)
|
||||||
|
{
|
||||||
|
(void) printf ("%s %s: Executing '", program_name,
|
||||||
|
command_name);
|
||||||
|
run_print (stdout);
|
||||||
|
(void) printf ("'\n");
|
||||||
|
}
|
||||||
|
err += run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* clean up */
|
||||||
|
if (mwhere)
|
||||||
|
free (mwhere);
|
||||||
|
free (zvalue);
|
||||||
|
|
||||||
|
return (err);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* - Read all the records from the modules database into an array.
|
||||||
|
- Sort the array depending on what format is desired.
|
||||||
|
- Print the array in the format desired.
|
||||||
|
|
||||||
|
Currently, there are only two "desires":
|
||||||
|
|
||||||
|
1. Sort by module name and format the whole entry including switches,
|
||||||
|
files and the comment field: (Including aliases)
|
||||||
|
|
||||||
|
modulename -s switches, one per line, even if
|
||||||
|
-i it has many switches.
|
||||||
|
Directories and files involved, formatted
|
||||||
|
to cover multiple lines if necessary.
|
||||||
|
# Comment, also formatted to cover multiple
|
||||||
|
# lines if necessary.
|
||||||
|
|
||||||
|
2. Sort by status field string and print: (*not* including aliases)
|
||||||
|
|
||||||
|
modulename STATUS Directories and files involved, formatted
|
||||||
|
to cover multiple lines if necessary.
|
||||||
|
# Comment, also formatted to cover multiple
|
||||||
|
# lines if necessary.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static struct sortrec *s_head;
|
||||||
|
|
||||||
|
static int s_max = 0; /* Number of elements allocated */
|
||||||
|
static int s_count = 0; /* Number of elements used */
|
||||||
|
|
||||||
|
static int Status;
|
||||||
|
static char def_status[] = "NONE";
|
||||||
|
|
||||||
|
/* Sort routine for qsort:
|
||||||
|
- If we want the "Status" field to be sorted, check it first.
|
||||||
|
- Then compare the "module name" fields. Since they are unique, we don't
|
||||||
|
have to look further.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
sort_order (l, r)
|
||||||
|
CONST PTR l;
|
||||||
|
CONST PTR r;
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
CONST struct sortrec *left = (CONST struct sortrec *) l;
|
||||||
|
CONST struct sortrec *right = (CONST struct sortrec *) r;
|
||||||
|
|
||||||
|
if (Status)
|
||||||
|
{
|
||||||
|
/* If Sort by status field, compare them. */
|
||||||
|
if ((i = strcmp (left->status, right->status)) != 0)
|
||||||
|
return (i);
|
||||||
|
}
|
||||||
|
return (strcmp (left->modname, right->modname));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
save_d (k, ks, d, ds)
|
||||||
|
char *k;
|
||||||
|
int ks;
|
||||||
|
char *d;
|
||||||
|
int ds;
|
||||||
|
{
|
||||||
|
char *cp, *cp2;
|
||||||
|
struct sortrec *s_rec;
|
||||||
|
|
||||||
|
if (Status && *d == '-' && *(d + 1) == 'a')
|
||||||
|
return; /* We want "cvs co -s" and it is an alias! */
|
||||||
|
|
||||||
|
if (s_count == s_max)
|
||||||
|
{
|
||||||
|
s_max += 64;
|
||||||
|
s_head = (struct sortrec *) xrealloc ((char *) s_head, s_max * sizeof (*s_head));
|
||||||
|
}
|
||||||
|
s_rec = &s_head[s_count];
|
||||||
|
s_rec->modname = cp = xmalloc (ks + 1);
|
||||||
|
(void) strncpy (cp, k, ks);
|
||||||
|
*(cp + ks) = '\0';
|
||||||
|
|
||||||
|
s_rec->rest = cp2 = xmalloc (ds + 1);
|
||||||
|
cp = d;
|
||||||
|
*(cp + ds) = '\0'; /* Assumes an extra byte at end of static dbm buffer */
|
||||||
|
|
||||||
|
while (isspace (*cp))
|
||||||
|
cp++;
|
||||||
|
/* Turn <spaces> into one ' ' -- makes the rest of this routine simpler */
|
||||||
|
while (*cp)
|
||||||
|
{
|
||||||
|
if (isspace (*cp))
|
||||||
|
{
|
||||||
|
*cp2++ = ' ';
|
||||||
|
while (isspace (*cp))
|
||||||
|
cp++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
*cp2++ = *cp++;
|
||||||
|
}
|
||||||
|
*cp2 = '\0';
|
||||||
|
|
||||||
|
/* Look for the "-s statusvalue" text */
|
||||||
|
if (Status)
|
||||||
|
{
|
||||||
|
s_rec->status = def_status;
|
||||||
|
|
||||||
|
/* Minor kluge, but general enough to maintain */
|
||||||
|
for (cp = s_rec->rest; (cp2 = index (cp, '-')) != NULL; cp = ++cp2)
|
||||||
|
{
|
||||||
|
if (*(cp2 + 1) == 's' && *(cp2 + 2) == ' ')
|
||||||
|
{
|
||||||
|
s_rec->status = (cp2 += 3);
|
||||||
|
while (*cp2 != ' ')
|
||||||
|
cp2++;
|
||||||
|
*cp2++ = '\0';
|
||||||
|
cp = cp2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
cp = s_rec->rest;
|
||||||
|
|
||||||
|
/* Find comment field, clean up on all three sides & compress blanks */
|
||||||
|
if ((cp2 = cp = index (cp, '#')) != NULL)
|
||||||
|
{
|
||||||
|
if (*--cp2 == ' ')
|
||||||
|
*cp2 = '\0';
|
||||||
|
if (*++cp == ' ')
|
||||||
|
cp++;
|
||||||
|
s_rec->comment = cp;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
s_rec->comment = "";
|
||||||
|
|
||||||
|
s_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
cat_module (status)
|
||||||
|
int status;
|
||||||
|
{
|
||||||
|
DBM *db;
|
||||||
|
datum key, val;
|
||||||
|
int i, c, wid, argc, cols = 80, indent, fill;
|
||||||
|
int moduleargc;
|
||||||
|
struct sortrec *s_h;
|
||||||
|
char *cp, *cp2, **argv;
|
||||||
|
char line[MAXLINELEN], *moduleargv[MAXFILEPERDIR];
|
||||||
|
|
||||||
|
#ifdef sun
|
||||||
|
#ifdef TIOCGSIZE
|
||||||
|
struct ttysize ts;
|
||||||
|
|
||||||
|
(void) ioctl (0, TIOCGSIZE, &ts);
|
||||||
|
cols = ts.ts_cols;
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#ifdef TIOCGWINSZ
|
||||||
|
struct winsize ws;
|
||||||
|
|
||||||
|
(void) ioctl (0, TIOCGWINSZ, &ws);
|
||||||
|
cols = ws.ws_col;
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
Status = status;
|
||||||
|
|
||||||
|
/* Read the whole modules file into allocated records */
|
||||||
|
if (!(db = open_module ()))
|
||||||
|
error (1, 0, "failed to open the modules file");
|
||||||
|
|
||||||
|
for (key = dbm_firstkey (db); key.dptr != NULL; key = dbm_nextkey (db))
|
||||||
|
{
|
||||||
|
val = dbm_fetch (db, key);
|
||||||
|
if (val.dptr != NULL)
|
||||||
|
save_d (key.dptr, key.dsize, val.dptr, val.dsize);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sort the list as requested */
|
||||||
|
qsort ((PTR) s_head, s_count, sizeof (struct sortrec), sort_order);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Run through the sorted array and format the entries
|
||||||
|
* indent = space for modulename + space for status field
|
||||||
|
*/
|
||||||
|
indent = 12 + (status * 12);
|
||||||
|
fill = cols - (indent + 2);
|
||||||
|
for (s_h = s_head, i = 0; i < s_count; i++, s_h++)
|
||||||
|
{
|
||||||
|
/* Print module name (and status, if wanted) */
|
||||||
|
(void) printf ("%-12s", s_h->modname);
|
||||||
|
if (status)
|
||||||
|
{
|
||||||
|
(void) printf (" %-11s", s_h->status);
|
||||||
|
if (s_h->status != def_status)
|
||||||
|
*(s_h->status + strlen (s_h->status)) = ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parse module file entry as command line and print options */
|
||||||
|
(void) sprintf (line, "%s %s", s_h->modname, s_h->rest);
|
||||||
|
line2argv (&moduleargc, moduleargv, line);
|
||||||
|
argc = moduleargc;
|
||||||
|
argv = moduleargv;
|
||||||
|
|
||||||
|
optind = 1;
|
||||||
|
wid = 0;
|
||||||
|
while ((c = gnu_getopt (argc, argv, CVSMODULE_OPTS)) != -1)
|
||||||
|
{
|
||||||
|
if (!status)
|
||||||
|
{
|
||||||
|
if (c == 'a')
|
||||||
|
{
|
||||||
|
(void) printf (" -a");
|
||||||
|
wid += 3; /* Could just set it to 3 */
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (strlen (optarg) + 4 + wid > (unsigned) fill)
|
||||||
|
{
|
||||||
|
(void) printf ("\n%*s", indent, "");
|
||||||
|
wid = 0;
|
||||||
|
}
|
||||||
|
(void) printf (" -%c %s", c, optarg);
|
||||||
|
wid += strlen (optarg) + 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
argc -= optind;
|
||||||
|
argv += optind;
|
||||||
|
|
||||||
|
/* Format and Print all the files and directories */
|
||||||
|
for (; argc--; argv++)
|
||||||
|
{
|
||||||
|
if (strlen (*argv) + wid > (unsigned) fill)
|
||||||
|
{
|
||||||
|
(void) printf ("\n%*s", indent, "");
|
||||||
|
wid = 0;
|
||||||
|
}
|
||||||
|
(void) printf (" %s", *argv);
|
||||||
|
wid += strlen (*argv) + 1;
|
||||||
|
}
|
||||||
|
(void) printf ("\n");
|
||||||
|
|
||||||
|
/* Format the comment field -- save_d (), compressed spaces */
|
||||||
|
for (cp2 = cp = s_h->comment; *cp; cp2 = cp)
|
||||||
|
{
|
||||||
|
(void) printf ("%*s # ", indent, "");
|
||||||
|
if (strlen (cp2) < (unsigned) (fill - 2))
|
||||||
|
{
|
||||||
|
(void) printf ("%s\n", cp2);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
cp += fill - 2;
|
||||||
|
while (*cp != ' ' && cp > cp2)
|
||||||
|
cp--;
|
||||||
|
if (cp == cp2)
|
||||||
|
{
|
||||||
|
(void) printf ("%s\n", cp2);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
*cp++ = '\0';
|
||||||
|
(void) printf ("%s\n", cp2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 1992, Brian Berliner and Jeff Polk
|
||||||
|
* Copyright (c) 1989-1992, Brian Berliner
|
||||||
|
*
|
||||||
|
* You may distribute under the terms of the GNU General Public License as
|
||||||
|
* specified in the README file that comes with the CVS 1.3 kit.
|
||||||
|
*
|
||||||
|
* No Difference
|
||||||
|
*
|
||||||
|
* The user file looks modified judging from its time stamp; however it needn't
|
||||||
|
* be. No_difference() finds out whether it is or not. If it is not, it
|
||||||
|
* updates the administration.
|
||||||
|
*
|
||||||
|
* returns 0 if no differences are found and non-zero otherwise
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "cvs.h"
|
||||||
|
|
||||||
|
#ifndef lint
|
||||||
|
static char rcsid[] = "@(#)no_diff.c 1.35 92/03/31";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int
|
||||||
|
No_Difference (file, vers, entries)
|
||||||
|
char *file;
|
||||||
|
Vers_TS *vers;
|
||||||
|
List *entries;
|
||||||
|
{
|
||||||
|
Node *p;
|
||||||
|
char tmp[L_tmpnam+1];
|
||||||
|
int ret;
|
||||||
|
char *ts, *options;
|
||||||
|
int retcode = 0;
|
||||||
|
|
||||||
|
if (!vers->srcfile || !vers->srcfile->path)
|
||||||
|
return (-1); /* different since we couldn't tell */
|
||||||
|
|
||||||
|
if (vers->entdata && vers->entdata->options)
|
||||||
|
options = xstrdup (vers->entdata->options);
|
||||||
|
else
|
||||||
|
options = xstrdup ("");
|
||||||
|
|
||||||
|
run_setup ("%s%s -p -q -r%s %s", Rcsbin, RCS_CO,
|
||||||
|
vers->vn_user ? vers->vn_user : "", options);
|
||||||
|
run_arg (vers->srcfile->path);
|
||||||
|
if ((retcode = run_exec (RUN_TTY, tmpnam (tmp), RUN_TTY, RUN_REALLY)) == 0)
|
||||||
|
{
|
||||||
|
if (!iswritable (file)) /* fix the modes as a side effect */
|
||||||
|
xchmod (file, 1);
|
||||||
|
|
||||||
|
/* do the byte by byte compare */
|
||||||
|
if (xcmp (file, tmp) == 0)
|
||||||
|
{
|
||||||
|
if (cvswrite == FALSE) /* fix the modes as a side effect */
|
||||||
|
xchmod (file, 0);
|
||||||
|
|
||||||
|
/* no difference was found, so fix the entries file */
|
||||||
|
ts = time_stamp (file);
|
||||||
|
Register (entries, file,
|
||||||
|
vers->vn_user ? vers->vn_user : vers->vn_rcs, ts,
|
||||||
|
options, vers->tag, vers->date);
|
||||||
|
free (ts);
|
||||||
|
|
||||||
|
/* update the entdata pointer in the vers_ts structure */
|
||||||
|
p = findnode (entries, file);
|
||||||
|
vers->entdata = (Entnode *) p->data;
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ret = 1; /* files were really different */
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
error (0, retcode == -1 ? errno : 0,
|
||||||
|
"could not check out revision %s of %s", vers->vn_user, file);
|
||||||
|
ret = -1; /* different since we couldn't tell */
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trace)
|
||||||
|
(void) fprintf (stderr, "-> unlink(%s)\n", tmp);
|
||||||
|
(void) unlink (tmp);
|
||||||
|
free (options);
|
||||||
|
return (ret);
|
||||||
|
}
|
||||||
@@ -0,0 +1,147 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 1992, Brian Berliner and Jeff Polk
|
||||||
|
* Copyright (c) 1989-1992, Brian Berliner
|
||||||
|
*
|
||||||
|
* You may distribute under the terms of the GNU General Public License as
|
||||||
|
* specified in the README file that comes with the CVS 1.3 kit.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "cvs.h"
|
||||||
|
|
||||||
|
#ifndef lint
|
||||||
|
static char rcsid[] = "@(#)parseinfo.c 1.16 92/04/10";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parse the INFOFILE file for the specified REPOSITORY. Invoke CALLPROC for
|
||||||
|
* each line in the file that matches the REPOSITORY.
|
||||||
|
* Return 0 for success, -1 if there was not an INFOFILE, and >0 for failure.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
Parse_Info (infofile, repository, callproc, all)
|
||||||
|
char *infofile;
|
||||||
|
char *repository;
|
||||||
|
int (*callproc) ();
|
||||||
|
int all;
|
||||||
|
{
|
||||||
|
int err = 0;
|
||||||
|
FILE *fp_info;
|
||||||
|
char infopath[PATH_MAX];
|
||||||
|
char line[MAXLINELEN];
|
||||||
|
char *default_value = NULL;
|
||||||
|
int callback_done, line_number;
|
||||||
|
char *cp, *exp, *value, *srepos;
|
||||||
|
CONST char *regex_err;
|
||||||
|
|
||||||
|
if (CVSroot == NULL)
|
||||||
|
{
|
||||||
|
/* XXX - should be error maybe? */
|
||||||
|
error (0, 0, "CVSROOT variable not set");
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* find the info file and open it */
|
||||||
|
(void) sprintf (infopath, "%s/%s/%s", CVSroot,
|
||||||
|
CVSROOTADM, infofile);
|
||||||
|
if ((fp_info = fopen (infopath, "r")) == NULL)
|
||||||
|
return (0); /* no file -> nothing special done */
|
||||||
|
|
||||||
|
/* strip off the CVSROOT if repository was absolute */
|
||||||
|
srepos = Short_Repository (repository);
|
||||||
|
|
||||||
|
/* search the info file for lines that match */
|
||||||
|
callback_done = line_number = 0;
|
||||||
|
while (fgets (line, sizeof (line), fp_info) != NULL)
|
||||||
|
{
|
||||||
|
line_number++;
|
||||||
|
|
||||||
|
/* skip lines starting with # */
|
||||||
|
if (line[0] == '#')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* skip whitespace at beginning of line */
|
||||||
|
for (cp = line; *cp && isspace (*cp); cp++)
|
||||||
|
;
|
||||||
|
|
||||||
|
/* if *cp is null, the whole line was blank */
|
||||||
|
if (*cp == '\0')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* the regular expression is everything up to the first space */
|
||||||
|
for (exp = cp; *cp && !isspace (*cp); cp++)
|
||||||
|
;
|
||||||
|
if (*cp != '\0')
|
||||||
|
*cp++ = '\0';
|
||||||
|
|
||||||
|
/* skip whitespace up to the start of the matching value */
|
||||||
|
while (*cp && isspace (*cp))
|
||||||
|
cp++;
|
||||||
|
|
||||||
|
/* no value to match with the regular expression is an error */
|
||||||
|
if (*cp == '\0')
|
||||||
|
{
|
||||||
|
error (0, 0, "syntax error at line %d file %s; ignored",
|
||||||
|
line_number, infofile);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
value = cp;
|
||||||
|
|
||||||
|
/* strip the newline off the end of the value */
|
||||||
|
if ((cp = rindex (value, '\n')) != NULL)
|
||||||
|
*cp = '\0';
|
||||||
|
|
||||||
|
/*
|
||||||
|
* At this point, exp points to the regular expression, and value
|
||||||
|
* points to the value to call the callback routine with. Evaluate
|
||||||
|
* the regular expression against srepos and callback with the value
|
||||||
|
* if it matches.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* save the default value so we have it later if we need it */
|
||||||
|
if (strcmp (exp, "DEFAULT") == 0)
|
||||||
|
{
|
||||||
|
default_value = xstrdup (value);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For a regular expression of "ALL", do the callback always We may
|
||||||
|
* execute lots of ALL callbacks in addition to one regular matching
|
||||||
|
* callback or default
|
||||||
|
*/
|
||||||
|
if (strcmp (exp, "ALL") == 0)
|
||||||
|
{
|
||||||
|
if (all)
|
||||||
|
err += callproc (repository, value);
|
||||||
|
else
|
||||||
|
error(0, 0, "Keyword `ALL' is ignored at line %d in %s file",
|
||||||
|
line_number, infofile);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* see if the repository matched this regular expression */
|
||||||
|
if ((regex_err = re_comp (exp)) != NULL)
|
||||||
|
{
|
||||||
|
error (0, 0, "bad regular expression at line %d file %s: %s",
|
||||||
|
line_number, infofile, regex_err);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (re_exec (srepos) == 0)
|
||||||
|
continue; /* no match */
|
||||||
|
|
||||||
|
/* it did, so do the callback and note that we did one */
|
||||||
|
err += callproc (repository, value);
|
||||||
|
callback_done = 1;
|
||||||
|
}
|
||||||
|
(void) fclose (fp_info);
|
||||||
|
|
||||||
|
/* if we fell through and didn't callback at all, do the default */
|
||||||
|
if (callback_done == 0 && default_value != NULL)
|
||||||
|
err += callproc (repository, default_value);
|
||||||
|
|
||||||
|
/* free up space if necessary */
|
||||||
|
if (default_value != NULL)
|
||||||
|
free (default_value);
|
||||||
|
|
||||||
|
return (err);
|
||||||
|
}
|
||||||
@@ -0,0 +1,523 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 1992, Brian Berliner and Jeff Polk
|
||||||
|
* Copyright (c) 1989-1992, Brian Berliner
|
||||||
|
*
|
||||||
|
* You may distribute under the terms of the GNU General Public License as
|
||||||
|
* specified in the README file that comes with the CVS 1.3 kit.
|
||||||
|
*
|
||||||
|
* Patch
|
||||||
|
*
|
||||||
|
* Create a Larry Wall format "patch" file between a previous release and the
|
||||||
|
* current head of a module, or between two releases. Can specify the
|
||||||
|
* release as either a date or a revision number.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "cvs.h"
|
||||||
|
|
||||||
|
#ifndef lint
|
||||||
|
static char rcsid[] = "@(#)patch.c 1.50 92/04/10";
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __STDC__
|
||||||
|
static SIGTYPE patch_cleanup (void);
|
||||||
|
static Dtype patch_dirproc (char *dir, char *repos, char *update_dir);
|
||||||
|
static int patch_fileproc (char *file, char *update_dir, char *repository,
|
||||||
|
List * entries, List * srcfiles);
|
||||||
|
static int patch_proc (int *pargc, char *argv[], char *xwhere,
|
||||||
|
char *mwhere, char *mfile, int shorten,
|
||||||
|
int local_specified, char *mname, char *msg);
|
||||||
|
#else
|
||||||
|
static int patch_proc ();
|
||||||
|
static int patch_fileproc ();
|
||||||
|
static Dtype patch_dirproc ();
|
||||||
|
static SIGTYPE patch_cleanup ();
|
||||||
|
#endif /* __STDC__ */
|
||||||
|
|
||||||
|
static int force_tag_match = 1;
|
||||||
|
static int patch_short = 0;
|
||||||
|
static int toptwo_diffs = 0;
|
||||||
|
static int local = 0;
|
||||||
|
static char *options = NULL;
|
||||||
|
static char *rev1 = NULL;
|
||||||
|
static char *rev2 = NULL;
|
||||||
|
static char *date1 = NULL;
|
||||||
|
static char *date2 = NULL;
|
||||||
|
static char tmpfile1[L_tmpnam+1], tmpfile2[L_tmpnam+1], tmpfile3[L_tmpnam+1];
|
||||||
|
static int unidiff = 0;
|
||||||
|
|
||||||
|
static char *patch_usage[] =
|
||||||
|
{
|
||||||
|
"Usage: %s %s [-Qflq] [-c|-u] [-s|-t] [-V %%d]\n",
|
||||||
|
" -r rev|-D date [-r rev2 | -D date2] modules...\n",
|
||||||
|
"\t-Q\tReally quiet.\n",
|
||||||
|
"\t-f\tForce a head revision match if tag/date not found.\n",
|
||||||
|
"\t-l\tLocal directory only, not recursive\n",
|
||||||
|
"\t-c\tContext diffs (default)\n",
|
||||||
|
"\t-u\tUnidiff format.\n",
|
||||||
|
"\t-s\tShort patch - one liner per file.\n",
|
||||||
|
"\t-t\tTop two diffs - last change made to the file.\n",
|
||||||
|
"\t-D date\tDate.\n",
|
||||||
|
"\t-r rev\tRevision - symbolic or numeric.\n",
|
||||||
|
"\t-V vers\tUse RCS Version \"vers\" for keyword expansion.\n",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
int
|
||||||
|
patch (argc, argv)
|
||||||
|
int argc;
|
||||||
|
char *argv[];
|
||||||
|
{
|
||||||
|
register int i;
|
||||||
|
int c;
|
||||||
|
int err = 0;
|
||||||
|
DBM *db;
|
||||||
|
|
||||||
|
if (argc == -1)
|
||||||
|
usage (patch_usage);
|
||||||
|
|
||||||
|
optind = 1;
|
||||||
|
while ((c = gnu_getopt (argc, argv, "V:k:cuftsQqlRD:r:")) != -1)
|
||||||
|
{
|
||||||
|
switch (c)
|
||||||
|
{
|
||||||
|
case 'Q':
|
||||||
|
really_quiet = 1;
|
||||||
|
/* FALL THROUGH */
|
||||||
|
case 'q':
|
||||||
|
quiet = 1;
|
||||||
|
break;
|
||||||
|
case 'f':
|
||||||
|
force_tag_match = 0;
|
||||||
|
break;
|
||||||
|
case 'l':
|
||||||
|
local = 1;
|
||||||
|
break;
|
||||||
|
case 'R':
|
||||||
|
local = 0;
|
||||||
|
break;
|
||||||
|
case 't':
|
||||||
|
toptwo_diffs = 1;
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
patch_short = 1;
|
||||||
|
break;
|
||||||
|
case 'D':
|
||||||
|
if (rev2 != NULL || date2 != NULL)
|
||||||
|
error (1, 0,
|
||||||
|
"no more than two revisions/dates can be specified");
|
||||||
|
if (rev1 != NULL || date1 != NULL)
|
||||||
|
date2 = Make_Date (optarg);
|
||||||
|
else
|
||||||
|
date1 = Make_Date (optarg);
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
if (rev2 != NULL || date2 != NULL)
|
||||||
|
error (1, 0,
|
||||||
|
"no more than two revisions/dates can be specified");
|
||||||
|
if (rev1 != NULL || date1 != NULL)
|
||||||
|
rev2 = optarg;
|
||||||
|
else
|
||||||
|
rev1 = optarg;
|
||||||
|
break;
|
||||||
|
case 'k':
|
||||||
|
if (options)
|
||||||
|
free (options);
|
||||||
|
options = RCS_check_kflag (optarg);
|
||||||
|
break;
|
||||||
|
case 'V':
|
||||||
|
if (atoi (optarg) <= 0)
|
||||||
|
error (1, 0, "must specify a version number to -V");
|
||||||
|
if (options)
|
||||||
|
free (options);
|
||||||
|
options = xmalloc (strlen (optarg) + 1 + 2); /* for the -V */
|
||||||
|
(void) sprintf (options, "-V%s", optarg);
|
||||||
|
break;
|
||||||
|
case 'u':
|
||||||
|
unidiff = 1; /* Unidiff */
|
||||||
|
break;
|
||||||
|
case 'c': /* Context diff */
|
||||||
|
unidiff = 0;
|
||||||
|
break;
|
||||||
|
case '?':
|
||||||
|
default:
|
||||||
|
usage (patch_usage);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
argc -= optind;
|
||||||
|
argv += optind;
|
||||||
|
|
||||||
|
/* Sanity checks */
|
||||||
|
if (argc < 1)
|
||||||
|
usage (patch_usage);
|
||||||
|
|
||||||
|
if (toptwo_diffs && patch_short)
|
||||||
|
error (1, 0, "-t and -s options are mutually exclusive");
|
||||||
|
if (toptwo_diffs && (date1 != NULL || date2 != NULL ||
|
||||||
|
rev1 != NULL || rev2 != NULL))
|
||||||
|
error (1, 0, "must not specify revisions/dates with -t option!");
|
||||||
|
|
||||||
|
if (!toptwo_diffs && (date1 == NULL && date2 == NULL &&
|
||||||
|
rev1 == NULL && rev2 == NULL))
|
||||||
|
error (1, 0, "must specify at least one revision/date!");
|
||||||
|
if (date1 != NULL && date2 != NULL)
|
||||||
|
if (RCS_datecmp (date1, date2) >= 0)
|
||||||
|
error (1, 0, "second date must come after first date!");
|
||||||
|
|
||||||
|
/* if options is NULL, make it a NULL string */
|
||||||
|
if (options == NULL)
|
||||||
|
options = xstrdup ("");
|
||||||
|
|
||||||
|
/* clean up if we get a signal */
|
||||||
|
(void) SIG_register (SIGHUP, patch_cleanup);
|
||||||
|
(void) SIG_register (SIGINT, patch_cleanup);
|
||||||
|
(void) SIG_register (SIGQUIT, patch_cleanup);
|
||||||
|
(void) SIG_register (SIGPIPE, patch_cleanup);
|
||||||
|
(void) SIG_register (SIGTERM, patch_cleanup);
|
||||||
|
|
||||||
|
db = open_module ();
|
||||||
|
for (i = 0; i < argc; i++)
|
||||||
|
err += do_module (db, argv[i], PATCH, "Patching", patch_proc,
|
||||||
|
(char *) NULL, 0, 0, 0, (char *) NULL);
|
||||||
|
close_module (db);
|
||||||
|
free (options);
|
||||||
|
patch_cleanup ();
|
||||||
|
return (err);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* callback proc for doing the real work of patching
|
||||||
|
*/
|
||||||
|
/* ARGSUSED */
|
||||||
|
static char where[PATH_MAX];
|
||||||
|
static int
|
||||||
|
patch_proc (pargc, argv, xwhere, mwhere, mfile, shorten, local_specified,
|
||||||
|
mname, msg)
|
||||||
|
int *pargc;
|
||||||
|
char *argv[];
|
||||||
|
char *xwhere;
|
||||||
|
char *mwhere;
|
||||||
|
char *mfile;
|
||||||
|
int shorten;
|
||||||
|
int local_specified;
|
||||||
|
char *mname;
|
||||||
|
char *msg;
|
||||||
|
{
|
||||||
|
int err = 0;
|
||||||
|
int which;
|
||||||
|
char repository[PATH_MAX];
|
||||||
|
|
||||||
|
(void) sprintf (repository, "%s/%s", CVSroot, argv[0]);
|
||||||
|
(void) strcpy (where, argv[0]);
|
||||||
|
|
||||||
|
/* if mfile isn't null, we need to set up to do only part of the module */
|
||||||
|
if (mfile != NULL)
|
||||||
|
{
|
||||||
|
char *cp;
|
||||||
|
char path[PATH_MAX];
|
||||||
|
|
||||||
|
/* if the portion of the module is a path, put the dir part on repos */
|
||||||
|
if ((cp = rindex (mfile, '/')) != NULL)
|
||||||
|
{
|
||||||
|
*cp = '\0';
|
||||||
|
(void) strcat (repository, "/");
|
||||||
|
(void) strcat (repository, mfile);
|
||||||
|
(void) strcat (where, "/");
|
||||||
|
(void) strcat (where, mfile);
|
||||||
|
mfile = cp + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* take care of the rest */
|
||||||
|
(void) sprintf (path, "%s/%s", repository, mfile);
|
||||||
|
if (isdir (path))
|
||||||
|
{
|
||||||
|
/* directory means repository gets the dir tacked on */
|
||||||
|
(void) strcpy (repository, path);
|
||||||
|
(void) strcat (where, "/");
|
||||||
|
(void) strcat (where, mfile);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* a file means muck argv */
|
||||||
|
for (i = 1; i < *pargc; i++)
|
||||||
|
free (argv[i]);
|
||||||
|
argv[1] = xstrdup (mfile);
|
||||||
|
(*pargc) = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* cd to the starting repository */
|
||||||
|
if (chdir (repository) < 0)
|
||||||
|
{
|
||||||
|
error (0, errno, "cannot chdir to %s", repository);
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (force_tag_match)
|
||||||
|
which = W_REPOS | W_ATTIC;
|
||||||
|
else
|
||||||
|
which = W_REPOS;
|
||||||
|
|
||||||
|
/* start the recursion processor */
|
||||||
|
err = start_recursion (patch_fileproc, (int (*) ()) NULL, patch_dirproc,
|
||||||
|
(int (*) ()) NULL, *pargc - 1, argv + 1, local,
|
||||||
|
which, 0, 1, where, 1);
|
||||||
|
|
||||||
|
return (err);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Called to examine a particular RCS file, as appropriate with the options
|
||||||
|
* that were set above.
|
||||||
|
*/
|
||||||
|
/* ARGSUSED */
|
||||||
|
static int
|
||||||
|
patch_fileproc (file, update_dir, repository, entries, srcfiles)
|
||||||
|
char *file;
|
||||||
|
char *update_dir;
|
||||||
|
char *repository;
|
||||||
|
List *entries;
|
||||||
|
List *srcfiles;
|
||||||
|
{
|
||||||
|
char *vers_tag, *vers_head;
|
||||||
|
char rcsspace[PATH_MAX];
|
||||||
|
char *rcs = rcsspace;
|
||||||
|
Node *p;
|
||||||
|
RCSNode *rcsfile;
|
||||||
|
FILE *fp1, *fp2, *fp3;
|
||||||
|
int ret = 0;
|
||||||
|
int isattic = 0;
|
||||||
|
int retcode = 0;
|
||||||
|
char file1[PATH_MAX], file2[PATH_MAX], strippath[PATH_MAX];
|
||||||
|
char line1[MAXLINELEN], line2[MAXLINELEN];
|
||||||
|
char *cp1, *cp2, *commap;
|
||||||
|
FILE *fp;
|
||||||
|
|
||||||
|
|
||||||
|
/* find the parsed rcs file */
|
||||||
|
p = findnode (srcfiles, file);
|
||||||
|
if (p == NULL)
|
||||||
|
return (1);
|
||||||
|
rcsfile = (RCSNode *) p->data;
|
||||||
|
if ((rcsfile->flags & VALID) && (rcsfile->flags & INATTIC))
|
||||||
|
isattic = 1;
|
||||||
|
|
||||||
|
(void) sprintf (rcs, "%s%s", file, RCSEXT);
|
||||||
|
|
||||||
|
/* if vers_head is NULL, may have been removed from the release */
|
||||||
|
if (isattic && rev2 == NULL && date2 == NULL)
|
||||||
|
vers_head = NULL;
|
||||||
|
else
|
||||||
|
vers_head = RCS_getversion (rcsfile, rev2, date2, force_tag_match);
|
||||||
|
|
||||||
|
if (toptwo_diffs)
|
||||||
|
{
|
||||||
|
if (vers_head == NULL)
|
||||||
|
return (1);
|
||||||
|
|
||||||
|
if (!date1)
|
||||||
|
date1 = xmalloc (50); /* plenty big :-) */
|
||||||
|
*date1 = '\0';
|
||||||
|
if (RCS_getrevtime (rcsfile, vers_head, date1, 1) == -1)
|
||||||
|
{
|
||||||
|
if (!really_quiet)
|
||||||
|
error (0, 0, "cannot find date in rcs file %s revision %s",
|
||||||
|
rcs, vers_head);
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vers_tag = RCS_getversion (rcsfile, rev1, date1, force_tag_match);
|
||||||
|
|
||||||
|
if (vers_tag == NULL && (vers_head == NULL || isattic))
|
||||||
|
return (0); /* nothing known about specified revs */
|
||||||
|
|
||||||
|
if (vers_tag && vers_head && strcmp (vers_head, vers_tag) == 0)
|
||||||
|
return (0); /* not changed between releases */
|
||||||
|
|
||||||
|
if (patch_short)
|
||||||
|
{
|
||||||
|
(void) printf ("File ");
|
||||||
|
if (vers_tag == NULL)
|
||||||
|
(void) printf ("%s is new; current revision %s\n", rcs, vers_head);
|
||||||
|
else if (vers_head == NULL)
|
||||||
|
(void) printf ("%s is removed; not included in release %s\n",
|
||||||
|
rcs, rev2 ? rev2 : date2);
|
||||||
|
else
|
||||||
|
(void) printf ("%s changed from revision %s to %s\n",
|
||||||
|
rcs, vers_tag, vers_head);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
if ((fp1 = fopen (tmpnam (tmpfile1), "w+")) != NULL)
|
||||||
|
(void) fclose (fp1);
|
||||||
|
if ((fp2 = fopen (tmpnam (tmpfile2), "w+")) != NULL)
|
||||||
|
(void) fclose (fp2);
|
||||||
|
if ((fp3 = fopen (tmpnam (tmpfile3), "w+")) != NULL)
|
||||||
|
(void) fclose (fp3);
|
||||||
|
if (fp1 == NULL || fp2 == NULL || fp3 == NULL)
|
||||||
|
{
|
||||||
|
error (0, 0, "cannot create temporary files");
|
||||||
|
ret = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (vers_tag != NULL)
|
||||||
|
{
|
||||||
|
run_setup ("%s%s %s -p -q -r%s", Rcsbin, RCS_CO, options, vers_tag);
|
||||||
|
run_arg (rcsfile->path);
|
||||||
|
if ((retcode = run_exec (RUN_TTY, tmpfile1, RUN_TTY, RUN_NORMAL)) != 0)
|
||||||
|
{
|
||||||
|
if (!really_quiet)
|
||||||
|
error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0,
|
||||||
|
"co of revision %s in %s failed", vers_tag, rcs);
|
||||||
|
ret = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (toptwo_diffs)
|
||||||
|
{
|
||||||
|
ret = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (vers_head != NULL)
|
||||||
|
{
|
||||||
|
run_setup ("%s%s %s -p -q -r%s", Rcsbin, RCS_CO, options, vers_head);
|
||||||
|
run_arg (rcsfile->path);
|
||||||
|
if ((retcode = run_exec (RUN_TTY, tmpfile2, RUN_TTY, RUN_NORMAL)) != 0)
|
||||||
|
{
|
||||||
|
if (!really_quiet)
|
||||||
|
error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0,
|
||||||
|
"co of revision %s in %s failed", vers_head, rcs);
|
||||||
|
ret = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
run_setup ("%s -%c", DIFF, unidiff ? 'u' : 'c');
|
||||||
|
run_arg (tmpfile1);
|
||||||
|
run_arg (tmpfile2);
|
||||||
|
switch (run_exec (RUN_TTY, tmpfile3, RUN_TTY, RUN_NORMAL))
|
||||||
|
{
|
||||||
|
case -1: /* fork/wait failure */
|
||||||
|
error (1, errno, "fork for diff failed on %s", rcs);
|
||||||
|
break;
|
||||||
|
case 0: /* nothing to do */
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
/*
|
||||||
|
* The two revisions are really different, so read the first two
|
||||||
|
* lines of the diff output file, and munge them to include more
|
||||||
|
* reasonable file names that "patch" will understand.
|
||||||
|
*/
|
||||||
|
fp = open_file (tmpfile3, "r");
|
||||||
|
if (fgets (line1, sizeof (line1), fp) == NULL ||
|
||||||
|
fgets (line2, sizeof (line2), fp) == NULL)
|
||||||
|
{
|
||||||
|
error (0, errno, "failed to read diff file header %s for %s",
|
||||||
|
tmpfile3, rcs);
|
||||||
|
ret = 1;
|
||||||
|
(void) fclose (fp);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (!unidiff)
|
||||||
|
{
|
||||||
|
if (strncmp (line1, "*** ", 4) != 0 ||
|
||||||
|
strncmp (line2, "--- ", 4) != 0 ||
|
||||||
|
(cp1 = index (line1, '\t')) == NULL ||
|
||||||
|
(cp2 = index (line2, '\t')) == NULL)
|
||||||
|
{
|
||||||
|
error (0, 0, "invalid diff header for %s", rcs);
|
||||||
|
ret = 1;
|
||||||
|
(void) fclose (fp);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (strncmp (line1, "--- ", 4) != 0 ||
|
||||||
|
strncmp (line2, "+++ ", 4) != 0 ||
|
||||||
|
(cp1 = index (line1, '\t')) == NULL ||
|
||||||
|
(cp2 = index (line2, '\t')) == NULL)
|
||||||
|
{
|
||||||
|
error (0, 0, "invalid unidiff header for %s", rcs);
|
||||||
|
ret = 1;
|
||||||
|
(void) fclose (fp);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (CVSroot != NULL)
|
||||||
|
(void) sprintf (strippath, "%s/", CVSroot);
|
||||||
|
else
|
||||||
|
(void) strcpy (strippath, REPOS_STRIP);
|
||||||
|
if (strncmp (rcs, strippath, strlen (strippath)) == 0)
|
||||||
|
rcs += strlen (strippath);
|
||||||
|
commap = rindex (rcs, ',');
|
||||||
|
*commap = '\0';
|
||||||
|
if (vers_tag != NULL)
|
||||||
|
{
|
||||||
|
(void) sprintf (file1, "%s%s%s:%s", update_dir,
|
||||||
|
update_dir[0] ? "/" : "", rcs, vers_tag);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
(void) strcpy (file1, DEVNULL);
|
||||||
|
}
|
||||||
|
(void) sprintf (file2, "%s%s%s:%s", update_dir,
|
||||||
|
update_dir[0] ? "/" : "", rcs,
|
||||||
|
vers_head ? vers_head : "removed");
|
||||||
|
if (unidiff)
|
||||||
|
{
|
||||||
|
(void) printf ("diff -u %s %s\n", file1, file2);
|
||||||
|
(void) printf ("--- %s%s+++ ", file1, cp1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
(void) printf ("diff -c %s %s\n", file1, file2);
|
||||||
|
(void) printf ("*** %s%s--- ", file1, cp1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (update_dir[0] != '\0')
|
||||||
|
(void) printf ("%s/", update_dir);
|
||||||
|
(void) printf ("%s%s", rcs, cp2);
|
||||||
|
while (fgets (line1, sizeof (line1), fp) != NULL)
|
||||||
|
(void) printf ("%s", line1);
|
||||||
|
(void) fclose (fp);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
error (0, 0, "diff failed for %s", rcs);
|
||||||
|
}
|
||||||
|
out:
|
||||||
|
(void) unlink_file (tmpfile1);
|
||||||
|
(void) unlink_file (tmpfile2);
|
||||||
|
(void) unlink_file (tmpfile3);
|
||||||
|
return (ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Print a warm fuzzy message
|
||||||
|
*/
|
||||||
|
/* ARGSUSED */
|
||||||
|
static Dtype
|
||||||
|
patch_dirproc (dir, repos, update_dir)
|
||||||
|
char *dir;
|
||||||
|
char *repos;
|
||||||
|
char *update_dir;
|
||||||
|
{
|
||||||
|
if (!quiet)
|
||||||
|
error (0, 0, "Diffing %s", update_dir);
|
||||||
|
return (R_PROCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Clean up temporary files
|
||||||
|
*/
|
||||||
|
static SIGTYPE
|
||||||
|
patch_cleanup ()
|
||||||
|
{
|
||||||
|
if (tmpfile1[0] != '\0')
|
||||||
|
(void) unlink_file (tmpfile1);
|
||||||
|
if (tmpfile2[0] != '\0')
|
||||||
|
(void) unlink_file (tmpfile2);
|
||||||
|
if (tmpfile3[0] != '\0')
|
||||||
|
(void) unlink_file (tmpfile3);
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
#define PATCHLEVEL 0
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,102 @@
|
|||||||
|
/* @(#)rcs.h 1.14 92/03/31 */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) 1992, Brian Berliner and Jeff Polk
|
||||||
|
* Copyright (c) 1989-1992, Brian Berliner
|
||||||
|
*
|
||||||
|
* You may distribute under the terms of the GNU General Public License as
|
||||||
|
* specified in the README file that comes with the CVS 1.3 kit.
|
||||||
|
*
|
||||||
|
* RCS source control definitions needed by rcs.c and friends
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define RCS "rcs"
|
||||||
|
#define RCS_CI "ci"
|
||||||
|
#define RCS_CO "co"
|
||||||
|
#define RCS_RLOG "rlog"
|
||||||
|
#define RCS_DIFF "rcsdiff"
|
||||||
|
#define RCS_MERGE "merge"
|
||||||
|
#define RCS_RCSMERGE "rcsmerge"
|
||||||
|
#define RCS_MERGE_PAT "^>>>>>>> " /* runs "grep" with this pattern */
|
||||||
|
#define RCSEXT ",v"
|
||||||
|
#define RCSHEAD "head"
|
||||||
|
#define RCSBRANCH "branch"
|
||||||
|
#define RCSSYMBOLS "symbols"
|
||||||
|
#define RCSDATE "date"
|
||||||
|
#define RCSDESC "desc"
|
||||||
|
#define DATEFORM "%02d.%02d.%02d.%02d.%02d.%02d"
|
||||||
|
#define SDATEFORM "%d.%d.%d.%d.%d.%d"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Opaque structure definitions used by RCS specific lookup routines
|
||||||
|
*/
|
||||||
|
#define VALID 0x1 /* flags field contains valid data */
|
||||||
|
#define INATTIC 0x2 /* RCS file is located in the Attic */
|
||||||
|
struct rcsnode
|
||||||
|
{
|
||||||
|
int refcount;
|
||||||
|
int flags;
|
||||||
|
char *path;
|
||||||
|
char *head;
|
||||||
|
char *branch;
|
||||||
|
List *symbols;
|
||||||
|
List *versions;
|
||||||
|
List *dates;
|
||||||
|
};
|
||||||
|
typedef struct rcsnode RCSNode;
|
||||||
|
|
||||||
|
struct rcsversnode
|
||||||
|
{
|
||||||
|
char *version;
|
||||||
|
char *date;
|
||||||
|
char *next;
|
||||||
|
List *branches;
|
||||||
|
};
|
||||||
|
typedef struct rcsversnode RCSVers;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CVS reserves all even-numbered branches for its own use. "magic" branches
|
||||||
|
* (see rcs.c) are contained as virtual revision numbers (within symbolic
|
||||||
|
* tags only) off the RCS_MAGIC_BRANCH, which is 0. CVS also reserves the
|
||||||
|
* ".1" branch for vendor revisions. So, if you do your own branching, you
|
||||||
|
* should limit your use to odd branch numbers starting at 3.
|
||||||
|
*/
|
||||||
|
#define RCS_MAGIC_BRANCH 0
|
||||||
|
|
||||||
|
/*
|
||||||
|
* exported interfaces
|
||||||
|
*/
|
||||||
|
#if __STDC__
|
||||||
|
List *RCS_parsefiles (List * files, char *xrepos);
|
||||||
|
RCSNode *RCS_parse (char *file, char *repos);
|
||||||
|
RCSNode *RCS_parsercsfile (char *rcsfile);
|
||||||
|
char *RCS_check_kflag (char *arg);
|
||||||
|
char *RCS_getdate (RCSNode * rcs, char *date, int force_tag_match);
|
||||||
|
char *RCS_gettag (RCSNode * rcs, char *tag, int force_tag_match);
|
||||||
|
char *RCS_getversion (RCSNode * rcs, char *tag, char *date,
|
||||||
|
int force_tag_match);
|
||||||
|
char *RCS_magicrev (RCSNode *rcs, char *rev);
|
||||||
|
int RCS_isbranch (char *file, char *rev, List *srcfiles);
|
||||||
|
char *RCS_whatbranch (char *file, char *tag, List *srcfiles);
|
||||||
|
char *RCS_head (RCSNode * rcs);
|
||||||
|
int RCS_datecmp (char *date1, char *date2);
|
||||||
|
time_t RCS_getrevtime (RCSNode * rcs, char *rev, char *date, int fudge);
|
||||||
|
void RCS_check_tag (char *tag);
|
||||||
|
void freercsnode (RCSNode ** rnodep);
|
||||||
|
#else
|
||||||
|
List *RCS_parsefiles ();
|
||||||
|
RCSNode *RCS_parse ();
|
||||||
|
char *RCS_head ();
|
||||||
|
char *RCS_getversion ();
|
||||||
|
char *RCS_magicrev ();
|
||||||
|
int RCS_isbranch ();
|
||||||
|
char *RCS_whatbranch ();
|
||||||
|
char *RCS_gettag ();
|
||||||
|
char *RCS_getdate ();
|
||||||
|
char *RCS_check_kflag ();
|
||||||
|
void RCS_check_tag ();
|
||||||
|
time_t RCS_getrevtime ();
|
||||||
|
RCSNode *RCS_parsercsfile ();
|
||||||
|
int RCS_datecmp ();
|
||||||
|
void freercsnode ();
|
||||||
|
#endif /* __STDC__ */
|
||||||
@@ -0,0 +1,535 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 1992, Brian Berliner and Jeff Polk
|
||||||
|
*
|
||||||
|
* You may distribute under the terms of the GNU General Public License as
|
||||||
|
* specified in the README file that comes with the CVS 1.3 kit.
|
||||||
|
*
|
||||||
|
* General recursion handler
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "cvs.h"
|
||||||
|
|
||||||
|
#ifndef lint
|
||||||
|
static char rcsid[] = "@(#)recurse.c 1.22 92/04/10";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __STDC__
|
||||||
|
static int do_dir_proc (Node * p);
|
||||||
|
static int do_file_proc (Node * p);
|
||||||
|
static void addlist (List ** listp, char *key);
|
||||||
|
#else
|
||||||
|
static int do_file_proc ();
|
||||||
|
static int do_dir_proc ();
|
||||||
|
static void addlist ();
|
||||||
|
#endif /* __STDC__ */
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Local static versions eliminates the need for globals
|
||||||
|
*/
|
||||||
|
static int (*fileproc) ();
|
||||||
|
static int (*filesdoneproc) ();
|
||||||
|
static Dtype (*direntproc) ();
|
||||||
|
static int (*dirleaveproc) ();
|
||||||
|
static int which;
|
||||||
|
static Dtype flags;
|
||||||
|
static int aflag;
|
||||||
|
static int readlock;
|
||||||
|
static int dosrcs;
|
||||||
|
static char update_dir[PATH_MAX];
|
||||||
|
static char *repository = NULL;
|
||||||
|
static List *entries = NULL;
|
||||||
|
static List *srcfiles = NULL;
|
||||||
|
static List *filelist = NULL;
|
||||||
|
static List *dirlist = NULL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Called to start a recursive command Command line arguments are processed
|
||||||
|
* if present, otherwise the local directory is processed.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
start_recursion (fileproc, filesdoneproc, direntproc, dirleaveproc,
|
||||||
|
argc, argv, local, which, aflag, readlock,
|
||||||
|
update_preload, dosrcs)
|
||||||
|
int (*fileproc) ();
|
||||||
|
int (*filesdoneproc) ();
|
||||||
|
Dtype (*direntproc) ();
|
||||||
|
int (*dirleaveproc) ();
|
||||||
|
int argc;
|
||||||
|
char *argv[];
|
||||||
|
int local;
|
||||||
|
int which;
|
||||||
|
int aflag;
|
||||||
|
int readlock;
|
||||||
|
char *update_preload;
|
||||||
|
int dosrcs;
|
||||||
|
{
|
||||||
|
int i, err = 0;
|
||||||
|
Dtype flags;
|
||||||
|
|
||||||
|
if (update_preload == NULL)
|
||||||
|
update_dir[0] = '\0';
|
||||||
|
else
|
||||||
|
(void) strcpy (update_dir, update_preload);
|
||||||
|
|
||||||
|
if (local)
|
||||||
|
flags = R_SKIP_DIRS;
|
||||||
|
else
|
||||||
|
flags = R_PROCESS;
|
||||||
|
|
||||||
|
/* clean up from any previous calls to start_recursion */
|
||||||
|
if (repository)
|
||||||
|
{
|
||||||
|
free (repository);
|
||||||
|
repository = (char *) NULL;
|
||||||
|
}
|
||||||
|
if (entries)
|
||||||
|
dellist (&entries);
|
||||||
|
if (srcfiles)
|
||||||
|
dellist (&srcfiles);
|
||||||
|
if (filelist)
|
||||||
|
dellist (&filelist);
|
||||||
|
if (dirlist)
|
||||||
|
dellist (&dirlist);
|
||||||
|
|
||||||
|
if (argc == 0)
|
||||||
|
{
|
||||||
|
|
||||||
|
/*
|
||||||
|
* There were no arguments, so we'll probably just recurse. The
|
||||||
|
* exception to the rule is when we are called from a directory
|
||||||
|
* without any CVS administration files. That has always meant to
|
||||||
|
* process each of the sub-directories, so we pretend like we were
|
||||||
|
* called with the list of sub-dirs of the current dir as args
|
||||||
|
*/
|
||||||
|
if ((which & W_LOCAL) && !isdir (CVSADM) && !isdir (OCVSADM))
|
||||||
|
dirlist = Find_Dirs ((char *) NULL, W_LOCAL);
|
||||||
|
else
|
||||||
|
addlist (&dirlist, ".");
|
||||||
|
|
||||||
|
err += do_recursion (fileproc, filesdoneproc, direntproc,
|
||||||
|
dirleaveproc, flags, which, aflag,
|
||||||
|
readlock, dosrcs);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
/*
|
||||||
|
* There were arguments, so we have to handle them by hand. To do
|
||||||
|
* that, we set up the filelist and dirlist with the arguments and
|
||||||
|
* call do_recursion. do_recursion recognizes the fact that the
|
||||||
|
* lists are non-null when it starts and doesn't update them
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* look for args with /-s in them */
|
||||||
|
for (i = 0; i < argc; i++)
|
||||||
|
if (index (argv[i], '/') != NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* if we didn't find any hard one's, do it the easy way */
|
||||||
|
if (i == argc)
|
||||||
|
{
|
||||||
|
/* set up the lists */
|
||||||
|
for (i = 0; i < argc; i++)
|
||||||
|
{
|
||||||
|
if (isdir (argv[i]))
|
||||||
|
addlist (&dirlist, argv[i]);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (isdir (CVSADM) || isdir (OCVSADM))
|
||||||
|
{
|
||||||
|
char *repos;
|
||||||
|
char tmp[PATH_MAX];
|
||||||
|
|
||||||
|
repos = Name_Repository ((char *) NULL, update_dir);
|
||||||
|
(void) sprintf (tmp, "%s/%s", repos, argv[i]);
|
||||||
|
if (isdir (tmp))
|
||||||
|
addlist (&dirlist, argv[i]);
|
||||||
|
else
|
||||||
|
addlist (&filelist, argv[i]);
|
||||||
|
free (repos);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
addlist (&filelist, argv[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* we aren't recursive if no directories were specified */
|
||||||
|
if (dirlist == NULL)
|
||||||
|
local = 1;
|
||||||
|
|
||||||
|
/* process the lists */
|
||||||
|
err += do_recursion (fileproc, filesdoneproc, direntproc,
|
||||||
|
dirleaveproc, flags, which, aflag,
|
||||||
|
readlock, dosrcs);
|
||||||
|
}
|
||||||
|
/* otherwise - do it the hard way */
|
||||||
|
else
|
||||||
|
{
|
||||||
|
char *cp;
|
||||||
|
char *dir = (char *) NULL;
|
||||||
|
char *comp = (char *) NULL;
|
||||||
|
char *oldupdate = (char *) NULL;
|
||||||
|
char savewd[PATH_MAX];
|
||||||
|
|
||||||
|
if (getwd (savewd) == NULL)
|
||||||
|
error (1, 0, "could not get working directory: %s", savewd);
|
||||||
|
|
||||||
|
for (i = 0; i < argc; i++)
|
||||||
|
{
|
||||||
|
/* split the arg into the dir and component parts */
|
||||||
|
dir = xstrdup (argv[i]);
|
||||||
|
if ((cp = rindex (dir, '/')) != NULL)
|
||||||
|
{
|
||||||
|
*cp = '\0';
|
||||||
|
comp = xstrdup (cp + 1);
|
||||||
|
oldupdate = xstrdup (update_dir);
|
||||||
|
if (update_dir[0] != '\0')
|
||||||
|
(void) strcat (update_dir, "/");
|
||||||
|
(void) strcat (update_dir, dir);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
comp = xstrdup (dir);
|
||||||
|
if (dir)
|
||||||
|
free (dir);
|
||||||
|
dir = (char *) NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* chdir to the appropriate place if necessary */
|
||||||
|
if (dir && chdir (dir) < 0)
|
||||||
|
error (1, errno, "could not chdir to %s", dir);
|
||||||
|
|
||||||
|
/* set up the list */
|
||||||
|
if (isdir (comp))
|
||||||
|
addlist (&dirlist, comp);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (isdir (CVSADM) || isdir (OCVSADM))
|
||||||
|
{
|
||||||
|
char *repos;
|
||||||
|
char tmp[PATH_MAX];
|
||||||
|
|
||||||
|
repos = Name_Repository ((char *) NULL, update_dir);
|
||||||
|
(void) sprintf (tmp, "%s/%s", repos, comp);
|
||||||
|
if (isdir (tmp))
|
||||||
|
addlist (&dirlist, comp);
|
||||||
|
else
|
||||||
|
addlist (&filelist, comp);
|
||||||
|
free (repos);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
addlist (&filelist, comp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* do the recursion */
|
||||||
|
err += do_recursion (fileproc, filesdoneproc, direntproc,
|
||||||
|
dirleaveproc, flags, which,
|
||||||
|
aflag, readlock, dosrcs);
|
||||||
|
|
||||||
|
/* chdir back and fix update_dir if necessary */
|
||||||
|
if (dir && chdir (savewd) < 0)
|
||||||
|
error (1, errno, "could not chdir to %s", dir);
|
||||||
|
if (oldupdate)
|
||||||
|
{
|
||||||
|
(void) strcpy (update_dir, oldupdate);
|
||||||
|
free (oldupdate);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if (dir)
|
||||||
|
free (dir);
|
||||||
|
if (comp)
|
||||||
|
free (comp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (err);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Implement the recursive policies on the local directory. This may be
|
||||||
|
* called directly, or may be called by start_recursion
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
do_recursion (xfileproc, xfilesdoneproc, xdirentproc, xdirleaveproc,
|
||||||
|
xflags, xwhich, xaflag, xreadlock, xdosrcs)
|
||||||
|
int (*xfileproc) ();
|
||||||
|
int (*xfilesdoneproc) ();
|
||||||
|
Dtype (*xdirentproc) ();
|
||||||
|
int (*xdirleaveproc) ();
|
||||||
|
Dtype xflags;
|
||||||
|
int xwhich;
|
||||||
|
int xaflag;
|
||||||
|
int xreadlock;
|
||||||
|
int xdosrcs;
|
||||||
|
{
|
||||||
|
int err = 0;
|
||||||
|
int dodoneproc = 1;
|
||||||
|
char *srepository;
|
||||||
|
|
||||||
|
/* do nothing if told */
|
||||||
|
if (xflags == R_SKIP_ALL)
|
||||||
|
return (0);
|
||||||
|
|
||||||
|
/* set up the static vars */
|
||||||
|
fileproc = xfileproc;
|
||||||
|
filesdoneproc = xfilesdoneproc;
|
||||||
|
direntproc = xdirentproc;
|
||||||
|
dirleaveproc = xdirleaveproc;
|
||||||
|
flags = xflags;
|
||||||
|
which = xwhich;
|
||||||
|
aflag = xaflag;
|
||||||
|
readlock = noexec ? 0 : xreadlock;
|
||||||
|
dosrcs = xdosrcs;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fill in repository with the current repository
|
||||||
|
*/
|
||||||
|
if (which & W_LOCAL)
|
||||||
|
{
|
||||||
|
if (isdir (CVSADM) || isdir (OCVSADM))
|
||||||
|
repository = Name_Repository ((char *) NULL, update_dir);
|
||||||
|
else
|
||||||
|
repository = NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
repository = xmalloc (PATH_MAX);
|
||||||
|
(void) getwd (repository);
|
||||||
|
}
|
||||||
|
srepository = repository; /* remember what to free */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The filesdoneproc needs to be called for each directory where files
|
||||||
|
* processed, or each directory that is processed by a call where no
|
||||||
|
* directories were passed in. In fact, the only time we don't want to
|
||||||
|
* call back the filesdoneproc is when we are processing directories that
|
||||||
|
* were passed in on the command line (or in the special case of `.' when
|
||||||
|
* we were called with no args
|
||||||
|
*/
|
||||||
|
if (dirlist != NULL && filelist == NULL)
|
||||||
|
dodoneproc = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If filelist or dirlist is already set, we don't look again. Otherwise,
|
||||||
|
* find the files and directories
|
||||||
|
*/
|
||||||
|
if (filelist == NULL && dirlist == NULL)
|
||||||
|
{
|
||||||
|
/* both lists were NULL, so start from scratch */
|
||||||
|
if (fileproc != NULL && flags != R_SKIP_FILES)
|
||||||
|
{
|
||||||
|
int lwhich = which;
|
||||||
|
|
||||||
|
/* be sure to look in the attic if we have sticky tags/date */
|
||||||
|
if ((lwhich & W_ATTIC) == 0)
|
||||||
|
if (isreadable (CVSADM_TAG))
|
||||||
|
lwhich |= W_ATTIC;
|
||||||
|
|
||||||
|
/* find the files and fill in entries if appropriate */
|
||||||
|
filelist = Find_Names (repository, lwhich, aflag, &entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* find sub-directories if we will recurse */
|
||||||
|
if (flags != R_SKIP_DIRS)
|
||||||
|
dirlist = Find_Dirs (repository, which);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* something was passed on the command line */
|
||||||
|
if (filelist != NULL && fileproc != NULL)
|
||||||
|
{
|
||||||
|
/* we will process files, so pre-parse entries */
|
||||||
|
if (which & W_LOCAL)
|
||||||
|
entries = ParseEntries (aflag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* process the files (if any) */
|
||||||
|
if (filelist != NULL)
|
||||||
|
{
|
||||||
|
/* read lock it if necessary */
|
||||||
|
if (readlock && repository && Reader_Lock (repository) != 0)
|
||||||
|
error (1, 0, "read lock failed - giving up");
|
||||||
|
|
||||||
|
/* pre-parse the source files */
|
||||||
|
if (dosrcs && repository)
|
||||||
|
srcfiles = RCS_parsefiles (filelist, repository);
|
||||||
|
else
|
||||||
|
srcfiles = (List *) NULL;
|
||||||
|
|
||||||
|
/* process the files */
|
||||||
|
err += walklist (filelist, do_file_proc);
|
||||||
|
|
||||||
|
/* unlock it */
|
||||||
|
if (readlock)
|
||||||
|
Lock_Cleanup ();
|
||||||
|
|
||||||
|
/* clean up */
|
||||||
|
dellist (&filelist);
|
||||||
|
dellist (&srcfiles);
|
||||||
|
dellist (&entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* call-back files done proc (if any) */
|
||||||
|
if (dodoneproc && filesdoneproc != NULL)
|
||||||
|
err = filesdoneproc (err, repository, update_dir[0] ? update_dir : ".");
|
||||||
|
|
||||||
|
/* process the directories (if necessary) */
|
||||||
|
if (dirlist != NULL)
|
||||||
|
err += walklist (dirlist, do_dir_proc);
|
||||||
|
#ifdef notdef
|
||||||
|
else if (dirleaveproc != NULL)
|
||||||
|
err += dirleaveproc(".", err, ".");
|
||||||
|
#endif
|
||||||
|
dellist (&dirlist);
|
||||||
|
|
||||||
|
/* free the saved copy of the pointer if necessary */
|
||||||
|
if (srepository)
|
||||||
|
{
|
||||||
|
(void) free (srepository);
|
||||||
|
repository = (char *) NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (err);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Process each of the files in the list with the callback proc
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
do_file_proc (p)
|
||||||
|
Node *p;
|
||||||
|
{
|
||||||
|
if (fileproc != NULL)
|
||||||
|
return (fileproc (p->key, update_dir, repository, entries, srcfiles));
|
||||||
|
else
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Process each of the directories in the list (recursing as we go)
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
do_dir_proc (p)
|
||||||
|
Node *p;
|
||||||
|
{
|
||||||
|
char *dir = p->key;
|
||||||
|
char savewd[PATH_MAX];
|
||||||
|
char newrepos[PATH_MAX];
|
||||||
|
List *sdirlist;
|
||||||
|
char *srepository;
|
||||||
|
char *cp;
|
||||||
|
Dtype dir_return = R_PROCESS;
|
||||||
|
int stripped_dot = 0;
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
/* set up update_dir - skip dots if not at start */
|
||||||
|
if (strcmp (dir, ".") != 0)
|
||||||
|
{
|
||||||
|
if (update_dir[0] != '\0')
|
||||||
|
{
|
||||||
|
(void) strcat (update_dir, "/");
|
||||||
|
(void) strcat (update_dir, dir);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
(void) strcpy (update_dir, dir);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Here we need a plausible repository name for the sub-directory. We
|
||||||
|
* create one by concatenating the new directory name onto the
|
||||||
|
* previous repository name. The only case where the name should be
|
||||||
|
* used is in the case where we are creating a new sub-directory for
|
||||||
|
* update -d and in that case the generated name will be correct.
|
||||||
|
*/
|
||||||
|
if (repository == NULL)
|
||||||
|
newrepos[0] = '\0';
|
||||||
|
else
|
||||||
|
(void) sprintf (newrepos, "%s/%s", repository, dir);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (update_dir[0] == '\0')
|
||||||
|
(void) strcpy (update_dir, dir);
|
||||||
|
|
||||||
|
if (repository == NULL)
|
||||||
|
newrepos[0] = '\0';
|
||||||
|
else
|
||||||
|
(void) strcpy (newrepos, repository);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* call-back dir entry proc (if any) */
|
||||||
|
if (direntproc != NULL)
|
||||||
|
dir_return = direntproc (dir, newrepos, update_dir);
|
||||||
|
|
||||||
|
/* only process the dir if the return code was 0 */
|
||||||
|
if (dir_return != R_SKIP_ALL)
|
||||||
|
{
|
||||||
|
/* save our current directory and static vars */
|
||||||
|
if (getwd (savewd) == NULL)
|
||||||
|
error (1, 0, "could not get working directory: %s", savewd);
|
||||||
|
sdirlist = dirlist;
|
||||||
|
srepository = repository;
|
||||||
|
dirlist = NULL;
|
||||||
|
|
||||||
|
/* cd to the sub-directory */
|
||||||
|
if (chdir (dir) < 0)
|
||||||
|
error (1, errno, "could not chdir to %s", dir);
|
||||||
|
|
||||||
|
/* honor the global SKIP_DIRS (a.k.a. local) */
|
||||||
|
if (flags == R_SKIP_DIRS)
|
||||||
|
dir_return = R_SKIP_DIRS;
|
||||||
|
|
||||||
|
/* remember if the `.' will be stripped for subsequent dirs */
|
||||||
|
if (strcmp (update_dir, ".") == 0)
|
||||||
|
{
|
||||||
|
update_dir[0] = '\0';
|
||||||
|
stripped_dot = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* make the recursive call */
|
||||||
|
err += do_recursion (fileproc, filesdoneproc, direntproc, dirleaveproc,
|
||||||
|
dir_return, which, aflag, readlock, dosrcs);
|
||||||
|
|
||||||
|
/* put the `.' back if necessary */
|
||||||
|
if (stripped_dot)
|
||||||
|
(void) strcpy (update_dir, ".");
|
||||||
|
|
||||||
|
/* call-back dir leave proc (if any) */
|
||||||
|
if (dirleaveproc != NULL)
|
||||||
|
err = dirleaveproc (dir, err, update_dir);
|
||||||
|
|
||||||
|
/* get back to where we started and restore state vars */
|
||||||
|
if (chdir (savewd) < 0)
|
||||||
|
error (1, errno, "could not chdir to %s", savewd);
|
||||||
|
dirlist = sdirlist;
|
||||||
|
repository = srepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* put back update_dir */
|
||||||
|
if ((cp = rindex (update_dir, '/')) != NULL)
|
||||||
|
*cp = '\0';
|
||||||
|
else
|
||||||
|
update_dir[0] = '\0';
|
||||||
|
|
||||||
|
return (err);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add a node to a list allocating the list if necessary
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
addlist (listp, key)
|
||||||
|
List **listp;
|
||||||
|
char *key;
|
||||||
|
{
|
||||||
|
Node *p;
|
||||||
|
|
||||||
|
if (*listp == NULL)
|
||||||
|
*listp = getlist ();
|
||||||
|
p = getnode ();
|
||||||
|
p->type = FILES;
|
||||||
|
p->key = xstrdup (key);
|
||||||
|
(void) addnode (*listp, p);
|
||||||
|
}
|
||||||
@@ -0,0 +1,219 @@
|
|||||||
|
/*
|
||||||
|
* Release: "cancel" a checkout in the history log.
|
||||||
|
*
|
||||||
|
* - Don't allow release if anything is active - Don't allow release if not
|
||||||
|
* above or inside repository. - Don't allow release if ./CVS/Repository is
|
||||||
|
* not the same as the directory specified in the module database.
|
||||||
|
*
|
||||||
|
* - Enter a line in the history log indicating the "release". - If asked to,
|
||||||
|
* delete the local working directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "cvs.h"
|
||||||
|
|
||||||
|
#ifndef lint
|
||||||
|
static char rcsid[] = "@(#)release.c 1.21 92/02/29";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __STDC__
|
||||||
|
static void release_delete (char *dir);
|
||||||
|
#else
|
||||||
|
static void release_delete ();
|
||||||
|
#endif /* __STDC__ */
|
||||||
|
|
||||||
|
static char *release_usage[] =
|
||||||
|
{
|
||||||
|
"Usage: %s %s [-d] modules...\n",
|
||||||
|
"\t-Q\tReally quiet.\n",
|
||||||
|
"\t-d\tDelete the given directory.\n",
|
||||||
|
"\t-q\tSomewhat quiet.\n",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
static short delete;
|
||||||
|
|
||||||
|
int
|
||||||
|
release (argc, argv)
|
||||||
|
int argc;
|
||||||
|
char **argv;
|
||||||
|
{
|
||||||
|
FILE *fp;
|
||||||
|
register int i, c;
|
||||||
|
register char *cp;
|
||||||
|
int margc;
|
||||||
|
DBM *db;
|
||||||
|
datum key, val;
|
||||||
|
char *repository, *srepos;
|
||||||
|
char **margv, *modargv[MAXFILEPERDIR], line[PATH_MAX];
|
||||||
|
|
||||||
|
if (argc == -1)
|
||||||
|
usage (release_usage);
|
||||||
|
optind = 1;
|
||||||
|
while ((c = gnu_getopt (argc, argv, "Qdq")) != -1)
|
||||||
|
{
|
||||||
|
switch (c)
|
||||||
|
{
|
||||||
|
case 'Q':
|
||||||
|
really_quiet = 1;
|
||||||
|
/* FALL THROUGH */
|
||||||
|
case 'q':
|
||||||
|
quiet = 1;
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
delete++;
|
||||||
|
break;
|
||||||
|
case '?':
|
||||||
|
default:
|
||||||
|
usage (release_usage);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
argc -= optind;
|
||||||
|
argv += optind;
|
||||||
|
|
||||||
|
if (!(db = open_module ()))
|
||||||
|
return (1);
|
||||||
|
for (i = 0; i < argc; i++)
|
||||||
|
{
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we are in a repository, do it. Else if we are in the parent of
|
||||||
|
* a directory with the same name as the module, "cd" into it and
|
||||||
|
* look for a repository there.
|
||||||
|
*/
|
||||||
|
if (isdir (argv[i]))
|
||||||
|
{
|
||||||
|
if (chdir (argv[i]) < 0)
|
||||||
|
{
|
||||||
|
if (!really_quiet)
|
||||||
|
error (0, 0, "can't chdir to: %s", argv[i]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!isdir (CVSADM) && !isdir (OCVSADM))
|
||||||
|
{
|
||||||
|
if (!really_quiet)
|
||||||
|
error (0, 0, "no repository module: %s", argv[i]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!really_quiet)
|
||||||
|
error (0, 0, "no such directory/module: %s", argv[i]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
repository = Name_Repository ((char *) NULL, (char *) NULL);
|
||||||
|
srepos = Short_Repository (repository);
|
||||||
|
|
||||||
|
/* grab module entry from database and check against short repos */
|
||||||
|
key.dptr = argv[i];
|
||||||
|
key.dsize = strlen (key.dptr);
|
||||||
|
val = dbm_fetch (db, key);
|
||||||
|
if (!val.dptr)
|
||||||
|
{
|
||||||
|
error (0, 0, "no such module name: %s", argv[i]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
val.dptr[val.dsize] = '\0';
|
||||||
|
if ((cp = index (val.dptr, '#')) != NULL) /* Strip out a comment */
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
*cp-- = '\0';
|
||||||
|
} while (isspace (*cp));
|
||||||
|
}
|
||||||
|
(void) sprintf (line, "%s %s", key.dptr, val.dptr);
|
||||||
|
line2argv (&margc, modargv, line);
|
||||||
|
margv = modargv;
|
||||||
|
|
||||||
|
optind = 1;
|
||||||
|
while (gnu_getopt (margc, margv, CVSMODULE_OPTS) != -1)
|
||||||
|
/* do nothing */ ;
|
||||||
|
margc -= optind;
|
||||||
|
margv += optind;
|
||||||
|
|
||||||
|
if (margc < 1)
|
||||||
|
{
|
||||||
|
error (0, 0, "modules file missing directory for key %s value %s",
|
||||||
|
key.dptr, val.dptr);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (strcmp (*margv, srepos))
|
||||||
|
{
|
||||||
|
error (0, 0, "repository mismatch: module[%s], here[%s]",
|
||||||
|
*margv, srepos);
|
||||||
|
free (repository);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!really_quiet)
|
||||||
|
{
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now see if there is any reason not to allow a "Release" This
|
||||||
|
* is "popen()" instead of "Popen()" since we don't want "-n" to
|
||||||
|
* stop it.
|
||||||
|
*/
|
||||||
|
fp = popen ("cvs -n -q update", "r");
|
||||||
|
c = 0;
|
||||||
|
while (fgets (line, sizeof (line), fp))
|
||||||
|
{
|
||||||
|
if (index ("MARCZ", *line))
|
||||||
|
c++;
|
||||||
|
(void) printf (line);
|
||||||
|
}
|
||||||
|
(void) pclose (fp);
|
||||||
|
(void) printf ("You have [%d] altered files in this repository.\n",
|
||||||
|
c);
|
||||||
|
(void) printf ("Are you sure you want to release %smodule `%s': ",
|
||||||
|
delete ? "(and delete) " : "", argv[i]);
|
||||||
|
c = !yesno ();
|
||||||
|
if (c) /* "No" */
|
||||||
|
{
|
||||||
|
(void) fprintf (stderr, "** `%s' aborted by user choice.\n",
|
||||||
|
command_name);
|
||||||
|
free (repository);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* So, we've passed all the tests, go ahead and release it. First,
|
||||||
|
* log the release, then attempt to delete it.
|
||||||
|
*/
|
||||||
|
history_write ('F', argv[i], "", argv[i], ""); /* F == Free */
|
||||||
|
free (repository);
|
||||||
|
|
||||||
|
if (delete)
|
||||||
|
release_delete (argv[i]);
|
||||||
|
}
|
||||||
|
close_module (db);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We want to "rm -r" the repository, but let us be a little paranoid. */
|
||||||
|
static void
|
||||||
|
release_delete (dir)
|
||||||
|
char *dir;
|
||||||
|
{
|
||||||
|
struct stat st;
|
||||||
|
ino_t ino;
|
||||||
|
int retcode = 0;
|
||||||
|
|
||||||
|
(void) stat (".", &st);
|
||||||
|
ino = st.st_ino;
|
||||||
|
(void) chdir ("..");
|
||||||
|
(void) stat (dir, &st);
|
||||||
|
if (ino != st.st_ino)
|
||||||
|
{
|
||||||
|
error (0, 0,
|
||||||
|
"Parent dir on a different disk, delete of %s aborted", dir);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
run_setup ("%s -r", RM);
|
||||||
|
run_arg (dir);
|
||||||
|
if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL)) != 0)
|
||||||
|
error (0, retcode == -1 ? errno : 0,
|
||||||
|
"deletion of module %s failed.", dir);
|
||||||
|
}
|
||||||
@@ -0,0 +1,176 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 1992, Brian Berliner and Jeff Polk
|
||||||
|
* Copyright (c) 1989-1992, Brian Berliner
|
||||||
|
*
|
||||||
|
* You may distribute under the terms of the GNU General Public License as
|
||||||
|
* specified in the README file that comes with the CVS 1.3 kit.
|
||||||
|
*
|
||||||
|
* Remove a File
|
||||||
|
*
|
||||||
|
* Removes entries from the present version. The entries will be removed from
|
||||||
|
* the RCS repository upon the next "commit".
|
||||||
|
*
|
||||||
|
* "remove" accepts no options, only file names that are to be removed. The
|
||||||
|
* file must not exist in the current directory for "remove" to work
|
||||||
|
* correctly.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "cvs.h"
|
||||||
|
|
||||||
|
#ifndef lint
|
||||||
|
static char rcsid[] = "@(#)remove.c 1.34 92/04/10";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __STDC__
|
||||||
|
static int remove_fileproc (char *file, char *update_dir,
|
||||||
|
char *repository, List *entries,
|
||||||
|
List *srcfiles);
|
||||||
|
static Dtype remove_dirproc (char *dir, char *repos, char *update_dir);
|
||||||
|
#else
|
||||||
|
static Dtype remove_dirproc ();
|
||||||
|
static int remove_fileproc ();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int local;
|
||||||
|
static int removed_files;
|
||||||
|
static int auto_removed_files;
|
||||||
|
|
||||||
|
static char *remove_usage[] =
|
||||||
|
{
|
||||||
|
"Usage: %s %s [-lR] [files...]\n",
|
||||||
|
"\t-l\tProcess this directory only (not recursive).\n",
|
||||||
|
"\t-R\tProcess directories recursively.\n",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
int
|
||||||
|
cvsremove (argc, argv)
|
||||||
|
int argc;
|
||||||
|
char *argv[];
|
||||||
|
{
|
||||||
|
int c, err;
|
||||||
|
|
||||||
|
if (argc == -1)
|
||||||
|
usage (remove_usage);
|
||||||
|
|
||||||
|
optind = 1;
|
||||||
|
while ((c = gnu_getopt (argc, argv, "lR")) != -1)
|
||||||
|
{
|
||||||
|
switch (c)
|
||||||
|
{
|
||||||
|
case 'l':
|
||||||
|
local = 1;
|
||||||
|
break;
|
||||||
|
case 'R':
|
||||||
|
local = 0;
|
||||||
|
break;
|
||||||
|
case '?':
|
||||||
|
default:
|
||||||
|
usage (remove_usage);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
argc -= optind;
|
||||||
|
argv += optind;
|
||||||
|
|
||||||
|
/* start the recursion processor */
|
||||||
|
err = start_recursion (remove_fileproc, (int (*) ()) NULL, remove_dirproc,
|
||||||
|
(int (*) ()) NULL, argc, argv, local,
|
||||||
|
W_LOCAL, 0, 1, (char *) NULL, 1);
|
||||||
|
|
||||||
|
if (removed_files)
|
||||||
|
error (0, 0, "use '%s commit' to remove %s permanently", program_name,
|
||||||
|
(removed_files == 1) ? "this file" : "these files");
|
||||||
|
else
|
||||||
|
if (!auto_removed_files)
|
||||||
|
error (0, 0, "no files removed; use `%s' to remove the file first",
|
||||||
|
RM);
|
||||||
|
|
||||||
|
return (err);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* remove the file, only if it has already been physically removed
|
||||||
|
*/
|
||||||
|
/* ARGSUSED */
|
||||||
|
static int
|
||||||
|
remove_fileproc (file, update_dir, repository, entries, srcfiles)
|
||||||
|
char *file;
|
||||||
|
char *update_dir;
|
||||||
|
char *repository;
|
||||||
|
List *entries;
|
||||||
|
List *srcfiles;
|
||||||
|
{
|
||||||
|
char fname[PATH_MAX];
|
||||||
|
Vers_TS *vers;
|
||||||
|
|
||||||
|
vers = Version_TS (repository, (char *) NULL, (char *) NULL, (char *) NULL,
|
||||||
|
file, 0, 0, entries, srcfiles);
|
||||||
|
|
||||||
|
if (vers->ts_user != NULL)
|
||||||
|
{
|
||||||
|
freevers_ts (&vers);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vers->vn_user == NULL)
|
||||||
|
{
|
||||||
|
if (!quiet)
|
||||||
|
error (0, 0, "nothing known about %s", file);
|
||||||
|
freevers_ts (&vers);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vers->vn_user[0] == '0' && vers->vn_user[1] == '\0')
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* It's a file that has been added, but not commited yet. So,
|
||||||
|
* remove the ,p and ,t file for it and scratch it from the
|
||||||
|
* entries file.
|
||||||
|
*/
|
||||||
|
Scratch_Entry (entries, file);
|
||||||
|
(void) sprintf (fname, "%s/%s%s", CVSADM, file, CVSEXT_OPT);
|
||||||
|
(void) unlink_file (fname);
|
||||||
|
(void) sprintf (fname, "%s/%s%s", CVSADM, file, CVSEXT_LOG);
|
||||||
|
(void) unlink_file (fname);
|
||||||
|
if (!quiet)
|
||||||
|
error (0, 0, "removed `%s'.", file);
|
||||||
|
auto_removed_files++;
|
||||||
|
}
|
||||||
|
else if (vers->vn_user[0] == '-')
|
||||||
|
{
|
||||||
|
freevers_ts (&vers);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Re-register it with a negative version number. */
|
||||||
|
(void) strcpy (fname, "-");
|
||||||
|
(void) strcat (fname, vers->vn_user);
|
||||||
|
Register (entries, file, fname, vers->ts_rcs, vers->options,
|
||||||
|
vers->tag, vers->date);
|
||||||
|
if (!quiet)
|
||||||
|
{
|
||||||
|
error (0, 0, "scheduling %s for removal", file);
|
||||||
|
removed_files++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
freevers_ts (&vers);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Print a warm fuzzy message
|
||||||
|
*/
|
||||||
|
/* ARGSUSED */
|
||||||
|
static Dtype
|
||||||
|
remove_dirproc (dir, repos, update_dir)
|
||||||
|
char *dir;
|
||||||
|
char *repos;
|
||||||
|
char *update_dir;
|
||||||
|
{
|
||||||
|
if (!quiet)
|
||||||
|
error (0, 0, "Removing %s", update_dir);
|
||||||
|
return (R_PROCESS);
|
||||||
|
}
|
||||||
@@ -0,0 +1,169 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 1992, Brian Berliner and Jeff Polk
|
||||||
|
* Copyright (c) 1989-1992, Brian Berliner
|
||||||
|
*
|
||||||
|
* You may distribute under the terms of the GNU General Public License as
|
||||||
|
* specified in the README file that comes with the CVS 1.3 kit.
|
||||||
|
*
|
||||||
|
* Name of Repository
|
||||||
|
*
|
||||||
|
* Determine the name of the RCS repository and sets "Repository" accordingly.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "cvs.h"
|
||||||
|
|
||||||
|
#ifndef lint
|
||||||
|
static char rcsid[] = "@(#)repos.c 1.28 92/03/31";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
char *
|
||||||
|
Name_Repository (dir, update_dir)
|
||||||
|
char *dir;
|
||||||
|
char *update_dir;
|
||||||
|
{
|
||||||
|
FILE *fpin;
|
||||||
|
char *ret, *xupdate_dir;
|
||||||
|
char repos[PATH_MAX];
|
||||||
|
char path[PATH_MAX];
|
||||||
|
char tmp[PATH_MAX];
|
||||||
|
char cvsadm[PATH_MAX];
|
||||||
|
char ocvsadm[PATH_MAX];
|
||||||
|
char *cp;
|
||||||
|
int has_cvsadm = 0, has_ocvsadm = 0;
|
||||||
|
|
||||||
|
if (update_dir && *update_dir)
|
||||||
|
xupdate_dir = update_dir;
|
||||||
|
else
|
||||||
|
xupdate_dir = ".";
|
||||||
|
|
||||||
|
if (dir != NULL)
|
||||||
|
{
|
||||||
|
(void) sprintf (cvsadm, "%s/%s", dir, CVSADM);
|
||||||
|
(void) sprintf (ocvsadm, "%s/%s", dir, OCVSADM);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
(void) strcpy (cvsadm, CVSADM);
|
||||||
|
(void) strcpy (ocvsadm, OCVSADM);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* sanity checks */
|
||||||
|
if (!(has_cvsadm = isdir (cvsadm)) && !(has_ocvsadm = isdir (ocvsadm)))
|
||||||
|
{
|
||||||
|
error (0, 0, "in directory %s:", xupdate_dir);
|
||||||
|
error (1, 0, "there is no version here; do '%s checkout' first",
|
||||||
|
program_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_ocvsadm)
|
||||||
|
{
|
||||||
|
if (has_cvsadm)
|
||||||
|
{
|
||||||
|
error (0, 0, "in directory %s:", xupdate_dir);
|
||||||
|
error (1, 0, "error: both `%s' and `%s' exist; I give up",
|
||||||
|
CVSADM, OCVSADM);
|
||||||
|
}
|
||||||
|
if (rename (ocvsadm, cvsadm) < 0)
|
||||||
|
{
|
||||||
|
error (0, 0, "in directory %s:", xupdate_dir);
|
||||||
|
error (1, errno, "cannot rename `%s' to `%s'; I give up",
|
||||||
|
OCVSADM, CVSADM);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We have converted the old CVS.adm directory to the new CVS
|
||||||
|
* directory. Now, convert the Entries file to the new format, if
|
||||||
|
* necessary.
|
||||||
|
*/
|
||||||
|
check_entries (dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dir != NULL)
|
||||||
|
(void) sprintf (tmp, "%s/%s", dir, CVSADM_ENT);
|
||||||
|
else
|
||||||
|
(void) strcpy (tmp, CVSADM_ENT);
|
||||||
|
|
||||||
|
if (!isreadable (tmp))
|
||||||
|
{
|
||||||
|
error (0, 0, "in directory %s:", xupdate_dir);
|
||||||
|
error (1, 0, "*PANIC* administration files missing");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dir != NULL)
|
||||||
|
(void) sprintf (tmp, "%s/%s", dir, CVSADM_REP);
|
||||||
|
else
|
||||||
|
(void) strcpy (tmp, CVSADM_REP);
|
||||||
|
|
||||||
|
if (!isreadable (tmp))
|
||||||
|
{
|
||||||
|
error (0, 0, "in directory %s:", xupdate_dir);
|
||||||
|
error (1, 0, "*PANIC* administration files missing");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The assumption here is that the repository is always contained in the
|
||||||
|
* first line of the "Repository" file.
|
||||||
|
*/
|
||||||
|
fpin = open_file (tmp, "r");
|
||||||
|
|
||||||
|
if (fgets (repos, PATH_MAX, fpin) == NULL)
|
||||||
|
{
|
||||||
|
error (0, 0, "in directory %s:", xupdate_dir);
|
||||||
|
error (1, errno, "cannot read %s", CVSADM_REP);
|
||||||
|
}
|
||||||
|
(void) fclose (fpin);
|
||||||
|
if ((cp = rindex (repos, '\n')) != NULL)
|
||||||
|
*cp = '\0'; /* strip the newline */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If this is a relative repository pathname, turn it into an absolute
|
||||||
|
* one by tacking on the CVSROOT environment variable. If the CVSROOT
|
||||||
|
* environment variable is not set, die now.
|
||||||
|
*/
|
||||||
|
if (strcmp (repos, "..") == 0 || strncmp (repos, "../", 3) == 0)
|
||||||
|
{
|
||||||
|
error (0, 0, "in directory %s:", xupdate_dir);
|
||||||
|
error (0, 0, "`..'-relative repositories are not supported.");
|
||||||
|
error (1, 0, "illegal source repository");
|
||||||
|
}
|
||||||
|
if (repos[0] != '/')
|
||||||
|
{
|
||||||
|
if (CVSroot == NULL)
|
||||||
|
{
|
||||||
|
error (0, 0, "in directory %s:", xupdate_dir);
|
||||||
|
error (0, 0, "must set the CVSROOT environment variable\n");
|
||||||
|
error (0, 0, "or specify the '-d' option to %s.", program_name);
|
||||||
|
error (1, 0, "illegal repository setting");
|
||||||
|
}
|
||||||
|
(void) strcpy (path, repos);
|
||||||
|
(void) sprintf (repos, "%s/%s", CVSroot, path);
|
||||||
|
}
|
||||||
|
if (!isdir (repos))
|
||||||
|
{
|
||||||
|
error (0, 0, "in directory %s:", xupdate_dir);
|
||||||
|
error (1, 0, "there is no repository %s", repos);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* allocate space to return and fill it in */
|
||||||
|
strip_path (repos);
|
||||||
|
ret = xstrdup (repos);
|
||||||
|
return (ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return a pointer to the repository name relative to CVSROOT from a
|
||||||
|
* possibly fully qualified repository
|
||||||
|
*/
|
||||||
|
char *
|
||||||
|
Short_Repository (repository)
|
||||||
|
char *repository;
|
||||||
|
{
|
||||||
|
if (repository == NULL)
|
||||||
|
return (NULL);
|
||||||
|
|
||||||
|
/* if repository matches CVSroot at the beginning, strip off CVSroot */
|
||||||
|
if (strncmp (CVSroot, repository, strlen (CVSroot)) == 0)
|
||||||
|
return (repository + strlen (CVSroot) + 1);
|
||||||
|
else
|
||||||
|
return (repository);
|
||||||
|
}
|
||||||
@@ -0,0 +1,403 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 1992, Brian Berliner and Jeff Polk
|
||||||
|
* Copyright (c) 1989-1992, Brian Berliner
|
||||||
|
*
|
||||||
|
* You may distribute under the terms of the GNU General Public License as
|
||||||
|
* specified in the README file that comes with the CVS 1.3 kit.
|
||||||
|
*
|
||||||
|
* Rtag
|
||||||
|
*
|
||||||
|
* Add or delete a symbolic name to an RCS file, or a collection of RCS files.
|
||||||
|
* Uses the modules database, if necessary.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "cvs.h"
|
||||||
|
|
||||||
|
#ifndef lint
|
||||||
|
static char rcsid[] = "@(#)rtag.c 1.57 92/04/10";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __STDC__
|
||||||
|
static Dtype rtag_dirproc (char *dir, char *repos, char *update_dir);
|
||||||
|
static int rtag_fileproc (char *file, char *update_dir,
|
||||||
|
char *repository, List * entries,
|
||||||
|
List * srcfiles);
|
||||||
|
static int rtag_proc (int *pargc, char *argv[], char *xwhere,
|
||||||
|
char *mwhere, char *mfile, int shorten,
|
||||||
|
int local_specified, char *mname, char *msg);
|
||||||
|
static int rtag_delete (RCSNode *rcsfile);
|
||||||
|
#else
|
||||||
|
static int rtag_proc ();
|
||||||
|
static int rtag_fileproc ();
|
||||||
|
static Dtype rtag_dirproc ();
|
||||||
|
static int rtag_delete ();
|
||||||
|
#endif /* __STDC__ */
|
||||||
|
|
||||||
|
static char *symtag;
|
||||||
|
static char *numtag;
|
||||||
|
static int delete; /* adding a tag by default */
|
||||||
|
static int attic_too; /* remove tag from Attic files */
|
||||||
|
static int branch_mode; /* make an automagic "branch" tag */
|
||||||
|
static char *date;
|
||||||
|
static int local; /* recursive by default */
|
||||||
|
static int force_tag_match = 1; /* force by default */
|
||||||
|
|
||||||
|
static char *rtag_usage[] =
|
||||||
|
{
|
||||||
|
"Usage: %s %s [-QaflRnq] [-b] [-d] [-r tag|-D date] tag modules...\n",
|
||||||
|
"\t-Q\tReally quiet.\n",
|
||||||
|
"\t-a\tClear tag from removed files that would not otherwise be tagged.\n",
|
||||||
|
"\t-f\tForce a head revision match if tag/date not found.\n",
|
||||||
|
"\t-l\tLocal directory only, not recursive\n",
|
||||||
|
"\t-R\tProcess directories recursively.\n",
|
||||||
|
"\t-n\tNo execution of 'tag program'\n",
|
||||||
|
"\t-q\tSomewhat quiet.\n",
|
||||||
|
"\t-d\tDelete the given Tag.\n",
|
||||||
|
"\t-b\tMake the tag a \"branch\" tag, allowing concurrent development.\n",
|
||||||
|
"\t-[rD]\tExisting tag or Date.\n",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
int
|
||||||
|
rtag (argc, argv)
|
||||||
|
int argc;
|
||||||
|
char *argv[];
|
||||||
|
{
|
||||||
|
register int i;
|
||||||
|
int c;
|
||||||
|
DBM *db;
|
||||||
|
int run_module_prog = 1;
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
if (argc == -1)
|
||||||
|
usage (rtag_usage);
|
||||||
|
|
||||||
|
optind = 1;
|
||||||
|
while ((c = gnu_getopt (argc, argv, "anfQqlRdbr:D:")) != -1)
|
||||||
|
{
|
||||||
|
switch (c)
|
||||||
|
{
|
||||||
|
case 'a':
|
||||||
|
attic_too = 1;
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
run_module_prog = 0;
|
||||||
|
break;
|
||||||
|
case 'Q':
|
||||||
|
really_quiet = 1;
|
||||||
|
/* FALL THROUGH */
|
||||||
|
case 'q':
|
||||||
|
quiet = 1;
|
||||||
|
break;
|
||||||
|
case 'l':
|
||||||
|
local = 1;
|
||||||
|
break;
|
||||||
|
case 'R':
|
||||||
|
local = 0;
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
delete = 1;
|
||||||
|
break;
|
||||||
|
case 'f':
|
||||||
|
force_tag_match = 0;
|
||||||
|
break;
|
||||||
|
case 'b':
|
||||||
|
branch_mode = 1;
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
numtag = optarg;
|
||||||
|
break;
|
||||||
|
case 'D':
|
||||||
|
if (date)
|
||||||
|
free (date);
|
||||||
|
date = Make_Date (optarg);
|
||||||
|
break;
|
||||||
|
case '?':
|
||||||
|
default:
|
||||||
|
usage (rtag_usage);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
argc -= optind;
|
||||||
|
argv += optind;
|
||||||
|
if (argc < 2)
|
||||||
|
usage (rtag_usage);
|
||||||
|
symtag = argv[0];
|
||||||
|
argc--;
|
||||||
|
argv++;
|
||||||
|
|
||||||
|
if (date && numtag)
|
||||||
|
error (1, 0, "-r and -D options are mutually exclusive");
|
||||||
|
if (delete && branch_mode)
|
||||||
|
error (0, 0, "warning: -b ignored with -d options");
|
||||||
|
RCS_check_tag (symtag);
|
||||||
|
|
||||||
|
db = open_module ();
|
||||||
|
for (i = 0; i < argc; i++)
|
||||||
|
{
|
||||||
|
/* XXX last arg should be repository, but doesn't make sense here */
|
||||||
|
history_write ('T', (delete ? "D" : (numtag ? numtag :
|
||||||
|
(date ? date : "A"))), symtag, argv[i], "");
|
||||||
|
err += do_module (db, argv[i], TAG, delete ? "Untagging" : "Tagging",
|
||||||
|
rtag_proc, (char *) NULL, 0, 0, run_module_prog,
|
||||||
|
symtag);
|
||||||
|
}
|
||||||
|
close_module (db);
|
||||||
|
return (err);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* callback proc for doing the real work of tagging
|
||||||
|
*/
|
||||||
|
/* ARGSUSED */
|
||||||
|
static int
|
||||||
|
rtag_proc (pargc, argv, xwhere, mwhere, mfile, shorten, local_specified,
|
||||||
|
mname, msg)
|
||||||
|
int *pargc;
|
||||||
|
char *argv[];
|
||||||
|
char *xwhere;
|
||||||
|
char *mwhere;
|
||||||
|
char *mfile;
|
||||||
|
int shorten;
|
||||||
|
int local_specified;
|
||||||
|
char *mname;
|
||||||
|
char *msg;
|
||||||
|
{
|
||||||
|
int err = 0;
|
||||||
|
int which;
|
||||||
|
char repository[PATH_MAX];
|
||||||
|
char where[PATH_MAX];
|
||||||
|
|
||||||
|
(void) sprintf (repository, "%s/%s", CVSroot, argv[0]);
|
||||||
|
(void) strcpy (where, argv[0]);
|
||||||
|
|
||||||
|
/* if mfile isn't null, we need to set up to do only part of the module */
|
||||||
|
if (mfile != NULL)
|
||||||
|
{
|
||||||
|
char *cp;
|
||||||
|
char path[PATH_MAX];
|
||||||
|
|
||||||
|
/* if the portion of the module is a path, put the dir part on repos */
|
||||||
|
if ((cp = rindex (mfile, '/')) != NULL)
|
||||||
|
{
|
||||||
|
*cp = '\0';
|
||||||
|
(void) strcat (repository, "/");
|
||||||
|
(void) strcat (repository, mfile);
|
||||||
|
(void) strcat (where, "/");
|
||||||
|
(void) strcat (where, mfile);
|
||||||
|
mfile = cp + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* take care of the rest */
|
||||||
|
(void) sprintf (path, "%s/%s", repository, mfile);
|
||||||
|
if (isdir (path))
|
||||||
|
{
|
||||||
|
/* directory means repository gets the dir tacked on */
|
||||||
|
(void) strcpy (repository, path);
|
||||||
|
(void) strcat (where, "/");
|
||||||
|
(void) strcat (where, mfile);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* a file means muck argv */
|
||||||
|
for (i = 1; i < *pargc; i++)
|
||||||
|
free (argv[i]);
|
||||||
|
argv[1] = xstrdup (mfile);
|
||||||
|
(*pargc) = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* chdir to the starting directory */
|
||||||
|
if (chdir (repository) < 0)
|
||||||
|
{
|
||||||
|
error (0, errno, "cannot chdir to %s", repository);
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (delete || attic_too || (force_tag_match && numtag))
|
||||||
|
which = W_REPOS | W_ATTIC;
|
||||||
|
else
|
||||||
|
which = W_REPOS;
|
||||||
|
|
||||||
|
/* start the recursion processor */
|
||||||
|
err = start_recursion (rtag_fileproc, (int (*) ()) NULL, rtag_dirproc,
|
||||||
|
(int (*) ()) NULL, *pargc - 1, argv + 1, local,
|
||||||
|
which, 0, 1, where, 1);
|
||||||
|
|
||||||
|
return (err);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Called to tag a particular file, as appropriate with the options that were
|
||||||
|
* set above.
|
||||||
|
*/
|
||||||
|
/* ARGSUSED */
|
||||||
|
static int
|
||||||
|
rtag_fileproc (file, update_dir, repository, entries, srcfiles)
|
||||||
|
char *file;
|
||||||
|
char *update_dir;
|
||||||
|
char *repository;
|
||||||
|
List *entries;
|
||||||
|
List *srcfiles;
|
||||||
|
{
|
||||||
|
Node *p;
|
||||||
|
RCSNode *rcsfile;
|
||||||
|
char *version, *rev;
|
||||||
|
int retcode = 0;
|
||||||
|
|
||||||
|
/* find the parsed RCS data */
|
||||||
|
p = findnode (srcfiles, file);
|
||||||
|
if (p == NULL)
|
||||||
|
return (1);
|
||||||
|
rcsfile = (RCSNode *) p->data;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For tagging an RCS file which is a symbolic link, you'd best be
|
||||||
|
* running with RCS 5.6, since it knows how to handle symbolic links
|
||||||
|
* correctly without breaking your link!
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (delete)
|
||||||
|
return (rtag_delete (rcsfile));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we get here, we are adding a tag. But, if -a was specified, we
|
||||||
|
* need to check to see if a -r or -D option was specified. If neither
|
||||||
|
* was specified and the file is in the Attic, remove the tag.
|
||||||
|
*/
|
||||||
|
if (attic_too && (!numtag && !date))
|
||||||
|
{
|
||||||
|
if ((rcsfile->flags & VALID) && (rcsfile->flags & INATTIC))
|
||||||
|
return (rtag_delete (rcsfile));
|
||||||
|
}
|
||||||
|
|
||||||
|
version = RCS_getversion (rcsfile, numtag, date, force_tag_match);
|
||||||
|
if (version == NULL)
|
||||||
|
{
|
||||||
|
/* If -a specified, clean up any old tags */
|
||||||
|
if (attic_too)
|
||||||
|
(void) rtag_delete (rcsfile);
|
||||||
|
|
||||||
|
if (!quiet && !force_tag_match)
|
||||||
|
{
|
||||||
|
error (0, 0, "cannot find tag `%s' in `%s'",
|
||||||
|
numtag ? numtag : "head", rcsfile->path);
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
if (numtag && isdigit (*numtag) && strcmp (numtag, version) != 0)
|
||||||
|
{
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We didn't find a match for the numeric tag that was specified, but
|
||||||
|
* that's OK. just pass the numeric tag on to rcs, to be tagged as
|
||||||
|
* specified. Could get here if one tried to tag "1.1.1" and there
|
||||||
|
* was a 1.1.1 branch with some head revision. In this case, we want
|
||||||
|
* the tag to reference "1.1.1" and not the revision at the head of
|
||||||
|
* the branch. Use a symbolic tag for that.
|
||||||
|
*/
|
||||||
|
rev = branch_mode ? RCS_magicrev (rcsfile, version) : numtag;
|
||||||
|
run_setup ("%s%s -q -N%s:%s", Rcsbin, RCS, symtag, numtag);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
char *oversion;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* As an enhancement for the case where a tag is being re-applied to
|
||||||
|
* a large body of a module, make one extra call to Version_Number to
|
||||||
|
* see if the tag is already set in the RCS file. If so, check to
|
||||||
|
* see if it needs to be moved. If not, do nothing. This will
|
||||||
|
* likely save a lot of time when simply moving the tag to the
|
||||||
|
* "current" head revisions of a module -- which I have found to be a
|
||||||
|
* typical tagging operation.
|
||||||
|
*/
|
||||||
|
oversion = RCS_getversion (rcsfile, symtag, (char *) 0, 1);
|
||||||
|
if (oversion != NULL)
|
||||||
|
{
|
||||||
|
if (strcmp (version, oversion) == 0)
|
||||||
|
{
|
||||||
|
free (version);
|
||||||
|
free (oversion);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
free (oversion);
|
||||||
|
}
|
||||||
|
rev = branch_mode ? RCS_magicrev (rcsfile, version) : version;
|
||||||
|
run_setup ("%s%s -q -N%s:%s", Rcsbin, RCS, symtag, rev);
|
||||||
|
}
|
||||||
|
run_arg (rcsfile->path);
|
||||||
|
if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL)) != 0)
|
||||||
|
{
|
||||||
|
if (!quiet)
|
||||||
|
error (0, retcode == -1 ? errno : 0,
|
||||||
|
"failed to set tag `%s' to revision `%s' in `%s'",
|
||||||
|
symtag, rev, rcsfile->path);
|
||||||
|
free (version);
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
free (version);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If -d is specified, "force_tag_match" is set, so that this call to
|
||||||
|
* Version_Number() will return a NULL version string if the symbolic
|
||||||
|
* tag does not exist in the RCS file.
|
||||||
|
*
|
||||||
|
* If the -r flag was used, numtag is set, and we only delete the
|
||||||
|
* symtag from files that have numtag.
|
||||||
|
*
|
||||||
|
* This is done here because it's MUCH faster than just blindly calling
|
||||||
|
* "rcs" to remove the tag... trust me.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
rtag_delete (rcsfile)
|
||||||
|
RCSNode *rcsfile;
|
||||||
|
{
|
||||||
|
char *version;
|
||||||
|
int retcode;
|
||||||
|
|
||||||
|
if (numtag)
|
||||||
|
{
|
||||||
|
version = RCS_getversion (rcsfile, numtag, (char *) 0, 1);
|
||||||
|
if (version == NULL)
|
||||||
|
return (0);
|
||||||
|
free (version);
|
||||||
|
}
|
||||||
|
|
||||||
|
version = RCS_getversion (rcsfile, symtag, (char *) 0, 1);
|
||||||
|
if (version == NULL)
|
||||||
|
return (0);
|
||||||
|
free (version);
|
||||||
|
|
||||||
|
run_setup ("%s%s -q -N%s", Rcsbin, RCS, symtag);
|
||||||
|
run_arg (rcsfile->path);
|
||||||
|
if ((retcode = run_exec (RUN_TTY, RUN_TTY, DEVNULL, RUN_NORMAL)) != 0)
|
||||||
|
{
|
||||||
|
if (!quiet)
|
||||||
|
error (0, retcode == -1 ? errno : 0,
|
||||||
|
"failed to remove tag `%s' from `%s'", symtag,
|
||||||
|
rcsfile->path);
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Print a warm fuzzy message
|
||||||
|
*/
|
||||||
|
/* ARGSUSED */
|
||||||
|
static Dtype
|
||||||
|
rtag_dirproc (dir, repos, update_dir)
|
||||||
|
char *dir;
|
||||||
|
char *repos;
|
||||||
|
char *update_dir;
|
||||||
|
{
|
||||||
|
if (!quiet)
|
||||||
|
error (0, 0, "%s %s", delete ? "Untagging" : "Tagging", update_dir);
|
||||||
|
return (R_PROCESS);
|
||||||
|
}
|
||||||
@@ -0,0 +1,230 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 1992, Brian Berliner and Jeff Polk
|
||||||
|
* Copyright (c) 1989-1992, Brian Berliner
|
||||||
|
*
|
||||||
|
* You may distribute under the terms of the GNU General Public License as
|
||||||
|
* specified in the README file that comes with the CVS 1.3 kit.
|
||||||
|
*
|
||||||
|
* Status Information
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "cvs.h"
|
||||||
|
|
||||||
|
#ifndef lint
|
||||||
|
static char rcsid[] = "@(#)status.c 1.48 92/03/31";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __STDC__
|
||||||
|
static Dtype status_dirproc (char *dir, char *repos, char *update_dir);
|
||||||
|
static int status_fileproc (char *file, char *update_dir,
|
||||||
|
char *repository, List * entries,
|
||||||
|
List * srcfiles);
|
||||||
|
static int tag_list_proc (Node * p);
|
||||||
|
#else
|
||||||
|
static int tag_list_proc ();
|
||||||
|
static int status_fileproc ();
|
||||||
|
static Dtype status_dirproc ();
|
||||||
|
#endif /* __STDC__ */
|
||||||
|
|
||||||
|
static int local = 0;
|
||||||
|
static int long_format = 0;
|
||||||
|
|
||||||
|
static char *status_usage[] =
|
||||||
|
{
|
||||||
|
"Usage: %s %s [-vlR] [files...]\n",
|
||||||
|
"\t-v\tVerbose format; includes tag information for the file\n",
|
||||||
|
"\t-l\tProcess this directory only (not recursive).\n",
|
||||||
|
"\t-R\tProcess directories recursively.\n",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
int
|
||||||
|
status (argc, argv)
|
||||||
|
int argc;
|
||||||
|
char *argv[];
|
||||||
|
{
|
||||||
|
int c;
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
if (argc == -1)
|
||||||
|
usage (status_usage);
|
||||||
|
|
||||||
|
optind = 1;
|
||||||
|
while ((c = gnu_getopt (argc, argv, "vlR")) != -1)
|
||||||
|
{
|
||||||
|
switch (c)
|
||||||
|
{
|
||||||
|
case 'v':
|
||||||
|
long_format = 1;
|
||||||
|
break;
|
||||||
|
case 'l':
|
||||||
|
local = 1;
|
||||||
|
break;
|
||||||
|
case 'R':
|
||||||
|
local = 0;
|
||||||
|
break;
|
||||||
|
case '?':
|
||||||
|
default:
|
||||||
|
usage (status_usage);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
argc -= optind;
|
||||||
|
argv += optind;
|
||||||
|
|
||||||
|
/* start the recursion processor */
|
||||||
|
err = start_recursion (status_fileproc, (int (*) ()) NULL, status_dirproc,
|
||||||
|
(int (*) ()) NULL, argc, argv, local,
|
||||||
|
W_LOCAL, 0, 1, (char *) NULL, 1);
|
||||||
|
|
||||||
|
return (err);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* display the status of a file
|
||||||
|
*/
|
||||||
|
/* ARGSUSED */
|
||||||
|
static int
|
||||||
|
status_fileproc (file, update_dir, repository, entries, srcfiles)
|
||||||
|
char *file;
|
||||||
|
char *update_dir;
|
||||||
|
char *repository;
|
||||||
|
List *entries;
|
||||||
|
List *srcfiles;
|
||||||
|
{
|
||||||
|
Ctype status;
|
||||||
|
char *sstat;
|
||||||
|
Vers_TS *vers;
|
||||||
|
|
||||||
|
status = Classify_File (file, (char *) NULL, (char *) NULL, (char *) NULL,
|
||||||
|
1, 0, repository, entries, srcfiles, &vers);
|
||||||
|
switch (status)
|
||||||
|
{
|
||||||
|
case T_UNKNOWN:
|
||||||
|
sstat = "Unknown";
|
||||||
|
break;
|
||||||
|
case T_CHECKOUT:
|
||||||
|
sstat = "Needs Checkout";
|
||||||
|
break;
|
||||||
|
case T_CONFLICT:
|
||||||
|
sstat = "Unresolved Conflict";
|
||||||
|
break;
|
||||||
|
case T_ADDED:
|
||||||
|
sstat = "Locally Added";
|
||||||
|
break;
|
||||||
|
case T_REMOVED:
|
||||||
|
sstat = "Locally Removed";
|
||||||
|
break;
|
||||||
|
case T_MODIFIED:
|
||||||
|
sstat = "Locally Modified";
|
||||||
|
break;
|
||||||
|
case T_REMOVE_ENTRY:
|
||||||
|
sstat = "Entry Invalid";
|
||||||
|
break;
|
||||||
|
case T_UPTODATE:
|
||||||
|
sstat = "Up-to-date";
|
||||||
|
break;
|
||||||
|
case T_NEEDS_MERGE:
|
||||||
|
sstat = "Needs Merge";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
sstat = "Classify Error";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
(void) printf ("===================================================================\n");
|
||||||
|
if (vers->ts_user == NULL)
|
||||||
|
(void) printf ("File: no file %s\t\tStatus: %s\n\n", file, sstat);
|
||||||
|
else
|
||||||
|
(void) printf ("File: %-17.17s\tStatus: %s\n\n", file, sstat);
|
||||||
|
|
||||||
|
if (vers->vn_user == NULL)
|
||||||
|
(void) printf (" Version:\t\tNo entry for %s\n", file);
|
||||||
|
else if (vers->vn_user[0] == '0' && vers->vn_user[1] == '\0')
|
||||||
|
(void) printf (" Version:\t\tNew file!\n");
|
||||||
|
else
|
||||||
|
(void) printf (" Version:\t\t%s\t%s\n", vers->vn_user,
|
||||||
|
&vers->ts_rcs[25]);
|
||||||
|
|
||||||
|
if (vers->vn_rcs == NULL)
|
||||||
|
(void) printf (" RCS Version:\tNo revision control file\n");
|
||||||
|
else
|
||||||
|
(void) printf (" RCS Version:\t%s\t%s\n", vers->vn_rcs,
|
||||||
|
vers->srcfile->path);
|
||||||
|
|
||||||
|
if (vers->entdata)
|
||||||
|
{
|
||||||
|
Entnode *edata;
|
||||||
|
|
||||||
|
edata = vers->entdata;
|
||||||
|
if (edata->tag)
|
||||||
|
{
|
||||||
|
if (vers->vn_rcs == NULL)
|
||||||
|
(void) printf (
|
||||||
|
" Sticky Tag:\t\t%s - MISSING from RCS file!\n",
|
||||||
|
edata->tag);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (isdigit (edata->tag[0]))
|
||||||
|
(void) printf (" Sticky Tag:\t\t%s\n", edata->tag);
|
||||||
|
else
|
||||||
|
(void) printf (" Sticky Tag:\t\t%s (%s: %s)\n",
|
||||||
|
edata->tag, numdots (vers->vn_rcs) % 2 ?
|
||||||
|
"revision" : "branch", vers->vn_rcs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
(void) printf (" Sticky Tag:\t\t(none)\n");
|
||||||
|
|
||||||
|
if (edata->date)
|
||||||
|
(void) printf (" Sticky Date:\t%s\n", edata->date);
|
||||||
|
else
|
||||||
|
(void) printf (" Sticky Date:\t(none)\n");
|
||||||
|
|
||||||
|
if (edata->options && edata->options[0])
|
||||||
|
(void) printf (" Sticky Options:\t%s\n", edata->options);
|
||||||
|
else
|
||||||
|
(void) printf (" Sticky Options:\t(none)\n");
|
||||||
|
|
||||||
|
if (long_format && vers->srcfile)
|
||||||
|
{
|
||||||
|
(void) printf ("\n Existing Tags:\n");
|
||||||
|
if (vers->srcfile->symbols)
|
||||||
|
(void) walklist (vers->srcfile->symbols, tag_list_proc);
|
||||||
|
else
|
||||||
|
(void) printf ("\tNo Tags Exist\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(void) printf ("\n");
|
||||||
|
freevers_ts (&vers);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Print a warm fuzzy message
|
||||||
|
*/
|
||||||
|
/* ARGSUSED */
|
||||||
|
static Dtype
|
||||||
|
status_dirproc (dir, repos, update_dir)
|
||||||
|
char *dir;
|
||||||
|
char *repos;
|
||||||
|
char *update_dir;
|
||||||
|
{
|
||||||
|
if (!quiet)
|
||||||
|
error (0, 0, "Examining %s", update_dir);
|
||||||
|
return (R_PROCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Print out a tag and its type
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
tag_list_proc (p)
|
||||||
|
Node *p;
|
||||||
|
{
|
||||||
|
(void) printf ("\t%-25.25s\t(%s: %s)\n", p->key,
|
||||||
|
numdots (p->data) % 2 ? "revision" : "branch",
|
||||||
|
p->data);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
@@ -0,0 +1,263 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 1992, Brian Berliner and Jeff Polk
|
||||||
|
* Copyright (c) 1989-1992, Brian Berliner
|
||||||
|
*
|
||||||
|
* You may distribute under the terms of the GNU General Public License as
|
||||||
|
* specified in the README file that comes with the CVS 1.3 kit.
|
||||||
|
*
|
||||||
|
* Tag
|
||||||
|
*
|
||||||
|
* Add or delete a symbolic name to an RCS file, or a collection of RCS files.
|
||||||
|
* Uses the checked out revision in the current directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "cvs.h"
|
||||||
|
|
||||||
|
#ifndef lint
|
||||||
|
static char rcsid[] = "@(#)tag.c 1.56 92/03/31";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __STDC__
|
||||||
|
static Dtype tag_dirproc (char *dir, char *repos, char *update_dir);
|
||||||
|
static int tag_fileproc (char *file, char *update_dir,
|
||||||
|
char *repository, List * entries,
|
||||||
|
List * srcfiles);
|
||||||
|
#else
|
||||||
|
static int tag_fileproc ();
|
||||||
|
static Dtype tag_dirproc ();
|
||||||
|
#endif /* __STDC__ */
|
||||||
|
|
||||||
|
static char *symtag;
|
||||||
|
static int delete; /* adding a tag by default */
|
||||||
|
static int branch_mode; /* make an automagic "branch" tag */
|
||||||
|
static int local; /* recursive by default */
|
||||||
|
|
||||||
|
static char *tag_usage[] =
|
||||||
|
{
|
||||||
|
"Usage: %s %s [-QlRq] [-b] [-d] tag [files...]\n",
|
||||||
|
"\t-Q\tReally quiet.\n",
|
||||||
|
"\t-l\tLocal directory only, not recursive.\n",
|
||||||
|
"\t-R\tProcess directories recursively.\n",
|
||||||
|
"\t-q\tSomewhat quiet.\n",
|
||||||
|
"\t-d\tDelete the given Tag.\n",
|
||||||
|
"\t-b\tMake the tag a \"branch\" tag, allowing concurrent development.\n",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
int
|
||||||
|
tag (argc, argv)
|
||||||
|
int argc;
|
||||||
|
char *argv[];
|
||||||
|
{
|
||||||
|
int c;
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
if (argc == -1)
|
||||||
|
usage (tag_usage);
|
||||||
|
|
||||||
|
optind = 1;
|
||||||
|
while ((c = gnu_getopt (argc, argv, "QqlRdb")) != -1)
|
||||||
|
{
|
||||||
|
switch (c)
|
||||||
|
{
|
||||||
|
case 'Q':
|
||||||
|
really_quiet = 1;
|
||||||
|
/* FALL THROUGH */
|
||||||
|
case 'q':
|
||||||
|
quiet = 1;
|
||||||
|
break;
|
||||||
|
case 'l':
|
||||||
|
local = 1;
|
||||||
|
break;
|
||||||
|
case 'R':
|
||||||
|
local = 0;
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
delete = 1;
|
||||||
|
break;
|
||||||
|
case 'b':
|
||||||
|
branch_mode = 1;
|
||||||
|
break;
|
||||||
|
case '?':
|
||||||
|
default:
|
||||||
|
usage (tag_usage);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
argc -= optind;
|
||||||
|
argv += optind;
|
||||||
|
|
||||||
|
if (argc == 0)
|
||||||
|
usage (tag_usage);
|
||||||
|
symtag = argv[0];
|
||||||
|
argc--;
|
||||||
|
argv++;
|
||||||
|
|
||||||
|
if (delete && branch_mode)
|
||||||
|
error (0, 0, "warning: -b ignored with -d options");
|
||||||
|
RCS_check_tag (symtag);
|
||||||
|
|
||||||
|
/* start the recursion processor */
|
||||||
|
err = start_recursion (tag_fileproc, (int (*) ()) NULL, tag_dirproc,
|
||||||
|
(int (*) ()) NULL, argc, argv, local,
|
||||||
|
W_LOCAL, 0, 1, (char *) NULL, 1);
|
||||||
|
return (err);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Called to tag a particular file (the currently checked out version is
|
||||||
|
* tagged with the specified tag - or the specified tag is deleted).
|
||||||
|
*/
|
||||||
|
/* ARGSUSED */
|
||||||
|
static int
|
||||||
|
tag_fileproc (file, update_dir, repository, entries, srcfiles)
|
||||||
|
char *file;
|
||||||
|
char *update_dir;
|
||||||
|
char *repository;
|
||||||
|
List *entries;
|
||||||
|
List *srcfiles;
|
||||||
|
{
|
||||||
|
char *version, *oversion;
|
||||||
|
char *rev;
|
||||||
|
Vers_TS *vers;
|
||||||
|
int retcode = 0;
|
||||||
|
|
||||||
|
vers = Version_TS (repository, (char *) NULL, (char *) NULL, (char *) NULL,
|
||||||
|
file, 0, 0, entries, srcfiles);
|
||||||
|
|
||||||
|
if (delete)
|
||||||
|
{
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If -d is specified, "force_tag_match" is set, so that this call to
|
||||||
|
* Version_Number() will return a NULL version string if the symbolic
|
||||||
|
* tag does not exist in the RCS file.
|
||||||
|
*
|
||||||
|
* This is done here because it's MUCH faster than just blindly calling
|
||||||
|
* "rcs" to remove the tag... trust me.
|
||||||
|
*/
|
||||||
|
|
||||||
|
version = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1);
|
||||||
|
if (version == NULL || vers->srcfile == NULL)
|
||||||
|
{
|
||||||
|
freevers_ts (&vers);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
free (version);
|
||||||
|
|
||||||
|
run_setup ("%s%s -q -N%s", Rcsbin, RCS, symtag);
|
||||||
|
run_arg (vers->srcfile->path);
|
||||||
|
if ((retcode = run_exec (RUN_TTY, RUN_TTY, DEVNULL, RUN_NORMAL)) != 0)
|
||||||
|
{
|
||||||
|
if (!quiet)
|
||||||
|
error (0, retcode == -1 ? errno : 0,
|
||||||
|
"failed to remove tag %s from %s", symtag,
|
||||||
|
vers->srcfile->path);
|
||||||
|
freevers_ts (&vers);
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* warm fuzzies */
|
||||||
|
if (!really_quiet)
|
||||||
|
{
|
||||||
|
if (update_dir[0])
|
||||||
|
(void) printf ("D %s/%s\n", update_dir, file);
|
||||||
|
else
|
||||||
|
(void) printf ("D %s\n", file);
|
||||||
|
}
|
||||||
|
|
||||||
|
freevers_ts (&vers);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we are adding a tag, we need to know which version we have checked
|
||||||
|
* out and we'll tag that version.
|
||||||
|
*/
|
||||||
|
version = vers->vn_user;
|
||||||
|
if (version == NULL)
|
||||||
|
{
|
||||||
|
freevers_ts (&vers);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
else if (strcmp (version, "0") == 0)
|
||||||
|
{
|
||||||
|
if (!quiet)
|
||||||
|
error (0, 0, "couldn't tag added but un-commited file `%s'", file);
|
||||||
|
freevers_ts (&vers);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
else if (version[0] == '-')
|
||||||
|
{
|
||||||
|
if (!quiet)
|
||||||
|
error (0, 0, "skipping removed but un-commited file `%s'", file);
|
||||||
|
freevers_ts (&vers);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
else if (vers->srcfile == NULL)
|
||||||
|
{
|
||||||
|
if (!quiet)
|
||||||
|
error (0, 0, "cannot find revision control file for `%s'", file);
|
||||||
|
freevers_ts (&vers);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* As an enhancement for the case where a tag is being re-applied to a
|
||||||
|
* large number of files, make one extra call to Version_Number to see if
|
||||||
|
* the tag is already set in the RCS file. If so, check to see if it
|
||||||
|
* needs to be moved. If not, do nothing. This will likely save a lot of
|
||||||
|
* time when simply moving the tag to the "current" head revisions of a
|
||||||
|
* module -- which I have found to be a typical tagging operation.
|
||||||
|
*/
|
||||||
|
oversion = RCS_getversion (vers->srcfile, symtag, (char *) NULL, 1);
|
||||||
|
if (oversion != NULL)
|
||||||
|
{
|
||||||
|
if (strcmp (version, oversion) == 0)
|
||||||
|
{
|
||||||
|
free (oversion);
|
||||||
|
freevers_ts (&vers);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
free (oversion);
|
||||||
|
}
|
||||||
|
rev = branch_mode ? RCS_magicrev (vers->srcfile, version) : version;
|
||||||
|
run_setup ("%s%s -q -N%s:%s", Rcsbin, RCS, symtag, rev);
|
||||||
|
run_arg (vers->srcfile->path);
|
||||||
|
if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL)) != 0)
|
||||||
|
{
|
||||||
|
if (!quiet)
|
||||||
|
error (0, retcode == -1 ? errno : 0,
|
||||||
|
"failed to set tag %s to revision %s in %s",
|
||||||
|
symtag, rev, vers->srcfile->path);
|
||||||
|
freevers_ts (&vers);
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* more warm fuzzies */
|
||||||
|
if (!really_quiet)
|
||||||
|
{
|
||||||
|
if (update_dir[0])
|
||||||
|
(void) printf ("T %s/%s\n", update_dir, file);
|
||||||
|
else
|
||||||
|
(void) printf ("T %s\n", file);
|
||||||
|
}
|
||||||
|
|
||||||
|
freevers_ts (&vers);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Print a warm fuzzy message
|
||||||
|
*/
|
||||||
|
/* ARGSUSED */
|
||||||
|
static Dtype
|
||||||
|
tag_dirproc (dir, repos, update_dir)
|
||||||
|
char *dir;
|
||||||
|
char *repos;
|
||||||
|
char *update_dir;
|
||||||
|
{
|
||||||
|
if (!quiet)
|
||||||
|
error (0, 0, "%s %s", delete ? "Untagging" : "Tagging", update_dir);
|
||||||
|
return (R_PROCESS);
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,224 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 1992, Brian Berliner and Jeff Polk
|
||||||
|
* Copyright (c) 1989-1992, Brian Berliner
|
||||||
|
*
|
||||||
|
* You may distribute under the terms of the GNU General Public License as
|
||||||
|
* specified in the README file that comes with the CVS 1.3 kit.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "cvs.h"
|
||||||
|
|
||||||
|
#ifndef lint
|
||||||
|
static char rcsid[] = "@(#)vers_ts.c 1.36 92/03/31";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern char *ctime (); /* XXX - should use gmtime/asctime */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fill in and return a Vers_TS structure "user" is the name of the local
|
||||||
|
* file; entries is the entries file - preparsed for our pleasure. xfiles is
|
||||||
|
* all source code control files, preparsed for our pleasure
|
||||||
|
*/
|
||||||
|
Vers_TS *
|
||||||
|
Version_TS (repository, options, tag, date, user, force_tag_match,
|
||||||
|
set_time, entries, xfiles)
|
||||||
|
char *repository;
|
||||||
|
char *options;
|
||||||
|
char *tag;
|
||||||
|
char *date;
|
||||||
|
char *user;
|
||||||
|
int force_tag_match;
|
||||||
|
int set_time;
|
||||||
|
List *entries;
|
||||||
|
List *xfiles;
|
||||||
|
{
|
||||||
|
Node *p;
|
||||||
|
RCSNode *rcsdata;
|
||||||
|
Vers_TS *vers_ts;
|
||||||
|
struct stickydirtag *sdtp;
|
||||||
|
|
||||||
|
/* get a new Vers_TS struct */
|
||||||
|
vers_ts = (Vers_TS *) xmalloc (sizeof (Vers_TS));
|
||||||
|
bzero ((char *) vers_ts, sizeof (*vers_ts));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* look up the entries file entry and fill in the version and timestamp
|
||||||
|
* if entries is NULL, there is no entries file so don't bother trying to
|
||||||
|
* look it up (used by checkout -P)
|
||||||
|
*/
|
||||||
|
if (entries == NULL)
|
||||||
|
{
|
||||||
|
sdtp = NULL;
|
||||||
|
p = NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
p = findnode (entries, user);
|
||||||
|
sdtp = (struct stickydirtag *) entries->list->data; /* list-private */
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p != NULL)
|
||||||
|
{
|
||||||
|
Entnode *entdata = (Entnode *) p->data;
|
||||||
|
|
||||||
|
vers_ts->vn_user = xstrdup (entdata->version);
|
||||||
|
vers_ts->ts_rcs = xstrdup (entdata->timestamp);
|
||||||
|
if (!tag)
|
||||||
|
{
|
||||||
|
if (!(sdtp && sdtp->aflag))
|
||||||
|
vers_ts->tag = xstrdup (entdata->tag);
|
||||||
|
}
|
||||||
|
if (!date)
|
||||||
|
{
|
||||||
|
if (!(sdtp && sdtp->aflag))
|
||||||
|
vers_ts->date = xstrdup (entdata->date);
|
||||||
|
}
|
||||||
|
if (!options || (options && *options == '\0'))
|
||||||
|
{
|
||||||
|
if (!(sdtp && sdtp->aflag))
|
||||||
|
vers_ts->options = xstrdup (entdata->options);
|
||||||
|
}
|
||||||
|
vers_ts->entdata = entdata;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* -k options specified on the command line override (and overwrite)
|
||||||
|
* options stored in the entries file
|
||||||
|
*/
|
||||||
|
if (options)
|
||||||
|
vers_ts->options = xstrdup (options);
|
||||||
|
else if (sdtp && sdtp->aflag == 0)
|
||||||
|
{
|
||||||
|
if (!vers_ts->options)
|
||||||
|
vers_ts->options = xstrdup (sdtp->options);
|
||||||
|
}
|
||||||
|
if (!vers_ts->options)
|
||||||
|
vers_ts->options = xstrdup ("");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* if tags were specified on the command line, they override what is in
|
||||||
|
* the Entries file
|
||||||
|
*/
|
||||||
|
if (tag || date)
|
||||||
|
{
|
||||||
|
vers_ts->tag = xstrdup (tag);
|
||||||
|
vers_ts->date = xstrdup (date);
|
||||||
|
}
|
||||||
|
else if (!vers_ts->entdata && (sdtp && sdtp->aflag == 0))
|
||||||
|
{
|
||||||
|
if (!vers_ts->tag)
|
||||||
|
vers_ts->tag = xstrdup (sdtp->tag);
|
||||||
|
if (!vers_ts->date)
|
||||||
|
vers_ts->date = xstrdup (sdtp->date);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Now look up the info on the source controlled file */
|
||||||
|
if (xfiles != (List *) NULL)
|
||||||
|
{
|
||||||
|
p = findnode (xfiles, user);
|
||||||
|
if (p != NULL)
|
||||||
|
{
|
||||||
|
rcsdata = (RCSNode *) p->data;
|
||||||
|
rcsdata->refcount++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
rcsdata = NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
rcsdata = RCS_parse (user, repository);
|
||||||
|
|
||||||
|
if (rcsdata != NULL)
|
||||||
|
{
|
||||||
|
/* squirrel away the rcsdata pointer for others */
|
||||||
|
vers_ts->srcfile = rcsdata;
|
||||||
|
|
||||||
|
/* get RCS version number into vn_rcs (if appropriate) */
|
||||||
|
if (((vers_ts->tag || vers_ts->date) && force_tag_match) ||
|
||||||
|
((rcsdata->flags & VALID) && (rcsdata->flags & INATTIC) == 0))
|
||||||
|
{
|
||||||
|
if (vers_ts->tag && strcmp (vers_ts->tag, TAG_BASE) == 0)
|
||||||
|
vers_ts->vn_rcs = xstrdup (vers_ts->vn_user);
|
||||||
|
else
|
||||||
|
vers_ts->vn_rcs = RCS_getversion (rcsdata, vers_ts->tag,
|
||||||
|
vers_ts->date, force_tag_match);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the source control file exists and has the requested revision,
|
||||||
|
* get the Date the revision was checked in. If "user" exists, set
|
||||||
|
* its mtime.
|
||||||
|
*/
|
||||||
|
if (set_time)
|
||||||
|
{
|
||||||
|
struct utimbuf t;
|
||||||
|
|
||||||
|
if (vers_ts->vn_rcs &&
|
||||||
|
(t.actime = t.modtime = RCS_getrevtime (rcsdata, vers_ts->vn_rcs,
|
||||||
|
(char *) 0, 0)) != -1)
|
||||||
|
(void) utime (user, &t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* get user file time-stamp in ts_user */
|
||||||
|
if (entries != (List *) NULL)
|
||||||
|
vers_ts->ts_user = time_stamp (user);
|
||||||
|
|
||||||
|
return (vers_ts);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Gets the time-stamp for the file "file" and returns it in space it
|
||||||
|
* allocates
|
||||||
|
*/
|
||||||
|
char *
|
||||||
|
time_stamp (file)
|
||||||
|
char *file;
|
||||||
|
{
|
||||||
|
struct stat sb;
|
||||||
|
char *cp;
|
||||||
|
char *ts;
|
||||||
|
|
||||||
|
if (stat (file, &sb) < 0)
|
||||||
|
{
|
||||||
|
ts = NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ts = xmalloc (51); /* 51 = 2 ctime strings + NULL */
|
||||||
|
cp = ctime (&sb.st_ctime); /* copy in the create time */
|
||||||
|
cp[24] = ' ';
|
||||||
|
(void) strcpy (ts, cp);
|
||||||
|
cp = ctime (&sb.st_mtime); /* copy in the modify time */
|
||||||
|
cp[24] = '\0';
|
||||||
|
(void) strcat (ts, cp);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (ts);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* free up a Vers_TS struct
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
freevers_ts (versp)
|
||||||
|
Vers_TS **versp;
|
||||||
|
{
|
||||||
|
if ((*versp)->srcfile)
|
||||||
|
freercsnode (&((*versp)->srcfile));
|
||||||
|
if ((*versp)->vn_user)
|
||||||
|
free ((*versp)->vn_user);
|
||||||
|
if ((*versp)->vn_rcs)
|
||||||
|
free ((*versp)->vn_rcs);
|
||||||
|
if ((*versp)->ts_user)
|
||||||
|
free ((*versp)->ts_user);
|
||||||
|
if ((*versp)->ts_rcs)
|
||||||
|
free ((*versp)->ts_rcs);
|
||||||
|
if ((*versp)->options)
|
||||||
|
free ((*versp)->options);
|
||||||
|
if ((*versp)->tag)
|
||||||
|
free ((*versp)->tag);
|
||||||
|
if ((*versp)->date)
|
||||||
|
free ((*versp)->date);
|
||||||
|
free ((char *) *versp);
|
||||||
|
*versp = (Vers_TS *) NULL;
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 1992, Brian Berliner and Jeff Polk
|
||||||
|
* Copyright (c) 1989-1992, Brian Berliner
|
||||||
|
*
|
||||||
|
* You may distribute under the terms of the GNU General Public License as
|
||||||
|
* specified in the README file that comes with the CVS 1.3 kit.
|
||||||
|
*
|
||||||
|
* version.c - the CVS version number
|
||||||
|
*/
|
||||||
|
|
||||||
|
char *version_string = "\nConcurrent Versions System (CVS) 1.3\n";
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,21 @@
|
|||||||
|
#
|
||||||
|
# commitinfo,v 1.2 1992/03/31 04:19:47 berliner Exp
|
||||||
|
#
|
||||||
|
# The "commitinfo" file is used to control pre-commit checks.
|
||||||
|
# The filter on the right is invoked with the repository and a list
|
||||||
|
# of files to check. A non-zero exit of the filter program will
|
||||||
|
# cause the commit to be aborted.
|
||||||
|
#
|
||||||
|
# The first entry on a line is a regular expression which is tested
|
||||||
|
# against the directory that the change is being committed to, relative to the
|
||||||
|
# $CVSROOT. If a match is found, then the remainder of the line is the
|
||||||
|
# name of the filter to run.
|
||||||
|
#
|
||||||
|
# If the repository name does not match any of the regular expressions in this
|
||||||
|
# file, the "DEFAULT" line is used, if it is specified.
|
||||||
|
#
|
||||||
|
# If the name ALL appears as a regular expression it is always used
|
||||||
|
# in addition to the first matching regex or DEFAULT.
|
||||||
|
#
|
||||||
|
^cvs checkforcvsid
|
||||||
|
DEFAULT checkforid
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
#
|
||||||
|
# editinfo,v 1.1 1992/03/21 06:49:39 berliner Exp
|
||||||
|
#
|
||||||
|
# The "editinfo" file is used to allow verification of logging
|
||||||
|
# information. It works best when a template (as specified in the
|
||||||
|
# rcsinfo file) is provided for the logging procedure. Given a
|
||||||
|
# template with locations for, a bug-id number, a list of people who
|
||||||
|
# reviewed the code before it can be checked in, and an external
|
||||||
|
# process to catalog the differences that were code reviewed, the
|
||||||
|
# following test can be applied to the code:
|
||||||
|
#
|
||||||
|
# Making sure that the entered bug-id number is correct.
|
||||||
|
# Validating that the code that was reviewed is indeed the code being
|
||||||
|
# checked in (using the bug-id number or a seperate review
|
||||||
|
# number to identify this particular code set.).
|
||||||
|
#
|
||||||
|
# If any of the above test failed, then the commit would be aborted.
|
||||||
|
#
|
||||||
|
# Actions such as mailing a copy of the report to each reviewer are
|
||||||
|
# better handled by an entry in the loginfo file.
|
||||||
|
#
|
||||||
|
# Although these test could be handled by an interactive script being
|
||||||
|
# called via an entry in commitinfo, The information reported in
|
||||||
|
# such a script can't be easily merged into the report.
|
||||||
|
#
|
||||||
|
# One thing that should be noted is the the ALL keyword is not
|
||||||
|
# supported. There can be only one entry that matches a given
|
||||||
|
# repository.
|
||||||
|
#
|
||||||
|
DEFAULT $CVSROOT/CVSROOT/edit "%s"
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
#
|
||||||
|
# @(#)loginfo 1.5 92/03/31
|
||||||
|
#
|
||||||
|
# The "loginfo" file is used to control where "cvs commit" log information
|
||||||
|
# is sent. The first entry on a line is a regular expression which is tested
|
||||||
|
# against the directory that the change is being made to, relative to the
|
||||||
|
# $CVSROOT. If a match is found, then the remainder of the line is a filter
|
||||||
|
# program that should expect log information on its standard input.
|
||||||
|
#
|
||||||
|
# The filter program may use one and only one % modifier (ala printf). If
|
||||||
|
# %s is specified in the filter program, a brief title is included (enclosed
|
||||||
|
# in single quotes) showing the modified file names.
|
||||||
|
#
|
||||||
|
# If the repository name does not match any of the regular expressions in this
|
||||||
|
# file, the "DEFAULT" line is used, if it is specified.
|
||||||
|
#
|
||||||
|
# If the name ALL appears as a regular expression it is always used
|
||||||
|
# in addition to the first matching regex or DEFAULT.
|
||||||
|
#
|
||||||
|
DEFAULT $CVSROOT/CVSROOT/log.pl %s $CVSROOT/CVSROOT/commitlog
|
||||||
@@ -0,0 +1,566 @@
|
|||||||
|
#
|
||||||
|
# CVS Modules file for Prisma sources
|
||||||
|
# @(#)modules 1.5 92/03/31
|
||||||
|
#
|
||||||
|
# Three different line formats are valid:
|
||||||
|
# key -a aliases...
|
||||||
|
# key [options] directory
|
||||||
|
# key [options] directory files...
|
||||||
|
#
|
||||||
|
# Where "options" are composed of:
|
||||||
|
# -i prog Run "prog" on "cvs commit" from top-level of module.
|
||||||
|
# -o prog Run "prog" on "cvs checkout" of module.
|
||||||
|
# -t prog Run "prog" on "cvs rtag" of module.
|
||||||
|
# -u prog Run "prog" on "cvs update" of module.
|
||||||
|
# -d dir Place module in directory "dir" instead of module name.
|
||||||
|
# -l Top-level directory only -- do not recurse.
|
||||||
|
#
|
||||||
|
# And "directory" is a path to a directory relative to $CVSROOT.
|
||||||
|
#
|
||||||
|
# The "-a" option specifies an alias. An alias is interpreted as if
|
||||||
|
# everything on the right of the "-a" had been typed on the command line.
|
||||||
|
#
|
||||||
|
# You can encode a module within a module by using the special '&'
|
||||||
|
# character to interpose another module into the current module. This
|
||||||
|
# can be useful for creating a module that consists of many directories
|
||||||
|
# spread out over the entire source repository.
|
||||||
|
#
|
||||||
|
|
||||||
|
# Convenient aliases
|
||||||
|
world -a .
|
||||||
|
kernel -a sys lang/adb sparcsim
|
||||||
|
|
||||||
|
# CVSROOT support
|
||||||
|
CVSROOT -i /usr/local/bin/mkmodules CVSROOT
|
||||||
|
modules -i /usr/local/bin/mkmodules CVSROOT modules
|
||||||
|
loginfo -i /usr/local/bin/mkmodules CVSROOT loginfo
|
||||||
|
commitinfo -i /usr/local/bin/mkmodules CVSROOT commitinfo
|
||||||
|
rcsinfo -i /usr/local/bin/mkmodules CVSROOT rcsinfo
|
||||||
|
|
||||||
|
# The "sys" entry exists only to make symbolic links after checkout
|
||||||
|
sys -o sys/tools/make_links sys
|
||||||
|
|
||||||
|
# Sub-directories of "bin"
|
||||||
|
awk bin/awk
|
||||||
|
csh bin/csh
|
||||||
|
diff bin/diff
|
||||||
|
make bin/make
|
||||||
|
sed bin/sed
|
||||||
|
sh bin/sh
|
||||||
|
|
||||||
|
# Programs that live in "bin"
|
||||||
|
cat bin Makefile cat.c
|
||||||
|
chgrp bin Makefile chgrp.c
|
||||||
|
chmod bin Makefile chmod.c
|
||||||
|
cmp bin Makefile cmp.c
|
||||||
|
cp bin Makefile cp.c
|
||||||
|
date bin Makefile date.c
|
||||||
|
dd bin Makefile dd.c
|
||||||
|
df bin Makefile df.c
|
||||||
|
domainname bin Makefile domainname.c
|
||||||
|
du bin Makefile du.c
|
||||||
|
echo bin Makefile echo.c
|
||||||
|
ed bin Makefile ed.c
|
||||||
|
env bin Makefile env.c
|
||||||
|
expr bin Makefile expr.c
|
||||||
|
grep bin Makefile grep.c
|
||||||
|
hostid bin Makefile hostid.c
|
||||||
|
hostname bin Makefile hostname.c
|
||||||
|
kill bin Makefile kill.c
|
||||||
|
ldd bin Makefile ldd.c
|
||||||
|
line bin Makefile line.c
|
||||||
|
ln bin Makefile ln.c
|
||||||
|
login bin Makefile login.c
|
||||||
|
ls bin Makefile ls.c
|
||||||
|
mail bin Makefile mail.c
|
||||||
|
mkdir bin Makefile mkdir.c
|
||||||
|
mt bin Makefile mt.c
|
||||||
|
mv bin Makefile mv.c
|
||||||
|
newgrp bin Makefile newgrp.c
|
||||||
|
nice bin Makefile nice.c
|
||||||
|
od bin Makefile od.c
|
||||||
|
pagesize bin Makefile pagesize.c
|
||||||
|
passwd bin Makefile passwd.c
|
||||||
|
pr bin Makefile pr.c
|
||||||
|
ps bin Makefile ps.c
|
||||||
|
pwd bin Makefile pwd.c
|
||||||
|
rm bin Makefile rm.c
|
||||||
|
rmail bin Makefile rmail.c
|
||||||
|
rmdir bin Makefile rmdir.c
|
||||||
|
stty bin Makefile stty.c
|
||||||
|
su bin Makefile su.c
|
||||||
|
sync bin Makefile sync.c
|
||||||
|
tar bin Makefile tar.c
|
||||||
|
tee bin Makefile tee.c
|
||||||
|
test bin Makefile test.c
|
||||||
|
time bin Makefile time.c
|
||||||
|
wall bin Makefile wall.c
|
||||||
|
who bin Makefile who.c
|
||||||
|
write bin Makefile write.c
|
||||||
|
|
||||||
|
# Sub-directories of "etc"
|
||||||
|
dump etc/dump
|
||||||
|
files etc/files
|
||||||
|
fsck etc/fsck
|
||||||
|
getty etc/getty
|
||||||
|
in.routed etc/in.routed
|
||||||
|
restore etc/restore
|
||||||
|
rpc.lockd etc/rpc.lockd
|
||||||
|
rpc.statd etc/rpc.statd
|
||||||
|
|
||||||
|
# Programs that live in "etc"
|
||||||
|
arp etc Makefile arp.c
|
||||||
|
biod etc Makefile biod.c
|
||||||
|
chown etc Makefile chown.c
|
||||||
|
clri etc Makefile clri.c
|
||||||
|
dkinfo etc Makefile dkinfo.c
|
||||||
|
dmesg etc Makefile dmesg.c
|
||||||
|
fsirand etc Makefile fsirand.c
|
||||||
|
halt etc Makefile halt.c
|
||||||
|
ifconfig etc Makefile ifconfig.c
|
||||||
|
in.rlogind etc Makefile in.rlogind.c
|
||||||
|
in.rshd etc Makefile in.rshd.c
|
||||||
|
inetd etc Makefile inetd.c
|
||||||
|
init etc Makefile init.c
|
||||||
|
mkfs etc Makefile mkfs.c
|
||||||
|
mknod etc Makefile mknod.c
|
||||||
|
mount etc Makefile mount.c
|
||||||
|
newfs etc Makefile newfs.c
|
||||||
|
nfsd etc Makefile nfsd.c
|
||||||
|
portmap etc Makefile portmap.c
|
||||||
|
pstat etc Makefile pstat.c
|
||||||
|
reboot etc Makefile reboot.c
|
||||||
|
renice etc Makefile renice.c
|
||||||
|
rmt etc Makefile rmt.c
|
||||||
|
shutdown etc Makefile shutdown.c
|
||||||
|
syslogd etc Makefile syslogd.c
|
||||||
|
umount etc Makefile umount.c
|
||||||
|
update etc Makefile update.c
|
||||||
|
vipw etc Makefile vipw.c
|
||||||
|
ypbind etc Makefile ypbind.c
|
||||||
|
|
||||||
|
# Sub-directories of "games"
|
||||||
|
adventure games/adventure
|
||||||
|
backgammon games/backgammon
|
||||||
|
battlestar games/battlestar
|
||||||
|
boggle games/boggle
|
||||||
|
chess games/chess
|
||||||
|
ching games/ching
|
||||||
|
cribbage games/cribbage
|
||||||
|
fortune games/fortune
|
||||||
|
hack games/hack
|
||||||
|
hangman games/hangman
|
||||||
|
hunt games/hunt
|
||||||
|
life games/life
|
||||||
|
mille games/mille
|
||||||
|
monop games/monop
|
||||||
|
quiz games/quiz
|
||||||
|
robots games/robots
|
||||||
|
sail games/sail
|
||||||
|
snake games/snake
|
||||||
|
trek games/trek
|
||||||
|
|
||||||
|
# Programs that live in "games"
|
||||||
|
arithmetic games Makefile arithmetic.c
|
||||||
|
banner games Makefile banner.c
|
||||||
|
bcd games Makefile bcd.c
|
||||||
|
bj games Makefile bj.c
|
||||||
|
btlgammon games Makefile btlgammon.c
|
||||||
|
canfield games Makefile canfield.c
|
||||||
|
cfscores games Makefile cfscores.c
|
||||||
|
craps games Makefile craps.c
|
||||||
|
factor games Makefile factor.c
|
||||||
|
fish games Makefile fish.c
|
||||||
|
moo games Makefile moo.c
|
||||||
|
number games Makefile number.c
|
||||||
|
primes games Makefile primes.c
|
||||||
|
rain games Makefile rain.c
|
||||||
|
random games Makefile random.c
|
||||||
|
worm games Makefile worm.c
|
||||||
|
worms games Makefile worms.c
|
||||||
|
wump games Makefile wump.c
|
||||||
|
|
||||||
|
# Sub-directories of "lang"
|
||||||
|
adb lang/adb
|
||||||
|
as lang/as
|
||||||
|
boot lang/boot
|
||||||
|
c2 lang/c2
|
||||||
|
cgrdr lang/cgrdr
|
||||||
|
compile lang/compile
|
||||||
|
cpp lang/cpp
|
||||||
|
dbx lang/dbx
|
||||||
|
f77 lang/f77
|
||||||
|
inline lang/inline
|
||||||
|
iropt lang/iropt
|
||||||
|
ld lang/ld
|
||||||
|
lint lang/lint
|
||||||
|
m4 lang/m4
|
||||||
|
pascal lang/pascal
|
||||||
|
pcc lang/pcc
|
||||||
|
ratfor lang/ratfor
|
||||||
|
rtld lang/rtld
|
||||||
|
tcov lang/tcov
|
||||||
|
vroot lang/vroot
|
||||||
|
|
||||||
|
# Programs that live in "lang"
|
||||||
|
ar lang Makefile ar.c
|
||||||
|
nm lang Makefile nm.c
|
||||||
|
ranlib lang Makefile ranlib.c
|
||||||
|
size lang Makefile size.c
|
||||||
|
strip lang Makefile strip.c
|
||||||
|
symorder lang Makefile symorder.c
|
||||||
|
|
||||||
|
# Sub-directories of "lib"
|
||||||
|
csu lib/csu
|
||||||
|
libc lib/libc
|
||||||
|
|
||||||
|
# Programs that live in "lib"
|
||||||
|
# NONE
|
||||||
|
|
||||||
|
# Sub-directories of "lib/libc"
|
||||||
|
libc_compat lib/libc/compat
|
||||||
|
libc_crt lib/libc/crt
|
||||||
|
libc_des lib/libc/des
|
||||||
|
libc_gen lib/libc/gen
|
||||||
|
libc_net lib/libc/net
|
||||||
|
libc_inet lib/libc/inet
|
||||||
|
libc_rpc lib/libc/rpc
|
||||||
|
libc_stdio lib/libc/stdio
|
||||||
|
libc_sun lib/libc/sun
|
||||||
|
libc_sys lib/libc/sys
|
||||||
|
libc_yp lib/libc/yp
|
||||||
|
|
||||||
|
# Programs that live in "lib/libc"
|
||||||
|
# NONE
|
||||||
|
|
||||||
|
#Sub-directories of "local"
|
||||||
|
notes local/notes
|
||||||
|
|
||||||
|
# Sub-directories of "man"
|
||||||
|
man1 man/man1
|
||||||
|
man2 man/man2
|
||||||
|
man3 man/man3
|
||||||
|
man4 man/man4
|
||||||
|
man5 man/man5
|
||||||
|
man6 man/man6
|
||||||
|
man7 man/man7
|
||||||
|
man8 man/man8
|
||||||
|
manl man/manl
|
||||||
|
|
||||||
|
# Programs that live in "man"
|
||||||
|
# NONE
|
||||||
|
|
||||||
|
# Sub-directories of "old"
|
||||||
|
old_compact old/compact
|
||||||
|
old_eyacc old/eyacc
|
||||||
|
old_filemerge old/filemerge
|
||||||
|
old_make old/make
|
||||||
|
|
||||||
|
# Programs that live in "old"
|
||||||
|
old_analyze old Makefile analyze.c
|
||||||
|
old_prmail old Makefile prmail.c
|
||||||
|
old_pti old Makefile pti.c
|
||||||
|
old_syslog old Makefile syslog.c
|
||||||
|
|
||||||
|
# Sub-directories of "ucb"
|
||||||
|
Mail ucb/Mail
|
||||||
|
compress ucb/compress
|
||||||
|
error ucb/error
|
||||||
|
ex ucb/ex
|
||||||
|
ftp ucb/ftp
|
||||||
|
gprof ucb/gprof
|
||||||
|
indent ucb/indent
|
||||||
|
lpr ucb/lpr
|
||||||
|
more ucb/more
|
||||||
|
msgs ucb/msgs
|
||||||
|
netstat ucb/netstat
|
||||||
|
rdist ucb/rdist
|
||||||
|
talk ucb/talk
|
||||||
|
tftp ucb/tftp
|
||||||
|
tset ucb/tset
|
||||||
|
vgrind ucb/vgrind
|
||||||
|
|
||||||
|
# Programs that live in "ucb"
|
||||||
|
biff ucb Makefile biff.c
|
||||||
|
checknr ucb Makefile checknr.c
|
||||||
|
clear ucb Makefile clear.c
|
||||||
|
colcrt ucb Makefile colcrt.c
|
||||||
|
colrm ucb Makefile colrm.c
|
||||||
|
ctags ucb Makefile ctags.c
|
||||||
|
expand ucb Makefile expand.c
|
||||||
|
finger ucb Makefile finger.c
|
||||||
|
fold ucb Makefile fold.c
|
||||||
|
from ucb Makefile from.c
|
||||||
|
fsplit ucb Makefile fsplit.c
|
||||||
|
gcore ucb Makefile gcore.c
|
||||||
|
groups ucb Makefile groups.c
|
||||||
|
head ucb Makefile head.c
|
||||||
|
last ucb Makefile last.c
|
||||||
|
lastcomm ucb Makefile lastcomm.c
|
||||||
|
leave ucb Makefile leave.c
|
||||||
|
logger ucb Makefile logger.c
|
||||||
|
man_prog ucb Makefile man.c
|
||||||
|
mkstr ucb Makefile mkstr.c
|
||||||
|
printenv ucb Makefile printenv.c
|
||||||
|
quota ucb Makefile quota.c
|
||||||
|
rcp ucb Makefile rcp.c
|
||||||
|
rdate ucb Makefile rdate.c
|
||||||
|
rlogin ucb Makefile rlogin.c
|
||||||
|
rsh ucb Makefile rsh.c
|
||||||
|
rup ucb Makefile rup.c
|
||||||
|
ruptime ucb Makefile ruptime.c
|
||||||
|
rusers ucb Makefile rusers.c
|
||||||
|
rwho ucb Makefile rwho.c
|
||||||
|
sccs ucb Makefile sccs.c
|
||||||
|
script ucb Makefile script.c
|
||||||
|
soelim ucb Makefile soelim.c
|
||||||
|
strings ucb Makefile strings.c
|
||||||
|
tail ucb Makefile tail.c
|
||||||
|
tcopy ucb Makefile tcopy.c
|
||||||
|
telnet ucb Makefile telnet.c
|
||||||
|
ul ucb Makefile ul.c
|
||||||
|
unexpand ucb Makefile unexpand.c
|
||||||
|
unifdef ucb Makefile unifdef.c
|
||||||
|
users ucb Makefile users.c
|
||||||
|
vmstat ucb Makefile vmstat.c
|
||||||
|
w ucb Makefile w.c
|
||||||
|
wc ucb Makefile wc.c
|
||||||
|
what ucb Makefile what.c
|
||||||
|
whatis ucb Makefile whatis.c
|
||||||
|
whereis ucb Makefile whereis.c
|
||||||
|
whoami ucb Makefile whoami.c
|
||||||
|
whois ucb Makefile whois.c
|
||||||
|
xstr ucb Makefile xstr.c
|
||||||
|
yes ucb Makefile yes.c
|
||||||
|
|
||||||
|
# Sub-directories of "usr.bin"
|
||||||
|
calendar usr.bin/calendar
|
||||||
|
cflow usr.bin/cflow
|
||||||
|
ctrace usr.bin/ctrace
|
||||||
|
cxref usr.bin/cxref
|
||||||
|
dc usr.bin/dc
|
||||||
|
des usr.bin/des
|
||||||
|
diff3 usr.bin/diff3
|
||||||
|
sun_eqn usr.bin/eqn
|
||||||
|
file usr.bin/file
|
||||||
|
find usr.bin/find
|
||||||
|
graph usr.bin/graph
|
||||||
|
lex usr.bin/lex
|
||||||
|
sun_neqn usr.bin/neqn
|
||||||
|
sun_nroff usr.bin/nroff
|
||||||
|
sun_plot usr.bin/plot
|
||||||
|
prof usr.bin/prof
|
||||||
|
refer usr.bin/refer
|
||||||
|
rpcgen usr.bin/rpcgen
|
||||||
|
spell usr.bin/spell
|
||||||
|
sun_tbl usr.bin/tbl
|
||||||
|
tip usr.bin/tip
|
||||||
|
trace usr.bin/trace
|
||||||
|
sun_troff usr.bin/troff
|
||||||
|
uucp usr.bin/uucp
|
||||||
|
xsend usr.bin/xsend
|
||||||
|
yacc usr.bin/yacc
|
||||||
|
|
||||||
|
# Programs that live in "usr.bin"
|
||||||
|
basename usr.bin Makefile basename.c
|
||||||
|
bc usr.bin Makefile bc.c
|
||||||
|
cal usr.bin Makefile cal.c
|
||||||
|
cb usr.bin Makefile cb.c
|
||||||
|
checkeq usr.bin Makefile checkeq.c
|
||||||
|
chkey usr.bin Makefile chkey.c
|
||||||
|
click usr.bin Makefile click.c
|
||||||
|
col usr.bin Makefile col.c
|
||||||
|
comm usr.bin Makefile comm.c
|
||||||
|
cpio usr.bin Makefile cpio.c
|
||||||
|
crypt usr.bin Makefile crypt.c
|
||||||
|
csplit usr.bin Makefile csplit.c
|
||||||
|
cut usr.bin Makefile cut.c
|
||||||
|
deroff usr.bin Makefile deroff.c
|
||||||
|
egrep usr.bin Makefile egrep.c
|
||||||
|
fgrep usr.bin Makefile fgrep.c
|
||||||
|
getopt usr.bin Makefile getopt.c
|
||||||
|
id usr.bin Makefile id.c
|
||||||
|
installcmd usr.bin Makefile installcmd.c
|
||||||
|
iostat usr.bin Makefile iostat.c
|
||||||
|
ipcrm usr.bin Makefile ipcrm.c
|
||||||
|
ipcs usr.bin Makefile ipcs.c
|
||||||
|
join usr.bin Makefile join.c
|
||||||
|
keylogin usr.bin Makefile keylogin.c
|
||||||
|
logname usr.bin Makefile logname.c
|
||||||
|
look usr.bin Makefile look.c
|
||||||
|
mesg usr.bin Makefile mesg.c
|
||||||
|
nl usr.bin Makefile nl.c
|
||||||
|
pack usr.bin Makefile pack.c
|
||||||
|
paste usr.bin Makefile paste.c
|
||||||
|
ptx usr.bin Makefile ptx.c
|
||||||
|
rev usr.bin Makefile rev.c
|
||||||
|
screenblank usr.bin Makefile screenblank.c
|
||||||
|
sdiff usr.bin Makefile sdiff.c
|
||||||
|
sleep usr.bin Makefile sleep.c
|
||||||
|
sort usr.bin Makefile sort.c
|
||||||
|
spline usr.bin Makefile spline.c
|
||||||
|
split usr.bin Makefile split.c
|
||||||
|
sum usr.bin Makefile sum.c
|
||||||
|
touch usr.bin Makefile touch.c
|
||||||
|
tr usr.bin Makefile tr.c
|
||||||
|
tsort usr.bin Makefile tsort.c
|
||||||
|
tty usr.bin Makefile tty.c
|
||||||
|
uniq usr.bin Makefile uniq.c
|
||||||
|
units usr.bin Makefile units.c
|
||||||
|
unpack usr.bin Makefile unpack.c
|
||||||
|
xargs usr.bin Makefile xargs.c
|
||||||
|
ypcat usr.bin Makefile ypcat.c
|
||||||
|
ypmatch usr.bin Makefile ypmatch.c
|
||||||
|
yppasswd usr.bin Makefile yppasswd.c
|
||||||
|
ypwhich usr.bin Makefile ypwhich.c
|
||||||
|
|
||||||
|
# Sub-directories of "usr.etc"
|
||||||
|
automount usr.etc/automount
|
||||||
|
c2convert usr.etc/c2convert
|
||||||
|
config usr.etc/config
|
||||||
|
cron usr.etc/cron
|
||||||
|
eeprom usr.etc/eeprom
|
||||||
|
etherfind usr.etc/etherfind
|
||||||
|
format usr.etc/format
|
||||||
|
htable usr.etc/htable
|
||||||
|
implog usr.etc/implog
|
||||||
|
in.ftpd -a usr.etc/in.ftpd ucb/ftp
|
||||||
|
in.named usr.etc/in.named
|
||||||
|
in.rwhod usr.etc/in.rwhod
|
||||||
|
keyserv usr.etc/keyserv
|
||||||
|
ndbootd usr.etc/ndbootd
|
||||||
|
praudit usr.etc/praudit
|
||||||
|
rexd usr.etc/rexd
|
||||||
|
rpc.bootparamd usr.etc/rpc.bootparamd
|
||||||
|
termcap usr.etc/termcap
|
||||||
|
upgrade usr.etc/upgrade
|
||||||
|
yp usr.etc/yp
|
||||||
|
zic usr.etc/zic
|
||||||
|
|
||||||
|
# Programs that live in "usr.etc"
|
||||||
|
ac usr.etc Makefile ac.c
|
||||||
|
accton usr.etc Makefile accton.c
|
||||||
|
audit usr.etc Makefile audit.c
|
||||||
|
auditd usr.etc Makefile auditd.c
|
||||||
|
catman usr.etc Makefile catman.c
|
||||||
|
chroot usr.etc Makefile chroot.c
|
||||||
|
dcheck usr.etc Makefile dcheck.c
|
||||||
|
devnm usr.etc Makefile devnm.c
|
||||||
|
dumpfs usr.etc Makefile dumpfs.c
|
||||||
|
edquota usr.etc Makefile edquota.c
|
||||||
|
exportfs usr.etc Makefile exportfs.c
|
||||||
|
foption usr.etc Makefile foption.c
|
||||||
|
gettable usr.etc Makefile gettable.c
|
||||||
|
grpck usr.etc Makefile grpck.c
|
||||||
|
icheck usr.etc Makefile icheck.c
|
||||||
|
in.comsat usr.etc Makefile in.comsat.c
|
||||||
|
in.fingerd usr.etc Makefile in.fingerd.c
|
||||||
|
in.rexecd usr.etc Makefile in.rexecd.c
|
||||||
|
in.telnetd usr.etc Makefile in.telnetd.c
|
||||||
|
in.tnamed usr.etc Makefile in.tnamed.c
|
||||||
|
kgmon usr.etc Makefile kgmon.c
|
||||||
|
link usr.etc Makefile link.c
|
||||||
|
mkfile usr.etc Makefile mkfile.c
|
||||||
|
mkproto usr.etc Makefile mkproto.c
|
||||||
|
mount_lo usr.etc Makefile mount_lo.c
|
||||||
|
ncheck usr.etc Makefile ncheck.c
|
||||||
|
nfsstat usr.etc Makefile nfsstat.c
|
||||||
|
ping usr.etc Makefile ping.c
|
||||||
|
pwck usr.etc Makefile pwck.c
|
||||||
|
quot usr.etc Makefile quot.c
|
||||||
|
quotacheck usr.etc Makefile quotacheck.c
|
||||||
|
quotaon usr.etc Makefile quotaon.c
|
||||||
|
rarpd usr.etc Makefile rarpd.c
|
||||||
|
repquota usr.etc Makefile repquota.c
|
||||||
|
route usr.etc Makefile route.c
|
||||||
|
rpc.etherd usr.etc Makefile rpc.etherd.c
|
||||||
|
rpc.mountd usr.etc Makefile rpc.mountd.c
|
||||||
|
rpc.pwdauthd usr.etc Makefile rpc.pwdauthd.c
|
||||||
|
rpc.rquotad usr.etc Makefile rpc.rquotad.c
|
||||||
|
rpc.rstatd usr.etc Makefile rpc.rstatd.c
|
||||||
|
rpc.rusersd usr.etc Makefile rpc.rusersd.c
|
||||||
|
rpc.rwalld usr.etc Makefile rpc.rwalld.c
|
||||||
|
rpc.sprayd usr.etc Makefile rpc.sprayd.c
|
||||||
|
rpc.yppasswdd usr.etc Makefile rpc.yppasswdd.c
|
||||||
|
rpc.ypupdated usr.etc Makefile rpc.ypupdated.c
|
||||||
|
rpcinfo usr.etc Makefile rpcinfo.c
|
||||||
|
rwall usr.etc Makefile rwall.c
|
||||||
|
sa usr.etc Makefile sa.c
|
||||||
|
savecore usr.etc Makefile savecore.c
|
||||||
|
showmount usr.etc Makefile showmount.c
|
||||||
|
spray usr.etc Makefile spray.c
|
||||||
|
swapon usr.etc Makefile swapon.c
|
||||||
|
trpt usr.etc Makefile trpt.c
|
||||||
|
tunefs usr.etc Makefile tunefs.c
|
||||||
|
unlink usr.etc Makefile unlink.c
|
||||||
|
|
||||||
|
# Sub-directories of "usr.lib"
|
||||||
|
bb_count usr.lib/bb_count
|
||||||
|
fixedwidthfonts usr.lib/fixedwidthfonts
|
||||||
|
libcurses usr.lib/libcurses
|
||||||
|
libdbm usr.lib/libdbm
|
||||||
|
libg usr.lib/libg
|
||||||
|
libkvm usr.lib/libkvm
|
||||||
|
libln usr.lib/libln
|
||||||
|
liblwp usr.lib/liblwp
|
||||||
|
libm usr.lib/libm
|
||||||
|
libmp usr.lib/libmp
|
||||||
|
libpixrect usr.lib/libpixrect
|
||||||
|
libplot usr.lib/libplot
|
||||||
|
libresolv usr.lib/libresolv
|
||||||
|
librpcsvc usr.lib/librpcsvc
|
||||||
|
libtermlib usr.lib/libtermlib
|
||||||
|
liby usr.lib/liby
|
||||||
|
me usr.lib/me
|
||||||
|
ms usr.lib/ms
|
||||||
|
sendmail usr.lib/sendmail
|
||||||
|
sun_tmac usr.lib/tmac
|
||||||
|
vfont usr.lib/vfont
|
||||||
|
|
||||||
|
# Programs that live in "usr.lib"
|
||||||
|
getNAME usr.lib Makefile getNAME
|
||||||
|
makekey usr.lib Makefile makekey
|
||||||
|
|
||||||
|
# Sub-directories of "5bin"
|
||||||
|
5diff3 5bin/diff3
|
||||||
|
5m4 5bin/m4
|
||||||
|
|
||||||
|
# Sub-directories of "5bin", but use sources from other places
|
||||||
|
5cxref -a 5bin/cxref usr.bin/cxref
|
||||||
|
5sed -a 5bin/sed bin/sed
|
||||||
|
5lint -a 5bin/lint lang/pcc lang/lint
|
||||||
|
|
||||||
|
# Programs that live in "5bin"
|
||||||
|
5banner 5bin Makefile banner.c
|
||||||
|
5cat 5bin Makefile cat.c
|
||||||
|
5du 5bin Makefile du.c
|
||||||
|
5echo 5bin Makefile echo.c
|
||||||
|
5expr 5bin Makefile expr.c
|
||||||
|
5ls 5bin Makefile ls.c
|
||||||
|
5nohup 5bin Makefile nohup.c
|
||||||
|
5od 5bin Makefile od.c
|
||||||
|
5pg 5bin Makefile pg.c
|
||||||
|
5pr 5bin Makefile pr.c
|
||||||
|
5sum 5bin Makefile sum.c
|
||||||
|
5tabs 5bin Makefile tabs.c
|
||||||
|
5time 5bin Makefile time.c
|
||||||
|
5tr 5bin Makefile tr.c
|
||||||
|
5uname 5bin Makefile uname.c
|
||||||
|
|
||||||
|
# Programs that live in "5bin", but use sources from other places
|
||||||
|
5chmod -a 5bin/Makefile bin/chmod.c
|
||||||
|
5date -a 5bin/Makefile bin/date.c
|
||||||
|
5grep -a 5bin/Makefile bin/grep.c
|
||||||
|
5stty -a 5bin/Makefile bin/stty.c
|
||||||
|
5col -a 5bin/Makefile usr.bin/col.c
|
||||||
|
5sort -a 5bin/Makefile usr.bin/sort.c
|
||||||
|
5touch -a 5bin/Makefile usr.bin/touch.c
|
||||||
|
|
||||||
|
# Sub-directories of "5lib"
|
||||||
|
5compile 5lib/compile
|
||||||
|
5libcurses 5lib/libcurses
|
||||||
|
5liby 5lib/liby
|
||||||
|
5terminfo 5lib/terminfo
|
||||||
|
|
||||||
|
# Programs that live in "5lib"
|
||||||
|
# NONE
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
#
|
||||||
|
# rcsinfo,v 1.3 1992/04/10 18:59:14 berliner Exp
|
||||||
|
#
|
||||||
|
# The "rcsinfo" file is used to control templates with which the editor
|
||||||
|
# is invoked on commit and import.
|
||||||
|
#
|
||||||
|
# The first entry on a line is a regular expression which is tested
|
||||||
|
# against the directory that the change is being made to, relative to the
|
||||||
|
# $CVSROOT. If a match is found, then the remainder of the line is the
|
||||||
|
# name of the file that contains the template.
|
||||||
|
#
|
||||||
|
# If the repository name does not match any of the regular expressions in this
|
||||||
|
# file, the "DEFAULT" line is used, if it is specified.
|
||||||
|
#
|
||||||
|
# If the name ALL appears as a regular expression it is always used
|
||||||
|
# in addition to the first matching regex or DEFAULT.
|
||||||
|
#
|
||||||
|
DEFAULT /src/master/CVSROOT/rcstemplate
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
LIB = cvs
|
||||||
|
|
||||||
|
CFLAGS += -I${.CURDIR} -I${.CURDIR}/../cvs -DFTIME_MISSING -DHAVE_TIMEZONE
|
||||||
|
|
||||||
|
SRCS = argmatch.c error.c getopt.c sighandle.c strippath.c stripslash.c yesno.c \
|
||||||
|
getdate.y fnmatch.c regex.c subr.c myndbm.c hash.c
|
||||||
|
|
||||||
|
.include <bsd.lib.mk>
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
# Makefile for library files used by GNU CVS.
|
||||||
|
# Do not use this makefile directly, but only from `../Makefile'.
|
||||||
|
# Copyright (C) 1986, 1988-1992 Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2, or (at your option)
|
||||||
|
# any later version.
|
||||||
|
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
|
||||||
|
# @(#)Makefile.in 1.12 92/03/31
|
||||||
|
|
||||||
|
SHELL = /bin/sh
|
||||||
|
|
||||||
|
srcdir = @srcdir@
|
||||||
|
@VPATH@
|
||||||
|
|
||||||
|
SOURCES = argmatch.c \
|
||||||
|
error.c getopt.c getopt1.c \
|
||||||
|
sighandle.c \
|
||||||
|
strippath.c stripslash.c yesno.c \
|
||||||
|
getdate.y \
|
||||||
|
hostname.c fnmatch.c ftruncate.c mkdir.c rename.c regex.c \
|
||||||
|
strdup.c getwd.c alloca.c
|
||||||
|
|
||||||
|
OBJECTS = argmatch.o \
|
||||||
|
error.o getopt.o getopt1.o \
|
||||||
|
sighandle.o \
|
||||||
|
strippath.o stripslash.o yesno.o \
|
||||||
|
getdate.o \
|
||||||
|
@LIBOBJS@
|
||||||
|
|
||||||
|
DISTFILES = Makefile.in getopt.h \
|
||||||
|
fnmatch.h regex.h system.h wait.h $(SOURCES)
|
||||||
|
|
||||||
|
xxx:
|
||||||
|
@cd ..; $(MAKE) all SUBDIRS=lib
|
||||||
|
|
||||||
|
all: libcvs.a
|
||||||
|
.PHONY: all
|
||||||
|
|
||||||
|
install: all
|
||||||
|
.PHONY: install
|
||||||
|
|
||||||
|
tags: $(DISTFILES)
|
||||||
|
ctags $(DISTFILES)
|
||||||
|
|
||||||
|
TAGS: $(DISTFILES)
|
||||||
|
etags $(DISTFILES)
|
||||||
|
|
||||||
|
ls:
|
||||||
|
@echo $(DISTFILES)
|
||||||
|
.PHONY: ls
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f *.a *.o *.tab.c getdate.c
|
||||||
|
.PHONY: clean
|
||||||
|
|
||||||
|
distclean: clean
|
||||||
|
rm -f tags TAGS Makefile
|
||||||
|
.PHONY: distclean
|
||||||
|
|
||||||
|
realclean: distclean
|
||||||
|
.PHONY: realclean
|
||||||
|
|
||||||
|
dist:
|
||||||
|
ln $(DISTFILES) ../`cat ../.fname`/lib
|
||||||
|
.PHONY: dist
|
||||||
|
|
||||||
|
libcvs.a: $(OBJECTS)
|
||||||
|
$(AR) cr $@ $(OBJECTS)
|
||||||
|
-$(RANLIB) $@
|
||||||
|
|
||||||
|
getdate.c: getdate.y
|
||||||
|
@echo expect 8 shift/reduce conflicts
|
||||||
|
$(YACC) $(srcdir)/getdate.y
|
||||||
|
-if test -f y.tab.c ; then mv y.tab.c getdate.c ; fi
|
||||||
|
-if test -f getdate.tab.c ; then mv getdate.tab.c getdate.c ; fi
|
||||||
|
|
||||||
|
fnmatch.o: fnmatch.h
|
||||||
|
getopt1.o: getopt.h
|
||||||
|
regex.o: regex.h
|
||||||
|
getwd.o: system.h
|
||||||
@@ -0,0 +1,191 @@
|
|||||||
|
/*
|
||||||
|
alloca -- (mostly) portable public-domain implementation -- D A Gwyn
|
||||||
|
|
||||||
|
last edit: 86/05/30 rms
|
||||||
|
include config.h, since on VMS it renames some symbols.
|
||||||
|
Use xmalloc instead of malloc.
|
||||||
|
|
||||||
|
This implementation of the PWB library alloca() function,
|
||||||
|
which is used to allocate space off the run-time stack so
|
||||||
|
that it is automatically reclaimed upon procedure exit,
|
||||||
|
was inspired by discussions with J. Q. Johnson of Cornell.
|
||||||
|
|
||||||
|
It should work under any C implementation that uses an
|
||||||
|
actual procedure stack (as opposed to a linked list of
|
||||||
|
frames). There are some preprocessor constants that can
|
||||||
|
be defined when compiling for your specific system, for
|
||||||
|
improved efficiency; however, the defaults should be okay.
|
||||||
|
|
||||||
|
The general concept of this implementation is to keep
|
||||||
|
track of all alloca()-allocated blocks, and reclaim any
|
||||||
|
that are found to be deeper in the stack than the current
|
||||||
|
invocation. This heuristic does not reclaim storage as
|
||||||
|
soon as it becomes invalid, but it will do so eventually.
|
||||||
|
|
||||||
|
As a special case, alloca(0) reclaims storage without
|
||||||
|
allocating any. It is a good idea to use alloca(0) in
|
||||||
|
your main control loop, etc. to force garbage collection.
|
||||||
|
*/
|
||||||
|
#ifndef lint
|
||||||
|
static char SCCSid[] = "@(#)alloca.c 1.1"; /* for the "what" utility */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef emacs
|
||||||
|
#include "config.h"
|
||||||
|
#ifdef static
|
||||||
|
/* actually, only want this if static is defined as ""
|
||||||
|
-- this is for usg, in which emacs must undefine static
|
||||||
|
in order to make unexec workable
|
||||||
|
*/
|
||||||
|
#ifndef STACK_DIRECTION
|
||||||
|
you
|
||||||
|
lose
|
||||||
|
-- must know STACK_DIRECTION at compile-time
|
||||||
|
#endif /* STACK_DIRECTION undefined */
|
||||||
|
#endif /* static */
|
||||||
|
#endif /* emacs */
|
||||||
|
|
||||||
|
#if __STDC__
|
||||||
|
typedef void *pointer; /* generic pointer type */
|
||||||
|
#else
|
||||||
|
typedef char *pointer; /* generic pointer type */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define NULL 0 /* null pointer constant */
|
||||||
|
|
||||||
|
extern void free();
|
||||||
|
extern pointer xmalloc();
|
||||||
|
|
||||||
|
/*
|
||||||
|
Define STACK_DIRECTION if you know the direction of stack
|
||||||
|
growth for your system; otherwise it will be automatically
|
||||||
|
deduced at run-time.
|
||||||
|
|
||||||
|
STACK_DIRECTION > 0 => grows toward higher addresses
|
||||||
|
STACK_DIRECTION < 0 => grows toward lower addresses
|
||||||
|
STACK_DIRECTION = 0 => direction of growth unknown
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef STACK_DIRECTION
|
||||||
|
#define STACK_DIRECTION 0 /* direction unknown */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if STACK_DIRECTION != 0
|
||||||
|
|
||||||
|
#define STACK_DIR STACK_DIRECTION /* known at compile-time */
|
||||||
|
|
||||||
|
#else /* STACK_DIRECTION == 0; need run-time code */
|
||||||
|
|
||||||
|
static int stack_dir; /* 1 or -1 once known */
|
||||||
|
#define STACK_DIR stack_dir
|
||||||
|
|
||||||
|
static void
|
||||||
|
find_stack_direction (/* void */)
|
||||||
|
{
|
||||||
|
static char *addr = NULL; /* address of first
|
||||||
|
`dummy', once known */
|
||||||
|
auto char dummy; /* to get stack address */
|
||||||
|
|
||||||
|
if (addr == NULL)
|
||||||
|
{ /* initial entry */
|
||||||
|
addr = &dummy;
|
||||||
|
|
||||||
|
find_stack_direction (); /* recurse once */
|
||||||
|
}
|
||||||
|
else /* second entry */
|
||||||
|
if (&dummy > addr)
|
||||||
|
stack_dir = 1; /* stack grew upward */
|
||||||
|
else
|
||||||
|
stack_dir = -1; /* stack grew downward */
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* STACK_DIRECTION == 0 */
|
||||||
|
|
||||||
|
/*
|
||||||
|
An "alloca header" is used to:
|
||||||
|
(a) chain together all alloca()ed blocks;
|
||||||
|
(b) keep track of stack depth.
|
||||||
|
|
||||||
|
It is very important that sizeof(header) agree with malloc()
|
||||||
|
alignment chunk size. The following default should work okay.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ALIGN_SIZE
|
||||||
|
#define ALIGN_SIZE sizeof(double)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef union hdr
|
||||||
|
{
|
||||||
|
char align[ALIGN_SIZE]; /* to force sizeof(header) */
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
union hdr *next; /* for chaining headers */
|
||||||
|
char *deep; /* for stack depth measure */
|
||||||
|
} h;
|
||||||
|
} header;
|
||||||
|
|
||||||
|
/*
|
||||||
|
alloca( size ) returns a pointer to at least `size' bytes of
|
||||||
|
storage which will be automatically reclaimed upon exit from
|
||||||
|
the procedure that called alloca(). Originally, this space
|
||||||
|
was supposed to be taken from the current stack frame of the
|
||||||
|
caller, but that method cannot be made to work for some
|
||||||
|
implementations of C, for example under Gould's UTX/32.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static header *last_alloca_header = NULL; /* -> last alloca header */
|
||||||
|
|
||||||
|
pointer
|
||||||
|
alloca (size) /* returns pointer to storage */
|
||||||
|
unsigned size; /* # bytes to allocate */
|
||||||
|
{
|
||||||
|
auto char probe; /* probes stack depth: */
|
||||||
|
register char *depth = &probe;
|
||||||
|
|
||||||
|
#if STACK_DIRECTION == 0
|
||||||
|
if (STACK_DIR == 0) /* unknown growth direction */
|
||||||
|
find_stack_direction ();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Reclaim garbage, defined as all alloca()ed storage that
|
||||||
|
was allocated from deeper in the stack than currently. */
|
||||||
|
|
||||||
|
{
|
||||||
|
register header *hp; /* traverses linked list */
|
||||||
|
|
||||||
|
for (hp = last_alloca_header; hp != NULL;)
|
||||||
|
if (STACK_DIR > 0 && hp->h.deep > depth
|
||||||
|
|| STACK_DIR < 0 && hp->h.deep < depth)
|
||||||
|
{
|
||||||
|
register header *np = hp->h.next;
|
||||||
|
|
||||||
|
free ((pointer) hp); /* collect garbage */
|
||||||
|
|
||||||
|
hp = np; /* -> next header */
|
||||||
|
}
|
||||||
|
else
|
||||||
|
break; /* rest are not deeper */
|
||||||
|
|
||||||
|
last_alloca_header = hp; /* -> last valid storage */
|
||||||
|
}
|
||||||
|
|
||||||
|
if (size == 0)
|
||||||
|
return NULL; /* no allocation required */
|
||||||
|
|
||||||
|
/* Allocate combined header + user data storage. */
|
||||||
|
|
||||||
|
{
|
||||||
|
register pointer new = xmalloc (sizeof (header) + size);
|
||||||
|
/* address of header */
|
||||||
|
|
||||||
|
((header *)new)->h.next = last_alloca_header;
|
||||||
|
((header *)new)->h.deep = depth;
|
||||||
|
|
||||||
|
last_alloca_header = (header *)new;
|
||||||
|
|
||||||
|
/* User storage begins just after header. */
|
||||||
|
|
||||||
|
return (pointer)((char *)new + sizeof(header));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
/* argmatch.c -- find a match for a string in an array
|
||||||
|
Copyright (C) 1990 Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2, or (at your option)
|
||||||
|
any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
|
||||||
|
|
||||||
|
/* Written by David MacKenzie <djm@ai.mit.edu> */
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#ifdef STDC_HEADERS
|
||||||
|
#include <string.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern char *program_name;
|
||||||
|
|
||||||
|
/* If ARG is an unambiguous match for an element of the
|
||||||
|
null-terminated array OPTLIST, return the index in OPTLIST
|
||||||
|
of the matched element, else -1 if it does not match any element
|
||||||
|
or -2 if it is ambiguous (is a prefix of more than one element). */
|
||||||
|
|
||||||
|
int
|
||||||
|
argmatch (arg, optlist)
|
||||||
|
char *arg;
|
||||||
|
char **optlist;
|
||||||
|
{
|
||||||
|
int i; /* Temporary index in OPTLIST. */
|
||||||
|
int arglen; /* Length of ARG. */
|
||||||
|
int matchind = -1; /* Index of first nonexact match. */
|
||||||
|
int ambiguous = 0; /* If nonzero, multiple nonexact match(es). */
|
||||||
|
|
||||||
|
arglen = strlen (arg);
|
||||||
|
|
||||||
|
/* Test all elements for either exact match or abbreviated matches. */
|
||||||
|
for (i = 0; optlist[i]; i++)
|
||||||
|
{
|
||||||
|
if (!strncmp (optlist[i], arg, arglen))
|
||||||
|
{
|
||||||
|
if (strlen (optlist[i]) == arglen)
|
||||||
|
/* Exact match found. */
|
||||||
|
return i;
|
||||||
|
else if (matchind == -1)
|
||||||
|
/* First nonexact match found. */
|
||||||
|
matchind = i;
|
||||||
|
else
|
||||||
|
/* Second nonexact match found. */
|
||||||
|
ambiguous = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ambiguous)
|
||||||
|
return -2;
|
||||||
|
else
|
||||||
|
return matchind;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Error reporting for argmatch.
|
||||||
|
KIND is a description of the type of entity that was being matched.
|
||||||
|
VALUE is the invalid value that was given.
|
||||||
|
PROBLEM is the return value from argmatch. */
|
||||||
|
|
||||||
|
void
|
||||||
|
invalid_arg (kind, value, problem)
|
||||||
|
char *kind;
|
||||||
|
char *value;
|
||||||
|
int problem;
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s: ", program_name);
|
||||||
|
if (problem == -1)
|
||||||
|
fprintf (stderr, "invalid");
|
||||||
|
else /* Assume -2. */
|
||||||
|
fprintf (stderr, "ambiguous");
|
||||||
|
fprintf (stderr, " %s `%s'\n", kind, value);
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
dup2 -- 7th Edition UNIX system call emulation for UNIX System V
|
||||||
|
|
||||||
|
last edit: 11-Feb-1987 D A Gwyn
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
extern int close(), fcntl();
|
||||||
|
|
||||||
|
int
|
||||||
|
dup2( oldfd, newfd )
|
||||||
|
int oldfd; /* already-open file descriptor */
|
||||||
|
int newfd; /* desired duplicate descriptor */
|
||||||
|
{
|
||||||
|
register int ret; /* for fcntl() return value */
|
||||||
|
register int save; /* for saving entry errno */
|
||||||
|
|
||||||
|
if ( oldfd == newfd )
|
||||||
|
return oldfd; /* be careful not to close() */
|
||||||
|
|
||||||
|
save = errno; /* save entry errno */
|
||||||
|
(void) close( newfd ); /* in case newfd is open */
|
||||||
|
/* (may have just clobbered the original errno value) */
|
||||||
|
|
||||||
|
ret = fcntl( oldfd, F_DUPFD, newfd ); /* dupe it */
|
||||||
|
|
||||||
|
if ( ret >= 0 )
|
||||||
|
errno = save; /* restore entry errno */
|
||||||
|
else /* fcntl() returned error */
|
||||||
|
if ( errno == EINVAL )
|
||||||
|
errno = EBADF; /* we think of everything */
|
||||||
|
|
||||||
|
return ret; /* return file descriptor */
|
||||||
|
}
|
||||||
@@ -0,0 +1,193 @@
|
|||||||
|
/* error.c -- error handler for noninteractive utilities
|
||||||
|
Copyright (C) 1990-1992 Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2, or (at your option)
|
||||||
|
any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
|
||||||
|
|
||||||
|
/* David MacKenzie */
|
||||||
|
/* Brian Berliner added support for CVS */
|
||||||
|
|
||||||
|
#ifndef lint
|
||||||
|
static char rcsid[] = "@(#)error.c 1.9 92/03/31";
|
||||||
|
#endif /* not lint */
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
/* turn on CVS support by default, since this is the CVS distribution */
|
||||||
|
#define CVS_SUPPORT
|
||||||
|
|
||||||
|
#ifdef CVS_SUPPORT
|
||||||
|
#if __STDC__
|
||||||
|
void Lock_Cleanup(void);
|
||||||
|
#else
|
||||||
|
void Lock_Cleanup();
|
||||||
|
#endif /* __STDC__ */
|
||||||
|
#endif /* CVS_SUPPORT */
|
||||||
|
|
||||||
|
#ifndef VPRINTF_MISSING
|
||||||
|
|
||||||
|
#if __STDC__
|
||||||
|
#include <stdarg.h>
|
||||||
|
#define VA_START(args, lastarg) va_start(args, lastarg)
|
||||||
|
#else
|
||||||
|
#include <varargs.h>
|
||||||
|
#define VA_START(args, lastarg) va_start(args)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#ifndef DOPRNT_MISSING
|
||||||
|
#define va_alist args
|
||||||
|
#define va_dcl int args;
|
||||||
|
#else
|
||||||
|
#define va_alist a1, a2, a3, a4, a5, a6, a7, a8
|
||||||
|
#define va_dcl char *a1, *a2, *a3, *a4, *a5, *a6, *a7, *a8;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef STDC_HEADERS
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#else
|
||||||
|
#if __STDC__
|
||||||
|
void exit(int status);
|
||||||
|
#else
|
||||||
|
void exit ();
|
||||||
|
#endif /* __STDC__ */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef STRERROR_MISSING
|
||||||
|
static char *
|
||||||
|
strerror (errnum)
|
||||||
|
int errnum;
|
||||||
|
{
|
||||||
|
extern char *sys_errlist[];
|
||||||
|
extern int sys_nerr;
|
||||||
|
|
||||||
|
if (errnum > 0 && errnum < sys_nerr)
|
||||||
|
return sys_errlist[errnum];
|
||||||
|
return "Unknown system error";
|
||||||
|
}
|
||||||
|
#endif /* STRERROR_MISSING */
|
||||||
|
|
||||||
|
/* Print the program name and error message MESSAGE, which is a printf-style
|
||||||
|
format string with optional args.
|
||||||
|
If ERRNUM is nonzero, print its corresponding system error message.
|
||||||
|
Exit with status STATUS if it is nonzero. */
|
||||||
|
/* VARARGS */
|
||||||
|
void
|
||||||
|
#if !defined (VPRINTF_MISSING) && __STDC__
|
||||||
|
error (int status, int errnum, char *message, ...)
|
||||||
|
#else
|
||||||
|
error (status, errnum, message, va_alist)
|
||||||
|
int status;
|
||||||
|
int errnum;
|
||||||
|
char *message;
|
||||||
|
va_dcl
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
extern char *program_name;
|
||||||
|
#ifdef CVS_SUPPORT
|
||||||
|
extern char *command_name;
|
||||||
|
#endif
|
||||||
|
#ifndef VPRINTF_MISSING
|
||||||
|
va_list args;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef CVS_SUPPORT
|
||||||
|
if (command_name && *command_name)
|
||||||
|
if (status)
|
||||||
|
fprintf (stderr, "%s [%s aborted]: ", program_name, command_name);
|
||||||
|
else
|
||||||
|
fprintf (stderr, "%s %s: ", program_name, command_name);
|
||||||
|
else
|
||||||
|
fprintf (stderr, "%s: ", program_name);
|
||||||
|
#else
|
||||||
|
fprintf (stderr, "%s: ", program_name);
|
||||||
|
#endif
|
||||||
|
#ifndef VPRINTF_MISSING
|
||||||
|
VA_START (args, message);
|
||||||
|
vfprintf (stderr, message, args);
|
||||||
|
va_end (args);
|
||||||
|
#else
|
||||||
|
#ifndef DOPRNT_MISSING
|
||||||
|
_doprnt (message, &args, stderr);
|
||||||
|
#else
|
||||||
|
fprintf (stderr, message, a1, a2, a3, a4, a5, a6, a7, a8);
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
if (errnum)
|
||||||
|
fprintf (stderr, ": %s", strerror (errnum));
|
||||||
|
putc ('\n', stderr);
|
||||||
|
fflush (stderr);
|
||||||
|
if (status)
|
||||||
|
{
|
||||||
|
#ifdef CVS_SUPPORT
|
||||||
|
Lock_Cleanup();
|
||||||
|
#endif
|
||||||
|
exit (status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CVS_SUPPORT
|
||||||
|
|
||||||
|
/* Print the program name and error message MESSAGE, which is a printf-style
|
||||||
|
format string with optional args to the file specified by FP.
|
||||||
|
If ERRNUM is nonzero, print its corresponding system error message.
|
||||||
|
Exit with status STATUS if it is nonzero. */
|
||||||
|
/* VARARGS */
|
||||||
|
void
|
||||||
|
#if !defined (VPRINTF_MISSING) && __STDC__
|
||||||
|
fperror (FILE *fp, int status, int errnum, char *message, ...)
|
||||||
|
#else
|
||||||
|
fperror (fp, status, errnum, message, va_alist)
|
||||||
|
FILE *fp;
|
||||||
|
int status;
|
||||||
|
int errnum;
|
||||||
|
char *message;
|
||||||
|
va_dcl
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
extern char *program_name;
|
||||||
|
#ifndef VPRINTF_MISSING
|
||||||
|
va_list args;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
fprintf (fp, "%s: ", program_name);
|
||||||
|
#ifndef VPRINTF_MISSING
|
||||||
|
VA_START (args, message);
|
||||||
|
vfprintf (fp, message, args);
|
||||||
|
va_end (args);
|
||||||
|
#else
|
||||||
|
#ifndef DOPRNT_MISSING
|
||||||
|
_doprnt (message, &args, fp);
|
||||||
|
#else
|
||||||
|
fprintf (fp, message, a1, a2, a3, a4, a5, a6, a7, a8);
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
if (errnum)
|
||||||
|
fprintf (fp, ": %s", strerror (errnum));
|
||||||
|
putc ('\n', fp);
|
||||||
|
fflush (fp);
|
||||||
|
if (status)
|
||||||
|
{
|
||||||
|
#ifdef CVS_SUPPORT
|
||||||
|
Lock_Cleanup();
|
||||||
|
#endif
|
||||||
|
exit (status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* CVS_SUPPORT */
|
||||||
@@ -0,0 +1,183 @@
|
|||||||
|
/* Copyright (C) 1992 Free Software Foundation, Inc.
|
||||||
|
This file is part of the GNU C Library.
|
||||||
|
|
||||||
|
The GNU C Library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Library General Public License as
|
||||||
|
published by the Free Software Foundation; either version 2 of the
|
||||||
|
License, or (at your option) any later version.
|
||||||
|
|
||||||
|
The GNU C Library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Library General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Library General Public
|
||||||
|
License along with the GNU C Library; see the file COPYING.LIB. If
|
||||||
|
not, write to the Free Software Foundation, Inc., 675 Mass Ave,
|
||||||
|
Cambridge, MA 02139, USA. */
|
||||||
|
|
||||||
|
/* Modified slightly by Brian Berliner <berliner@sun.com> for CVS use */
|
||||||
|
|
||||||
|
/* IGNORE(@ */
|
||||||
|
/* #include <ansidecl.h> */
|
||||||
|
/* @) */
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fnmatch.h>
|
||||||
|
|
||||||
|
#if !defined(__GNU_LIBRARY__) && !defined(STDC_HEADERS)
|
||||||
|
extern int errno;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !__STDC__
|
||||||
|
#define const
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Match STRING against the filename pattern PATTERN, returning zero if
|
||||||
|
it matches, nonzero if not. */
|
||||||
|
int
|
||||||
|
#if __STDC__
|
||||||
|
fnmatch (const char *pattern, const char *string, int flags)
|
||||||
|
#else
|
||||||
|
fnmatch (pattern, string, flags)
|
||||||
|
char *pattern;
|
||||||
|
char *string;
|
||||||
|
int flags;
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
register const char *p = pattern, *n = string;
|
||||||
|
register char c;
|
||||||
|
|
||||||
|
if ((flags & ~__FNM_FLAGS) != 0)
|
||||||
|
{
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
while ((c = *p++) != '\0')
|
||||||
|
{
|
||||||
|
switch (c)
|
||||||
|
{
|
||||||
|
case '?':
|
||||||
|
if (*n == '\0')
|
||||||
|
return FNM_NOMATCH;
|
||||||
|
else if ((flags & FNM_PATHNAME) && *n == '/')
|
||||||
|
return FNM_NOMATCH;
|
||||||
|
else if ((flags & FNM_PERIOD) && *n == '.' &&
|
||||||
|
(n == string || ((flags & FNM_PATHNAME) && n[-1] == '/')))
|
||||||
|
return FNM_NOMATCH;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '\\':
|
||||||
|
if (!(flags & FNM_NOESCAPE))
|
||||||
|
c = *p++;
|
||||||
|
if (*n != c)
|
||||||
|
return FNM_NOMATCH;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '*':
|
||||||
|
if ((flags & FNM_PERIOD) && *n == '.' &&
|
||||||
|
(n == string || ((flags & FNM_PATHNAME) && n[-1] == '/')))
|
||||||
|
return FNM_NOMATCH;
|
||||||
|
|
||||||
|
for (c = *p++; c == '?' || c == '*'; c = *p++, ++n)
|
||||||
|
if (((flags & FNM_PATHNAME) && *n == '/') ||
|
||||||
|
(c == '?' && *n == '\0'))
|
||||||
|
return FNM_NOMATCH;
|
||||||
|
|
||||||
|
if (c == '\0')
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
{
|
||||||
|
char c1 = (!(flags & FNM_NOESCAPE) && c == '\\') ? *p : c;
|
||||||
|
for (--p; *n != '\0'; ++n)
|
||||||
|
if ((c == '[' || *n == c1) &&
|
||||||
|
fnmatch(p, n, flags & ~FNM_PERIOD) == 0)
|
||||||
|
return 0;
|
||||||
|
return FNM_NOMATCH;
|
||||||
|
}
|
||||||
|
|
||||||
|
case '[':
|
||||||
|
{
|
||||||
|
/* Nonzero if the sense of the character class is inverted. */
|
||||||
|
register int not;
|
||||||
|
|
||||||
|
if (*n == '\0')
|
||||||
|
return FNM_NOMATCH;
|
||||||
|
|
||||||
|
if ((flags & FNM_PERIOD) && *n == '.' &&
|
||||||
|
(n == string || ((flags & FNM_PATHNAME) && n[-1] == '/')))
|
||||||
|
return FNM_NOMATCH;
|
||||||
|
|
||||||
|
not = (*p == '!' || *p == '^');
|
||||||
|
if (not)
|
||||||
|
++p;
|
||||||
|
|
||||||
|
c = *p++;
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
register char cstart = c, cend = c;
|
||||||
|
|
||||||
|
if (!(flags & FNM_NOESCAPE) && c == '\\')
|
||||||
|
cstart = cend = *p++;
|
||||||
|
|
||||||
|
if (c == '\0')
|
||||||
|
/* [ (unterminated) loses. */
|
||||||
|
return FNM_NOMATCH;
|
||||||
|
|
||||||
|
c = *p++;
|
||||||
|
|
||||||
|
if ((flags & FNM_PATHNAME) && c == '/')
|
||||||
|
/* [/] can never match. */
|
||||||
|
return FNM_NOMATCH;
|
||||||
|
|
||||||
|
if (c == '-' && *p != ']')
|
||||||
|
{
|
||||||
|
cend = *p++;
|
||||||
|
if (!(flags & FNM_NOESCAPE) && cend == '\\')
|
||||||
|
cend = *p++;
|
||||||
|
if (cend == '\0')
|
||||||
|
return FNM_NOMATCH;
|
||||||
|
c = *p++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*n >= cstart && *n <= cend)
|
||||||
|
goto matched;
|
||||||
|
|
||||||
|
if (c == ']')
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!not)
|
||||||
|
return FNM_NOMATCH;
|
||||||
|
break;
|
||||||
|
|
||||||
|
matched:;
|
||||||
|
/* Skip the rest of the [...] that already matched. */
|
||||||
|
while (c != ']')
|
||||||
|
{
|
||||||
|
if (c == '\0')
|
||||||
|
/* [... (unterminated) loses. */
|
||||||
|
return FNM_NOMATCH;
|
||||||
|
|
||||||
|
c = *p++;
|
||||||
|
if (!(flags & FNM_NOESCAPE) && c == '\\')
|
||||||
|
/* 1003.2d11 is unclear if this is right. %%% */
|
||||||
|
++p;
|
||||||
|
}
|
||||||
|
if (not)
|
||||||
|
return FNM_NOMATCH;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if (c != *n)
|
||||||
|
return FNM_NOMATCH;
|
||||||
|
}
|
||||||
|
|
||||||
|
++n;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*n == '\0')
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return FNM_NOMATCH;
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
/* Copyright (C) 1992 Free Software Foundation, Inc.
|
||||||
|
This file is part of the GNU C Library.
|
||||||
|
|
||||||
|
The GNU C Library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Library General Public License as
|
||||||
|
published by the Free Software Foundation; either version 2 of the
|
||||||
|
License, or (at your option) any later version.
|
||||||
|
|
||||||
|
The GNU C Library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Library General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Library General Public
|
||||||
|
License along with the GNU C Library; see the file COPYING.LIB. If
|
||||||
|
not, write to the Free Software Foundation, Inc., 675 Mass Ave,
|
||||||
|
Cambridge, MA 02139, USA. */
|
||||||
|
|
||||||
|
#ifndef _FNMATCH_H
|
||||||
|
|
||||||
|
#define _FNMATCH_H 1
|
||||||
|
|
||||||
|
/* Bits set in the FLAGS argument to `fnmatch'. */
|
||||||
|
#undef FNM_PATHNAME
|
||||||
|
#define FNM_PATHNAME (1 << 0)/* No wildcard can ever match `/'. */
|
||||||
|
#undef FNM_NOESCAPE
|
||||||
|
#define FNM_NOESCAPE (1 << 1)/* Backslashes don't quote special chars. */
|
||||||
|
#undef FNM_PERIOD
|
||||||
|
#define FNM_PERIOD (1 << 2)/* Leading `.' is matched only explicitly. */
|
||||||
|
#undef __FNM_FLAGS
|
||||||
|
#define __FNM_FLAGS (FNM_PATHNAME|FNM_NOESCAPE|FNM_PERIOD)
|
||||||
|
|
||||||
|
/* Value returned by `fnmatch' if STRING does not match PATTERN. */
|
||||||
|
#undef FNM_NOMATCH
|
||||||
|
#define FNM_NOMATCH 1
|
||||||
|
|
||||||
|
/* Match STRING against the filename pattern PATTERN,
|
||||||
|
returning zero if it matches, FNM_NOMATCH if not. */
|
||||||
|
#if __STDC__
|
||||||
|
extern int fnmatch (const char *pattern, const char *string, int flags);
|
||||||
|
#else
|
||||||
|
extern int fnmatch ();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* fnmatch.h */
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
/* ftruncate emulations that work on some System V's.
|
||||||
|
This file is in the public domain. */
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
#ifdef F_CHSIZE
|
||||||
|
int
|
||||||
|
ftruncate (fd, length)
|
||||||
|
int fd;
|
||||||
|
off_t length;
|
||||||
|
{
|
||||||
|
return fcntl (fd, F_CHSIZE, length);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#ifdef F_FREESP
|
||||||
|
/* The following function was written by
|
||||||
|
kucharsk@Solbourne.com (William Kucharski) */
|
||||||
|
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
int
|
||||||
|
ftruncate (fd, length)
|
||||||
|
int fd;
|
||||||
|
off_t length;
|
||||||
|
{
|
||||||
|
struct flock fl;
|
||||||
|
struct stat filebuf;
|
||||||
|
|
||||||
|
if (fstat (fd, &filebuf) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (filebuf.st_size < length)
|
||||||
|
{
|
||||||
|
/* Extend file length. */
|
||||||
|
if (lseek (fd, (length - 1), SEEK_SET) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/* Write a "0" byte. */
|
||||||
|
if (write (fd, "", 1) != 1)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Truncate length. */
|
||||||
|
fl.l_whence = 0;
|
||||||
|
fl.l_len = 0;
|
||||||
|
fl.l_start = length;
|
||||||
|
fl.l_type = F_WRLCK; /* Write lock on file space. */
|
||||||
|
|
||||||
|
/* This relies on the UNDOCUMENTED F_FREESP argument to
|
||||||
|
fcntl, which truncates the file so that it ends at the
|
||||||
|
position indicated by fl.l_start.
|
||||||
|
Will minor miracles never cease? */
|
||||||
|
if (fcntl (fd, F_FREESP, &fl) < 0)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
int
|
||||||
|
ftruncate (fd, length)
|
||||||
|
int fd;
|
||||||
|
off_t length;
|
||||||
|
{
|
||||||
|
return chsize (fd, length);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,889 @@
|
|||||||
|
%{
|
||||||
|
/* 1.8
|
||||||
|
** @(#)getdate.y 1.8 92/03/03
|
||||||
|
**
|
||||||
|
** Originally written by Steven M. Bellovin <smb@research.att.com> while
|
||||||
|
** at the University of North Carolina at Chapel Hill. Later tweaked by
|
||||||
|
** a couple of people on Usenet. Completely overhauled by Rich $alz
|
||||||
|
** <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990;
|
||||||
|
** send any email to Rich.
|
||||||
|
**
|
||||||
|
** This grammar has eight shift/reduce conflicts.
|
||||||
|
**
|
||||||
|
** This code is in the public domain and has no copyright.
|
||||||
|
*/
|
||||||
|
/* SUPPRESS 287 on yaccpar_sccsid *//* Unused static variable */
|
||||||
|
/* SUPPRESS 288 on yyerrlab *//* Label unused */
|
||||||
|
|
||||||
|
#include "system.h"
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
#if (!defined (__STDC__) && defined (sparc)) || defined (__sparc__)
|
||||||
|
#ifdef __GNUC__
|
||||||
|
#undef alloca /* might get redefined below */
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern struct tm *localtime();
|
||||||
|
|
||||||
|
#define yyparse getdate_yyparse
|
||||||
|
#define yylex getdate_yylex
|
||||||
|
#define yyerror getdate_yyerror
|
||||||
|
|
||||||
|
#if !defined(lint) && !defined(SABER)
|
||||||
|
static char RCS[] = "@(#)getdate.y 1.8 92/03/03";
|
||||||
|
#endif /* !defined(lint) && !defined(SABER) */
|
||||||
|
|
||||||
|
|
||||||
|
#define EPOCH 1970
|
||||||
|
#define HOUR(x) ((time_t)(x) * 60)
|
||||||
|
#define SECSPERDAY (24L * 60L * 60L)
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** An entry in the lexical lookup table.
|
||||||
|
*/
|
||||||
|
typedef struct _TABLE {
|
||||||
|
char *name;
|
||||||
|
int type;
|
||||||
|
time_t value;
|
||||||
|
} TABLE;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Daylight-savings mode: on, off, or not yet known.
|
||||||
|
*/
|
||||||
|
typedef enum _DSTMODE {
|
||||||
|
DSTon, DSToff, DSTmaybe
|
||||||
|
} DSTMODE;
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Meridian: am, pm, or 24-hour style.
|
||||||
|
*/
|
||||||
|
typedef enum _MERIDIAN {
|
||||||
|
MERam, MERpm, MER24
|
||||||
|
} MERIDIAN;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Global variables. We could get rid of most of these by using a good
|
||||||
|
** union as the yacc stack. (This routine was originally written before
|
||||||
|
** yacc had the %union construct.) Maybe someday; right now we only use
|
||||||
|
** the %union very rarely.
|
||||||
|
*/
|
||||||
|
static char *yyInput;
|
||||||
|
static DSTMODE yyDSTmode;
|
||||||
|
static time_t yyDayOrdinal;
|
||||||
|
static time_t yyDayNumber;
|
||||||
|
static int yyHaveDate;
|
||||||
|
static int yyHaveDay;
|
||||||
|
static int yyHaveRel;
|
||||||
|
static int yyHaveTime;
|
||||||
|
static int yyHaveZone;
|
||||||
|
static time_t yyTimezone;
|
||||||
|
static time_t yyDay;
|
||||||
|
static time_t yyHour;
|
||||||
|
static time_t yyMinutes;
|
||||||
|
static time_t yyMonth;
|
||||||
|
static time_t yySeconds;
|
||||||
|
static time_t yyYear;
|
||||||
|
static MERIDIAN yyMeridian;
|
||||||
|
static time_t yyRelMonth;
|
||||||
|
static time_t yyRelSeconds;
|
||||||
|
|
||||||
|
%}
|
||||||
|
|
||||||
|
%union {
|
||||||
|
time_t Number;
|
||||||
|
enum _MERIDIAN Meridian;
|
||||||
|
}
|
||||||
|
|
||||||
|
%token tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT
|
||||||
|
%token tSEC_UNIT tSNUMBER tUNUMBER tZONE tDST
|
||||||
|
|
||||||
|
%type <Number> tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT
|
||||||
|
%type <Number> tSEC_UNIT tSNUMBER tUNUMBER tZONE
|
||||||
|
%type <Meridian> tMERIDIAN o_merid
|
||||||
|
|
||||||
|
%%
|
||||||
|
|
||||||
|
spec : /* NULL */
|
||||||
|
| spec item
|
||||||
|
;
|
||||||
|
|
||||||
|
item : time {
|
||||||
|
yyHaveTime++;
|
||||||
|
}
|
||||||
|
| zone {
|
||||||
|
yyHaveZone++;
|
||||||
|
}
|
||||||
|
| date {
|
||||||
|
yyHaveDate++;
|
||||||
|
}
|
||||||
|
| day {
|
||||||
|
yyHaveDay++;
|
||||||
|
}
|
||||||
|
| rel {
|
||||||
|
yyHaveRel++;
|
||||||
|
}
|
||||||
|
| number
|
||||||
|
;
|
||||||
|
|
||||||
|
time : tUNUMBER tMERIDIAN {
|
||||||
|
yyHour = $1;
|
||||||
|
yyMinutes = 0;
|
||||||
|
yySeconds = 0;
|
||||||
|
yyMeridian = $2;
|
||||||
|
}
|
||||||
|
| tUNUMBER ':' tUNUMBER o_merid {
|
||||||
|
yyHour = $1;
|
||||||
|
yyMinutes = $3;
|
||||||
|
yySeconds = 0;
|
||||||
|
yyMeridian = $4;
|
||||||
|
}
|
||||||
|
| tUNUMBER ':' tUNUMBER tSNUMBER {
|
||||||
|
yyHour = $1;
|
||||||
|
yyMinutes = $3;
|
||||||
|
yyMeridian = MER24;
|
||||||
|
yyDSTmode = DSToff;
|
||||||
|
yyTimezone = - ($4 % 100 + ($4 / 100) * 60);
|
||||||
|
}
|
||||||
|
| tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
|
||||||
|
yyHour = $1;
|
||||||
|
yyMinutes = $3;
|
||||||
|
yySeconds = $5;
|
||||||
|
yyMeridian = $6;
|
||||||
|
}
|
||||||
|
| tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER {
|
||||||
|
yyHour = $1;
|
||||||
|
yyMinutes = $3;
|
||||||
|
yySeconds = $5;
|
||||||
|
yyMeridian = MER24;
|
||||||
|
yyDSTmode = DSToff;
|
||||||
|
yyTimezone = - ($6 % 100 + ($6 / 100) * 60);
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
zone : tZONE {
|
||||||
|
yyTimezone = $1;
|
||||||
|
yyDSTmode = DSToff;
|
||||||
|
}
|
||||||
|
| tDAYZONE {
|
||||||
|
yyTimezone = $1;
|
||||||
|
yyDSTmode = DSTon;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
tZONE tDST {
|
||||||
|
yyTimezone = $1;
|
||||||
|
yyDSTmode = DSTon;
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
day : tDAY {
|
||||||
|
yyDayOrdinal = 1;
|
||||||
|
yyDayNumber = $1;
|
||||||
|
}
|
||||||
|
| tDAY ',' {
|
||||||
|
yyDayOrdinal = 1;
|
||||||
|
yyDayNumber = $1;
|
||||||
|
}
|
||||||
|
| tUNUMBER tDAY {
|
||||||
|
yyDayOrdinal = $1;
|
||||||
|
yyDayNumber = $2;
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
date : tUNUMBER '/' tUNUMBER {
|
||||||
|
yyMonth = $1;
|
||||||
|
yyDay = $3;
|
||||||
|
}
|
||||||
|
| tUNUMBER '/' tUNUMBER '/' tUNUMBER {
|
||||||
|
yyMonth = $1;
|
||||||
|
yyDay = $3;
|
||||||
|
yyYear = $5;
|
||||||
|
}
|
||||||
|
| tMONTH tUNUMBER {
|
||||||
|
yyMonth = $1;
|
||||||
|
yyDay = $2;
|
||||||
|
}
|
||||||
|
| tMONTH tUNUMBER ',' tUNUMBER {
|
||||||
|
yyMonth = $1;
|
||||||
|
yyDay = $2;
|
||||||
|
yyYear = $4;
|
||||||
|
}
|
||||||
|
| tUNUMBER tMONTH {
|
||||||
|
yyMonth = $2;
|
||||||
|
yyDay = $1;
|
||||||
|
}
|
||||||
|
| tUNUMBER tMONTH tUNUMBER {
|
||||||
|
yyMonth = $2;
|
||||||
|
yyDay = $1;
|
||||||
|
yyYear = $3;
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
rel : relunit tAGO {
|
||||||
|
yyRelSeconds = -yyRelSeconds;
|
||||||
|
yyRelMonth = -yyRelMonth;
|
||||||
|
}
|
||||||
|
| relunit
|
||||||
|
;
|
||||||
|
|
||||||
|
relunit : tUNUMBER tMINUTE_UNIT {
|
||||||
|
yyRelSeconds += $1 * $2 * 60L;
|
||||||
|
}
|
||||||
|
| tSNUMBER tMINUTE_UNIT {
|
||||||
|
yyRelSeconds += $1 * $2 * 60L;
|
||||||
|
}
|
||||||
|
| tMINUTE_UNIT {
|
||||||
|
yyRelSeconds += $1 * 60L;
|
||||||
|
}
|
||||||
|
| tSNUMBER tSEC_UNIT {
|
||||||
|
yyRelSeconds += $1;
|
||||||
|
}
|
||||||
|
| tUNUMBER tSEC_UNIT {
|
||||||
|
yyRelSeconds += $1;
|
||||||
|
}
|
||||||
|
| tSEC_UNIT {
|
||||||
|
yyRelSeconds++;
|
||||||
|
}
|
||||||
|
| tSNUMBER tMONTH_UNIT {
|
||||||
|
yyRelMonth += $1 * $2;
|
||||||
|
}
|
||||||
|
| tUNUMBER tMONTH_UNIT {
|
||||||
|
yyRelMonth += $1 * $2;
|
||||||
|
}
|
||||||
|
| tMONTH_UNIT {
|
||||||
|
yyRelMonth += $1;
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
number : tUNUMBER {
|
||||||
|
if (yyHaveTime && yyHaveDate && !yyHaveRel)
|
||||||
|
yyYear = $1;
|
||||||
|
else {
|
||||||
|
if($1>10000) {
|
||||||
|
time_t date_part;
|
||||||
|
|
||||||
|
date_part= $1/10000;
|
||||||
|
yyHaveDate++;
|
||||||
|
yyDay= (date_part)%100;
|
||||||
|
yyMonth= (date_part/100)%100;
|
||||||
|
yyYear = date_part/10000;
|
||||||
|
}
|
||||||
|
yyHaveTime++;
|
||||||
|
if ($1 < 100) {
|
||||||
|
yyHour = $1;
|
||||||
|
yyMinutes = 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
yyHour = $1 / 100;
|
||||||
|
yyMinutes = $1 % 100;
|
||||||
|
}
|
||||||
|
yySeconds = 0;
|
||||||
|
yyMeridian = MER24;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
o_merid : /* NULL */ {
|
||||||
|
$$ = MER24;
|
||||||
|
}
|
||||||
|
| tMERIDIAN {
|
||||||
|
$$ = $1;
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
%%
|
||||||
|
|
||||||
|
/* Month and day table. */
|
||||||
|
static TABLE MonthDayTable[] = {
|
||||||
|
{ "january", tMONTH, 1 },
|
||||||
|
{ "february", tMONTH, 2 },
|
||||||
|
{ "march", tMONTH, 3 },
|
||||||
|
{ "april", tMONTH, 4 },
|
||||||
|
{ "may", tMONTH, 5 },
|
||||||
|
{ "june", tMONTH, 6 },
|
||||||
|
{ "july", tMONTH, 7 },
|
||||||
|
{ "august", tMONTH, 8 },
|
||||||
|
{ "september", tMONTH, 9 },
|
||||||
|
{ "sept", tMONTH, 9 },
|
||||||
|
{ "october", tMONTH, 10 },
|
||||||
|
{ "november", tMONTH, 11 },
|
||||||
|
{ "december", tMONTH, 12 },
|
||||||
|
{ "sunday", tDAY, 0 },
|
||||||
|
{ "monday", tDAY, 1 },
|
||||||
|
{ "tuesday", tDAY, 2 },
|
||||||
|
{ "tues", tDAY, 2 },
|
||||||
|
{ "wednesday", tDAY, 3 },
|
||||||
|
{ "wednes", tDAY, 3 },
|
||||||
|
{ "thursday", tDAY, 4 },
|
||||||
|
{ "thur", tDAY, 4 },
|
||||||
|
{ "thurs", tDAY, 4 },
|
||||||
|
{ "friday", tDAY, 5 },
|
||||||
|
{ "saturday", tDAY, 6 },
|
||||||
|
{ NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Time units table. */
|
||||||
|
static TABLE UnitsTable[] = {
|
||||||
|
{ "year", tMONTH_UNIT, 12 },
|
||||||
|
{ "month", tMONTH_UNIT, 1 },
|
||||||
|
{ "fortnight", tMINUTE_UNIT, 14 * 24 * 60 },
|
||||||
|
{ "week", tMINUTE_UNIT, 7 * 24 * 60 },
|
||||||
|
{ "day", tMINUTE_UNIT, 1 * 24 * 60 },
|
||||||
|
{ "hour", tMINUTE_UNIT, 60 },
|
||||||
|
{ "minute", tMINUTE_UNIT, 1 },
|
||||||
|
{ "min", tMINUTE_UNIT, 1 },
|
||||||
|
{ "second", tSEC_UNIT, 1 },
|
||||||
|
{ "sec", tSEC_UNIT, 1 },
|
||||||
|
{ NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Assorted relative-time words. */
|
||||||
|
static TABLE OtherTable[] = {
|
||||||
|
{ "tomorrow", tMINUTE_UNIT, 1 * 24 * 60 },
|
||||||
|
{ "yesterday", tMINUTE_UNIT, -1 * 24 * 60 },
|
||||||
|
{ "today", tMINUTE_UNIT, 0 },
|
||||||
|
{ "now", tMINUTE_UNIT, 0 },
|
||||||
|
{ "last", tUNUMBER, -1 },
|
||||||
|
{ "this", tMINUTE_UNIT, 0 },
|
||||||
|
{ "next", tUNUMBER, 2 },
|
||||||
|
{ "first", tUNUMBER, 1 },
|
||||||
|
/* { "second", tUNUMBER, 2 }, */
|
||||||
|
{ "third", tUNUMBER, 3 },
|
||||||
|
{ "fourth", tUNUMBER, 4 },
|
||||||
|
{ "fifth", tUNUMBER, 5 },
|
||||||
|
{ "sixth", tUNUMBER, 6 },
|
||||||
|
{ "seventh", tUNUMBER, 7 },
|
||||||
|
{ "eighth", tUNUMBER, 8 },
|
||||||
|
{ "ninth", tUNUMBER, 9 },
|
||||||
|
{ "tenth", tUNUMBER, 10 },
|
||||||
|
{ "eleventh", tUNUMBER, 11 },
|
||||||
|
{ "twelfth", tUNUMBER, 12 },
|
||||||
|
{ "ago", tAGO, 1 },
|
||||||
|
{ NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
/* The timezone table. */
|
||||||
|
/* Some of these are commented out because a time_t can't store a float. */
|
||||||
|
static TABLE TimezoneTable[] = {
|
||||||
|
{ "gmt", tZONE, HOUR( 0) }, /* Greenwich Mean */
|
||||||
|
{ "ut", tZONE, HOUR( 0) }, /* Universal (Coordinated) */
|
||||||
|
{ "utc", tZONE, HOUR( 0) },
|
||||||
|
{ "wet", tZONE, HOUR( 0) }, /* Western European */
|
||||||
|
{ "bst", tDAYZONE, HOUR( 0) }, /* British Summer */
|
||||||
|
{ "wat", tZONE, HOUR( 1) }, /* West Africa */
|
||||||
|
{ "at", tZONE, HOUR( 2) }, /* Azores */
|
||||||
|
#if 0
|
||||||
|
/* For completeness. BST is also British Summer, and GST is
|
||||||
|
* also Guam Standard. */
|
||||||
|
{ "bst", tZONE, HOUR( 3) }, /* Brazil Standard */
|
||||||
|
{ "gst", tZONE, HOUR( 3) }, /* Greenland Standard */
|
||||||
|
#endif
|
||||||
|
#if 0
|
||||||
|
{ "nft", tZONE, HOUR(3.5) }, /* Newfoundland */
|
||||||
|
{ "nst", tZONE, HOUR(3.5) }, /* Newfoundland Standard */
|
||||||
|
{ "ndt", tDAYZONE, HOUR(3.5) }, /* Newfoundland Daylight */
|
||||||
|
#endif
|
||||||
|
{ "ast", tZONE, HOUR( 4) }, /* Atlantic Standard */
|
||||||
|
{ "adt", tDAYZONE, HOUR( 4) }, /* Atlantic Daylight */
|
||||||
|
{ "est", tZONE, HOUR( 5) }, /* Eastern Standard */
|
||||||
|
{ "edt", tDAYZONE, HOUR( 5) }, /* Eastern Daylight */
|
||||||
|
{ "cst", tZONE, HOUR( 6) }, /* Central Standard */
|
||||||
|
{ "cdt", tDAYZONE, HOUR( 6) }, /* Central Daylight */
|
||||||
|
{ "mst", tZONE, HOUR( 7) }, /* Mountain Standard */
|
||||||
|
{ "mdt", tDAYZONE, HOUR( 7) }, /* Mountain Daylight */
|
||||||
|
{ "pst", tZONE, HOUR( 8) }, /* Pacific Standard */
|
||||||
|
{ "pdt", tDAYZONE, HOUR( 8) }, /* Pacific Daylight */
|
||||||
|
{ "yst", tZONE, HOUR( 9) }, /* Yukon Standard */
|
||||||
|
{ "ydt", tDAYZONE, HOUR( 9) }, /* Yukon Daylight */
|
||||||
|
{ "hst", tZONE, HOUR(10) }, /* Hawaii Standard */
|
||||||
|
{ "hdt", tDAYZONE, HOUR(10) }, /* Hawaii Daylight */
|
||||||
|
{ "cat", tZONE, HOUR(10) }, /* Central Alaska */
|
||||||
|
{ "ahst", tZONE, HOUR(10) }, /* Alaska-Hawaii Standard */
|
||||||
|
{ "nt", tZONE, HOUR(11) }, /* Nome */
|
||||||
|
{ "idlw", tZONE, HOUR(12) }, /* International Date Line West */
|
||||||
|
{ "cet", tZONE, -HOUR(1) }, /* Central European */
|
||||||
|
{ "met", tZONE, -HOUR(1) }, /* Middle European */
|
||||||
|
{ "mewt", tZONE, -HOUR(1) }, /* Middle European Winter */
|
||||||
|
{ "mest", tDAYZONE, -HOUR(1) }, /* Middle European Summer */
|
||||||
|
{ "swt", tZONE, -HOUR(1) }, /* Swedish Winter */
|
||||||
|
{ "sst", tDAYZONE, -HOUR(1) }, /* Swedish Summer */
|
||||||
|
{ "fwt", tZONE, -HOUR(1) }, /* French Winter */
|
||||||
|
{ "fst", tDAYZONE, -HOUR(1) }, /* French Summer */
|
||||||
|
{ "eet", tZONE, -HOUR(2) }, /* Eastern Europe, USSR Zone 1 */
|
||||||
|
{ "bt", tZONE, -HOUR(3) }, /* Baghdad, USSR Zone 2 */
|
||||||
|
#if 0
|
||||||
|
{ "it", tZONE, -HOUR(3.5) },/* Iran */
|
||||||
|
#endif
|
||||||
|
{ "zp4", tZONE, -HOUR(4) }, /* USSR Zone 3 */
|
||||||
|
{ "zp5", tZONE, -HOUR(5) }, /* USSR Zone 4 */
|
||||||
|
#if 0
|
||||||
|
{ "ist", tZONE, -HOUR(5.5) },/* Indian Standard */
|
||||||
|
#endif
|
||||||
|
{ "zp6", tZONE, -HOUR(6) }, /* USSR Zone 5 */
|
||||||
|
#if 0
|
||||||
|
/* For completeness. NST is also Newfoundland Stanard, and SST is
|
||||||
|
* also Swedish Summer. */
|
||||||
|
{ "nst", tZONE, -HOUR(6.5) },/* North Sumatra */
|
||||||
|
{ "sst", tZONE, -HOUR(7) }, /* South Sumatra, USSR Zone 6 */
|
||||||
|
#endif /* 0 */
|
||||||
|
{ "wast", tZONE, -HOUR(7) }, /* West Australian Standard */
|
||||||
|
{ "wadt", tDAYZONE, -HOUR(7) }, /* West Australian Daylight */
|
||||||
|
#if 0
|
||||||
|
{ "jt", tZONE, -HOUR(7.5) },/* Java (3pm in Cronusland!) */
|
||||||
|
#endif
|
||||||
|
{ "cct", tZONE, -HOUR(8) }, /* China Coast, USSR Zone 7 */
|
||||||
|
{ "jst", tZONE, -HOUR(9) }, /* Japan Standard, USSR Zone 8 */
|
||||||
|
#if 0
|
||||||
|
{ "cast", tZONE, -HOUR(9.5) },/* Central Australian Standard */
|
||||||
|
{ "cadt", tDAYZONE, -HOUR(9.5) },/* Central Australian Daylight */
|
||||||
|
#endif
|
||||||
|
{ "east", tZONE, -HOUR(10) }, /* Eastern Australian Standard */
|
||||||
|
{ "eadt", tDAYZONE, -HOUR(10) }, /* Eastern Australian Daylight */
|
||||||
|
{ "gst", tZONE, -HOUR(10) }, /* Guam Standard, USSR Zone 9 */
|
||||||
|
{ "nzt", tZONE, -HOUR(12) }, /* New Zealand */
|
||||||
|
{ "nzst", tZONE, -HOUR(12) }, /* New Zealand Standard */
|
||||||
|
{ "nzdt", tDAYZONE, -HOUR(12) }, /* New Zealand Daylight */
|
||||||
|
{ "idle", tZONE, -HOUR(12) }, /* International Date Line East */
|
||||||
|
{ NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Military timezone table. */
|
||||||
|
static TABLE MilitaryTable[] = {
|
||||||
|
{ "a", tZONE, HOUR( 1) },
|
||||||
|
{ "b", tZONE, HOUR( 2) },
|
||||||
|
{ "c", tZONE, HOUR( 3) },
|
||||||
|
{ "d", tZONE, HOUR( 4) },
|
||||||
|
{ "e", tZONE, HOUR( 5) },
|
||||||
|
{ "f", tZONE, HOUR( 6) },
|
||||||
|
{ "g", tZONE, HOUR( 7) },
|
||||||
|
{ "h", tZONE, HOUR( 8) },
|
||||||
|
{ "i", tZONE, HOUR( 9) },
|
||||||
|
{ "k", tZONE, HOUR( 10) },
|
||||||
|
{ "l", tZONE, HOUR( 11) },
|
||||||
|
{ "m", tZONE, HOUR( 12) },
|
||||||
|
{ "n", tZONE, HOUR(- 1) },
|
||||||
|
{ "o", tZONE, HOUR(- 2) },
|
||||||
|
{ "p", tZONE, HOUR(- 3) },
|
||||||
|
{ "q", tZONE, HOUR(- 4) },
|
||||||
|
{ "r", tZONE, HOUR(- 5) },
|
||||||
|
{ "s", tZONE, HOUR(- 6) },
|
||||||
|
{ "t", tZONE, HOUR(- 7) },
|
||||||
|
{ "u", tZONE, HOUR(- 8) },
|
||||||
|
{ "v", tZONE, HOUR(- 9) },
|
||||||
|
{ "w", tZONE, HOUR(-10) },
|
||||||
|
{ "x", tZONE, HOUR(-11) },
|
||||||
|
{ "y", tZONE, HOUR(-12) },
|
||||||
|
{ "z", tZONE, HOUR( 0) },
|
||||||
|
{ NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* ARGSUSED */
|
||||||
|
int
|
||||||
|
yyerror(s)
|
||||||
|
char *s;
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static time_t
|
||||||
|
ToSeconds(Hours, Minutes, Seconds, Meridian)
|
||||||
|
time_t Hours;
|
||||||
|
time_t Minutes;
|
||||||
|
time_t Seconds;
|
||||||
|
MERIDIAN Meridian;
|
||||||
|
{
|
||||||
|
if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59)
|
||||||
|
return -1;
|
||||||
|
switch (Meridian) {
|
||||||
|
case MER24:
|
||||||
|
if (Hours < 0 || Hours > 23)
|
||||||
|
return -1;
|
||||||
|
return (Hours * 60L + Minutes) * 60L + Seconds;
|
||||||
|
case MERam:
|
||||||
|
if (Hours < 1 || Hours > 12)
|
||||||
|
return -1;
|
||||||
|
return (Hours * 60L + Minutes) * 60L + Seconds;
|
||||||
|
case MERpm:
|
||||||
|
if (Hours < 1 || Hours > 12)
|
||||||
|
return -1;
|
||||||
|
return ((Hours + 12) * 60L + Minutes) * 60L + Seconds;
|
||||||
|
}
|
||||||
|
/* NOTREACHED */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static time_t
|
||||||
|
Convert(Month, Day, Year, Hours, Minutes, Seconds, Meridian, DSTmode)
|
||||||
|
time_t Month;
|
||||||
|
time_t Day;
|
||||||
|
time_t Year;
|
||||||
|
time_t Hours;
|
||||||
|
time_t Minutes;
|
||||||
|
time_t Seconds;
|
||||||
|
MERIDIAN Meridian;
|
||||||
|
DSTMODE DSTmode;
|
||||||
|
{
|
||||||
|
static int DaysInMonth[12] = {
|
||||||
|
31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
|
||||||
|
};
|
||||||
|
time_t tod;
|
||||||
|
time_t Julian;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (Year < 0)
|
||||||
|
Year = -Year;
|
||||||
|
if (Year < 100)
|
||||||
|
Year += 1900;
|
||||||
|
DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0)
|
||||||
|
? 29 : 28;
|
||||||
|
if (Year < EPOCH || Year > 1999
|
||||||
|
|| Month < 1 || Month > 12
|
||||||
|
/* Lint fluff: "conversion from long may lose accuracy" */
|
||||||
|
|| Day < 1 || Day > DaysInMonth[(int)--Month])
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
for (Julian = Day - 1, i = 0; i < Month; i++)
|
||||||
|
Julian += DaysInMonth[i];
|
||||||
|
for (i = EPOCH; i < Year; i++)
|
||||||
|
Julian += 365 + (i % 4 == 0);
|
||||||
|
Julian *= SECSPERDAY;
|
||||||
|
Julian += yyTimezone * 60L;
|
||||||
|
if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0)
|
||||||
|
return -1;
|
||||||
|
Julian += tod;
|
||||||
|
if (DSTmode == DSTon
|
||||||
|
|| (DSTmode == DSTmaybe && localtime(&Julian)->tm_isdst))
|
||||||
|
Julian -= 60 * 60;
|
||||||
|
return Julian;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static time_t
|
||||||
|
DSTcorrect(Start, Future)
|
||||||
|
time_t Start;
|
||||||
|
time_t Future;
|
||||||
|
{
|
||||||
|
time_t StartDay;
|
||||||
|
time_t FutureDay;
|
||||||
|
|
||||||
|
StartDay = (localtime(&Start)->tm_hour + 1) % 24;
|
||||||
|
FutureDay = (localtime(&Future)->tm_hour + 1) % 24;
|
||||||
|
return (Future - Start) + (StartDay - FutureDay) * 60L * 60L;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static time_t
|
||||||
|
RelativeDate(Start, DayOrdinal, DayNumber)
|
||||||
|
time_t Start;
|
||||||
|
time_t DayOrdinal;
|
||||||
|
time_t DayNumber;
|
||||||
|
{
|
||||||
|
struct tm *tm;
|
||||||
|
time_t now;
|
||||||
|
|
||||||
|
now = Start;
|
||||||
|
tm = localtime(&now);
|
||||||
|
now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7);
|
||||||
|
now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1);
|
||||||
|
return DSTcorrect(Start, now);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static time_t
|
||||||
|
RelativeMonth(Start, RelMonth)
|
||||||
|
time_t Start;
|
||||||
|
time_t RelMonth;
|
||||||
|
{
|
||||||
|
struct tm *tm;
|
||||||
|
time_t Month;
|
||||||
|
time_t Year;
|
||||||
|
|
||||||
|
if (RelMonth == 0)
|
||||||
|
return 0;
|
||||||
|
tm = localtime(&Start);
|
||||||
|
Month = 12 * tm->tm_year + tm->tm_mon + RelMonth;
|
||||||
|
Year = Month / 12;
|
||||||
|
Month = Month % 12 + 1;
|
||||||
|
return DSTcorrect(Start,
|
||||||
|
Convert(Month, (time_t)tm->tm_mday, Year,
|
||||||
|
(time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec,
|
||||||
|
MER24, DSTmaybe));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
LookupWord(buff)
|
||||||
|
char *buff;
|
||||||
|
{
|
||||||
|
register char *p;
|
||||||
|
register char *q;
|
||||||
|
register TABLE *tp;
|
||||||
|
int i;
|
||||||
|
int abbrev;
|
||||||
|
|
||||||
|
/* Make it lowercase. */
|
||||||
|
for (p = buff; *p; p++)
|
||||||
|
if (isupper(*p))
|
||||||
|
*p = tolower(*p);
|
||||||
|
|
||||||
|
if (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0) {
|
||||||
|
yylval.Meridian = MERam;
|
||||||
|
return tMERIDIAN;
|
||||||
|
}
|
||||||
|
if (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0) {
|
||||||
|
yylval.Meridian = MERpm;
|
||||||
|
return tMERIDIAN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* See if we have an abbreviation for a month. */
|
||||||
|
if (strlen(buff) == 3)
|
||||||
|
abbrev = 1;
|
||||||
|
else if (strlen(buff) == 4 && buff[3] == '.') {
|
||||||
|
abbrev = 1;
|
||||||
|
buff[3] = '\0';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
abbrev = 0;
|
||||||
|
|
||||||
|
for (tp = MonthDayTable; tp->name; tp++) {
|
||||||
|
if (abbrev) {
|
||||||
|
if (strncmp(buff, tp->name, 3) == 0) {
|
||||||
|
yylval.Number = tp->value;
|
||||||
|
return tp->type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (strcmp(buff, tp->name) == 0) {
|
||||||
|
yylval.Number = tp->value;
|
||||||
|
return tp->type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (tp = TimezoneTable; tp->name; tp++)
|
||||||
|
if (strcmp(buff, tp->name) == 0) {
|
||||||
|
yylval.Number = tp->value;
|
||||||
|
return tp->type;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(buff, "dst") == 0)
|
||||||
|
return tDST;
|
||||||
|
|
||||||
|
for (tp = UnitsTable; tp->name; tp++)
|
||||||
|
if (strcmp(buff, tp->name) == 0) {
|
||||||
|
yylval.Number = tp->value;
|
||||||
|
return tp->type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Strip off any plural and try the units table again. */
|
||||||
|
i = strlen(buff) - 1;
|
||||||
|
if (buff[i] == 's') {
|
||||||
|
buff[i] = '\0';
|
||||||
|
for (tp = UnitsTable; tp->name; tp++)
|
||||||
|
if (strcmp(buff, tp->name) == 0) {
|
||||||
|
yylval.Number = tp->value;
|
||||||
|
return tp->type;
|
||||||
|
}
|
||||||
|
buff[i] = 's'; /* Put back for "this" in OtherTable. */
|
||||||
|
}
|
||||||
|
|
||||||
|
for (tp = OtherTable; tp->name; tp++)
|
||||||
|
if (strcmp(buff, tp->name) == 0) {
|
||||||
|
yylval.Number = tp->value;
|
||||||
|
return tp->type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Military timezones. */
|
||||||
|
if (buff[1] == '\0' && isalpha(*buff)) {
|
||||||
|
for (tp = MilitaryTable; tp->name; tp++)
|
||||||
|
if (strcmp(buff, tp->name) == 0) {
|
||||||
|
yylval.Number = tp->value;
|
||||||
|
return tp->type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Drop out any periods and try the timezone table again. */
|
||||||
|
for (i = 0, p = q = buff; *q; q++)
|
||||||
|
if (*q != '.')
|
||||||
|
*p++ = *q;
|
||||||
|
else
|
||||||
|
i++;
|
||||||
|
*p = '\0';
|
||||||
|
if (i)
|
||||||
|
for (tp = TimezoneTable; tp->name; tp++)
|
||||||
|
if (strcmp(buff, tp->name) == 0) {
|
||||||
|
yylval.Number = tp->value;
|
||||||
|
return tp->type;
|
||||||
|
}
|
||||||
|
|
||||||
|
return tID;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
yylex()
|
||||||
|
{
|
||||||
|
register char c;
|
||||||
|
register char *p;
|
||||||
|
char buff[20];
|
||||||
|
int Count;
|
||||||
|
int sign;
|
||||||
|
|
||||||
|
for ( ; ; ) {
|
||||||
|
while (isspace(*yyInput))
|
||||||
|
yyInput++;
|
||||||
|
|
||||||
|
if (isdigit(c = *yyInput) || c == '-' || c == '+') {
|
||||||
|
if (c == '-' || c == '+') {
|
||||||
|
sign = c == '-' ? -1 : 1;
|
||||||
|
if (!isdigit(*++yyInput))
|
||||||
|
/* skip the '-' sign */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
sign = 0;
|
||||||
|
for (yylval.Number = 0; isdigit(c = *yyInput++); )
|
||||||
|
yylval.Number = 10 * yylval.Number + c - '0';
|
||||||
|
yyInput--;
|
||||||
|
if (sign < 0)
|
||||||
|
yylval.Number = -yylval.Number;
|
||||||
|
return sign ? tSNUMBER : tUNUMBER;
|
||||||
|
}
|
||||||
|
if (isalpha(c)) {
|
||||||
|
for (p = buff; isalpha(c = *yyInput++) || c == '.'; )
|
||||||
|
if (p < &buff[sizeof buff - 1])
|
||||||
|
*p++ = c;
|
||||||
|
*p = '\0';
|
||||||
|
yyInput--;
|
||||||
|
return LookupWord(buff);
|
||||||
|
}
|
||||||
|
if (c != '(')
|
||||||
|
return *yyInput++;
|
||||||
|
Count = 0;
|
||||||
|
do {
|
||||||
|
c = *yyInput++;
|
||||||
|
if (c == '\0')
|
||||||
|
return c;
|
||||||
|
if (c == '(')
|
||||||
|
Count++;
|
||||||
|
else if (c == ')')
|
||||||
|
Count--;
|
||||||
|
} while (Count > 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
time_t
|
||||||
|
get_date(p, now)
|
||||||
|
char *p;
|
||||||
|
struct timeb *now;
|
||||||
|
{
|
||||||
|
struct tm *tm;
|
||||||
|
struct timeb ftz;
|
||||||
|
time_t Start;
|
||||||
|
time_t tod;
|
||||||
|
|
||||||
|
yyInput = p;
|
||||||
|
if (now == NULL) {
|
||||||
|
now = &ftz;
|
||||||
|
#if defined(FTIME_MISSING)
|
||||||
|
(void)time(&ftz.time);
|
||||||
|
/* Set the timezone global. */
|
||||||
|
tzset();
|
||||||
|
#if defined(HAVE_TIMEZONE)
|
||||||
|
tm = localtime(&ftz.time);
|
||||||
|
ftz.timezone = tm->tm_gmtoff / 60;
|
||||||
|
#else
|
||||||
|
#if defined(timezone)
|
||||||
|
ftz.tzone = (int) timezone / 60;
|
||||||
|
#else
|
||||||
|
ftz.timezone = (int) timezone / 60;
|
||||||
|
#endif /* defined(timezone) */
|
||||||
|
#endif /* defined(HAVE_TIMEZONE) */
|
||||||
|
#else
|
||||||
|
(void)ftime(&ftz);
|
||||||
|
#endif /* defined(FTIME_MISSING) */
|
||||||
|
}
|
||||||
|
|
||||||
|
tm = localtime(&now->time);
|
||||||
|
yyYear = tm->tm_year;
|
||||||
|
yyMonth = tm->tm_mon + 1;
|
||||||
|
yyDay = tm->tm_mday;
|
||||||
|
#if defined(timezone)
|
||||||
|
yyTimezone = now->tzone;
|
||||||
|
#else
|
||||||
|
yyTimezone = now->timezone;
|
||||||
|
#endif /* defined(timezone) */
|
||||||
|
yyDSTmode = DSTmaybe;
|
||||||
|
yyHour = 0;
|
||||||
|
yyMinutes = 0;
|
||||||
|
yySeconds = 0;
|
||||||
|
yyMeridian = MER24;
|
||||||
|
yyRelSeconds = 0;
|
||||||
|
yyRelMonth = 0;
|
||||||
|
yyHaveDate = 0;
|
||||||
|
yyHaveDay = 0;
|
||||||
|
yyHaveRel = 0;
|
||||||
|
yyHaveTime = 0;
|
||||||
|
yyHaveZone = 0;
|
||||||
|
|
||||||
|
if (yyparse()
|
||||||
|
|| yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (yyHaveDate || yyHaveTime || yyHaveDay) {
|
||||||
|
Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds,
|
||||||
|
yyMeridian, yyDSTmode);
|
||||||
|
if (Start < 0)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Start = now->time;
|
||||||
|
if (!yyHaveRel)
|
||||||
|
Start -= ((tm->tm_hour * 60L + tm->tm_min) * 60L) + tm->tm_sec;
|
||||||
|
}
|
||||||
|
|
||||||
|
Start += yyRelSeconds;
|
||||||
|
Start += RelativeMonth(Start, yyRelMonth);
|
||||||
|
|
||||||
|
if (yyHaveDay && !yyHaveDate) {
|
||||||
|
tod = RelativeDate(Start, yyDayOrdinal, yyDayNumber);
|
||||||
|
Start += tod;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Have to do *something* with a legitimate -1 so it's distinguishable
|
||||||
|
* from the error return value. (Alternately could set errno on error.) */
|
||||||
|
return Start == -1 ? 0 : Start;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#if defined(TEST)
|
||||||
|
|
||||||
|
/* ARGSUSED */
|
||||||
|
main(ac, av)
|
||||||
|
int ac;
|
||||||
|
char *av[];
|
||||||
|
{
|
||||||
|
char buff[128];
|
||||||
|
time_t d;
|
||||||
|
|
||||||
|
(void)printf("Enter date, or blank line to exit.\n\t> ");
|
||||||
|
(void)fflush(stdout);
|
||||||
|
while (gets(buff) && buff[0]) {
|
||||||
|
d = get_date(buff, (struct timeb *)NULL);
|
||||||
|
if (d == -1)
|
||||||
|
(void)printf("Bad format - couldn't convert.\n");
|
||||||
|
else
|
||||||
|
(void)printf("%s", ctime(&d));
|
||||||
|
(void)printf("\t> ");
|
||||||
|
(void)fflush(stdout);
|
||||||
|
}
|
||||||
|
exit(0);
|
||||||
|
/* NOTREACHED */
|
||||||
|
}
|
||||||
|
#endif /* defined(TEST) */
|
||||||
@@ -0,0 +1,604 @@
|
|||||||
|
/* Getopt for GNU.
|
||||||
|
Copyright (C) 1987-1992 Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2, or (at your option)
|
||||||
|
any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
|
||||||
|
|
||||||
|
#if !__STDC__
|
||||||
|
#define const
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* This version of `getopt' appears to the caller like standard Unix `getopt'
|
||||||
|
but it behaves differently for the user, since it allows the user
|
||||||
|
to intersperse the options with the other arguments.
|
||||||
|
|
||||||
|
As `getopt' works, it permutes the elements of `argv' so that,
|
||||||
|
when it is done, all the options precede everything else. Thus
|
||||||
|
all application programs are extended to handle flexible argument order.
|
||||||
|
|
||||||
|
Setting the environment variable _POSIX_OPTION_ORDER disables permutation.
|
||||||
|
Then the behavior is completely standard.
|
||||||
|
|
||||||
|
GNU application programs can use a third alternative mode in which
|
||||||
|
they can distinguish the relative order of options and other arguments. */
|
||||||
|
|
||||||
|
#ifndef lint
|
||||||
|
static char rcsid[] = "@(#)getopt.c 1.7 92/03/31";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#if defined(STDC_HEADERS) || defined(__GNU_LIBRARY__)
|
||||||
|
#include <stdlib.h>
|
||||||
|
#else /* STDC_HEADERS or __GNU_LIBRARY__ */
|
||||||
|
char *getenv ();
|
||||||
|
char *malloc ();
|
||||||
|
#endif /* STDC_HEADERS or __GNU_LIBRARY__ */
|
||||||
|
|
||||||
|
/* AIX requires this to be the first thing in the file. */
|
||||||
|
#ifdef __GNUC__
|
||||||
|
#if !defined(bsdi) && !defined(__386BSD__)
|
||||||
|
#define alloca __builtin_alloca
|
||||||
|
#endif
|
||||||
|
#else /* not __GNUC__ */
|
||||||
|
#ifdef sparc
|
||||||
|
#include <alloca.h>
|
||||||
|
#else
|
||||||
|
#ifdef _AIX
|
||||||
|
#pragma alloca
|
||||||
|
#else
|
||||||
|
char *alloca ();
|
||||||
|
#endif
|
||||||
|
#endif /* sparc */
|
||||||
|
#endif /* not __GNUC__ */
|
||||||
|
|
||||||
|
#if defined(USG) || defined(STDC_HEADERS) || defined(__GNU_LIBRARY__)
|
||||||
|
#include <string.h>
|
||||||
|
#ifndef bcopy
|
||||||
|
#define bcopy(s, d, n) memcpy ((d), (s), (n))
|
||||||
|
#endif
|
||||||
|
#ifndef index
|
||||||
|
#define index strchr
|
||||||
|
#endif
|
||||||
|
#else /* USG or STDC_HEADERS or __GNU_LIBRARY__ */
|
||||||
|
#ifdef VMS
|
||||||
|
#include <string.h>
|
||||||
|
#else /* VMS */
|
||||||
|
#include <strings.h>
|
||||||
|
#endif /* VMS */
|
||||||
|
/* Declaring bcopy causes errors on systems whose declarations are different.
|
||||||
|
If the declaration is omitted, everything works fine. */
|
||||||
|
#endif /* USG or STDC_HEADERS or __GNU_LIBRARY__ */
|
||||||
|
|
||||||
|
/* For communication from `getopt' to the caller.
|
||||||
|
When `getopt' finds an option that takes an argument,
|
||||||
|
the argument value is returned here.
|
||||||
|
Also, when `ordering' is RETURN_IN_ORDER,
|
||||||
|
each non-option ARGV-element is returned here. */
|
||||||
|
|
||||||
|
char *optarg = 0;
|
||||||
|
|
||||||
|
/* Index in ARGV of the next element to be scanned.
|
||||||
|
This is used for communication to and from the caller
|
||||||
|
and for communication between successive calls to `getopt'.
|
||||||
|
|
||||||
|
On entry to `getopt', zero means this is the first call; initialize.
|
||||||
|
|
||||||
|
When `getopt' returns EOF, this is the index of the first of the
|
||||||
|
non-option elements that the caller should itself scan.
|
||||||
|
|
||||||
|
Otherwise, `optind' communicates from one call to the next
|
||||||
|
how much of ARGV has been scanned so far. */
|
||||||
|
|
||||||
|
int optind = 0;
|
||||||
|
|
||||||
|
/* The next char to be scanned in the option-element
|
||||||
|
in which the last option character we returned was found.
|
||||||
|
This allows us to pick up the scan where we left off.
|
||||||
|
|
||||||
|
If this is zero, or a null string, it means resume the scan
|
||||||
|
by advancing to the next ARGV-element. */
|
||||||
|
|
||||||
|
static char *nextchar;
|
||||||
|
|
||||||
|
/* Callers store zero here to inhibit the error message
|
||||||
|
for unrecognized options. */
|
||||||
|
|
||||||
|
int opterr = 1;
|
||||||
|
|
||||||
|
/* Describe how to deal with options that follow non-option ARGV-elements.
|
||||||
|
|
||||||
|
If the caller did not specify anything,
|
||||||
|
the default is REQUIRE_ORDER if the environment variable
|
||||||
|
_POSIX_OPTION_ORDER is defined, PERMUTE otherwise.
|
||||||
|
|
||||||
|
REQUIRE_ORDER means don't recognize them as options;
|
||||||
|
stop option processing when the first non-option is seen.
|
||||||
|
This is what Unix does.
|
||||||
|
This mode of operation is selected by either setting the environment
|
||||||
|
variable POSIX_ME_HARDER, or using `+' as the first character
|
||||||
|
of the list of option characters.
|
||||||
|
|
||||||
|
PERMUTE is the default. We permute the contents of ARGV as we scan,
|
||||||
|
so that eventually all the non-options are at the end. This allows options
|
||||||
|
to be given in any order, even with programs that were not written to
|
||||||
|
expect this.
|
||||||
|
|
||||||
|
RETURN_IN_ORDER is an option available to programs that were written
|
||||||
|
to expect options and other ARGV-elements in any order and that care about
|
||||||
|
the ordering of the two. We describe each non-option ARGV-element
|
||||||
|
as if it were the argument of an option with character code 1.
|
||||||
|
Using `-' as the first character of the list of option characters
|
||||||
|
selects this mode of operation.
|
||||||
|
|
||||||
|
The special argument `--' forces an end of option-scanning regardless
|
||||||
|
of the value of `ordering'. In the case of RETURN_IN_ORDER, only
|
||||||
|
`--' can cause `getopt' to return EOF with `optind' != ARGC. */
|
||||||
|
|
||||||
|
static enum
|
||||||
|
{
|
||||||
|
REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
|
||||||
|
} ordering;
|
||||||
|
|
||||||
|
/* Describe the long-named options requested by the application.
|
||||||
|
_GETOPT_LONG_OPTIONS is a vector of `struct option' terminated by an
|
||||||
|
element containing a name which is zero.
|
||||||
|
The field `has_arg' is 1 if the option takes an argument,
|
||||||
|
2 if it takes an optional argument. */
|
||||||
|
|
||||||
|
struct option
|
||||||
|
{
|
||||||
|
char *name;
|
||||||
|
int has_arg;
|
||||||
|
int *flag;
|
||||||
|
int val;
|
||||||
|
};
|
||||||
|
|
||||||
|
const struct option *_getopt_long_options;
|
||||||
|
|
||||||
|
int _getopt_long_only = 0;
|
||||||
|
|
||||||
|
/* Index in _GETOPT_LONG_OPTIONS of the long-named option actually found.
|
||||||
|
Only valid when a long-named option was found. */
|
||||||
|
|
||||||
|
int option_index;
|
||||||
|
|
||||||
|
/* Handle permutation of arguments. */
|
||||||
|
|
||||||
|
/* Describe the part of ARGV that contains non-options that have
|
||||||
|
been skipped. `first_nonopt' is the index in ARGV of the first of them;
|
||||||
|
`last_nonopt' is the index after the last of them. */
|
||||||
|
|
||||||
|
static int first_nonopt;
|
||||||
|
static int last_nonopt;
|
||||||
|
|
||||||
|
/* Exchange two adjacent subsequences of ARGV.
|
||||||
|
One subsequence is elements [first_nonopt,last_nonopt)
|
||||||
|
which contains all the non-options that have been skipped so far.
|
||||||
|
The other is elements [last_nonopt,optind), which contains all
|
||||||
|
the options processed since those non-options were skipped.
|
||||||
|
|
||||||
|
`first_nonopt' and `last_nonopt' are relocated so that they describe
|
||||||
|
the new indices of the non-options in ARGV after they are moved. */
|
||||||
|
|
||||||
|
static void
|
||||||
|
exchange (argv)
|
||||||
|
char **argv;
|
||||||
|
{
|
||||||
|
int nonopts_size = (last_nonopt - first_nonopt) * sizeof (char *);
|
||||||
|
char **temp = (char **) alloca (nonopts_size);
|
||||||
|
|
||||||
|
/* Interchange the two blocks of data in ARGV. */
|
||||||
|
|
||||||
|
bcopy (&argv[first_nonopt], temp, nonopts_size);
|
||||||
|
bcopy (&argv[last_nonopt], &argv[first_nonopt],
|
||||||
|
(optind - last_nonopt) * sizeof (char *));
|
||||||
|
bcopy (temp, &argv[first_nonopt + optind - last_nonopt], nonopts_size);
|
||||||
|
|
||||||
|
/* Update records for the slots the non-options now occupy. */
|
||||||
|
|
||||||
|
first_nonopt += (optind - last_nonopt);
|
||||||
|
last_nonopt = optind;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Scan elements of ARGV (whose length is ARGC) for option characters
|
||||||
|
given in OPTSTRING.
|
||||||
|
|
||||||
|
If an element of ARGV starts with '-', and is not exactly "-" or "--",
|
||||||
|
then it is an option element. The characters of this element
|
||||||
|
(aside from the initial '-') are option characters. If `getopt'
|
||||||
|
is called repeatedly, it returns successively each of the option characters
|
||||||
|
from each of the option elements.
|
||||||
|
|
||||||
|
If `getopt' finds another option character, it returns that character,
|
||||||
|
updating `optind' and `nextchar' so that the next call to `getopt' can
|
||||||
|
resume the scan with the following option character or ARGV-element.
|
||||||
|
|
||||||
|
If there are no more option characters, `getopt' returns `EOF'.
|
||||||
|
Then `optind' is the index in ARGV of the first ARGV-element
|
||||||
|
that is not an option. (The ARGV-elements have been permuted
|
||||||
|
so that those that are not options now come last.)
|
||||||
|
|
||||||
|
OPTSTRING is a string containing the legitimate option characters.
|
||||||
|
If an option character is seen that is not listed in OPTSTRING,
|
||||||
|
return '?' after printing an error message. If you set `opterr' to
|
||||||
|
zero, the error message is suppressed but we still return '?'.
|
||||||
|
|
||||||
|
If a char in OPTSTRING is followed by a colon, that means it wants an arg,
|
||||||
|
so the following text in the same ARGV-element, or the text of the following
|
||||||
|
ARGV-element, is returned in `optarg'. Two colons mean an option that
|
||||||
|
wants an optional arg; if there is text in the current ARGV-element,
|
||||||
|
it is returned in `optarg', otherwise `optarg' is set to zero.
|
||||||
|
|
||||||
|
If OPTSTRING starts with `-' or `+', it requests different methods of
|
||||||
|
handling the non-option ARGV-elements.
|
||||||
|
See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
|
||||||
|
|
||||||
|
Long-named options begin with `+' instead of `-'.
|
||||||
|
Their names may be abbreviated as long as the abbreviation is unique
|
||||||
|
or is an exact match for some defined option. If they have an
|
||||||
|
argument, it follows the option name in the same ARGV-element, separated
|
||||||
|
from the option name by a `=', or else the in next ARGV-element.
|
||||||
|
When `getopt' finds a long-named option, it returns 0 if that option's
|
||||||
|
`flag' field is nonzero, the value of the option's `val' field
|
||||||
|
otherwise. */
|
||||||
|
|
||||||
|
int
|
||||||
|
gnu_getopt (argc, argv, optstring)
|
||||||
|
int argc;
|
||||||
|
char **argv;
|
||||||
|
const char *optstring;
|
||||||
|
{
|
||||||
|
optarg = 0;
|
||||||
|
|
||||||
|
/* Initialize the internal data when the first call is made.
|
||||||
|
Start processing options with ARGV-element 1 (since ARGV-element 0
|
||||||
|
is the program name); the sequence of previously skipped
|
||||||
|
non-option ARGV-elements is empty. */
|
||||||
|
|
||||||
|
if (optind == 0)
|
||||||
|
{
|
||||||
|
first_nonopt = last_nonopt = optind = 1;
|
||||||
|
|
||||||
|
nextchar = 0;
|
||||||
|
|
||||||
|
/* Determine how to handle the ordering of options and nonoptions. */
|
||||||
|
|
||||||
|
if (optstring[0] == '-')
|
||||||
|
{
|
||||||
|
ordering = RETURN_IN_ORDER;
|
||||||
|
++optstring;
|
||||||
|
}
|
||||||
|
else if (optstring[0] == '+')
|
||||||
|
{
|
||||||
|
ordering = REQUIRE_ORDER;
|
||||||
|
++optstring;
|
||||||
|
}
|
||||||
|
else if (getenv ("POSIX_ME_HARDER") != 0)
|
||||||
|
ordering = REQUIRE_ORDER;
|
||||||
|
else
|
||||||
|
ordering = PERMUTE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nextchar == 0 || *nextchar == 0)
|
||||||
|
{
|
||||||
|
if (ordering == PERMUTE)
|
||||||
|
{
|
||||||
|
/* If we have just processed some options following some non-options,
|
||||||
|
exchange them so that the options come first. */
|
||||||
|
|
||||||
|
if (first_nonopt != last_nonopt && last_nonopt != optind)
|
||||||
|
exchange (argv);
|
||||||
|
else if (last_nonopt != optind)
|
||||||
|
first_nonopt = optind;
|
||||||
|
|
||||||
|
/* Now skip any additional non-options
|
||||||
|
and extend the range of non-options previously skipped. */
|
||||||
|
|
||||||
|
while (optind < argc
|
||||||
|
&& (argv[optind][0] != '-'
|
||||||
|
|| argv[optind][1] == 0)
|
||||||
|
&& (_getopt_long_options == 0
|
||||||
|
|| argv[optind][0] != '+'
|
||||||
|
|| argv[optind][1] == 0))
|
||||||
|
optind++;
|
||||||
|
last_nonopt = optind;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Special ARGV-element `--' means premature end of options.
|
||||||
|
Skip it like a null option,
|
||||||
|
then exchange with previous non-options as if it were an option,
|
||||||
|
then skip everything else like a non-option. */
|
||||||
|
|
||||||
|
if (optind != argc && !strcmp (argv[optind], "--"))
|
||||||
|
{
|
||||||
|
optind++;
|
||||||
|
|
||||||
|
if (first_nonopt != last_nonopt && last_nonopt != optind)
|
||||||
|
exchange (argv);
|
||||||
|
else if (first_nonopt == last_nonopt)
|
||||||
|
first_nonopt = optind;
|
||||||
|
last_nonopt = argc;
|
||||||
|
|
||||||
|
optind = argc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If we have done all the ARGV-elements, stop the scan
|
||||||
|
and back over any non-options that we skipped and permuted. */
|
||||||
|
|
||||||
|
if (optind == argc)
|
||||||
|
{
|
||||||
|
/* Set the next-arg-index to point at the non-options
|
||||||
|
that we previously skipped, so the caller will digest them. */
|
||||||
|
if (first_nonopt != last_nonopt)
|
||||||
|
optind = first_nonopt;
|
||||||
|
return EOF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If we have come to a non-option and did not permute it,
|
||||||
|
either stop the scan or describe it to the caller and pass it by. */
|
||||||
|
|
||||||
|
if ((argv[optind][0] != '-' || argv[optind][1] == 0)
|
||||||
|
&& (_getopt_long_options == 0
|
||||||
|
|| argv[optind][0] != '+' || argv[optind][1] == 0))
|
||||||
|
{
|
||||||
|
if (ordering == REQUIRE_ORDER)
|
||||||
|
return EOF;
|
||||||
|
optarg = argv[optind++];
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We have found another option-ARGV-element.
|
||||||
|
Start decoding its characters. */
|
||||||
|
|
||||||
|
nextchar = argv[optind] + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_getopt_long_options != 0
|
||||||
|
&& (argv[optind][0] == '+'
|
||||||
|
|| (_getopt_long_only && argv[optind][0] == '-'))
|
||||||
|
)
|
||||||
|
{
|
||||||
|
const struct option *p;
|
||||||
|
char *s = nextchar;
|
||||||
|
int exact = 0;
|
||||||
|
int ambig = 0;
|
||||||
|
const struct option *pfound = 0;
|
||||||
|
int indfound = 0;
|
||||||
|
|
||||||
|
while (*s && *s != '=')
|
||||||
|
s++;
|
||||||
|
|
||||||
|
/* Test all options for either exact match or abbreviated matches. */
|
||||||
|
for (p = _getopt_long_options, option_index = 0; p->name;
|
||||||
|
p++, option_index++)
|
||||||
|
if (!strncmp (p->name, nextchar, s - nextchar))
|
||||||
|
{
|
||||||
|
if (s - nextchar == strlen (p->name))
|
||||||
|
{
|
||||||
|
/* Exact match found. */
|
||||||
|
pfound = p;
|
||||||
|
indfound = option_index;
|
||||||
|
exact = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (pfound == 0)
|
||||||
|
{
|
||||||
|
/* First nonexact match found. */
|
||||||
|
pfound = p;
|
||||||
|
indfound = option_index;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
/* Second nonexact match found. */
|
||||||
|
ambig = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ambig && !exact)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s: option `%s' is ambiguous\n",
|
||||||
|
argv[0], argv[optind]);
|
||||||
|
nextchar += strlen (nextchar);
|
||||||
|
optind++;
|
||||||
|
return '?';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pfound != 0)
|
||||||
|
{
|
||||||
|
option_index = indfound;
|
||||||
|
optind++;
|
||||||
|
if (*s)
|
||||||
|
{
|
||||||
|
if (pfound->has_arg > 0)
|
||||||
|
optarg = s + 1;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf (stderr,
|
||||||
|
"%s: option `%c%s' doesn't allow an argument\n",
|
||||||
|
argv[0], argv[optind - 1][0], pfound->name);
|
||||||
|
nextchar += strlen (nextchar);
|
||||||
|
return '?';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (pfound->has_arg == 1)
|
||||||
|
{
|
||||||
|
if (optind < argc)
|
||||||
|
optarg = argv[optind++];
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s: option `%s' requires an argument\n",
|
||||||
|
argv[0], argv[optind - 1]);
|
||||||
|
nextchar += strlen (nextchar);
|
||||||
|
return '?';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nextchar += strlen (nextchar);
|
||||||
|
if (pfound->flag)
|
||||||
|
{
|
||||||
|
*(pfound->flag) = pfound->val;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return pfound->val;
|
||||||
|
}
|
||||||
|
/* Can't find it as a long option. If this is getopt_long_only,
|
||||||
|
and the option starts with '-' and is a valid short
|
||||||
|
option, then interpret it as a short option. Otherwise it's
|
||||||
|
an error. */
|
||||||
|
if (_getopt_long_only == 0 || argv[optind][0] == '+' ||
|
||||||
|
index (optstring, *nextchar) == 0)
|
||||||
|
{
|
||||||
|
if (opterr != 0)
|
||||||
|
fprintf (stderr, "%s: unrecognized option `%c%s'\n",
|
||||||
|
argv[0], argv[optind][0], nextchar);
|
||||||
|
nextchar += strlen (nextchar);
|
||||||
|
optind++;
|
||||||
|
return '?';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Look at and handle the next option-character. */
|
||||||
|
|
||||||
|
{
|
||||||
|
char c = *nextchar++;
|
||||||
|
char *temp = index (optstring, c);
|
||||||
|
|
||||||
|
/* Increment `optind' when we start to process its last character. */
|
||||||
|
if (*nextchar == 0)
|
||||||
|
optind++;
|
||||||
|
|
||||||
|
if (temp == 0 || c == ':')
|
||||||
|
{
|
||||||
|
if (opterr != 0)
|
||||||
|
{
|
||||||
|
if (c < 040 || c >= 0177)
|
||||||
|
fprintf (stderr, "%s: unrecognized option, character code 0%o\n",
|
||||||
|
argv[0], c);
|
||||||
|
else
|
||||||
|
fprintf (stderr, "%s: unrecognized option `-%c'\n",
|
||||||
|
argv[0], c);
|
||||||
|
}
|
||||||
|
return '?';
|
||||||
|
}
|
||||||
|
if (temp[1] == ':')
|
||||||
|
{
|
||||||
|
if (temp[2] == ':')
|
||||||
|
{
|
||||||
|
/* This is an option that accepts an argument optionally. */
|
||||||
|
if (*nextchar != 0)
|
||||||
|
{
|
||||||
|
optarg = nextchar;
|
||||||
|
optind++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
optarg = 0;
|
||||||
|
nextchar = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* This is an option that requires an argument. */
|
||||||
|
if (*nextchar != 0)
|
||||||
|
{
|
||||||
|
optarg = nextchar;
|
||||||
|
/* If we end this ARGV-element by taking the rest as an arg,
|
||||||
|
we must advance to the next element now. */
|
||||||
|
optind++;
|
||||||
|
}
|
||||||
|
else if (optind == argc)
|
||||||
|
{
|
||||||
|
if (opterr != 0)
|
||||||
|
fprintf (stderr, "%s: option `-%c' requires an argument\n",
|
||||||
|
argv[0], c);
|
||||||
|
c = '?';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
/* We already incremented `optind' once;
|
||||||
|
increment it again when taking next ARGV-elt as argument. */
|
||||||
|
optarg = argv[optind++];
|
||||||
|
nextchar = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef TEST
|
||||||
|
|
||||||
|
/* Compile with -DTEST to make an executable for use in testing
|
||||||
|
the above definition of `getopt'. */
|
||||||
|
|
||||||
|
int
|
||||||
|
main (argc, argv)
|
||||||
|
int argc;
|
||||||
|
char **argv;
|
||||||
|
{
|
||||||
|
int c;
|
||||||
|
int digit_optind = 0;
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
int this_option_optind = optind ? optind : 1;
|
||||||
|
|
||||||
|
c = gnu_getopt (argc, argv, "abc:d:0123456789");
|
||||||
|
if (c == EOF)
|
||||||
|
break;
|
||||||
|
|
||||||
|
switch (c)
|
||||||
|
{
|
||||||
|
case '0':
|
||||||
|
case '1':
|
||||||
|
case '2':
|
||||||
|
case '3':
|
||||||
|
case '4':
|
||||||
|
case '5':
|
||||||
|
case '6':
|
||||||
|
case '7':
|
||||||
|
case '8':
|
||||||
|
case '9':
|
||||||
|
if (digit_optind != 0 && digit_optind != this_option_optind)
|
||||||
|
printf ("digits occur in two different argv-elements.\n");
|
||||||
|
digit_optind = this_option_optind;
|
||||||
|
printf ("option %c\n", c);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'a':
|
||||||
|
printf ("option a\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'b':
|
||||||
|
printf ("option b\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'c':
|
||||||
|
printf ("option c with value `%s'\n", optarg);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '?':
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
printf ("?? getopt returned character code 0%o ??\n", c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (optind < argc)
|
||||||
|
{
|
||||||
|
printf ("non-option ARGV-elements: ");
|
||||||
|
while (optind < argc)
|
||||||
|
printf ("%s ", argv[optind++]);
|
||||||
|
printf ("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
exit (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* TEST */
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
/* declarations for getopt
|
||||||
|
Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2, or (at your option)
|
||||||
|
any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
|
||||||
|
|
||||||
|
/* @(#)getopt.h 1.6 92/03/31 */
|
||||||
|
|
||||||
|
/* For communication from `getopt' to the caller.
|
||||||
|
When `getopt' finds an option that takes an argument,
|
||||||
|
the argument value is returned here.
|
||||||
|
Also, when `ordering' is RETURN_IN_ORDER,
|
||||||
|
each non-option ARGV-element is returned here. */
|
||||||
|
|
||||||
|
extern char *optarg;
|
||||||
|
|
||||||
|
/* Index in ARGV of the next element to be scanned.
|
||||||
|
This is used for communication to and from the caller
|
||||||
|
and for communication between successive calls to `getopt'.
|
||||||
|
|
||||||
|
On entry to `getopt', zero means this is the first call; initialize.
|
||||||
|
|
||||||
|
When `getopt' returns EOF, this is the index of the first of the
|
||||||
|
non-option elements that the caller should itself scan.
|
||||||
|
|
||||||
|
Otherwise, `optind' communicates from one call to the next
|
||||||
|
how much of ARGV has been scanned so far. */
|
||||||
|
|
||||||
|
extern int optind;
|
||||||
|
|
||||||
|
/* Callers store zero here to inhibit the error message `getopt' prints
|
||||||
|
for unrecognized options. */
|
||||||
|
|
||||||
|
extern int opterr;
|
||||||
|
|
||||||
|
/* Describe the long-named options requested by the application.
|
||||||
|
_GETOPT_LONG_OPTIONS is a vector of `struct option' terminated by an
|
||||||
|
element containing a name which is zero.
|
||||||
|
|
||||||
|
The field `has_arg' is:
|
||||||
|
0 if the option does not take an argument,
|
||||||
|
1 if the option requires an argument,
|
||||||
|
2 if the option takes an optional argument.
|
||||||
|
|
||||||
|
If the field `flag' is nonzero, it points to a variable that is set
|
||||||
|
to the value given in the field `val' when the option is found, but
|
||||||
|
left unchanged if the option is not found.
|
||||||
|
|
||||||
|
To have a long-named option do something other than set an `int' to
|
||||||
|
a compiled-in constant, such as set a value from `optarg', set the
|
||||||
|
option's `flag' field to zero and its `val' field to a nonzero
|
||||||
|
value (the equivalent single-letter option character, if there is
|
||||||
|
one). For long options that have a zero `flag' field, `getopt'
|
||||||
|
returns the contents of the `val' field. */
|
||||||
|
|
||||||
|
struct option
|
||||||
|
{
|
||||||
|
char *name;
|
||||||
|
int has_arg;
|
||||||
|
int *flag;
|
||||||
|
int val;
|
||||||
|
};
|
||||||
|
|
||||||
|
#if __STDC__
|
||||||
|
extern const struct option *_getopt_long_options;
|
||||||
|
#else
|
||||||
|
extern struct option *_getopt_long_options;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* If nonzero, '-' can introduce long-named options.
|
||||||
|
Set by getopt_long_only. */
|
||||||
|
|
||||||
|
extern int _getopt_long_only;
|
||||||
|
|
||||||
|
/* The index in GETOPT_LONG_OPTIONS of the long-named option found.
|
||||||
|
Only valid when a long-named option has been found by the most
|
||||||
|
recent call to `getopt'. */
|
||||||
|
|
||||||
|
extern int option_index;
|
||||||
|
|
||||||
|
#if __STDC__
|
||||||
|
int gnu_getopt (int argc, char **argv, const char *shortopts);
|
||||||
|
int gnu_getopt_long (int argc, char **argv, const char *shortopts,
|
||||||
|
const struct option *longopts, int *longind);
|
||||||
|
int gnu_getopt_long_only (int argc, char **argv, const char *shortopts,
|
||||||
|
const struct option *longopts, int *longind);
|
||||||
|
#else
|
||||||
|
int gnu_getopt ();
|
||||||
|
int gnu_getopt_long ();
|
||||||
|
int gnu_getopt_long_only ();
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,166 @@
|
|||||||
|
/* Getopt for GNU.
|
||||||
|
Copyright (C) 1987-1992 Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2, or (at your option)
|
||||||
|
any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
|
||||||
|
|
||||||
|
#include "getopt.h"
|
||||||
|
|
||||||
|
#if !__STDC__
|
||||||
|
#define const
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(STDC_HEADERS) || defined(__GNU_LIBRARY__)
|
||||||
|
#include <stdlib.h>
|
||||||
|
#else /* STDC_HEADERS or __GNU_LIBRARY__ */
|
||||||
|
char *getenv ();
|
||||||
|
#endif /* STDC_HEADERS or __GNU_LIBRARY__ */
|
||||||
|
|
||||||
|
#if !defined (NULL)
|
||||||
|
#define NULL 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int
|
||||||
|
gnu_getopt_long (argc, argv, options, long_options, opt_index)
|
||||||
|
int argc;
|
||||||
|
char **argv;
|
||||||
|
const char *options;
|
||||||
|
const struct option *long_options;
|
||||||
|
int *opt_index;
|
||||||
|
{
|
||||||
|
int val;
|
||||||
|
|
||||||
|
/* For strict POSIX compatibility, we must turn off long options. */
|
||||||
|
if (getenv ("POSIX_ME_HARDER") == 0)
|
||||||
|
_getopt_long_options = long_options;
|
||||||
|
val = gnu_getopt (argc, argv, options);
|
||||||
|
if (val == 0 && opt_index != NULL)
|
||||||
|
*opt_index = option_index;
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Like getopt_long, but '-' as well as '+' can indicate a long option.
|
||||||
|
If an option that starts with '-' doesn't match a long option,
|
||||||
|
but does match a short option, it is parsed as a short option
|
||||||
|
instead. */
|
||||||
|
|
||||||
|
int
|
||||||
|
gnu_getopt_long_only (argc, argv, options, long_options, opt_index)
|
||||||
|
int argc;
|
||||||
|
char **argv;
|
||||||
|
const char *options;
|
||||||
|
const struct option *long_options;
|
||||||
|
int *opt_index;
|
||||||
|
{
|
||||||
|
int val;
|
||||||
|
|
||||||
|
_getopt_long_options = long_options;
|
||||||
|
_getopt_long_only = 1;
|
||||||
|
val = gnu_getopt (argc, argv, options);
|
||||||
|
if (val == 0 && opt_index != NULL)
|
||||||
|
*opt_index = option_index;
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef TEST
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
int
|
||||||
|
main (argc, argv)
|
||||||
|
int argc;
|
||||||
|
char **argv;
|
||||||
|
{
|
||||||
|
int c;
|
||||||
|
int digit_optind = 0;
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
int this_option_optind = optind ? optind : 1;
|
||||||
|
char *name = '\0';
|
||||||
|
int option_index = 0;
|
||||||
|
static struct option long_options[] =
|
||||||
|
{
|
||||||
|
{"add", 1, 0, 0},
|
||||||
|
{"append", 0, 0, 0},
|
||||||
|
{"delete", 1, 0, 0},
|
||||||
|
{"verbose", 0, 0, 0},
|
||||||
|
{"create", 0, 0, 0},
|
||||||
|
{"file", 1, 0, 0},
|
||||||
|
{0, 0, 0, 0}
|
||||||
|
};
|
||||||
|
|
||||||
|
c = getopt_long (argc, argv, "abc:d:0123456789",
|
||||||
|
long_options, &option_index);
|
||||||
|
if (c == EOF)
|
||||||
|
break;
|
||||||
|
|
||||||
|
switch (c)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
printf ("option %s", (long_options[option_index]).name);
|
||||||
|
if (optarg)
|
||||||
|
printf (" with arg %s", optarg);
|
||||||
|
printf ("\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '0':
|
||||||
|
case '1':
|
||||||
|
case '2':
|
||||||
|
case '3':
|
||||||
|
case '4':
|
||||||
|
case '5':
|
||||||
|
case '6':
|
||||||
|
case '7':
|
||||||
|
case '8':
|
||||||
|
case '9':
|
||||||
|
if (digit_optind != 0 && digit_optind != this_option_optind)
|
||||||
|
printf ("digits occur in two different argv-elements.\n");
|
||||||
|
digit_optind = this_option_optind;
|
||||||
|
printf ("option %c\n", c);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'a':
|
||||||
|
printf ("option a\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'b':
|
||||||
|
printf ("option b\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'c':
|
||||||
|
printf ("option c with value `%s'\n", optarg);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '?':
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
printf ("?? getopt returned character code 0%o ??\n", c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (optind < argc)
|
||||||
|
{
|
||||||
|
printf ("non-option ARGV-elements: ");
|
||||||
|
while (optind < argc)
|
||||||
|
printf ("%s ", argv[optind++]);
|
||||||
|
printf ("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
exit (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* TEST */
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
/* getwd.c -- get current working directory pathname
|
||||||
|
Copyright (C) 1992 Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2, or (at your option)
|
||||||
|
any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
|
||||||
|
|
||||||
|
/* Some systems which include both getwd() and getcwd() have an implementation
|
||||||
|
of getwd() which is much faster than getcwd(). As a result, we use the
|
||||||
|
system's getwd() if it is available */
|
||||||
|
|
||||||
|
#include "system.h"
|
||||||
|
|
||||||
|
/* Get the current working directory into PATHNAME */
|
||||||
|
|
||||||
|
char *
|
||||||
|
getwd (pathname)
|
||||||
|
char *pathname;
|
||||||
|
{
|
||||||
|
return (getcwd(pathname, PATH_MAX));
|
||||||
|
}
|
||||||
@@ -0,0 +1,338 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 1992, Brian Berliner and Jeff Polk
|
||||||
|
*
|
||||||
|
* You may distribute under the terms of the GNU General Public License as
|
||||||
|
* specified in the README file that comes with the CVS 1.3 kit.
|
||||||
|
*
|
||||||
|
* Polk's hash list manager. So cool.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "cvs.h"
|
||||||
|
|
||||||
|
#ifndef lint
|
||||||
|
static char rcsid[] = "@(#)hash.c 1.14 92/03/31";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* global caches */
|
||||||
|
static List *listcache = NULL;
|
||||||
|
static Node *nodecache = NULL;
|
||||||
|
|
||||||
|
#if __STDC__
|
||||||
|
static void freenode_mem (Node * p);
|
||||||
|
#else
|
||||||
|
static void freenode_mem ();
|
||||||
|
#endif /* __STDC__ */
|
||||||
|
|
||||||
|
/* hash function */
|
||||||
|
static int
|
||||||
|
hashp (key)
|
||||||
|
char *key;
|
||||||
|
{
|
||||||
|
register char *p;
|
||||||
|
register int n = 0;
|
||||||
|
|
||||||
|
for (p = key; *p; p++)
|
||||||
|
n += *p;
|
||||||
|
|
||||||
|
return (n % HASHSIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* create a new list (or get an old one from the cache)
|
||||||
|
*/
|
||||||
|
List *
|
||||||
|
getlist ()
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
List *list;
|
||||||
|
Node *node;
|
||||||
|
|
||||||
|
if (listcache != NULL)
|
||||||
|
{
|
||||||
|
/* get a list from the cache and clear it */
|
||||||
|
list = listcache;
|
||||||
|
listcache = listcache->next;
|
||||||
|
list->next = (List *) NULL;
|
||||||
|
for (i = 0; i < HASHSIZE; i++)
|
||||||
|
list->hasharray[i] = (Node *) NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* make a new list from scratch */
|
||||||
|
list = (List *) xmalloc (sizeof (List));
|
||||||
|
bzero ((char *) list, sizeof (List));
|
||||||
|
node = getnode ();
|
||||||
|
list->list = node;
|
||||||
|
node->type = HEADER;
|
||||||
|
node->next = node->prev = node;
|
||||||
|
}
|
||||||
|
return (list);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* free up a list
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
dellist (listp)
|
||||||
|
List **listp;
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
Node *p;
|
||||||
|
|
||||||
|
if (*listp == (List *) NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
p = (*listp)->list;
|
||||||
|
|
||||||
|
/* free each node in the list (except header) */
|
||||||
|
while (p->next != p)
|
||||||
|
delnode (p->next);
|
||||||
|
|
||||||
|
/* free any list-private data, without freeing the actual header */
|
||||||
|
freenode_mem (p);
|
||||||
|
|
||||||
|
/* free up the header nodes for hash lists (if any) */
|
||||||
|
for (i = 0; i < HASHSIZE; i++)
|
||||||
|
{
|
||||||
|
if ((p = (*listp)->hasharray[i]) != (Node *) NULL)
|
||||||
|
{
|
||||||
|
/* put the nodes into the cache */
|
||||||
|
p->type = UNKNOWN;
|
||||||
|
p->next = nodecache;
|
||||||
|
nodecache = p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* put it on the cache */
|
||||||
|
(*listp)->next = listcache;
|
||||||
|
listcache = *listp;
|
||||||
|
*listp = (List *) NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* get a new list node
|
||||||
|
*/
|
||||||
|
Node *
|
||||||
|
getnode ()
|
||||||
|
{
|
||||||
|
Node *p;
|
||||||
|
|
||||||
|
if (nodecache != (Node *) NULL)
|
||||||
|
{
|
||||||
|
/* get one from the cache */
|
||||||
|
p = nodecache;
|
||||||
|
nodecache = p->next;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* make a new one */
|
||||||
|
p = (Node *) xmalloc (sizeof (Node));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* always make it clean */
|
||||||
|
bzero ((char *) p, sizeof (Node));
|
||||||
|
p->type = UNKNOWN;
|
||||||
|
|
||||||
|
return (p);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* remove a node from it's list (maybe hash list too) and free it
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
delnode (p)
|
||||||
|
Node *p;
|
||||||
|
{
|
||||||
|
if (p == (Node *) NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* take it out of the list */
|
||||||
|
p->next->prev = p->prev;
|
||||||
|
p->prev->next = p->next;
|
||||||
|
|
||||||
|
/* if it was hashed, remove it from there too */
|
||||||
|
if (p->hashnext != (Node *) NULL)
|
||||||
|
{
|
||||||
|
p->hashnext->hashprev = p->hashprev;
|
||||||
|
p->hashprev->hashnext = p->hashnext;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* free up the storage */
|
||||||
|
freenode (p);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* free up the storage associated with a node
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
freenode_mem (p)
|
||||||
|
Node *p;
|
||||||
|
{
|
||||||
|
if (p->delproc != (void (*) ()) NULL)
|
||||||
|
p->delproc (p); /* call the specified delproc */
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (p->data != NULL) /* otherwise free() it if necessary */
|
||||||
|
free (p->data);
|
||||||
|
}
|
||||||
|
if (p->key != NULL) /* free the key if necessary */
|
||||||
|
free (p->key);
|
||||||
|
|
||||||
|
/* to be safe, re-initialize these */
|
||||||
|
p->key = p->data = (char *) NULL;
|
||||||
|
p->delproc = (void (*) ()) NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* free up the storage associated with a node and recycle it
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
freenode (p)
|
||||||
|
Node *p;
|
||||||
|
{
|
||||||
|
/* first free the memory */
|
||||||
|
freenode_mem (p);
|
||||||
|
|
||||||
|
/* then put it in the cache */
|
||||||
|
p->type = UNKNOWN;
|
||||||
|
p->next = nodecache;
|
||||||
|
nodecache = p;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* insert item p at end of list "list" (maybe hash it too) if hashing and it
|
||||||
|
* already exists, return -1 and don't actually put it in the list
|
||||||
|
*
|
||||||
|
* return 0 on success
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
addnode (list, p)
|
||||||
|
List *list;
|
||||||
|
Node *p;
|
||||||
|
{
|
||||||
|
int hashval;
|
||||||
|
Node *q;
|
||||||
|
|
||||||
|
if (p->key != NULL) /* hash it too? */
|
||||||
|
{
|
||||||
|
hashval = hashp (p->key);
|
||||||
|
if (list->hasharray[hashval] == NULL) /* make a header for list? */
|
||||||
|
{
|
||||||
|
q = getnode ();
|
||||||
|
q->type = HEADER;
|
||||||
|
list->hasharray[hashval] = q->hashnext = q->hashprev = q;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* put it into the hash list if it's not already there */
|
||||||
|
for (q = list->hasharray[hashval]->hashnext;
|
||||||
|
q != list->hasharray[hashval]; q = q->hashnext)
|
||||||
|
{
|
||||||
|
if (strcmp (p->key, q->key) == 0)
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
q = list->hasharray[hashval];
|
||||||
|
p->hashprev = q->hashprev;
|
||||||
|
p->hashnext = q;
|
||||||
|
p->hashprev->hashnext = p;
|
||||||
|
q->hashprev = p;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* put it into the regular list */
|
||||||
|
p->prev = list->list->prev;
|
||||||
|
p->next = list->list;
|
||||||
|
list->list->prev->next = p;
|
||||||
|
list->list->prev = p;
|
||||||
|
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* look up an entry in hash list table
|
||||||
|
*/
|
||||||
|
Node *
|
||||||
|
findnode (list, key)
|
||||||
|
List *list;
|
||||||
|
char *key;
|
||||||
|
{
|
||||||
|
Node *head, *p;
|
||||||
|
|
||||||
|
if (list == (List *) NULL)
|
||||||
|
return ((Node *) NULL);
|
||||||
|
|
||||||
|
head = list->hasharray[hashp (key)];
|
||||||
|
if (head == (Node *) NULL)
|
||||||
|
return ((Node *) NULL);
|
||||||
|
|
||||||
|
for (p = head->hashnext; p != head; p = p->hashnext)
|
||||||
|
if (strcmp (p->key, key) == 0)
|
||||||
|
return (p);
|
||||||
|
return ((Node *) NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* walk a list with a specific proc
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
walklist (list, proc)
|
||||||
|
List *list;
|
||||||
|
int (*proc) ();
|
||||||
|
{
|
||||||
|
Node *head, *p;
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
if (list == NULL)
|
||||||
|
return (0);
|
||||||
|
|
||||||
|
head = list->list;
|
||||||
|
for (p = head->next; p != head; p = p->next)
|
||||||
|
err += proc (p);
|
||||||
|
return (err);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* sort the elements of a list (in place)
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
sortlist (list, comp)
|
||||||
|
List *list;
|
||||||
|
int (*comp) ();
|
||||||
|
{
|
||||||
|
Node *head, *remain, *p, *q;
|
||||||
|
|
||||||
|
/* save the old first element of the list */
|
||||||
|
head = list->list;
|
||||||
|
remain = head->next;
|
||||||
|
|
||||||
|
/* make the header node into a null list of it's own */
|
||||||
|
head->next = head->prev = head;
|
||||||
|
|
||||||
|
/* while there are nodes remaining, do insert sort */
|
||||||
|
while (remain != head)
|
||||||
|
{
|
||||||
|
/* take one from the list */
|
||||||
|
p = remain;
|
||||||
|
remain = remain->next;
|
||||||
|
|
||||||
|
/* traverse the sorted list looking for the place to insert it */
|
||||||
|
for (q = head->next; q != head; q = q->next)
|
||||||
|
{
|
||||||
|
if (comp (p, q) < 0)
|
||||||
|
{
|
||||||
|
/* p comes before q */
|
||||||
|
p->next = q;
|
||||||
|
p->prev = q->prev;
|
||||||
|
p->prev->next = p;
|
||||||
|
q->prev = p;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (q == head)
|
||||||
|
{
|
||||||
|
/* it belongs at the end of the list */
|
||||||
|
p->next = head;
|
||||||
|
p->prev = head->prev;
|
||||||
|
p->prev->next = p;
|
||||||
|
head->prev = p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
/* @(#)hash.h 1.18 92/03/31 */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) 1992, Brian Berliner and Jeff Polk
|
||||||
|
*
|
||||||
|
* You may distribute under the terms of the GNU General Public License as
|
||||||
|
* specified in the README file that comes with the CVS 1.3 kit.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The number of buckets for the hash table contained in each list. This
|
||||||
|
* should probably be prime.
|
||||||
|
*/
|
||||||
|
#define HASHSIZE 151
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Types of nodes
|
||||||
|
*/
|
||||||
|
enum ntype
|
||||||
|
{
|
||||||
|
UNKNOWN, HEADER, ENTRIES, FILES, LIST, RCSNODE,
|
||||||
|
RCSVERS, DIRS, UPDATE, LOCK, NDBMNODE
|
||||||
|
};
|
||||||
|
typedef enum ntype Ntype;
|
||||||
|
|
||||||
|
struct node
|
||||||
|
{
|
||||||
|
Ntype type;
|
||||||
|
struct node *next;
|
||||||
|
struct node *prev;
|
||||||
|
struct node *hashnext;
|
||||||
|
struct node *hashprev;
|
||||||
|
char *key;
|
||||||
|
char *data;
|
||||||
|
void (*delproc) ();
|
||||||
|
};
|
||||||
|
typedef struct node Node;
|
||||||
|
|
||||||
|
struct list
|
||||||
|
{
|
||||||
|
Node *list;
|
||||||
|
Node *hasharray[HASHSIZE];
|
||||||
|
struct list *next;
|
||||||
|
};
|
||||||
|
typedef struct list List;
|
||||||
|
|
||||||
|
struct entnode
|
||||||
|
{
|
||||||
|
char *version;
|
||||||
|
char *timestamp;
|
||||||
|
char *options;
|
||||||
|
char *tag;
|
||||||
|
char *date;
|
||||||
|
};
|
||||||
|
typedef struct entnode Entnode;
|
||||||
|
|
||||||
|
#if __STDC__
|
||||||
|
List *getlist (void);
|
||||||
|
Node *findnode (List * list, char *key);
|
||||||
|
Node *getnode (void);
|
||||||
|
int addnode (List * list, Node * p);
|
||||||
|
int walklist (List * list, int (*proc) ());
|
||||||
|
void dellist (List ** listp);
|
||||||
|
void delnode (Node * p);
|
||||||
|
void freenode (Node * p);
|
||||||
|
void sortlist (List * list, int (*comp) ());
|
||||||
|
#else
|
||||||
|
List *getlist ();
|
||||||
|
Node *findnode ();
|
||||||
|
Node *getnode ();
|
||||||
|
int addnode ();
|
||||||
|
int walklist ();
|
||||||
|
void dellist ();
|
||||||
|
void delnode ();
|
||||||
|
void freenode ();
|
||||||
|
void sortlist ();
|
||||||
|
#endif /* __STDC__ */
|
||||||
@@ -0,0 +1,125 @@
|
|||||||
|
/* mkrmdir.c -- BSD compatible directory functions for System V
|
||||||
|
Copyright (C) 1988, 1990 Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2, or (at your option)
|
||||||
|
any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#ifndef STDC_HEADERS
|
||||||
|
extern int errno;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* mkdir and rmdir adapted from GNU tar. */
|
||||||
|
|
||||||
|
/* Make directory DPATH, with permission mode DMODE.
|
||||||
|
|
||||||
|
Written by Robert Rother, Mariah Corporation, August 1985
|
||||||
|
(sdcsvax!rmr or rmr@uscd). If you want it, it's yours.
|
||||||
|
|
||||||
|
Severely hacked over by John Gilmore to make a 4.2BSD compatible
|
||||||
|
subroutine. 11Mar86; hoptoad!gnu
|
||||||
|
|
||||||
|
Modified by rmtodd@uokmax 6-28-87 -- when making an already existing dir,
|
||||||
|
subroutine didn't return EEXIST. It does now. */
|
||||||
|
|
||||||
|
int
|
||||||
|
mkdir (dpath, dmode)
|
||||||
|
char *dpath;
|
||||||
|
int dmode;
|
||||||
|
{
|
||||||
|
int cpid, status;
|
||||||
|
struct stat statbuf;
|
||||||
|
|
||||||
|
if (stat (dpath, &statbuf) == 0)
|
||||||
|
{
|
||||||
|
errno = EEXIST; /* stat worked, so it already exists. */
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If stat fails for a reason other than non-existence, return error. */
|
||||||
|
if (errno != ENOENT)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
cpid = fork ();
|
||||||
|
switch (cpid)
|
||||||
|
{
|
||||||
|
case -1: /* Cannot fork. */
|
||||||
|
return -1; /* errno is set already. */
|
||||||
|
|
||||||
|
case 0: /* Child process. */
|
||||||
|
/* Cheap hack to set mode of new directory. Since this child
|
||||||
|
process is going away anyway, we zap its umask.
|
||||||
|
This won't suffice to set SUID, SGID, etc. on this
|
||||||
|
directory, so the parent process calls chmod afterward. */
|
||||||
|
status = umask (0); /* Get current umask. */
|
||||||
|
umask (status | (0777 & ~dmode)); /* Set for mkdir. */
|
||||||
|
execl ("/bin/mkdir", "mkdir", dpath, (char *) 0);
|
||||||
|
_exit (1);
|
||||||
|
|
||||||
|
default: /* Parent process. */
|
||||||
|
while (wait (&status) != cpid) /* Wait for kid to finish. */
|
||||||
|
/* Do nothing. */ ;
|
||||||
|
|
||||||
|
if (status & 0xFFFF)
|
||||||
|
{
|
||||||
|
errno = EIO; /* /bin/mkdir failed. */
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return chmod (dpath, dmode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove directory DPATH.
|
||||||
|
Return 0 if successful, -1 if not. */
|
||||||
|
|
||||||
|
int
|
||||||
|
rmdir (dpath)
|
||||||
|
char *dpath;
|
||||||
|
{
|
||||||
|
int cpid, status;
|
||||||
|
struct stat statbuf;
|
||||||
|
|
||||||
|
if (stat (dpath, &statbuf) != 0)
|
||||||
|
return -1; /* stat set errno. */
|
||||||
|
|
||||||
|
if ((statbuf.st_mode & S_IFMT) != S_IFDIR)
|
||||||
|
{
|
||||||
|
errno = ENOTDIR;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
cpid = fork ();
|
||||||
|
switch (cpid)
|
||||||
|
{
|
||||||
|
case -1: /* Cannot fork. */
|
||||||
|
return -1; /* errno is set already. */
|
||||||
|
|
||||||
|
case 0: /* Child process. */
|
||||||
|
execl ("/bin/rmdir", "rmdir", dpath, (char *) 0);
|
||||||
|
_exit (1);
|
||||||
|
|
||||||
|
default: /* Parent process. */
|
||||||
|
while (wait (&status) != cpid) /* Wait for kid to finish. */
|
||||||
|
/* Do nothing. */ ;
|
||||||
|
|
||||||
|
if (status & 0xFFFF)
|
||||||
|
{
|
||||||
|
errno = EIO; /* /bin/rmdir failed. */
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,212 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 1992, Brian Berliner
|
||||||
|
*
|
||||||
|
* You may distribute under the terms of the GNU General Public License as
|
||||||
|
* specified in the README file that comes with the CVS 1.3 kit.
|
||||||
|
*
|
||||||
|
* A simple ndbm-emulator for CVS. It parses a text file of the format:
|
||||||
|
*
|
||||||
|
* key value
|
||||||
|
*
|
||||||
|
* at dbm_open time, and loads the entire file into memory. As such, it is
|
||||||
|
* probably only good for fairly small modules files. Ours is about 30K in
|
||||||
|
* size, and this code works fine.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "cvs.h"
|
||||||
|
|
||||||
|
#ifdef MY_NDBM
|
||||||
|
|
||||||
|
#ifndef lint
|
||||||
|
static char rcsid[] = "@(#)myndbm.c 1.5 92/03/31";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void mydbm_load_file ();
|
||||||
|
|
||||||
|
/* ARGSUSED */
|
||||||
|
DBM *
|
||||||
|
mydbm_open (file, flags, mode)
|
||||||
|
char *file;
|
||||||
|
int flags;
|
||||||
|
int mode;
|
||||||
|
{
|
||||||
|
FILE *fp;
|
||||||
|
DBM *db;
|
||||||
|
|
||||||
|
if ((fp = fopen (file, "r")) == NULL)
|
||||||
|
return ((DBM *) 0);
|
||||||
|
|
||||||
|
db = (DBM *) xmalloc (sizeof (*db));
|
||||||
|
db->dbm_list = getlist ();
|
||||||
|
|
||||||
|
mydbm_load_file (fp, db->dbm_list);
|
||||||
|
(void) fclose (fp);
|
||||||
|
return (db);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
mydbm_close (db)
|
||||||
|
DBM *db;
|
||||||
|
{
|
||||||
|
dellist (&db->dbm_list);
|
||||||
|
free ((char *) db);
|
||||||
|
}
|
||||||
|
|
||||||
|
datum
|
||||||
|
mydbm_fetch (db, key)
|
||||||
|
DBM *db;
|
||||||
|
datum key;
|
||||||
|
{
|
||||||
|
Node *p;
|
||||||
|
char *s;
|
||||||
|
datum val;
|
||||||
|
|
||||||
|
/* make sure it's null-terminated */
|
||||||
|
s = xmalloc (key.dsize + 1);
|
||||||
|
(void) strncpy (s, key.dptr, key.dsize);
|
||||||
|
s[key.dsize] = '\0';
|
||||||
|
|
||||||
|
p = findnode (db->dbm_list, s);
|
||||||
|
if (p)
|
||||||
|
{
|
||||||
|
val.dptr = p->data;
|
||||||
|
val.dsize = strlen (p->data);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
val.dptr = (char *) NULL;
|
||||||
|
val.dsize = 0;
|
||||||
|
}
|
||||||
|
free (s);
|
||||||
|
return (val);
|
||||||
|
}
|
||||||
|
|
||||||
|
datum
|
||||||
|
mydbm_firstkey (db)
|
||||||
|
DBM *db;
|
||||||
|
{
|
||||||
|
Node *head, *p;
|
||||||
|
datum key;
|
||||||
|
|
||||||
|
head = db->dbm_list->list;
|
||||||
|
p = head->next;
|
||||||
|
if (p != head)
|
||||||
|
{
|
||||||
|
key.dptr = p->key;
|
||||||
|
key.dsize = strlen (p->key);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
key.dptr = (char *) NULL;
|
||||||
|
key.dsize = 0;
|
||||||
|
}
|
||||||
|
db->dbm_next = p->next;
|
||||||
|
return (key);
|
||||||
|
}
|
||||||
|
|
||||||
|
datum
|
||||||
|
mydbm_nextkey (db)
|
||||||
|
DBM *db;
|
||||||
|
{
|
||||||
|
Node *head, *p;
|
||||||
|
datum key;
|
||||||
|
|
||||||
|
head = db->dbm_list->list;
|
||||||
|
p = db->dbm_next;
|
||||||
|
if (p != head)
|
||||||
|
{
|
||||||
|
key.dptr = p->key;
|
||||||
|
key.dsize = strlen (p->key);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
key.dptr = (char *) NULL;
|
||||||
|
key.dsize = 0;
|
||||||
|
}
|
||||||
|
db->dbm_next = p->next;
|
||||||
|
return (key);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
mydbm_load_file (fp, list)
|
||||||
|
FILE *fp;
|
||||||
|
List *list;
|
||||||
|
{
|
||||||
|
char line[MAXLINELEN], value[MAXLINELEN];
|
||||||
|
char *cp, *vp;
|
||||||
|
int len, cont;
|
||||||
|
|
||||||
|
for (cont = 0; fgets (line, sizeof (line), fp) != NULL;)
|
||||||
|
{
|
||||||
|
if ((cp = rindex (line, '\n')) != NULL)
|
||||||
|
*cp = '\0'; /* strip the newline */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add the line to the value, at the end if this is a continuation
|
||||||
|
* line; otherwise at the beginning, but only after any trailing
|
||||||
|
* backslash is removed.
|
||||||
|
*/
|
||||||
|
vp = value;
|
||||||
|
if (cont)
|
||||||
|
vp += strlen (value);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* See if the line we read is a continuation line, and strip the
|
||||||
|
* backslash if so.
|
||||||
|
*/
|
||||||
|
len = strlen (line);
|
||||||
|
if (len > 0)
|
||||||
|
cp = &line[len - 1];
|
||||||
|
else
|
||||||
|
cp = line;
|
||||||
|
if (*cp == '\\')
|
||||||
|
{
|
||||||
|
cont = 1;
|
||||||
|
*cp = '\0';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cont = 0;
|
||||||
|
}
|
||||||
|
(void) strcpy (vp, line);
|
||||||
|
if (value[0] == '#')
|
||||||
|
continue; /* comment line */
|
||||||
|
vp = value;
|
||||||
|
while (*vp && isspace (*vp))
|
||||||
|
vp++;
|
||||||
|
if (*vp == '\0')
|
||||||
|
continue; /* empty line */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If this was not a continuation line, add the entry to the database
|
||||||
|
*/
|
||||||
|
if (!cont)
|
||||||
|
{
|
||||||
|
Node *p = getnode ();
|
||||||
|
char *kp;
|
||||||
|
|
||||||
|
kp = vp;
|
||||||
|
while (*vp && !isspace (*vp))
|
||||||
|
vp++;
|
||||||
|
*vp++ = '\0'; /* NULL terminate the key */
|
||||||
|
p->type = NDBMNODE;
|
||||||
|
p->key = xstrdup (kp);
|
||||||
|
while (*vp && isspace (*vp))
|
||||||
|
vp++; /* skip whitespace to value */
|
||||||
|
if (*vp == '\0')
|
||||||
|
{
|
||||||
|
error (0, 0, "warning: NULL value for key `%s'", p->key);
|
||||||
|
freenode (p);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
p->data = xstrdup (vp);
|
||||||
|
if (addnode (list, p) == -1)
|
||||||
|
{
|
||||||
|
error (0, 0, "duplicate key found for `%s'", p->key);
|
||||||
|
freenode (p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* MY_NDBM */
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
/* @(#)myndbm.h 1.3 92/02/29 */
|
||||||
|
|
||||||
|
#ifdef MY_NDBM
|
||||||
|
|
||||||
|
#define DBLKSIZ 4096
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
List *dbm_list; /* cached database */
|
||||||
|
Node *dbm_next; /* next key to return for nextkey() */
|
||||||
|
} DBM;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
char *dptr;
|
||||||
|
int dsize;
|
||||||
|
} datum;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* So as not to conflict with other dbm_open, etc., routines that may
|
||||||
|
* be included by someone's libc, all of my emulation routines are prefixed
|
||||||
|
* by "my" and we define the "standard" ones to be "my" ones here.
|
||||||
|
*/
|
||||||
|
#define dbm_open mydbm_open
|
||||||
|
#define dbm_close mydbm_close
|
||||||
|
#define dbm_fetch mydbm_fetch
|
||||||
|
#define dbm_firstkey mydbm_firstkey
|
||||||
|
#define dbm_nextkey mydbm_nextkey
|
||||||
|
|
||||||
|
#if __STDC__
|
||||||
|
DBM *mydbm_open (char *file, int flags, int mode);
|
||||||
|
void mydbm_close (DBM * db);
|
||||||
|
datum mydbm_fetch (DBM * db, datum key);
|
||||||
|
datum mydbm_firstkey (DBM * db);
|
||||||
|
datum mydbm_nextkey (DBM * db);
|
||||||
|
#else
|
||||||
|
DBM *mydbm_open ();
|
||||||
|
void mydbm_close ();
|
||||||
|
datum mydbm_fetch ();
|
||||||
|
datum mydbm_firstkey ();
|
||||||
|
datum mydbm_nextkey ();
|
||||||
|
#endif /* __STDC__ */
|
||||||
|
|
||||||
|
#endif /* MY_NDBM */
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,479 @@
|
|||||||
|
/* Definitions for data structures and routines for the regular
|
||||||
|
expression library, version REPLACE-WITH-VERSION.
|
||||||
|
|
||||||
|
Copyright (C) 1985, 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2, or (at your option)
|
||||||
|
any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
|
||||||
|
|
||||||
|
#ifndef __REGEXP_LIBRARY_H__
|
||||||
|
#define __REGEXP_LIBRARY_H__
|
||||||
|
|
||||||
|
/* POSIX says that <sys/types.h> must be included before <regex.h>. */
|
||||||
|
|
||||||
|
/* The following bits are used to determine the regexp syntax we
|
||||||
|
recognize. The set/not-set meanings are chosen so that Emacs syntax
|
||||||
|
remains the value 0. The bits are given in alphabetical order, and
|
||||||
|
the definitions shifted by one from the previous bit; thus, when we
|
||||||
|
add or remove a bit, only one other definition need change. */
|
||||||
|
typedef unsigned reg_syntax_t;
|
||||||
|
|
||||||
|
/* If this bit is not set, then \ inside a bracket expression is literal.
|
||||||
|
If set, then such a \ quotes the following character. */
|
||||||
|
#define RE_BACKSLASH_ESCAPE_IN_LISTS (1)
|
||||||
|
|
||||||
|
/* If this bit is not set, then + and ? are operators, and \+ and \? are
|
||||||
|
literals.
|
||||||
|
If set, then \+ and \? are operators and + and ? are literals. */
|
||||||
|
#define RE_BK_PLUS_QM (RE_BACKSLASH_ESCAPE_IN_LISTS << 1)
|
||||||
|
|
||||||
|
/* If this bit is set, then character classes are supported. They are:
|
||||||
|
[:alpha:], [:upper:], [:lower:], [:digit:], [:alnum:], [:xdigit:],
|
||||||
|
[:space:], [:print:], [:punct:], [:graph:], and [:cntrl:].
|
||||||
|
If not set, then character classes are not supported. */
|
||||||
|
#define RE_CHAR_CLASSES (RE_BK_PLUS_QM << 1)
|
||||||
|
|
||||||
|
/* If this bit is set, then ^ and $ are always anchors (outside bracket
|
||||||
|
expressions).
|
||||||
|
If this bit is not set, then it depends:
|
||||||
|
^ is an anchor if it is at the beginning of a regular
|
||||||
|
expression or after an open-group or an alternation operator;
|
||||||
|
$ is an anchor if it is at the end of a regular expression, or
|
||||||
|
before a close-group or an alternation operator.
|
||||||
|
This bit could be (re)combined with RE_CONTEXT_INDEP_OPS, because
|
||||||
|
POSIX now says that the behavior of * etc. in leading positions is
|
||||||
|
undefined. We have already implemented a previous draft which
|
||||||
|
made those constructs invalid, so we may as well not change the code
|
||||||
|
back. */
|
||||||
|
#define RE_CONTEXT_INDEP_ANCHORS (RE_CHAR_CLASSES << 1)
|
||||||
|
|
||||||
|
/* If this bit is set, then special characters are always special
|
||||||
|
regardless of where they are in the pattern.
|
||||||
|
If this bit is not set, then special characters are special only in
|
||||||
|
some contexts; otherwise they are ordinary. Specifically,
|
||||||
|
* + ? and intervals are only special when not after the beginning,
|
||||||
|
open-group, or alternation operator. */
|
||||||
|
#define RE_CONTEXT_INDEP_OPS (RE_CONTEXT_INDEP_ANCHORS << 1)
|
||||||
|
|
||||||
|
/* If this bit is set, then *, +, ?, and { cannot be first in an re or
|
||||||
|
immediately after an alternation or begin-group operator.
|
||||||
|
Furthermore, alternation cannot be first or last in an re, or
|
||||||
|
immediately follow another alternation or begin-group. */
|
||||||
|
#define RE_CONTEXT_INVALID_OPS (RE_CONTEXT_INDEP_OPS << 1)
|
||||||
|
|
||||||
|
/* If this bit is set, then . matches a newline.
|
||||||
|
If not set, then it doesn't. */
|
||||||
|
#define RE_DOT_NEWLINE (RE_CONTEXT_INVALID_OPS << 1)
|
||||||
|
|
||||||
|
/* If this bit is set, then period doesn't match a null.
|
||||||
|
If not set, then it does. */
|
||||||
|
#define RE_DOT_NOT_NULL (RE_DOT_NEWLINE << 1)
|
||||||
|
|
||||||
|
/* If this bit is set, nonmatching lists [^...] do not match newline.
|
||||||
|
If not set, they do. */
|
||||||
|
#define RE_HAT_LISTS_NOT_NEWLINE (RE_DOT_NOT_NULL << 1)
|
||||||
|
|
||||||
|
/* If this bit is set, either \{...\} or {...} defines an
|
||||||
|
interval, depending on RE_NO_BK_BRACES.
|
||||||
|
If not set, \{, \}, {, and } are literals. */
|
||||||
|
#define RE_INTERVALS (RE_HAT_LISTS_NOT_NEWLINE << 1)
|
||||||
|
|
||||||
|
/* If this bit is set, +, ? and | aren't recognized as operators.
|
||||||
|
If not set, they are. */
|
||||||
|
#define RE_LIMITED_OPS (RE_INTERVALS << 1)
|
||||||
|
|
||||||
|
/* If this bit is set, newline is an alternation operator.
|
||||||
|
If not set, newline is literal. */
|
||||||
|
#define RE_NEWLINE_ALT (RE_LIMITED_OPS << 1)
|
||||||
|
|
||||||
|
/* If this bit is set, newline in the pattern is an ordinary character.
|
||||||
|
If not set, newline before ^ or after $ allows the ^ or $ to be an
|
||||||
|
anchor. */
|
||||||
|
#define RE_NEWLINE_ORDINARY (RE_NEWLINE_ALT << 1)
|
||||||
|
|
||||||
|
/* If this bit is not set, then \{ and \} defines an interval,
|
||||||
|
and { and } are literals.
|
||||||
|
If set, then { and } defines an interval, and \{ and \} are literals. */
|
||||||
|
#define RE_NO_BK_BRACES (RE_NEWLINE_ORDINARY << 1)
|
||||||
|
|
||||||
|
/* If this bit is set, (...) defines a group, and \( and \) are literals.
|
||||||
|
If not set, \(...\) defines a group, and ( and ) are literals. */
|
||||||
|
#define RE_NO_BK_PARENS (RE_NO_BK_BRACES << 1)
|
||||||
|
|
||||||
|
/* If this bit is set, then back references (i.e., \<digit>) are not
|
||||||
|
recognized.
|
||||||
|
If not set, then they are. */
|
||||||
|
#define RE_NO_BK_REFS (RE_NO_BK_PARENS << 1)
|
||||||
|
|
||||||
|
/* If this bit is set, then | is an alternation operator, and \| is literal.
|
||||||
|
If not set, then \| is an alternation operator, and | is literal. */
|
||||||
|
#define RE_NO_BK_VBAR (RE_NO_BK_REFS << 1)
|
||||||
|
|
||||||
|
/* If this bit is set, then you can't have empty alternatives.
|
||||||
|
If not set, then you can. */
|
||||||
|
#define RE_NO_EMPTY_ALTS (RE_NO_BK_VBAR << 1)
|
||||||
|
|
||||||
|
/* If this bit is set, then you can't have empty groups.
|
||||||
|
If not set, then you can. */
|
||||||
|
#define RE_NO_EMPTY_GROUPS (RE_NO_EMPTY_ALTS << 1)
|
||||||
|
|
||||||
|
/* If this bit is set, then an ending range point has to collate higher
|
||||||
|
than or equal to the starting range point.
|
||||||
|
If not set, then when the ending range point collates higher than the
|
||||||
|
starting range point, we consider such a range to be empty. */
|
||||||
|
#define RE_NO_EMPTY_RANGES (RE_NO_EMPTY_GROUPS << 1)
|
||||||
|
|
||||||
|
/* If this bit is set, then all back references must refer to a preceding
|
||||||
|
subexpression.
|
||||||
|
If not set, then a back reference to a nonexistent subexpression is
|
||||||
|
treated as literal characters. */
|
||||||
|
#define RE_NO_MISSING_BK_REF (RE_NO_EMPTY_RANGES << 1)
|
||||||
|
|
||||||
|
/* If this bit is set, then Regex considers an unmatched close-group
|
||||||
|
operator to be the ordinary character parenthesis.
|
||||||
|
If not set, then an unmatched close-group operator is invalid. */
|
||||||
|
#define RE_UNMATCHED_RIGHT_PAREN_ORD (RE_NO_MISSING_BK_REF << 1)
|
||||||
|
|
||||||
|
/* This global variable defines the particular regexp syntax to use (for
|
||||||
|
some interfaces). When a regexp is compiled, the syntax used is
|
||||||
|
stored in the pattern buffer, so changing this does not affect
|
||||||
|
already-compiled regexps. */
|
||||||
|
extern reg_syntax_t obscure_syntax;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Define combinations of the above bits for the standard possibilities.
|
||||||
|
(The [[[ comments delimit what gets put into the Texinfo file.) */
|
||||||
|
/* [[[begin syntaxes]]] */
|
||||||
|
#define RE_SYNTAX_EMACS 0
|
||||||
|
|
||||||
|
#define RE_SYNTAX_POSIX_AWK \
|
||||||
|
(RE_CONTEXT_INDEP_ANCHORS | RE_CONTEXT_INDEP_OPS | RE_NO_BK_PARENS \
|
||||||
|
| RE_NO_BK_VBAR)
|
||||||
|
|
||||||
|
#define RE_SYNTAX_AWK \
|
||||||
|
(RE_BACKSLASH_ESCAPE_IN_LISTS | RE_SYNTAX_POSIX_AWK)
|
||||||
|
|
||||||
|
#define RE_SYNTAX_GREP \
|
||||||
|
(RE_BK_PLUS_QM | RE_NEWLINE_ALT)
|
||||||
|
|
||||||
|
#define RE_SYNTAX_EGREP \
|
||||||
|
(RE_CONTEXT_INDEP_ANCHORS | RE_CONTEXT_INDEP_OPS \
|
||||||
|
| RE_NEWLINE_ALT | RE_NO_BK_PARENS | RE_NO_BK_VBAR)
|
||||||
|
|
||||||
|
#define RE_SYNTAX_POSIX_BASIC \
|
||||||
|
(RE_CHAR_CLASSES | RE_DOT_NEWLINE \
|
||||||
|
| RE_DOT_NOT_NULL | RE_INTERVALS | RE_LIMITED_OPS \
|
||||||
|
| RE_NEWLINE_ORDINARY | RE_NO_EMPTY_RANGES | RE_NO_MISSING_BK_REF)
|
||||||
|
|
||||||
|
#define RE_SYNTAX_POSIX_EXTENDED \
|
||||||
|
(RE_CHAR_CLASSES | RE_CONTEXT_INDEP_ANCHORS \
|
||||||
|
| RE_CONTEXT_INVALID_OPS | RE_DOT_NEWLINE | RE_DOT_NOT_NULL \
|
||||||
|
| RE_INTERVALS | RE_NEWLINE_ORDINARY | RE_NO_BK_BRACES \
|
||||||
|
| RE_NO_BK_PARENS | RE_NO_BK_REFS | RE_NO_BK_VBAR \
|
||||||
|
| RE_NO_EMPTY_ALTS | RE_NO_EMPTY_GROUPS | RE_NO_EMPTY_RANGES \
|
||||||
|
| RE_UNMATCHED_RIGHT_PAREN_ORD)
|
||||||
|
/* [[[end syntaxes]]] */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Maximum number of duplicates an interval can allow. */
|
||||||
|
#undef RE_DUP_MAX
|
||||||
|
#define RE_DUP_MAX ((1 << 15) - 1)
|
||||||
|
|
||||||
|
|
||||||
|
/* POSIX `cflags' bits (i.e., information for regcomp). */
|
||||||
|
|
||||||
|
/* If this bit is set, then use extended regular expression syntax.
|
||||||
|
If not set, then use basic regular expression syntax. */
|
||||||
|
#define REG_EXTENDED 1
|
||||||
|
|
||||||
|
/* If this bit is set, then ignore case when matching.
|
||||||
|
If not set, then case is significant. */
|
||||||
|
#define REG_ICASE (REG_EXTENDED << 1)
|
||||||
|
|
||||||
|
/* If this bit is set, then anchors do not match at newline
|
||||||
|
characters in the string.
|
||||||
|
If not set, then anchors do match at newlines. */
|
||||||
|
#define REG_NEWLINE (REG_ICASE << 1)
|
||||||
|
|
||||||
|
/* If this bit is set, then report only success or fail in regexec.
|
||||||
|
If not set, then returns differ between not matching and errors. */
|
||||||
|
#define REG_NOSUB (REG_NEWLINE << 1)
|
||||||
|
|
||||||
|
|
||||||
|
/* POSIX `eflags' bits (i.e., information for regexec). */
|
||||||
|
|
||||||
|
/* If this bit is set, then the beginning-of-line operator doesn't match
|
||||||
|
the beginning of the string (presumably because it's not the
|
||||||
|
beginning of a line).
|
||||||
|
If not set, then the beginning-of-line operator does match the
|
||||||
|
beginning of the string. */
|
||||||
|
#define REG_NOTBOL 1
|
||||||
|
|
||||||
|
/* Like REG_NOTBOL, except for the end-of-line. */
|
||||||
|
#define REG_NOTEOL (1 << 1)
|
||||||
|
|
||||||
|
|
||||||
|
/* If any error codes are removed, changed, or added, update the
|
||||||
|
`re_error_msg' table in regex.c. */
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
REG_NOERROR = 0, /* Success. */
|
||||||
|
REG_NOMATCH, /* Didn't find a match (for regexec). */
|
||||||
|
|
||||||
|
/* POSIX regcomp return error codes. (In the order listed in the
|
||||||
|
standard.) */
|
||||||
|
REG_BADPAT, /* Invalid pattern. */
|
||||||
|
REG_ECOLLATE, /* Not implemented. */
|
||||||
|
REG_ECTYPE, /* Invalid character class name. */
|
||||||
|
REG_EESCAPE, /* Trailing backslash. */
|
||||||
|
REG_ESUBREG, /* Invalid back reference. */
|
||||||
|
REG_EBRACK, /* Unmatched left bracket. */
|
||||||
|
REG_EPAREN, /* Parenthesis imbalance. */
|
||||||
|
REG_EBRACE, /* Unmatched \{. */
|
||||||
|
REG_BADBR, /* Invalid contents of \{\}. */
|
||||||
|
REG_ERANGE, /* Invalid range end. */
|
||||||
|
REG_ESPACE, /* Ran out of memory. */
|
||||||
|
REG_BADRPT, /* No preceding re for repetition op. */
|
||||||
|
|
||||||
|
/* Error codes we've added. */
|
||||||
|
REG_EEND, /* Premature end. */
|
||||||
|
REG_ESIZE, /* Compiled pattern bigger than 2^16 bytes. */
|
||||||
|
REG_ERPAREN /* Unmatched ) or \); not returned from regcomp. */
|
||||||
|
} reg_errcode_t;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* This data structure represents a compiled pattern. Before calling
|
||||||
|
the pattern compiler, the fields `buffer', `allocated', `fastmap',
|
||||||
|
`translate', and `no_sub' can be set. After the pattern has been
|
||||||
|
compiled, the `re_nsub' field is available. All other fields are
|
||||||
|
private to the regex routines. */
|
||||||
|
|
||||||
|
struct re_pattern_buffer
|
||||||
|
{
|
||||||
|
/* [[[begin pattern_buffer]]] */
|
||||||
|
/* Space that holds the compiled pattern. It is declared as
|
||||||
|
`unsigned char *' because its elements are
|
||||||
|
sometimes used as array indexes. */
|
||||||
|
unsigned char *buffer;
|
||||||
|
|
||||||
|
/* Number of bytes to which `buffer' points. */
|
||||||
|
unsigned long allocated;
|
||||||
|
|
||||||
|
/* Number of bytes actually used in `buffer'. */
|
||||||
|
unsigned long used;
|
||||||
|
|
||||||
|
/* Syntax setting with which the pattern was compiled. */
|
||||||
|
reg_syntax_t syntax;
|
||||||
|
|
||||||
|
/* Pointer to a fastmap, if any, otherwise zero. re_search uses
|
||||||
|
the fastmap, if there is one, to skip over impossible
|
||||||
|
starting points for matches. */
|
||||||
|
char *fastmap;
|
||||||
|
|
||||||
|
/* Either a translate table to apply to all characters before
|
||||||
|
comparing them, or zero for no translation. The translation
|
||||||
|
is applied to a pattern when it is compiled and to a string
|
||||||
|
when it is matched. */
|
||||||
|
char *translate;
|
||||||
|
|
||||||
|
/* Number of subexpressions found by the compiler. */
|
||||||
|
size_t re_nsub;
|
||||||
|
|
||||||
|
/* Set to 1 by re_compile_fastmap if this pattern can match the
|
||||||
|
null string; 0 prevents the searcher from matching it with
|
||||||
|
the null string. Set to 2 if it might match the null string
|
||||||
|
either at the end of a search range or just before a
|
||||||
|
character listed in the fastmap. */
|
||||||
|
unsigned can_be_null : 2;
|
||||||
|
|
||||||
|
/* Set to zero when regex_compile compiles a pattern; set to one
|
||||||
|
by re_compile_fastmap when it updates the fastmap, if any. */
|
||||||
|
unsigned fastmap_accurate : 1;
|
||||||
|
|
||||||
|
/* If set, regexec reports only success or failure and does not
|
||||||
|
return anything in pmatch. */
|
||||||
|
unsigned no_sub : 1;
|
||||||
|
|
||||||
|
/* If set, a beginning-of-line anchor doesn't match at the
|
||||||
|
beginning of the string. */
|
||||||
|
unsigned not_bol : 1;
|
||||||
|
|
||||||
|
/* Similarly for an end-of-line anchor. */
|
||||||
|
unsigned not_eol : 1;
|
||||||
|
|
||||||
|
/* If true, an anchor at a newline matches. */
|
||||||
|
unsigned newline_anchor : 1;
|
||||||
|
|
||||||
|
/* If set, re_match_2 assumes a non-null REGS argument is
|
||||||
|
initialized. If not set, REGS is initialized to the max of
|
||||||
|
RE_NREGS and re_nsub + 1 registers. */
|
||||||
|
unsigned caller_allocated_regs : 1;
|
||||||
|
/* [[[end pattern_buffer]]] */
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct re_pattern_buffer regex_t;
|
||||||
|
|
||||||
|
|
||||||
|
/* search.c (search_buffer) in Emacs needs this one opcode value. It is
|
||||||
|
defined both in `regex.c' and here. */
|
||||||
|
#define RE_EXACTN_VALUE 1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Type for byte offsets within the string. POSIX mandates us defining
|
||||||
|
this. */
|
||||||
|
typedef int regoff_t;
|
||||||
|
|
||||||
|
|
||||||
|
/* This is the structure we store register match data in. See
|
||||||
|
regex.texinfo for a full description of what registers match. */
|
||||||
|
struct re_registers
|
||||||
|
{
|
||||||
|
unsigned num_regs;
|
||||||
|
regoff_t *start;
|
||||||
|
regoff_t *end;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* If `caller_allocated_regs' is zero in the pattern buffer, re_match_2
|
||||||
|
returns information about this many registers. */
|
||||||
|
#ifndef RE_NREGS
|
||||||
|
#define RE_NREGS 30
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* POSIX specification for registers. Aside from the different names than
|
||||||
|
`re_registers', POSIX uses an array of structures, instead of a
|
||||||
|
structure of arrays. */
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
regoff_t rm_so; /* Byte offset from string's start to substring's start. */
|
||||||
|
regoff_t rm_eo; /* Byte offset from string's start to substring's end. */
|
||||||
|
} regmatch_t;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Declarations for routines. */
|
||||||
|
|
||||||
|
#if __STDC__
|
||||||
|
|
||||||
|
/* Sets the current syntax to SYNTAX. You can also simply assign to the
|
||||||
|
`obscure_syntax' variable. */
|
||||||
|
extern reg_syntax_t re_set_syntax (reg_syntax_t syntax);
|
||||||
|
|
||||||
|
/* Compile the regular expression PATTERN, with length LENGTH
|
||||||
|
and syntax given by the global `obscure_syntax', into the buffer
|
||||||
|
BUFFER. Return NULL if successful, and an error string if not. */
|
||||||
|
extern const char *re_compile_pattern (const char *pattern, int length,
|
||||||
|
struct re_pattern_buffer *buffer);
|
||||||
|
|
||||||
|
|
||||||
|
/* Compile a fastmap for the compiled pattern in BUFFER; used to
|
||||||
|
accelerate searches. Return 0 if successful and -2 if was an
|
||||||
|
internal error. */
|
||||||
|
extern int re_compile_fastmap (struct re_pattern_buffer *buffer);
|
||||||
|
|
||||||
|
|
||||||
|
/* Search in the string STRING (with length LENGTH) for the pattern
|
||||||
|
compiled into BUFFER. Start searching at position START, for RANGE
|
||||||
|
characters. Return the starting position of the match, -1 for no
|
||||||
|
match, or -2 for an internal error. Also return register
|
||||||
|
information in REGS (if REGS and BUFFER->no_sub are nonzero). */
|
||||||
|
extern int re_search (struct re_pattern_buffer *buffer,
|
||||||
|
const char *string, int length,
|
||||||
|
int start, int range,
|
||||||
|
struct re_registers *regs);
|
||||||
|
|
||||||
|
|
||||||
|
/* Like `re_search', but search in the concatenation of STRING1 and
|
||||||
|
STRING2. Also, stop searching at index START + STOP. */
|
||||||
|
extern int re_search_2 (struct re_pattern_buffer *buffer,
|
||||||
|
const char *string1, int length1,
|
||||||
|
const char *string2, int length2,
|
||||||
|
int start, int range,
|
||||||
|
struct re_registers *regs,
|
||||||
|
int stop);
|
||||||
|
|
||||||
|
|
||||||
|
/* Like `re_search', but return how many characters in STRING the regexp
|
||||||
|
in BUFFER matched, starting at position START. */
|
||||||
|
extern int re_match (const struct re_pattern_buffer *buffer,
|
||||||
|
const char *string, int length,
|
||||||
|
int start, struct re_registers *regs);
|
||||||
|
|
||||||
|
|
||||||
|
/* Relates to `re_match' as `re_search_2' relates to `re_search'. */
|
||||||
|
extern int re_match_2 (const struct re_pattern_buffer *buffer,
|
||||||
|
const char *string1, int length1,
|
||||||
|
const char *string2, int length2,
|
||||||
|
int start,
|
||||||
|
struct re_registers *regs,
|
||||||
|
int stop);
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef __386BSD__
|
||||||
|
/* 4.2 bsd compatibility. */
|
||||||
|
#ifndef bsdi
|
||||||
|
extern const char *re_comp (const char *);
|
||||||
|
#endif
|
||||||
|
extern int re_exec (const char *);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* POSIX compatibility. */
|
||||||
|
extern int regcomp (regex_t *preg, const char *pattern, int cflags);
|
||||||
|
extern int regexec (const regex_t *preg, const char *string, size_t nmatch,
|
||||||
|
regmatch_t pmatch[], int eflags);
|
||||||
|
extern size_t regerror (int errcode, const regex_t *preg, char *errbuf,
|
||||||
|
size_t errbuf_size);
|
||||||
|
extern void regfree (regex_t *preg);
|
||||||
|
|
||||||
|
#else /* not __STDC__ */
|
||||||
|
|
||||||
|
/* Support old C compilers. */
|
||||||
|
#define const
|
||||||
|
|
||||||
|
extern reg_syntax_t re_set_syntax ();
|
||||||
|
extern char *re_compile_pattern ();
|
||||||
|
extern int re_search (), re_search_2 ();
|
||||||
|
extern int re_match (), re_match_2 ();
|
||||||
|
|
||||||
|
/* 4.2 BSD compatibility. */
|
||||||
|
extern char *re_comp ();
|
||||||
|
extern int re_exec ();
|
||||||
|
|
||||||
|
/* POSIX compatibility. */
|
||||||
|
extern int regcomp ();
|
||||||
|
extern int regexec ();
|
||||||
|
extern size_t regerror ();
|
||||||
|
extern void regfree ();
|
||||||
|
|
||||||
|
#endif /* not __STDC__ */
|
||||||
|
#endif /* not __REGEXP_LIBRARY_H__ */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Local variables:
|
||||||
|
make-backup-files: t
|
||||||
|
version-control: t
|
||||||
|
trim-versions-without-asking: nil
|
||||||
|
End:
|
||||||
|
*/
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
/* rename.c -- BSD compatible directory function for System V
|
||||||
|
Copyright (C) 1988, 1990 Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2, or (at your option)
|
||||||
|
any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#ifndef STDC_HEADERS
|
||||||
|
extern int errno;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Rename file FROM to file TO.
|
||||||
|
Return 0 if successful, -1 if not. */
|
||||||
|
|
||||||
|
int
|
||||||
|
rename (from, to)
|
||||||
|
char *from;
|
||||||
|
char *to;
|
||||||
|
{
|
||||||
|
struct stat from_stats;
|
||||||
|
int pid, status;
|
||||||
|
|
||||||
|
if (stat (from, &from_stats) == 0)
|
||||||
|
{
|
||||||
|
if (unlink (to) && errno != ENOENT)
|
||||||
|
return -1;
|
||||||
|
if ((from_stats.st_mode & S_IFMT) == S_IFDIR)
|
||||||
|
{
|
||||||
|
/* Need a setuid root process to link and unlink directories. */
|
||||||
|
pid = fork ();
|
||||||
|
switch (pid)
|
||||||
|
{
|
||||||
|
case -1: /* Error. */
|
||||||
|
error (1, errno, "cannot fork");
|
||||||
|
|
||||||
|
case 0: /* Child. */
|
||||||
|
execl (MVDIR, "mvdir", from, to, (char *) 0);
|
||||||
|
error (255, errno, "cannot run `%s'", MVDIR);
|
||||||
|
|
||||||
|
default: /* Parent. */
|
||||||
|
while (wait (&status) != pid)
|
||||||
|
/* Do nothing. */ ;
|
||||||
|
|
||||||
|
errno = 0; /* mvdir printed the system error message. */
|
||||||
|
return status != 0 ? -1 : 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (link (from, to) == 0 && (unlink (from) == 0 || errno == ENOENT))
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
@@ -0,0 +1,412 @@
|
|||||||
|
/* sighandle.c -- Library routines for manipulating chains of signal handlers
|
||||||
|
Copyright (C) 1992 Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2, or (at your option)
|
||||||
|
any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
|
||||||
|
|
||||||
|
/* Written by Paul Sander, HaL Computer Systems, Inc. <paul@hal.com>
|
||||||
|
Brian Berliner <berliner@Sun.COM> added POSIX support */
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
*
|
||||||
|
* signal.c -- This file contains code that manipulates chains of signal
|
||||||
|
* handlers.
|
||||||
|
*
|
||||||
|
* Facilities are provided to register a signal handler for
|
||||||
|
* any specific signal. When a signal is received, all of the
|
||||||
|
* registered signal handlers are invoked in the reverse order
|
||||||
|
* in which they are registered. Note that the signal handlers
|
||||||
|
* must not themselves make calls to the signal handling
|
||||||
|
* facilities.
|
||||||
|
*
|
||||||
|
* @(#)sighandle.c 1.9 92/03/31
|
||||||
|
*
|
||||||
|
*************************************************************************/
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <signal.h>
|
||||||
|
|
||||||
|
#ifdef STDC_HEADERS
|
||||||
|
#include <stdlib.h>
|
||||||
|
#else
|
||||||
|
#if __STDC__
|
||||||
|
char *calloc(unsigned nelem, unsigned size);
|
||||||
|
char *malloc(unsigned size);
|
||||||
|
#else
|
||||||
|
char *calloc();
|
||||||
|
char *malloc();
|
||||||
|
#endif /* __STDC__ */
|
||||||
|
#endif /* STDC_HEADERS */
|
||||||
|
|
||||||
|
#ifdef _MINIX
|
||||||
|
#undef POSIX /* Minix 1.6 doesn't support POSIX.1 sigaction yet */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef SIGTYPE
|
||||||
|
#define SIGTYPE void
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Define the highest signal number (usually) */
|
||||||
|
#ifndef SIGMAX
|
||||||
|
#define SIGMAX 32
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Define linked list of signal handlers structure */
|
||||||
|
struct SIG_hlist {
|
||||||
|
SIGTYPE (*handler)();
|
||||||
|
struct SIG_hlist *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Define array of lists of signal handlers. Note that this depends on
|
||||||
|
* the implementation to initialize each element to a null pointer.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static struct SIG_hlist **SIG_handlers;
|
||||||
|
|
||||||
|
/* Define array of default signal vectors */
|
||||||
|
|
||||||
|
#ifdef POSIX
|
||||||
|
static struct sigaction *SIG_defaults;
|
||||||
|
#else
|
||||||
|
#ifdef BSD_SIGNALS
|
||||||
|
static struct sigvec *SIG_defaults;
|
||||||
|
#else
|
||||||
|
static SIGTYPE (**SIG_defaults)();
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Critical section housekeeping */
|
||||||
|
static int SIG_crSectNest = 0; /* Nesting level */
|
||||||
|
#ifdef POSIX
|
||||||
|
static sigset_t SIG_crSectMask; /* Signal mask */
|
||||||
|
#else
|
||||||
|
static int SIG_crSectMask; /* Signal mask */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialize the signal handler arrays
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int SIG_init()
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
#ifdef POSIX
|
||||||
|
sigset_t sigset_test;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (SIG_defaults && SIG_handlers) /* already allocated */
|
||||||
|
return (0);
|
||||||
|
|
||||||
|
#ifdef POSIX
|
||||||
|
(void) sigfillset(&sigset_test);
|
||||||
|
for (i = 1; sigismember(&sigset_test, i) == 1; i++)
|
||||||
|
#ifdef BROKEN_SIGISMEMBER
|
||||||
|
if ( i >= NSIG )
|
||||||
|
break
|
||||||
|
#endif
|
||||||
|
;
|
||||||
|
if (i < SIGMAX)
|
||||||
|
i = SIGMAX;
|
||||||
|
i++;
|
||||||
|
if (!SIG_defaults)
|
||||||
|
SIG_defaults = (struct sigaction *)
|
||||||
|
calloc(i, sizeof(struct sigaction));
|
||||||
|
(void) sigemptyset(&SIG_crSectMask);
|
||||||
|
#else
|
||||||
|
i = SIGMAX+1;
|
||||||
|
#ifdef BSD_SIGNALS
|
||||||
|
if (!SIG_defaults)
|
||||||
|
SIG_defaults = (struct sigvec *)
|
||||||
|
calloc(i, sizeof(struct sigvec));
|
||||||
|
#else
|
||||||
|
if (!SIG_defaults)
|
||||||
|
SIG_defaults = (SIGTYPE (**)())
|
||||||
|
calloc(i, sizeof(SIGTYPE (**)()));
|
||||||
|
#endif
|
||||||
|
SIG_crSectMask = 0;
|
||||||
|
#endif
|
||||||
|
if (!SIG_handlers)
|
||||||
|
SIG_handlers = (struct SIG_hlist **)
|
||||||
|
calloc(i, sizeof(struct SIG_hlist *));
|
||||||
|
return (!SIG_defaults || !SIG_handlers);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The following invokes each signal handler in the reverse order in which
|
||||||
|
* they were registered.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static SIGTYPE SIG_handle(sig)
|
||||||
|
int sig;
|
||||||
|
{
|
||||||
|
struct SIG_hlist *this;
|
||||||
|
|
||||||
|
/* Dispatch signal handlers */
|
||||||
|
this = SIG_handlers[sig];
|
||||||
|
while (this != (struct SIG_hlist *) NULL)
|
||||||
|
{
|
||||||
|
(*this->handler)(sig);
|
||||||
|
this = this->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The following registers a signal handler. If the handler is already
|
||||||
|
* registered, it is not registered twice, nor is the order in which signal
|
||||||
|
* handlers are invoked changed. If this is the first signal handler
|
||||||
|
* registered for a given signal, the old sigvec structure is saved for
|
||||||
|
* restoration later.
|
||||||
|
*/
|
||||||
|
|
||||||
|
int SIG_register(sig,fn)
|
||||||
|
int sig;
|
||||||
|
SIGTYPE (*fn)();
|
||||||
|
{
|
||||||
|
int val;
|
||||||
|
struct SIG_hlist *this;
|
||||||
|
#ifdef POSIX
|
||||||
|
struct sigaction act;
|
||||||
|
sigset_t sigset_mask, sigset_omask;
|
||||||
|
#else
|
||||||
|
#ifdef BSD_SIGNALS
|
||||||
|
struct sigvec vec;
|
||||||
|
int mask;
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Initialize */
|
||||||
|
if (SIG_init() != 0)
|
||||||
|
return (-1);
|
||||||
|
val = 0;
|
||||||
|
|
||||||
|
/* Block this signal while we look at handler chain */
|
||||||
|
#ifdef POSIX
|
||||||
|
(void) sigemptyset(&sigset_mask);
|
||||||
|
(void) sigaddset(&sigset_mask, sig);
|
||||||
|
(void) sigprocmask(SIG_BLOCK, &sigset_mask, &sigset_omask);
|
||||||
|
#else
|
||||||
|
#ifdef BSD_SIGNALS
|
||||||
|
mask = sigblock(sigmask(sig));
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* See if this handler was already registered */
|
||||||
|
this = SIG_handlers[sig];
|
||||||
|
while (this != (struct SIG_hlist *) NULL)
|
||||||
|
{
|
||||||
|
if (this->handler == fn) break;
|
||||||
|
this = this->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Register the new handler only if it is not already registered. */
|
||||||
|
if (this == (struct SIG_hlist *) NULL)
|
||||||
|
{
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If this is the first handler registered for this signal,
|
||||||
|
* set up the signal handler dispatcher
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (SIG_handlers[sig] == (struct SIG_hlist *) NULL)
|
||||||
|
{
|
||||||
|
#ifdef POSIX
|
||||||
|
act.sa_handler = SIG_handle;
|
||||||
|
(void) sigemptyset(&act.sa_mask);
|
||||||
|
act.sa_flags = 0;
|
||||||
|
val = sigaction(sig, &act, &SIG_defaults[sig]);
|
||||||
|
#else
|
||||||
|
#ifdef BSD_SIGNALS
|
||||||
|
bzero((char *)&vec, sizeof(vec));
|
||||||
|
vec.sv_handler = SIG_handle;
|
||||||
|
val = sigvec(sig, &vec, &SIG_defaults[sig]);
|
||||||
|
#else
|
||||||
|
if ((SIG_defaults[sig] = signal(sig, SIG_handle)) ==
|
||||||
|
(SIGTYPE (*)()) -1)
|
||||||
|
val = -1;
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If not, register it */
|
||||||
|
if ((val == 0) && (this == (struct SIG_hlist *) NULL))
|
||||||
|
{
|
||||||
|
this = (struct SIG_hlist *)
|
||||||
|
malloc(sizeof(struct SIG_hlist));
|
||||||
|
if (this == NULL)
|
||||||
|
{
|
||||||
|
val = -1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this->handler = fn;
|
||||||
|
this->next = SIG_handlers[sig];
|
||||||
|
SIG_handlers[sig] = this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Unblock the signal */
|
||||||
|
#ifdef POSIX
|
||||||
|
(void) sigprocmask(SIG_SETMASK, &sigset_omask, NULL);
|
||||||
|
#else
|
||||||
|
#ifdef BSD_SIGNALS
|
||||||
|
(void) sigsetmask(mask);
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The following deregisters a signal handler. If the last signal handler for
|
||||||
|
* a given signal is deregistered, the default sigvec information is restored.
|
||||||
|
*/
|
||||||
|
|
||||||
|
int SIG_deregister(sig,fn)
|
||||||
|
int sig;
|
||||||
|
SIGTYPE (*fn)();
|
||||||
|
{
|
||||||
|
int val;
|
||||||
|
struct SIG_hlist *this;
|
||||||
|
struct SIG_hlist *last;
|
||||||
|
#ifdef POSIX
|
||||||
|
sigset_t sigset_mask, sigset_omask;
|
||||||
|
#else
|
||||||
|
#ifdef BSD_SIGNALS
|
||||||
|
int mask;
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Initialize */
|
||||||
|
if (SIG_init() != 0)
|
||||||
|
return (-1);
|
||||||
|
val = 0;
|
||||||
|
last = (struct SIG_hlist *) NULL;
|
||||||
|
|
||||||
|
/* Block this signal while we look at handler chain */
|
||||||
|
#ifdef POSIX
|
||||||
|
(void) sigemptyset(&sigset_mask);
|
||||||
|
(void) sigaddset(&sigset_mask, sig);
|
||||||
|
(void) sigprocmask(SIG_BLOCK, &sigset_mask, &sigset_omask);
|
||||||
|
#else
|
||||||
|
#ifdef BSD_SIGNALS
|
||||||
|
mask = sigblock(sigmask(sig));
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Search for the signal handler */
|
||||||
|
this = SIG_handlers[sig];
|
||||||
|
while ((this != (struct SIG_hlist *) NULL) && (this->handler != fn))
|
||||||
|
{
|
||||||
|
last = this;
|
||||||
|
this = this->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If it was registered, remove it */
|
||||||
|
if (this != (struct SIG_hlist *) NULL)
|
||||||
|
{
|
||||||
|
if (last == (struct SIG_hlist *) NULL)
|
||||||
|
{
|
||||||
|
SIG_handlers[sig] = this->next;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
last->next = this->next;
|
||||||
|
}
|
||||||
|
free((char *) this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Restore default behavior if there are no registered handlers */
|
||||||
|
if (SIG_handlers[sig] == (struct SIG_hlist *) NULL)
|
||||||
|
{
|
||||||
|
#ifdef POSIX
|
||||||
|
val = sigaction(sig, &SIG_defaults[sig],
|
||||||
|
(struct sigaction *) NULL);
|
||||||
|
#else
|
||||||
|
#ifdef BSD_SIGNALS
|
||||||
|
val = sigvec(sig, &SIG_defaults[sig], (struct sigvec *) NULL);
|
||||||
|
#else
|
||||||
|
if (signal(sig, SIG_defaults[sig]) == (SIGTYPE (*)()) -1)
|
||||||
|
val = -1;
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Unblock the signal */
|
||||||
|
#ifdef POSIX
|
||||||
|
(void) sigprocmask(SIG_SETMASK, &sigset_omask, NULL);
|
||||||
|
#else
|
||||||
|
#ifdef BSD_SIGNALS
|
||||||
|
(void) sigsetmask(mask);
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The following begins a critical section.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void SIG_beginCrSect()
|
||||||
|
{
|
||||||
|
if (SIG_init() == 0)
|
||||||
|
{
|
||||||
|
if (SIG_crSectNest == 0)
|
||||||
|
{
|
||||||
|
#ifdef POSIX
|
||||||
|
sigset_t sigset_mask;
|
||||||
|
|
||||||
|
(void) sigfillset(&sigset_mask);
|
||||||
|
(void) sigprocmask(SIG_SETMASK,
|
||||||
|
&sigset_mask, &SIG_crSectMask);
|
||||||
|
#else
|
||||||
|
#ifdef BSD_SIGNALS
|
||||||
|
SIG_crSectMask = sigblock(~0);
|
||||||
|
#else
|
||||||
|
/* TBD */
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
SIG_crSectNest++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The following ends a critical section.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void SIG_endCrSect()
|
||||||
|
{
|
||||||
|
if (SIG_init() == 0)
|
||||||
|
{
|
||||||
|
SIG_crSectNest--;
|
||||||
|
if (SIG_crSectNest == 0)
|
||||||
|
{
|
||||||
|
#ifdef POSIX
|
||||||
|
(void) sigprocmask(SIG_SETMASK, &SIG_crSectMask, NULL);
|
||||||
|
#else
|
||||||
|
#ifdef BSD_SIGNALS
|
||||||
|
(void) sigsetmask(SIG_crSectMask);
|
||||||
|
#else
|
||||||
|
/* TBD */
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
/* strdup.c -- return a newly allocated copy of a string
|
||||||
|
Copyright (C) 1990 Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2, or (at your option)
|
||||||
|
any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
|
||||||
|
|
||||||
|
#ifdef STDC_HEADERS
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#else
|
||||||
|
char *malloc ();
|
||||||
|
char *strcpy ();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Return a newly allocated copy of STR,
|
||||||
|
or 0 if out of memory. */
|
||||||
|
|
||||||
|
char *
|
||||||
|
strdup (str)
|
||||||
|
char *str;
|
||||||
|
{
|
||||||
|
char *newstr;
|
||||||
|
|
||||||
|
newstr = (char *) malloc (strlen (str) + 1);
|
||||||
|
if (newstr)
|
||||||
|
strcpy (newstr, str);
|
||||||
|
return newstr;
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
/* strippath.c -- remove unnecessary components from a path specifier
|
||||||
|
Copyright (C) 1992 Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2, or (at your option)
|
||||||
|
any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
|
||||||
|
|
||||||
|
#if defined(STDC_HEADERS) || defined(USG)
|
||||||
|
#include <string.h>
|
||||||
|
#ifndef index
|
||||||
|
#define index strchr
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#include <strings.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#if __STDC__
|
||||||
|
static void remove_component(char *beginc, char *endc);
|
||||||
|
void strip_trailing_slashes(char *path);
|
||||||
|
#else
|
||||||
|
static void remove_component();
|
||||||
|
void strip_trailing_slashes();
|
||||||
|
#endif /* __STDC__ */
|
||||||
|
|
||||||
|
/* Remove unnecessary components from PATH. */
|
||||||
|
|
||||||
|
void
|
||||||
|
strip_path (path)
|
||||||
|
char *path;
|
||||||
|
{
|
||||||
|
int stripped = 0;
|
||||||
|
char *cp, *slash;
|
||||||
|
|
||||||
|
for (cp = path; (slash = index(cp, '/')) != NULL; cp = slash)
|
||||||
|
{
|
||||||
|
*slash = '\0';
|
||||||
|
if ((!*cp && (cp != path || stripped)) ||
|
||||||
|
strcmp(cp, ".") == 0 || strcmp(cp, "/") == 0)
|
||||||
|
{
|
||||||
|
stripped = 1;
|
||||||
|
remove_component(cp, slash);
|
||||||
|
slash = cp;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*slash++ = '/';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
strip_trailing_slashes(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove the component delimited by BEGINC and ENDC from the path */
|
||||||
|
|
||||||
|
static void
|
||||||
|
remove_component (beginc, endc)
|
||||||
|
char *beginc;
|
||||||
|
char *endc;
|
||||||
|
{
|
||||||
|
for (endc++; *endc; endc++)
|
||||||
|
*beginc++ = *endc;
|
||||||
|
*beginc = '\0';
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user