Revert r256095, r256120 (partial), r256121:

r256095:
 - Add gnu/usr.bin/rcs back to the base system.

r256120:
 - Add WITHOUT_RCS back to src.conf.5.

r256121:
 - Remove UPDATING entry regarding gnu/usr.bin/rcs removal.

Requested by:	many
Approved by:	re (marius)
Discussed with:	core
This commit is contained in:
Glen Barber 2013-10-09 17:07:20 +00:00
parent 02147e9cd0
commit c9fc60beee
73 changed files with 25657 additions and 27 deletions

View File

@ -38,29 +38,6 @@
# xargs -n1 | sort | uniq -d;
# done
# 20131015: removal of RCS from base
OLD_FILES+=usr/bin/ci
OLD_FILES+=usr/bin/co
OLD_FILES+=usr/bin/ident
OLD_FILES+=usr/bin/merge
OLD_FILES+=usr/bin/rcs
OLD_FILES+=usr/bin/rcsclean
OLD_FILES+=usr/bin/rcsdiff
OLD_FILES+=usr/bin/rcsfreeze
OLD_FILES+=usr/bin/rcsmerge
OLD_FILES+=usr/bin/rlog
OLD_FILES+=usr/share/man/man1/ci.1.gz
OLD_FILES+=usr/share/man/man1/co.1.gz
OLD_FILES+=usr/share/man/man1/ident.1.gz
OLD_FILES+=usr/share/man/man1/merge.1.gz
OLD_FILES+=usr/share/man/man1/rcs.1.gz
OLD_FILES+=usr/share/man/man1/rcsclean.1.gz
OLD_FILES+=usr/share/man/man1/rcsdiff.1.gz
OLD_FILES+=usr/share/man/man1/rcsfreeze.1.gz
OLD_FILES+=usr/share/man/man1/rcsintro.1.gz
OLD_FILES+=usr/share/man/man1/rcsmerge.1.gz
OLD_FILES+=usr/share/man/man1/rlog.1.gz
OLD_FILES+=usr/share/man/man5/rcsfile.5.gz
# 20131001: ar and ranlib from binutils not used
OLD_FILES+=usr/bin/gnu-ar
OLD_FILES+=usr/bin/gnu-ranlib

View File

@ -31,10 +31,6 @@ NOTE TO PEOPLE WHO THINK THAT FreeBSD 10.x IS SLOW:
disable the most expensive debugging functionality run
"ln -s 'abort:false,junk:false' /etc/malloc.conf".)
20131006:
RCS has been removed from the base system. If you need RCS
install either devel/rcs or devel/rcs57.
20130930:
BIND has been removed from the base system. If all you need
is a local resolver, simply enable and start the local_unbound

View File

@ -12,6 +12,7 @@ SUBDIR= ${_binutils} \
${_gperf} \
grep \
${_groff} \
${_rcs} \
sdiff \
send-pr \
${_texinfo}
@ -31,6 +32,10 @@ _dtc= dtc
_texinfo= texinfo
.endif
.if ${MK_RCS} != "no"
_rcs= rcs
.endif
.if ${MK_BINUTILS} != "no"
_binutils= binutils
.endif

24
gnu/usr.bin/rcs/CREDITS Normal file
View File

@ -0,0 +1,24 @@
RCS was designed and built by Walter F. Tichy of Purdue University.
RCS version 3 was released in 1983.
Adam Hammer, Thomas Narten, and Daniel Trinkle of Purdue supported RCS through
version 4.3, released in 1990. Guy Harris of Sun contributed many porting
fixes. Paul Eggert of System Development Corporation contributed bug fixes
and tuneups. Jay Lepreau contributed 4.3BSD support.
Paul Eggert of Twin Sun wrote the changes for RCS versions 5.5 and 5.6 (1991).
Rich Braun of Kronos and Andy Glew of Intel contributed ideas for new options.
Bill Hahn of Stratus contributed ideas for setuid support.
Ideas for piece tables came from Joe Berkovitz of Stratus and Walter F. Tichy.
Matt Cross of Stratus contributed test case ideas.
Adam Hammer of Purdue QAed.
Paul Eggert wrote most of the changes for this version of RCS,
currently in beta test. K. Richard Pixley of Cygnus Support
contributed several bug fixes. Robert Lupton of Princeton
and Daniel Trinkle contributed ideas for $Name expansion.
Brendan Kehoe of Cygnus Support suggested rlog's -N option.
Paul D. Smith of Data General suggested improvements in option
and error processing. Adam Hammer of Purdue QAed.
$FreeBSD$

3
gnu/usr.bin/rcs/Makefile Normal file
View File

@ -0,0 +1,3 @@
SUBDIR= lib ci co ident merge rcs rcsclean rcsdiff rcsmerge rlog rcsfreeze
.include <bsd.subdir.mk>

View File

@ -0,0 +1,3 @@
# $FreeBSD$
LIBRCS= ${.OBJDIR}/../lib/librcs.a

548
gnu/usr.bin/rcs/NEWS Normal file
View File

@ -0,0 +1,548 @@
Recent changes to RCS (and possible future changes)
$FreeBSD$
Copyright 1991, 1992, 1993, 1994, 1995 Paul Eggert
Distributed under license by the Free Software Foundation, Inc.
This file is part of RCS.
RCS 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.
RCS 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 RCS; see the file COPYING.
If not, write to the Free Software Foundation,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
Report problems and direct all questions to:
rcs-bugs@cs.purdue.edu
Here is a brief summary of user-visible changes since 5.6.
New options:
`-kb' supports binary files.
`-T' preserves the modification time of RCS files.
`-V' prints the version number.
`-zLT' causes RCS to use local time in working files and logs.
`rcsclean -n' outputs what rcsclean would do, without actually doing it.
`rlog -N' omits symbolic names.
There is a new keyword `Name'.
Inserted log lines now have the same prefix as the preceding `$Log' line.
Most changes for RCS version 5.7 are to fix bugs and improve portability.
RCS now conforms to GNU configuration standards and to Posix 1003.1b-1993.
Features new to RCS version 5.7, and possibly incompatible
in minor ways with previous practice, include:
Inserted log lines now have the same prefix as the preceding `$Log' line.
E.g. if a $Log line starts with `// $Log', log lines are prefixed with `// '.
RCS still records the (now obsolescent) comment leader inside RCS files,
but it ignores the comment leader unless it is emulating older RCS versions.
If you plan to access a file with both old and new versions of RCS,
make sure its comment leader matches its `$Log' line prefix.
For backwards compatibility with older versions of RCS,
if the log prefix is `/*' or `(*' surrounded by optional white space,
inserted log lines contain ` *' instead of `/*' or `(*';
however, this usage is obsolescent and should not be relied on.
$Log string `Revision' times now use the same format as other times.
Log lines are now inserted even if -kk is specified; this simplifies merging.
ci's -rR option (with a nonempty R) now just specifies a revision number R.
In some beta versions, it also reestablished the default behavior of
releasing a lock and removing the working file.
Now, only the bare -r option does this.
With an empty extension, any appearance of a directory named `RCS'
in a pathname identifies the pathname as being that of an RCS file.
For example, `a/RCS/b/c' is now an RCS file with an empty extension.
Formerly, `RCS' had to be the last directory in the pathname.
rlog's -d option by default now uses exclusive time ranges.
E.g. `rlog -d"<T"' now excludes revisions whose times equal T exactly.
Use `rlog -d"<=T"' to get the old behavior.
merge now takes up to three -L options, one for each input file.
Formerly, it took at most two -L options, for the 1st and 3rd input files.
`rcs' now requires at least one option; this is for future expansion.
Other features new to RCS version 5.7 include:
merge and rcsmerge now pass -A, -E, and -e options to the subsidiary diff3.
rcs -kb acts like rcs -ko, except it uses binary I/O on working files.
This makes no difference under Posix or Unix, but it does matter elsewhere.
With -kb in effect, rcsmerge refuses to merge;
this avoids common problems with CVS merging.
The following is for future use by GNU Emacs 19's version control package:
rcs's new -M option causes it to not send mail when you break somebody
else's lock. This is not meant for casual use; see rcs(1).
ci's new -i option causes an error if the RCS file already exists.
Similarly, -j causes an error if the RCS file does not already exist.
The new keyword `Name' is supported; its value is the name, if any,
used to check out the revision. E.g. `co -rN foo' causes foo's
$Name...$ keyword strings to end in `: N $'.
The new -zZONE option causes RCS to output dates and times using ISO 8601
format with ZONE as the time zone, and to use ZONE as the default time
zone for input. Its most common use is the -zLT option, which causes RCS
to use local time externally. You can also specify foreign time zones;
e.g. -z+05:30 causes RCS to use India time (5 hours 30 minutes east of UTC).
This option does not affect RCS files themselves, which always use UTC;
it affects only output (e.g. rlog output, keyword expansion, diff -c times)
and interpretation of options (e.g. the -d option of ci, co, and rlog).
Bare -z restores the default behavior of UTC with no time zone indication,
and the traditional RCS date separator `/' instead of the ISO 8601 `-'.
RCSINIT may contain a -z option. ci -k parses UTC offsets.
The new -T option of ci, co, rcs, and rcsclean preserves the modification
time of the RCS file unless a revision is added or removed.
ci -T sets the RCS file's modification time to the new revision's time
if the former precedes the latter and there is a new revision;
otherwise, it preserves the RCS file's modification time.
Use this option with care, as it can confuse `make'; see ci(1).
The new -N option of rlog omits symbolic names from the output.
A revision number that starts with `.' is considered to be relative to
the default branch (normally the trunk). A branch number followed by `.'
stands for the last revision on that branch.
If someone else already holds the lock, rcs -l now asks whether you want
to break it, instead of immediately reporting an error.
ci now always unlocks a revision like 3.5 if you check in a revision
like 3.5.2.1 that is the first of a new branch of that revision.
Formerly it was inconsistent.
File names may now contain tab, newline, space, and '$'.
They are represented in keyword strings with \t, \n, \040, and \044.
\ in a file name is now represented by \\ in a keyword string.
Identifiers may now start with a digit and (unless they are symbolic names)
may contain `.'. This permits author names like `john.doe' and `4tran'.
A bare -V option now prints the current version number.
rcsdiff outputs more readable context diff headers if diff -L works.
rcsdiff -rN -rN now suppresses needless checkout and comparison
of identical revisions.
Error messages now contain the names of files to which they apply.
Mach style memory mapping is now supported.
The installation procedure now conforms to the GNU coding standards.
When properly configured, RCS now strictly conforms to Posix 1003.1b-1993.
Features new to RCS version 5.6 include:
Security holes have been plugged; setgid use is no longer supported.
co can retrieve old revisions much more efficiently.
To generate the Nth youngest revision on the trunk,
the old method used up to N passes through copies of the working file;
the new method uses a piece table to generate the working file in one pass.
When ci finds no changes in the working file,
it automatically reverts to the previous revision unless -f is given.
RCS follows symbolic links to RCS files instead of breaking them,
and warns when it breaks hard links to RCS files.
`$' stands for the revision number taken from working file keyword strings.
E.g. if F contains an Id keyword string,
`rcsdiff -r$ F' compares F to its checked-in revision, and
`rcs -nL:$ F' gives the symbolic name L to F's revision.
co and ci's new -M option sets the modification time
of the working file to be that of the revision.
Without -M, ci now tries to avoid changing the working file's
modification time if its contents are unchanged.
rcs's new -m option changes the log message of an old revision.
RCS is portable to hosts that do not permit `,' in filenames.
(`,' is not part of the Posix portable filename character set.)
A new -x option specifies extensions other than `,v' for RCS files.
The Unix default is `-x,v/', so that the working file `w' corresponds
to the first file in the list `RCS/w,v', `w,v', `RCS/w' that works.
The non-Unix default is `-x', so that only `RCS/w' is tried.
Eventually, the Unix default should change to `-x/,v'
to encourage interoperability among all Posix hosts.
A new RCSINIT environment variable specifies defaults for options like -x.
The separator for revision ranges has been changed from `-' to `:', because
the range `A-B' is ambiguous if `A', `B' and `A-B' are all symbolic names.
E.g. the old `rlog -r1.5-1.7' is now `rlog -r1.5:1.7'; ditto for `rcs -o'.
For a while RCS will still support (but warn about) the old `-' separator.
RCS manipulates its lock files using a method that is more reliable under NFS.
Features new to RCS version 5 include:
RCS can check in arbitrary files, not just text files, if diff -a works.
RCS can merge lines containing just a single `.' if diff3 -m works.
GNU diff supports the -a and -m options.
RCS can now be used as a setuid program.
See ci(1) for how users can employ setuid copies of ci, co, and rcsclean.
Setuid privileges yield extra security if the effective user owns RCS files
and directories, and if only the effective user can write RCS directories.
RCS uses the real user for all accesses other than writing RCS directories.
As described in ci(1), there are three levels of setuid support.
1. Setuid works fully if the seteuid() system call lets any
process switch back and forth between real and effective users,
as specified in Posix 1003.1a Draft 5.
2. On hosts with saved setuids (a Posix 1003.1-1990 option) and without
a modern seteuid(), setuid works unless the real or effective user is root.
3. On hosts that lack both modern seteuid() and saved setuids,
setuid does not work, and RCS uses the effective user for all accesses;
formerly it was inconsistent.
New options to co, rcsdiff, and rcsmerge give more flexibility to keyword
substitution.
-kkv substitutes the default `$Keyword: value $' for keyword strings.
However, a locker's name is inserted only as a file is being locked,
i.e. by `ci -l' and `co -l'. This is normally the default.
-kkvl acts like -kkv, except that a locker's name is always inserted
if the given revision is currently locked. This was the default in
version 4. It is now the default only with when using rcsdiff to
compare a revision to a working file whose mode is that of a file
checked out for changes.
-kk substitutes just `$Keyword$', which helps to ignore keyword values
when comparing revisions.
-ko retrieves the old revision's keyword string, thus bypassing keyword
substitution.
-kv retrieves just `value'. This can ease the use of keyword values, but
it is dangerous because it causes RCS to lose track of where the keywords
are, so for safety the owner write permission of the working file is
turned off when -kv is used; to edit the file later, check it out again
without -kv.
rcs -ko sets the default keyword substitution to be in the style of co -ko,
and similarly for the other -k options. This can be useful with file
formats that cannot tolerate changing the lengths of keyword strings.
However it also renders a RCS file readable only by RCS version 5 or later.
Use rcs -kkv to restore the usual default substitution.
RCS can now be used by development groups that span time zone boundaries.
All times are now displayed in UTC, and UTC is the default time zone.
To use local time with co -d, append ` LT' to the time.
When interchanging RCS files with sites running older versions of RCS,
time stamp discrepancies may prevent checkins; to work around this,
use `ci -d' with a time slightly in the future.
Dates are now displayed using four-digit years, not two-digit years.
Years given in -d options must now have four digits.
This change is required for RCS to continue to work after 1999/12/31.
The form of dates in version 5 RCS files will not change until 2000/01/01,
so in the meantime RCS files can still be interchanged with sites
running older versions of RCS. To make room for the longer dates,
rlog now outputs `lines: +A -D' instead of `lines added/del: A/D'.
To help prevent diff programs that are broken or have run out of memory
from trashing an RCS file, ci now checks diff output more carefully.
ci -k now handles the Log keyword, so that checking in a file
with -k does not normally alter the file's contents.
RCS no longer outputs white space at the ends of lines
unless the original working file had it.
For consistency with other keywords,
a space, not a tab, is now output after `$Log:'.
Rlog now puts lockers and symbolic names on separate lines in the output
to avoid generating lines that are too long.
A similar fix has been made to lists in the RCS files themselves.
RCS no longer outputs the string `Locker: ' when expanding Header or Id
keywords. This saves space and reverts back to version 3 behavior.
The default branch is not put into the RCS file unless it is nonempty.
Therefore, files generated by RCS version 5 can be read by RCS version 3
unless they use the default branch feature introduced in version 4.
This fixes a compatibility problem introduced by version 4.
RCS can now emulate older versions of RCS; see `co -V'.
This may be useful to overcome compatibility problems
due to the above changes.
Programs like Emacs can now interact with RCS commands via a pipe:
the new -I option causes ci, co, and rcs to run interactively,
even if standard input is not a terminal.
These commands now accept multiple inputs from stdin separated by `.' lines.
ci now silently ignores the -t option if the RCS file already exists.
This simplifies some shell scripts and improves security in setuid sites.
Descriptive text may be given directly in an argument of the form -t-string.
The character set for symbolic names has been upgraded
from Ascii to ISO 8859.
rcsdiff now passes through all options used by GNU diff;
this is a longer list than 4.3BSD diff.
merge's new -L option gives tags for merge's overlap report lines.
This ability used to be present in a different, undocumented form;
the new form is chosen for compatibility with GNU diff3's -L option.
rcsmerge and merge now have a -q option, just like their siblings do.
rcsclean's new -n option outputs what rcsclean would do,
without actually doing it.
RCS now attempts to ignore parts of an RCS file that look like they come
from a future version of RCS.
When properly configured, RCS now strictly conforms with Posix 1003.1-1990.
RCS can still be compiled in non-Posix traditional Unix environments,
and can use common BSD and USG extensions to Posix.
RCS is a conforming Standard C program, and also compiles under traditional C.
Arbitrary limits on internal table sizes have been removed.
The only limit now is the amount of memory available via malloc().
File temporaries, lock files, signals, and system call return codes
are now handled more cleanly, portably, and quickly.
Some race conditions have been removed.
A new compile-time option RCSPREFIX lets administrators avoid absolute path
names for subsidiary programs, trading speed for flexibility.
The configuration procedure is now more automatic.
Snooping has been removed.
Version 4 was the first version distributed by FSF.
Beside bug fixes, features new to RCS version 4 include:
The notion of default branch has been added; see rcs -b.
Version 3 was included in the 4.3BSD distribution.
Here are some possible future changes for RCS:
Bring back sccstorcs.
Add an option to `rcsmerge' so that it can use an arbitrary program
to do the 3-way merge, instead of the default `merge'.
Likewise for `rcsdiff' and `diff'. It should be possible to pass
arbitrary options to these programs, and to the subsidiary `co's.
Add format options for finer control over the output of ident and rlog.
E.g. there should be an easy way for rlog to output lines like
`src/main.c 2.4 wft', one for each locked revision.
rlog options should have three orthogonal types: selecting files,
selecting revisions, and selecting rlog format.
Add format options for finer control over the output of keyword strings.
E.g. there should be some way to prepend @(#), and there should be some
way to change $ to some other character to disable further substitution.
These options should make the resulting files uneditable, like -kv.
Add long options, e.g. `--version'. Unfortunately RCS's option syntax
is incompatible with getopt. Perhaps the best way is to overload `rcs', e.g.
`rcs diff --keyword-substitution=old file' instead of `rcsdiff -ko file'.
Add a way to put only the interesting part of the path into the $Header
keyword expansion.
rlog -rM:N should work even if M and N have different numbers of fields,
so long as M is an ancestor of N or vice versa.
rcs should evaluate options in order; this allows rcs -oS -nS.
rcs should be able to fix minor mistakes in checkin dates and authors.
Be able to redo your most recent checkin with minor changes.
co -u shouldn't complain about a writable working file if it won't change
its contents.
Configure the Makefile automatically, as well as conf.h.
Add a new option to rcs that behaves like -o, but that doesn't lose the
nonempty log messages, but instead merges them with the next revision
if it exists, perhaps with a 1-line header containing author, date, etc.
Add a `-' option to take the list of pathnames from standard input.
Perhaps the pathnames should be null-terminated, not newline-terminated,
so that pathnames that contain newlines are handled properly.
Permit multiple option-pathname pairs, e.g. co -r1.4 a -r1.5 b.
Add options to allow arbitrary combinations of working file names
with RCS file names -- they shouldn't have to match.
Add an option to break a symbolic link to an RCS file,
instead of breaking the hard link that it points to.
Add ways to specify the earliest revision, the most recent revision,
the earliest or latest revision on a particular branch, and
the parent or child of some other revision.
If a user has multiple locks, perhaps ci should fall back on ci -k's
method to figure out which revision to use.
Symbolic names need not refer to existing branches and revisions.
rcs(1)'s BUGS section says this is a bug. Is it? If so, it should be fixed.
Add an option to rcs -o so that old log messages are not deleted if
the next undeleted revision exists, but are merely appended to the log
message of that revision.
ci -k should be able to get keyword values from the first `$Log' entry.
Add an option to rcsclean to clean directories recursively.
Write an rcsck program that repairs corrupted RCS files,
much as fsck repairs corrupted file systems.
For example, it should remove stale lock files.
Clean up the source code with a consistent indenting style.
Update the date parser to use the more modern getdate.y by Bellovin,
Salz, and Berets, or the even more modern getdate by Moraes. None of
these getdate implementations are as robust as RCS's old warhorse in
avoiding problems like arithmetic overflow, so they'll have to be
fixed first.
Break up the code into a library so that it's easier to write new programs
that manipulate RCS files, and so that useless code is removed from the
existing programs. For example, the rcs command contains unnecessary
keyword substitution baggage, and the merge command can be greatly pruned.
Make it easier to use your favorite text editor to edit log messages,
etc. instead of having to type them in irretrievably at the terminal.
Let the user specify a search path for default branches,
e.g. to use L as the default branch if it works, and M otherwise.
Let the user require that at least one entry in the default branch path works.
Let the user say that later entries in the default branch path are read only,
i.e. one cannot check in changes to them.
This should be an option settable by RCSINIT.
Add a way for a user to see which revisions affected which lines.
Have `rlog -nN F' print just the revision number that N translates to.
E.g. `rlog -nB. F' would print the highest revision on the branch B.
Use this to add an option -bB to rcsbranch, to freeze the named branch.
This should interact well with default branches.
Add a co option that prints the revision number before each line,
as SCCS's `get -m' does.
The following projects require a change to RCS file format.
Allow keyword expansion to be changed on a per-revision basis,
not on a per-file basis as now. This would allow -ko to be used
on imported revisions, with the default -kkv otherwise.
When two or more branches are merged, record all the ancestors
of the new revision. The hard part of this is keeping track of all
the ancestors of a working file while it's checked out.
Add loose locking, which is like non-strict but applies to all users,
not just the owner of the RCS file.
Be able to store RCS files in compressed format.
Don't bother to use a .Z extension that would exceed file name length limits;
just look at the magic number.
Add locker commentary, e.g. `co -l -m"checkout to fix merge bug" foo'
to tell others why you checked out `foo'.
Also record the time when the revision was locked,
and perhaps the working pathname (if applicable).
Let the user mark an RCS revision as deleted; checking out such a revision
would result in no working file. Similarly, using `co -d' with a date either
before the initial revision or after the file was marked deleted should
remove the working file. For extra credit, extend the notion of `deleted' to
include `renamed'. RCS should support arbitrary combinations of renaming and
deletion, e.g. renaming A to B and B to A, checking in new revisions to both
files, and then renaming them back.
Be able to check in an entire directory structure into a single RCS file.
Use a better scheme for locking revisions; the current scheme requires
changing the RCS file just to lock or unlock a revision.
The new scheme should coexist as well as possible with older versions of RCS,
and should avoid the rare NFS bugs mentioned in rcsedit.c.
E.g. if there's a reliable lockd running, RCS should use it
instead of relying on NFS.
Add rcs options for changing keyword names, e.g. XConsortium instead of Id.
Add a `$Description' keyword; but this may be tricky, since descriptions can
contain newlines and $s.
Add a `$Copyright' keyword that expands to a copyright notice.
Add frozen branches a la SCCS. In general, be able to emulate all of
SCCS, so that an SCCS-to-RCS program can be practical. For example,
there should be an equivalent to the SCCS prt command.
Add support for distributed RCS, where widely separated
users cannot easily access each others' RCS files,
and must periodically distribute and reconcile new revisions.
Be able to create empty branches.
Be able to store just deltas from a read-only principal copy,
e.g. from source on CD-ROM.
Improve RCS's method for storing binary files.
Although it is more efficient than SCCS's,
the diff algorithm is still line oriented,
and often generates long output for minor changes to an executable file.
From the user's point of view, it would be best if
RCS detected and handled binary files without human intervention,
switching expansion methods as needed from revision to revision.
Allow RCS to determine automagically whether -ko or -kb should be the default
by inspecting the file's contents or name. The magic should be optional
and user-programmable.
Extend the grammar of RCS files so that keywords need not be in a fixed order.
Internationalize messages; unfortunately, there's no common standard yet.
This requires a change in RCS file format because of the
`empty log message' and `checked in with -k' hacks inside RCS files.
Add documentation in texinfo format.

90
gnu/usr.bin/rcs/REFS Normal file
View File

@ -0,0 +1,90 @@
Here are references to RCS and related free software and documentation.
Some of this information changes often; see the Frequently Asked Questions
for more up-to-date references.
$FreeBSD$
Frequently Asked Questions (FAQs)
<http://www.qucis.queensu.ca/Software-Engineering/>
<ftp://rtfm.mit.edu//pub/usenet-by-hierarchy/comp/software-eng/>
for software engineering; e.g. see
<http://www.qucis.queensu.ca/Software-Engineering/blurb/rcs>.
<http://www.iac.honeywell.com/Pub/Tech/CM/CMFAQ.html>
<ftp://rtfm.mit.edu//pub/usenet-by-hierarchy/comp/software/config-mgmt/>
for configuration management
<http://www.winternet.com/~zoo/cvs/FAQ.txt>
<ftp://ftp.odi.com/pub/users/dgg/FAQ.gz>
for CVS (see below)
RCS and related GNU project software
<ftp://ftp.cs.purdue.edu/pub/RCS/>
The RCS project distribution directory also contains beta versions,
ports, and prebuilt documentation.
<ftp://prep.ai.mit.edu/pub/gnu/>
The GNU project distribution directory contains:
diffutils-N-tar.gz
the latest diffutils release; recommended for RCS
emacs-N-tar.gz
The latest Emacs release contains VC, a version-control package
that makes RCS easier to use.
make-N-tar.gz
GNU Make, which can automatically build from RCS files.
rcs-N-tar.gz
the latest RCS release
cvs-N-tar.gz
the latest official CVS release (see below)
<ftp://ftp.leo.org/pub/comp/os/os2/gnu/devtools/> DOS, OS/2 ports
<ftp://ftp.cc.utexas.edu/microlib/nt/gnu/> NT port
CVS
CVS, the Concurrent Versions System, keeps tracks of source changes
made by groups of developers working on the same files concurrently,
allowing them to resync as needed.
<http://www.winternet.com/~zoo/cvs/>
<http://www.loria.fr/~molli/cvs-index.html>
These pages have useful information about CVS.
<ftp://prep.ai.mit.edu/pub/gnu/cvs-1.3.tar.gz>
CVS 1.3 is the latest released version.
<ftp://ftp.delos.com/pub/cvs/alpha/cvs-1.4A2.tar.gz>
CVS 1.4 is in alpha test, but it is recommended if you are installing CVS
for the first time, or on a recent operating system.
<ftp://ftp-os2.cdrom.com/pub/os2/unix/> DOS, OS/2 ports
<ftp://ftp.cc.utexas.edu/microlib/nt/gnu/> NT port
<ftp://ftp.cyclic.com/pub/cvs/>
Cyclic CVS adds network transparency to CVS; it supports efficient,
reliable, and authenticated repository access via TCP/IP.
Other software that uses RCS
<ftp://ftp.nau.edu/pub/Aegis/>
Aegis manages revisions, baselines, mandatory reviews, and mandatory testing.
<ftp://ftp.vix.com/pub/patches/csu/>
BCS, the Baseline Configuration System,
manages revisions, baselines, and staging areas.
<ftp://riftp.osf.org/pub/ode/>
ODE, the Open Software Foundation Development Environment,
manages revisions, builds, and sandboxes.
OSF uses it for their own development.
<ftp://bellcore.com/pub/Odin/>
Odin, a `make' replacement, can build directly from arbitrary revisions
without requiring checkouts of working copies. It also handles
parallel builds on multiple remote hosts and of multiple variants.

View File

@ -0,0 +1,8 @@
PROG= ci
SRCS= ci.c
CFLAGS+= -I${.CURDIR}/../lib
LDADD= ${LIBRCS}
DPADD= ${LIBRCS}
.include "../../Makefile.inc"
.include <bsd.prog.mk>

898
gnu/usr.bin/rcs/ci/ci.1 Normal file
View File

@ -0,0 +1,898 @@
.de Id
.ds Rv \\$3
.ds Dt \\$4
..
.Id $FreeBSD$
.ds i \&\s-1ISO\s0
.ds r \&\s-1RCS\s0
.ds u \&\s-1UTC\s0
.if n .ds - \%--
.if t .ds - \(em
.TH CI 1 \*(Dt GNU
.SH NAME
ci \- check in RCS revisions
.SH SYNOPSIS
.B ci
.RI [ options ] " file " .\|.\|.
.SH DESCRIPTION
.B ci
stores new revisions into \*r files.
Each pathname matching an \*r suffix
is taken to be an \*r file.
All others
are assumed to be working files containing new revisions.
.B ci
deposits the contents of each working file
into the corresponding \*r file.
If only a working file is given,
.B ci
tries to find the corresponding \*r file in an \*r subdirectory
and then in the working file's directory.
For more details, see
.SM "FILE NAMING"
below.
.PP
For
.B ci
to work, the caller's login must be on the access list,
except if the access list is empty or the caller is the superuser or the
owner of the file.
To append a new revision to an existing branch, the tip revision on
that branch must be locked by the caller. Otherwise, only a
new branch can be created. This restriction is not enforced
for the owner of the file if non-strict locking is used
(see
.BR rcs (1)).
A lock held by someone else can be broken with the
.B rcs
command.
.PP
Unless the
.B \-f
option is given,
.B ci
checks whether the revision to be deposited differs from the preceding one.
If not, instead of creating a new revision
.B ci
reverts to the preceding one.
To revert, ordinary
.B ci
removes the working file and any lock;
.B "ci\ \-l"
keeps and
.B "ci\ \-u"
removes any lock, and then they both generate a new working file much as if
.B "co\ \-l"
or
.B "co\ \-u"
had been applied to the preceding revision.
When reverting, any
.B \-n
and
.B \-s
options apply to the preceding revision.
.PP
For each revision deposited,
.B ci
prompts for a log message.
The log message should summarize the change and must be terminated by
end-of-file or by a line containing
.BR \&. "\ by"
itself.
If several files are checked in
.B ci
asks whether to reuse the
previous log message.
If the standard input is not a terminal,
.B ci
suppresses the prompt
and uses the same log message for all files.
See also
.BR \-m .
.PP
If the \*r file does not exist,
.B ci
creates it and
deposits the contents of the working file as the initial revision
(default number:
.BR 1.1 ).
The access list is initialized to empty.
Instead of the log message,
.B ci
requests descriptive text (see
.B \-t
below).
.PP
The number
.I rev
of the deposited revision can be given by any of the options
.BR \-f ,
.BR \-i ,
.BR \-I ,
.BR \-j ,
.BR \-k ,
.BR \-l ,
.BR \-M ,
.BR \-q ,
.BR \-r ,
or
.BR \-u .
.I rev
can be symbolic, numeric, or mixed.
Symbolic names in
.I rev
must already be defined;
see the
.B \-n
and
.B \-N
options for assigning names during checkin.
If
.I rev
is
.BR $ ,
.B ci
determines the revision number from keyword values in the working file.
.PP
If
.I rev
begins with a period,
then the default branch (normally the trunk) is prepended to it.
If
.I rev
is a branch number followed by a period,
then the latest revision on that branch is used.
.PP
If
.I rev
is a revision number, it must be higher than the latest
one on the branch to which
.I rev
belongs, or must start a new branch.
.PP
If
.I rev
is a branch rather than a revision number,
the new revision is appended to that branch. The level number is obtained
by incrementing the tip revision number of that branch.
If
.I rev
indicates a non-existing branch,
that branch is created with the initial revision numbered
.IB rev .1\f1.\fP
.br
.ne 8
.PP
If
.I rev
is omitted,
.B ci
tries to derive the new revision number from
the caller's last lock. If the caller has locked the tip revision of a branch,
the new revision is appended to that branch.
The new revision number is obtained
by incrementing the tip revision number.
If the caller locked a non-tip revision, a new branch is started at
that revision by incrementing the highest branch number at that revision.
The default initial branch and level numbers are
.BR 1 .
.PP
If
.I rev
is omitted and the caller has no lock, but owns
the file and locking
is not set to
.IR strict ,
then the revision is appended to the
default branch (normally the trunk; see the
.B \-b
option of
.BR rcs (1)).
.PP
Exception: On the trunk, revisions can be appended to the end, but
not inserted.
.SH OPTIONS
.TP
.BI \-r rev
Check in revision
.IR rev .
.TP
.BR \-r
The bare
.B \-r
option (without any revision) has an unusual meaning in
.BR ci .
With other \*r commands, a bare
.B \-r
option specifies the most recent revision on the default branch,
but with
.BR ci ,
a bare
.B \-r
option reestablishes the default behavior of releasing a lock and
removing the working file, and is used to override any default
.B \-l
or
.B \-u
options established by shell aliases or scripts.
.TP
.BR \-l [\f2rev\fP]
works like
.BR \-r ,
except it performs an additional
.B "co\ \-l"
for the
deposited revision. Thus, the deposited revision is immediately
checked out again and locked.
This is useful for saving a revision although one wants to continue
editing it after the checkin.
.TP
.BR \-u [\f2rev\fP]
works like
.BR \-l ,
except that the deposited revision is not locked.
This lets one read the working file
immediately after checkin.
.RS
.PP
The
.BR \-l ,
bare
.BR \-r ,
and
.B \-u
options are mutually exclusive and silently override each other.
For example,
.B "ci\ \-u\ \-r"
is equivalent to
.B "ci\ \-r"
because bare
.B \-r
overrides
.BR \-u .
.RE
.TP
.BR \-f [\f2rev\fP]
forces a deposit; the new revision is deposited even it is not different
from the preceding one.
.TP
.BR \-k [\f2rev\fP]
searches the working file for keyword values to determine its revision number,
creation date, state, and author (see
.BR co (1)),
and assigns these
values to the deposited revision, rather than computing them locally.
It also generates a default login message noting the login of the caller
and the actual checkin date.
This option is useful for software distribution. A revision that is sent to
several sites should be checked in with the
.B \-k
option at these sites to
preserve the original number, date, author, and state.
The extracted keyword values and the default log message can be overridden
with the options
.BR \-d ,
.BR \-m ,
.BR \-s ,
.BR \-w ,
and any option that carries a revision number.
.TP
.BR \-q [\f2rev\fP]
quiet mode; diagnostic output is not printed.
A revision that is not different from the preceding one is not deposited,
unless
.B \-f
is given.
.TP
.BR \-i [\f2rev\fP]
initial checkin; report an error if the \*r file already exists.
This avoids race conditions in certain applications.
.TP
.BR \-j [\f2rev\fP]
just checkin and do not initialize;
report an error if the \*r file does not already exist.
.TP
.BR \-I [\f2rev\fP]
interactive mode;
the user is prompted and questioned
even if the standard input is not a terminal.
.TP
.BR \-d "[\f2date\fP]"
uses
.I date
for the checkin date and time.
The
.I date
is specified in free format as explained in
.BR co (1).
This is useful for lying about the checkin date, and for
.B \-k
if no date is available.
If
.I date
is empty, the working file's time of last modification is used.
.TP
.BR \-M [\f2rev\fP]
Set the modification time on any new working file
to be the date of the retrieved revision.
For example,
.BI "ci\ \-d\ \-M\ \-u" "\ f"
does not alter
.IR f 's
modification time, even if
.IR f 's
contents change due to keyword substitution.
Use this option with care; it can confuse
.BR make (1).
.TP
.BI \-m "msg"
uses the string
.I msg
as the log message for all revisions checked in.
By convention, log messages that start with
.B #
are comments and are ignored by programs like GNU Emacs's
.B vc
package.
Also, log messages that start with
.BI { clumpname }
(followed by white space) are meant to be clumped together if possible,
even if they are associated with different files; the
.BI { clumpname }
label is used only for clumping,
and is not considered to be part of the log message itself.
.TP
.BI \-n "name"
assigns the symbolic name
.I name
to the number of the checked-in revision.
.B ci
prints an error message if
.I name
is already assigned to another
number.
.TP
.BI \-N "name"
same as
.BR \-n ,
except that it overrides a previous assignment of
.IR name .
.TP
.BI \-s "state"
sets the state of the checked-in revision to the identifier
.IR state .
The default state is
.BR Exp .
.TP
.BI \-t file
writes descriptive text from the contents of the named
.I file
into the \*r file,
deleting the existing text.
The
.I file
cannot begin with
.BR \- .
.TP
.BI \-t\- string
Write descriptive text from the
.I string
into the \*r file, deleting the existing text.
.RS
.PP
The
.B \-t
option, in both its forms, has effect only during an initial checkin;
it is silently ignored otherwise.
.PP
During the initial checkin, if
.B \-t
is not given,
.B ci
obtains the text from standard input,
terminated by end-of-file or by a line containing
.BR \&. "\ by"
itself.
The user is prompted for the text if interaction is possible; see
.BR \-I .
.PP
For backward compatibility with older versions of \*r, a bare
.B \-t
option is ignored.
.RE
.TP
.B \-T
Set the \*r file's modification time to the new revision's time
if the former precedes the latter and there is a new revision;
preserve the \*r file's modification time otherwise.
If you have locked a revision,
.B ci
usually updates the \*r file's modification time to the current time,
because the lock is stored in the \*r file
and removing the lock requires changing the \*r file.
This can create an \*r file newer than the working file in one of two ways:
first,
.B "ci\ \-M"
can create a working file with a date before the current time;
second, when reverting to the previous revision
the \*r file can change while the working file remains unchanged.
These two cases can cause excessive recompilation caused by a
.BR make (1)
dependency of the working file on the \*r file.
The
.B \-T
option inhibits this recompilation by lying about the \*r file's date.
Use this option with care; it can suppress recompilation even when
a checkin of one working file should affect
another working file associated with the same \*r file.
For example, suppose the \*r file's time is 01:00,
the (changed) working file's time is 02:00,
some other copy of the working file has a time of 03:00,
and the current time is 04:00.
Then
.B "ci\ \-d\ \-T"
sets the \*r file's time to 02:00 instead of the usual 04:00;
this causes
.BR make (1)
to think (incorrectly) that the other copy is newer than the \*r file.
.TP
.BI \-w "login"
uses
.I login
for the author field of the deposited revision.
Useful for lying about the author, and for
.B \-k
if no author is available.
.TP
.BI \-V
Print \*r's version number.
.TP
.BI \-V n
Emulate \*r version
.IR n .
See
.BR co (1)
for details.
.TP
.BI \-x "suffixes"
specifies the suffixes for \*r files.
A nonempty suffix matches any pathname ending in the suffix.
An empty suffix matches any pathname of the form
.BI RCS/ path
or
.IB path1 /RCS/ path2.
The
.B \-x
option can specify a list of suffixes
separated by
.BR / .
For example,
.B \-x,v/
specifies two suffixes:
.B ,v
and the empty suffix.
If two or more suffixes are specified,
they are tried in order when looking for an \*r file;
the first one that works is used for that file.
If no \*r file is found but an \*r file can be created,
the suffixes are tried in order
to determine the new \*r file's name.
The default for
.IR suffixes
is installation-dependent; normally it is
.B ,v/
for hosts like Unix that permit commas in filenames,
and is empty (i.e. just the empty suffix) for other hosts.
.TP
.BI \-z zone
specifies the date output format in keyword substitution,
and specifies the default time zone for
.I date
in the
.BI \-d date
option.
The
.I zone
should be empty, a numeric \*u offset, or the special string
.B LT
for local time.
The default is an empty
.IR zone ,
which uses the traditional \*r format of \*u without any time zone indication
and with slashes separating the parts of the date;
otherwise, times are output in \*i 8601 format with time zone indication.
For example, if local time is January 11, 1990, 8pm Pacific Standard Time,
eight hours west of \*u,
then the time is output as follows:
.RS
.LP
.RS
.nf
.ta \w'\f3\-z+05:30\fP 'u +\w'\f31990-01-11 09:30:00+05:30\fP 'u
.ne 4
\f2option\fP \f2time output\fP
\f3\-z\fP \f31990/01/12 04:00:00\fP \f2(default)\fP
\f3\-zLT\fP \f31990-01-11 20:00:00\-08\fP
\f3\-z+05:30\fP \f31990-01-12 09:30:00+05:30\fP
.ta 4n +4n +4n +4n
.fi
.RE
.LP
The
.B \-z
option does not affect dates stored in \*r files,
which are always \*u.
.SH "FILE NAMING"
Pairs of \*r files and working files can be specified in three ways
(see also the
example section).
.PP
1) Both the \*r file and the working file are given. The \*r pathname is of
the form
.IB path1 / workfileX
and the working pathname is of the form
.IB path2 / workfile
where
.IB path1 /
and
.IB path2 /
are (possibly different or empty) paths,
.I workfile
is a filename, and
.I X
is an \*r suffix.
If
.I X
is empty,
.IB path1 /
must start with
.B RCS/
or must contain
.BR /RCS/ .
.PP
2) Only the \*r file is given. Then the working file is created in the current
directory and its name is derived from the name of the \*r file
by removing
.IB path1 /
and the suffix
.IR X .
.PP
3) Only the working file is given.
Then
.B ci
considers each \*r suffix
.I X
in turn, looking for an \*r file of the form
.IB path2 /RCS/ workfileX
or (if the former is not found and
.I X
is nonempty)
.IB path2 / workfileX.
.PP
If the \*r file is specified without a path in 1) and 2),
.B ci
looks for the \*r file first in the directory
.B ./RCS
and then in the current
directory.
.PP
.B ci
reports an error if an attempt to open an \*r file fails for an unusual reason,
even if the \*r file's pathname is just one of several possibilities.
For example, to suppress use of \*r commands in a directory
.IR d ,
create a regular file named
.IB d /RCS
so that casual attempts to use \*r commands in
.I d
fail because
.IB d /RCS
is not a directory.
.SH EXAMPLES
Suppose
.B ,v
is an \*r suffix and the current directory contains a subdirectory
.B RCS
with an \*r file
.BR io.c,v .
Then each of the following commands check in a copy of
.B io.c
into
.B RCS/io.c,v
as the latest revision, removing
.BR io.c .
.LP
.RS
.nf
.ft 3
ci io.c; ci RCS/io.c,v; ci io.c,v;
ci io.c RCS/io.c,v; ci io.c io.c,v;
ci RCS/io.c,v io.c; ci io.c,v io.c;
.ft
.fi
.RE
.PP
Suppose instead that the empty suffix
is an \*r suffix and the current directory contains a subdirectory
.B RCS
with an \*r file
.BR io.c .
The each of the following commands checks in a new revision.
.LP
.RS
.nf
.ft 3
ci io.c; ci RCS/io.c;
ci io.c RCS/io.c;
ci RCS/io.c io.c;
.ft
.fi
.RE
.SH "FILE MODES"
An \*r file created by
.B ci
inherits the read and execute permissions
from the working file. If the \*r file exists already,
.B ci
preserves its read and execute permissions.
.B ci
always turns off all write permissions of \*r files.
.SH FILES
Temporary files are created in the directory containing
the working file, and also in the temporary directory (see
.B \s-1TMPDIR\s0
under
.BR \s-1ENVIRONMENT\s0 ).
A semaphore file or files are created in the directory containing the \*r file.
With a nonempty suffix, the semaphore names begin with
the first character of the suffix; therefore, do not specify an suffix
whose first character could be that of a working filename.
With an empty suffix, the semaphore names end with
.B _
so working filenames should not end in
.BR _ .
.PP
.B ci
never changes an \*r or working file.
Normally,
.B ci
unlinks the file and creates a new one;
but instead of breaking a chain of one or more symbolic links to an \*r file,
it unlinks the destination file instead.
Therefore,
.B ci
breaks any hard or symbolic links to any working file it changes;
and hard links to \*r files are ineffective,
but symbolic links to \*r files are preserved.
.PP
The effective user must be able to
search and write the directory containing the \*r file.
Normally, the real user must be able to
read the \*r and working files
and to search and write the directory containing the working file;
however, some older hosts
cannot easily switch between real and effective users,
so on these hosts the effective user is used for all accesses.
The effective user is the same as the real user
unless your copies of
.B ci
and
.B co
have setuid privileges.
As described in the next section,
these privileges yield extra security if
the effective user owns all \*r files and directories,
and if only the effective user can write \*r directories.
.PP
Users can control access to \*r files by setting the permissions
of the directory containing the files; only users with write access
to the directory can use \*r commands to change its \*r files.
For example, in hosts that allow a user to belong to several groups,
one can make a group's \*r directories writable to that group only.
This approach suffices for informal projects,
but it means that any group member can arbitrarily change the group's \*r files,
and can even remove them entirely.
Hence more formal projects sometimes distinguish between an \*r administrator,
who can change the \*r files at will, and other project members,
who can check in new revisions but cannot otherwise change the \*r files.
.SH "SETUID USE"
To prevent anybody but their \*r administrator from deleting revisions,
a set of users can employ setuid privileges as follows.
.nr n \w'\(bu'+2n-1/1n
.ds n \nn
.if \n(.g .if r an-tag-sep .ds n \w'\(bu'u+\n[an-tag-sep]u
.IP \(bu \*n
Check that the host supports \*r setuid use.
Consult a trustworthy expert if there are any doubts.
It is best if the
.B seteuid
system call works as described in Posix 1003.1a Draft 5,
because \*r can switch back and forth easily
between real and effective users, even if the real user is
.BR root .
If not, the second best is if the
.B setuid
system call supports saved setuid
(the {\s-1_POSIX_SAVED_IDS\s0} behavior of Posix 1003.1-1990);
this fails only if the real or effective user is
.BR root .
If \*r detects any failure in setuid, it quits immediately.
.IP \(bu \nn
Choose a user
.I A
to serve as \*r administrator for the set of users.
Only
.I A
can invoke the
.B rcs
command on the users' \*r files.
.I A
should not be
.B root
or any other user with special powers.
Mutually suspicious sets of users should use different administrators.
.IP \(bu \nn
Choose a pathname
.I B
to be a directory of files to be executed by the users.
.IP \(bu \nn
Have
.I A
set up
.I B
to contain copies of
.B ci
and
.B co
that are setuid to
.I A
by copying the commands from their standard installation directory
.I D
as follows:
.LP
.RS
.nf
.ne 3
\f3mkdir\fP \f2B\fP
\f3cp\fP \f2D\fP\^\f3/c[io]\fP \f2B\fP
\f3chmod go\-w,u+s\fP \f2B\fP\f3/c[io]\fP
.fi
.RE
.IP \(bu \nn
Have each user prepend
.I B
to their path as follows:
.LP
.RS
.nf
.ne 2
\f3PATH=\fP\f2B\fP\f3:$PATH; export PATH\fP # ordinary shell
\f3set path=(\fP\f2B\fP \f3$path)\fP # C shell
.fi
.RE
.IP \(bu \nn
Have
.I A
create each \*r directory
.I R
with write access only to
.I A
as follows:
.LP
.RS
.nf
.ne 2
\f3mkdir\fP \f2R\fP
\f3chmod go\-w\fP \f2R\fP
.fi
.RE
.IP \(bu \nn
If you want to let only certain users read the \*r files,
put the users into a group
.IR G ,
and have
.I A
further protect the \*r directory as follows:
.LP
.RS
.nf
.ne 2
\f3chgrp\fP \f2G R\fP
\f3chmod g\-w,o\-rwx\fP \f2R\fP
.fi
.RE
.IP \(bu \nn
Have
.I A
copy old \*r files (if any) into
.IR R ,
to ensure that
.I A
owns them.
.IP \(bu \nn
An \*r file's access list limits who can check in and lock revisions.
The default access list is empty,
which grants checkin access to anyone who can read the \*r file.
If you want limit checkin access,
have
.I A
invoke
.B "rcs\ \-a"
on the file; see
.BR rcs (1).
In particular,
.BI "rcs\ \-e\ \-a" A
limits access to just
.IR A .
.IP \(bu \nn
Have
.I A
initialize any new \*r files with
.B "rcs\ \-i"
before initial checkin, adding the
.B \-a
option if you want to limit checkin access.
.IP \(bu \nn
Give setuid privileges only to
.BR ci ,
.BR co ,
and
.BR rcsclean ;
do not give them to
.B rcs
or to any other command.
.IP \(bu \nn
Do not use other setuid commands to invoke \*r commands;
setuid is trickier than you think!
.SH ENVIRONMENT
.TP
.B \s-1RCSINIT\s0
options prepended to the argument list, separated by spaces.
A backslash escapes spaces within an option.
The
.B \s-1RCSINIT\s0
options are prepended to the argument lists of most \*r commands.
Useful
.B \s-1RCSINIT\s0
options include
.BR \-q ,
.BR \-V ,
.BR \-x ,
and
.BR \-z .
.TP
.B \s-1TMPDIR\s0
Name of the temporary directory.
If not set, the environment variables
.B \s-1TMP\s0
and
.B \s-1TEMP\s0
are inspected instead and the first value found is taken;
if none of them are set,
a host-dependent default is used, typically
.BR /tmp .
.SH DIAGNOSTICS
For each revision,
.B ci
prints the \*r file, the working file, and the number
of both the deposited and the preceding revision.
The exit status is zero if and only if all operations were successful.
.SH IDENTIFICATION
Author: Walter F. Tichy.
.br
Manual Page Revision: \*(Rv; Release Date: \*(Dt.
.br
Copyright \(co 1982, 1988, 1989 Walter F. Tichy.
.br
Copyright \(co 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert.
.SH "SEE ALSO"
co(1),
ident(1), make(1), rcs(1), rcsclean(1), rcsdiff(1),
rcsintro(1), rcsmerge(1), rlog(1), setuid(2), rcsfile(5)
.br
Walter F. Tichy,
\*r\*-A System for Version Control,
.I "Software\*-Practice & Experience"
.BR 15 ,
7 (July 1985), 637-654.
.br

1318
gnu/usr.bin/rcs/ci/ci.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,8 @@
PROG= co
SRCS= co.c
CFLAGS+= -I${.CURDIR}/../lib
LDADD= ${LIBRCS}
DPADD= ${LIBRCS}
.include "../../Makefile.inc"
.include <bsd.prog.mk>

736
gnu/usr.bin/rcs/co/co.1 Normal file
View File

@ -0,0 +1,736 @@
.de Id
.ds Rv \\$3
.ds Dt \\$4
..
.Id $FreeBSD$
.ds i \&\s-1ISO\s0
.ds r \&\s-1RCS\s0
.ds u \&\s-1UTC\s0
.if n .ds - \%--
.if t .ds - \(em
.TH CO 1 \*(Dt GNU
.SH NAME
co \- check out RCS revisions
.SH SYNOPSIS
.B co
.RI [ options ] " file " .\|.\|.
.SH DESCRIPTION
.B co
retrieves a revision from each \*r file and stores it into
the corresponding working file.
.PP
Pathnames matching an \*r suffix denote \*r files;
all others denote working files.
Names are paired as explained in
.BR ci (1).
.PP
Revisions of an \*r file can be checked out locked or unlocked. Locking a
revision prevents overlapping updates. A revision checked out for reading or
processing (e.g., compiling) need not be locked. A revision checked out
for editing and later checkin must normally be locked. Checkout with locking
fails if the revision to be checked out is currently locked by another user.
(A lock can be broken with
.BR rcs "(1).)\ \&"
Checkout with locking also requires the caller to be on the access list of
the \*r file, unless he is the owner of the
file or the superuser, or the access list is empty.
Checkout without locking is not subject to accesslist restrictions, and is
not affected by the presence of locks.
.PP
A revision is selected by options for revision or branch number,
checkin date/time, author, or state.
When the selection options
are applied in combination,
.B co
retrieves the latest revision
that satisfies all of them.
If none of the selection options
is specified,
.B co
retrieves the latest revision
on the default branch (normally the trunk, see the
.B \-b
option of
.BR rcs (1)).
A revision or branch number can be attached
to any of the options
.BR \-f ,
.BR \-I ,
.BR \-l ,
.BR \-M ,
.BR \-p ,
.BR \-q ,
.BR \-r ,
or
.BR \-u .
The options
.B \-d
(date),
.B \-s
(state), and
.B \-w
(author)
retrieve from a single branch, the
.I selected
branch,
which is either specified by one of
.BR \-f ,
\&.\|.\|.,
.BR \-u ,
or the default branch.
.PP
A
.B co
command applied to an \*r
file with no revisions creates a zero-length working file.
.B co
always performs keyword substitution (see below).
.SH OPTIONS
.TP
.BR \-r [\f2rev\fP]
retrieves the latest revision whose number is less than or equal to
.IR rev .
If
.I rev
indicates a branch rather than a revision,
the latest revision on that branch is retrieved.
If
.I rev
is omitted, the latest revision on the default branch
(see the
.B \-b
option of
.BR rcs (1))
is retrieved.
If
.I rev
is
.BR $ ,
.B co
determines the revision number from keyword values in the working file.
Otherwise, a revision is composed of one or more numeric or symbolic fields
separated by periods.
If
.I rev
begins with a period,
then the default branch (normally the trunk) is prepended to it.
If
.I rev
is a branch number followed by a period,
then the latest revision on that branch is used.
The numeric equivalent of a symbolic field
is specified with the
.B \-n
option of the commands
.BR ci (1)
and
.BR rcs (1).
.TP
.BR \-l [\f2rev\fP]
same as
.BR \-r ,
except that it also locks the retrieved revision for
the caller.
.TP
.BR \-u [\f2rev\fP]
same as
.BR \-r ,
except that it unlocks the retrieved revision if it was
locked by the caller. If
.I rev
is omitted,
.B \-u
retrieves the revision locked by the caller, if there is one; otherwise,
it retrieves the latest revision on the default branch.
.TP
.BR \-f [\f2rev\fP]
forces the overwriting of the working file;
useful in connection with
.BR \-q .
See also
.SM "FILE MODES"
below.
.TP
.B \-kkv
Generate keyword strings using the default form, e.g.\&
.B "$\&Revision: \*(Rv $"
for the
.B Revision
keyword.
A locker's name is inserted in the value of the
.BR Header ,
.BR Id ,
and
.B Locker
keyword strings
only as a file is being locked,
i.e. by
.B "ci\ \-l"
and
.BR "co\ \-l".
This is the default.
.TP
.B \-kkvl
Like
.BR \-kkv ,
except that a locker's name is always inserted
if the given revision is currently locked.
.TP
.B \-kk
Generate only keyword names in keyword strings; omit their values.
See
.SM "KEYWORD SUBSTITUTION"
below.
For example, for the
.B Revision
keyword, generate the string
.B $\&Revision$
instead of
.BR "$\&Revision: \*(Rv $" .
This option is useful to ignore differences due to keyword substitution
when comparing different revisions of a file.
Log messages are inserted after
.B $\&Log$
keywords even if
.B \-kk
is specified,
since this tends to be more useful when merging changes.
.TP
.B \-ko
Generate the old keyword string,
present in the working file just before it was checked in.
For example, for the
.B Revision
keyword, generate the string
.B "$\&Revision: 1.1 $"
instead of
.B "$\&Revision: \*(Rv $"
if that is how the string appeared when the file was checked in.
This can be useful for file formats
that cannot tolerate any changes to substrings
that happen to take the form of keyword strings.
.TP
.B \-kb
Generate a binary image of the old keyword string.
This acts like
.BR \-ko ,
except it performs all working file input and output in binary mode.
This makes little difference on Posix and Unix hosts,
but on DOS-like hosts one should use
.B "rcs\ \-i\ \-kb"
to initialize an \*r file intended to be used for binary files.
Also, on all hosts,
.BR rcsmerge (1)
normally refuses to merge files when
.B \-kb
is in effect.
.TP
.B \-kv
Generate only keyword values for keyword strings.
For example, for the
.B Revision
keyword, generate the string
.B \*(Rv
instead of
.BR "$\&Revision: \*(Rv $" .
This can help generate files in programming languages where it is hard to
strip keyword delimiters like
.B "$\&Revision:\ $"
from a string.
However, further keyword substitution cannot be performed once the
keyword names are removed, so this option should be used with care.
Because of this danger of losing keywords,
this option cannot be combined with
.BR \-l ,
and the owner write permission of the working file is turned off;
to edit the file later, check it out again without
.BR \-kv .
.TP
.BR \-p [\f2rev\fP]
prints the retrieved revision on the standard output rather than storing it
in the working file.
This option is useful when
.B co
is part of a pipe.
.TP
.BR \-q [\f2rev\fP]
quiet mode; diagnostics are not printed.
.TP
.BR \-I [\f2rev\fP]
interactive mode;
the user is prompted and questioned
even if the standard input is not a terminal.
.TP
.BI \-d date
retrieves the latest revision on the selected branch whose checkin date/time is
less than or equal to
.IR date .
The date and time can be given in free format.
The time zone
.B LT
stands for local time;
other common time zone names are understood.
For example, the following
.IR date s
are equivalent
if local time is January 11, 1990, 8pm Pacific Standard Time,
eight hours west of Coordinated Universal Time (\*u):
.RS
.LP
.RS
.nf
.ta \w'\f3Thu, 11 Jan 1990 20:00:00 \-0800\fP 'u
.ne 10
\f38:00 pm lt\fP
\f34:00 AM, Jan. 12, 1990\fP default is \*u
\f31990-01-12 04:00:00+00\fP \*i 8601 (\*u)
\f31990-01-11 20:00:00\-08\fP \*i 8601 (local time)
\f31990/01/12 04:00:00\fP traditional \*r format
\f3Thu Jan 11 20:00:00 1990 LT\fP output of \f3ctime\fP(3) + \f3LT\fP
\f3Thu Jan 11 20:00:00 PST 1990\fP output of \f3date\fP(1)
\f3Fri Jan 12 04:00:00 GMT 1990\fP
\f3Thu, 11 Jan 1990 20:00:00 \-0800\fP Internet RFC 822
\f312-January-1990, 04:00 WET\fP
.ta 4n +4n +4n +4n
.fi
.RE
.LP
Most fields in the date and time can be defaulted.
The default time zone is normally \*u, but this can be overridden by the
.B \-z
option.
The other defaults are determined in the order year, month, day,
hour, minute, and second (most to least significant). At least one of these
fields must be provided. For omitted fields that are of higher significance
than the highest provided field, the time zone's current values are assumed.
For all other omitted fields,
the lowest possible values are assumed.
For example, without
.BR \-z ,
the date
.B "20, 10:30"
defaults to
10:30:00 \*u of the 20th of the \*u time zone's current month and year.
The date/time must be quoted if it contains spaces.
.RE
.TP
.BR \-M [\f2rev\fP]
Set the modification time on the new working file
to be the date of the retrieved revision.
Use this option with care; it can confuse
.BR make (1).
.TP
.BI \-s state
retrieves the latest revision on the selected branch whose state is set to
.IR state .
.TP
.B \-T
Preserve the modification time on the \*r file
even if the \*r file changes because a lock is added or removed.
This option can suppress extensive recompilation caused by a
.BR make (1)
dependency of some other copy of the working file on the \*r file.
Use this option with care; it can suppress recompilation even when it is needed,
i.e. when the change of lock
would mean a change to keyword strings in the other working file.
.TP
.BR \-w [\f2login\fP]
retrieves the latest revision on the selected branch which was checked in
by the user with login name
.IR login .
If the argument
.I login
is
omitted, the caller's login is assumed.
.TP
.BI \-j joinlist
generates a new revision which is the join of the revisions on
.IR joinlist .
This option is largely obsoleted by
.BR rcsmerge (1)
but is retained for backwards compatibility.
.RS
.PP
The
.I joinlist
is a comma-separated list of pairs of the form
.IB rev2 : rev3,
where
.I rev2
and
.I rev3
are (symbolic or numeric)
revision numbers.
For the initial such pair,
.I rev1
denotes the revision selected
by the above options
.BR \-f ,
\&.\|.\|.,
.BR \-w .
For all other pairs,
.I rev1
denotes the revision generated by the previous pair.
(Thus, the output
of one join becomes the input to the next.)
.PP
For each pair,
.B co
joins revisions
.I rev1
and
.I rev3
with respect to
.IR rev2 .
This means that all changes that transform
.I rev2
into
.I rev1
are applied to a copy of
.IR rev3 .
This is particularly useful if
.I rev1
and
.I rev3
are the ends of two branches that have
.I rev2
as a common ancestor. If
.IR rev1 < rev2 < rev3
on the same branch,
joining generates a new revision which is like
.I rev3,
but with all changes that lead from
.I rev1
to
.I rev2
undone.
If changes from
.I rev2
to
.I rev1
overlap with changes from
.I rev2
to
.I rev3,
.B co
reports overlaps as described in
.BR merge (1).
.PP
For the initial pair,
.I rev2
can be omitted. The default is the common
ancestor.
If any of the arguments indicate branches, the latest revisions
on those branches are assumed.
The options
.B \-l
and
.B \-u
lock or unlock
.IR rev1 .
.RE
.TP
.BI \-V
Print \*r's version number.
.TP
.BI \-V n
Emulate \*r version
.I n,
where
.I n
can be
.BR 3 ,
.BR 4 ,
or
.BR 5 .
This can be useful when interchanging \*r files with others who are
running older versions of \*r.
To see which version of \*r your correspondents are running, have them invoke
.BR "rcs \-V" ;
this works with newer versions of \*r.
If it doesn't work, have them invoke
.B rlog
on an \*r file;
if none of the first few lines of output contain the string
.B branch:
it is version 3;
if the dates' years have just two digits, it is version 4;
otherwise, it is version 5.
An \*r file generated while emulating version 3 loses its default branch.
An \*r revision generated while emulating version 4 or earlier has
a time stamp that is off by up to 13 hours.
A revision extracted while emulating version 4 or earlier contains
abbreviated dates of the form
.IB yy / mm / dd
and can also contain different white space and line prefixes
in the substitution for
.BR $\&Log$ .
.TP
.BI \-x "suffixes"
Use
.I suffixes
to characterize \*r files.
See
.BR ci (1)
for details.
.TP
.BI \-z zone
specifies the date output format in keyword substitution,
and specifies the default time zone for
.I date
in the
.BI \-d date
option.
The
.I zone
should be empty, a numeric \*u offset, or the special string
.B LT
for local time.
The default is an empty
.IR zone ,
which uses the traditional \*r format of \*u without any time zone indication
and with slashes separating the parts of the date;
otherwise, times are output in \*i 8601 format with time zone indication.
For example, if local time is January 11, 1990, 8pm Pacific Standard Time,
eight hours west of \*u,
then the time is output as follows:
.RS
.LP
.RS
.nf
.ta \w'\f3\-z+05:30\fP 'u +\w'\f31990-01-11 09:30:00+05:30\fP 'u
.ne 4
\f2option\fP \f2time output\fP
\f3\-z\fP \f31990/01/12 04:00:00\fP \f2(default)\fP
\f3\-zLT\fP \f31990-01-11 20:00:00\-08\fP
\f3\-z+05:30\fP \f31990-01-12 09:30:00+05:30\fP
.ta 4n +4n +4n +4n
.fi
.RE
.LP
The
.B \-z
option does not affect dates stored in \*r files,
which are always \*u.
.RE
.SH "KEYWORD SUBSTITUTION"
Strings of the form
.BI $ keyword $
and
.BI $ keyword : .\|.\|. $
embedded in
the text are replaced
with strings of the form
.BI $ keyword : value $
where
.I keyword
and
.I value
are pairs listed below.
Keywords can be embedded in literal strings
or comments to identify a revision.
.PP
Initially, the user enters strings of the form
.BI $ keyword $ .
On checkout,
.B co
replaces these strings with strings of the form
.BI $ keyword : value $ .
If a revision containing strings of the latter form
is checked back in, the value fields will be replaced during the next
checkout.
Thus, the keyword values are automatically updated on checkout.
This automatic substitution can be modified by the
.B \-k
options.
.PP
Keywords and their corresponding values:
.TP
.B $\&Author$
The login name of the user who checked in the revision.
.TP
.B $\&Date$
The date and time the revision was checked in.
With
.BI \-z zone
a numeric time zone offset is appended; otherwise, the date is \*u.
.TP
.B $\&Header$
A standard header containing the full pathname of the \*r file, the
revision number, the date and time, the author, the state,
and the locker (if locked).
With
.BI \-z zone
a numeric time zone offset is appended to the date; otherwise, the date is \*u.
.TP
.B $\&Id$
Same as
.BR $\&Header$ ,
except that the \*r filename is without a path.
.TP
.B $\&Locker$
The login name of the user who locked the revision (empty if not locked).
.TP
.B $\&Log$
The log message supplied during checkin, preceded by a header
containing the \*r filename, the revision number, the author, and the date
and time.
With
.BI \-z zone
a numeric time zone offset is appended; otherwise, the date is \*u.
Existing log messages are
.I not
replaced.
Instead, the new log message is inserted after
.BR $\&Log: .\|.\|. $ .
This is useful for
accumulating a complete change log in a source file.
.RS
.LP
Each inserted line is prefixed by the string that prefixes the
.B $\&Log$
line. For example, if the
.B $\&Log$
line is
.RB \*(lq "//\ $\&Log: tan.cc\ $" \*(rq,
\*r prefixes each line of the log with
.RB \*(lq "//\ " \*(rq.
This is useful for languages with comments that go to the end of the line.
The convention for other languages is to use a
.RB \*(lq " \(** " \(rq
prefix inside a multiline comment.
For example, the initial log comment of a C program
conventionally is of the following form:
.RS
.LP
.nf
.ft 3
.ne 3
/\(**
.in +\w'/'u
\(** $\&Log$
\(**/
.in
.ft
.fi
.RE
.LP
For backwards compatibility with older versions of \*r, if the log prefix is
.B /\(**
or
.B (\(**
surrounded by optional white space, inserted log lines contain a space
instead of
.B /
or
.BR ( ;
however, this usage is obsolescent and should not be relied on.
.RE
.TP
.B $\&Name$
The symbolic name used to check out the revision, if any.
For example,
.B "co\ \-rJoe"
generates
.BR "$\&Name:\ Joe\ $" .
Plain
.B co
generates just
.BR "$\&Name:\ \ $" .
.TP
.B $\&RCSfile$
The name of the \*r file without a path.
.TP
.B $\&Revision$
The revision number assigned to the revision.
.TP
.B $\&Source$
The full pathname of the \*r file.
.TP
.B $\&State$
The state assigned to the revision with the
.B \-s
option of
.BR rcs (1)
or
.BR ci (1).
.PP
The following characters in keyword values are represented by escape sequences
to keep keyword strings well-formed.
.LP
.RS
.nf
.ne 6
.ta \w'newline 'u
\f2char escape sequence\fP
tab \f3\et\fP
newline \f3\en\fP
space \f3\e040
$ \e044
\e \e\e\fP
.fi
.RE
.SH "FILE MODES"
The working file inherits the read and execute permissions from the \*r
file. In addition, the owner write permission is turned on, unless
.B \-kv
is set or the file
is checked out unlocked and locking is set to strict (see
.BR rcs (1)).
.PP
If a file with the name of the working file exists already and has write
permission,
.B co
aborts the checkout,
asking beforehand if possible.
If the existing working file is
not writable or
.B \-f
is given, the working file is deleted without asking.
.SH FILES
.B co
accesses files much as
.BR ci (1)
does, except that it does not need to read the working file
unless a revision number of
.B $
is specified.
.SH ENVIRONMENT
.TP
.B \s-1RCSINIT\s0
options prepended to the argument list, separated by spaces.
See
.BR ci (1)
for details.
.SH DIAGNOSTICS
The \*r pathname, the working pathname,
and the revision number retrieved are
written to the diagnostic output.
The exit status is zero if and only if all operations were successful.
.SH IDENTIFICATION
Author: Walter F. Tichy.
.br
Manual Page Revision: \*(Rv; Release Date: \*(Dt.
.br
Copyright \(co 1982, 1988, 1989 Walter F. Tichy.
.br
Copyright \(co 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert.
.SH "SEE ALSO"
rcsintro(1), ci(1), ctime(3), date(1), ident(1), make(1),
rcs(1), rcsclean(1), rcsdiff(1), rcsmerge(1), rlog(1),
rcsfile(5)
.br
Walter F. Tichy,
\*r\*-A System for Version Control,
.I "Software\*-Practice & Experience"
.BR 15 ,
7 (July 1985), 637-654.
.SH LIMITS
Links to the \*r and working files are not preserved.
.PP
There is no way to selectively suppress the expansion of keywords, except
by writing them differently. In nroff and troff, this is done by embedding the
null-character
.B \e&
into the keyword.
.br

826
gnu/usr.bin/rcs/co/co.c Normal file
View File

@ -0,0 +1,826 @@
/* Check out working files from revisions of RCS files. */
/* Copyright 1982, 1988, 1989 Walter Tichy
Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert
Distributed under license by the Free Software Foundation, Inc.
This file is part of RCS.
RCS 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.
RCS 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 RCS; see the file COPYING.
If not, write to the Free Software Foundation,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
Report problems and direct all questions to:
rcs-bugs@cs.purdue.edu
*/
/*
* Revision 5.18 1995/06/16 06:19:24 eggert
* Update FSF address.
*
* Revision 5.17 1995/06/01 16:23:43 eggert
* (main, preparejoin): Pass argument instead of using `join' static variable.
* (main): Add -kb.
*
* Revision 5.16 1994/03/17 14:05:48 eggert
* Move buffer-flushes out of critical sections, since they aren't critical.
* Use ORCSerror to clean up after a fatal error. Remove lint.
* Specify subprocess input via file descriptor, not file name.
*
* Revision 5.15 1993/11/09 17:40:15 eggert
* -V now prints version on stdout and exits. Don't print usage twice.
*
* Revision 5.14 1993/11/03 17:42:27 eggert
* Add -z. Generate a value for the Name keyword.
* Don't arbitrarily limit the number of joins.
* Improve quality of diagnostics.
*
* Revision 5.13 1992/07/28 16:12:44 eggert
* Add -V. Check that working and RCS files are distinct.
*
* Revision 5.12 1992/02/17 23:02:08 eggert
* Add -T.
*
* Revision 5.11 1992/01/24 18:44:19 eggert
* Add support for bad_creat0. lint -> RCS_lint
*
* Revision 5.10 1992/01/06 02:42:34 eggert
* Update usage string.
*
* Revision 5.9 1991/10/07 17:32:46 eggert
* -k affects just working file, not RCS file.
*
* Revision 5.8 1991/08/19 03:13:55 eggert
* Warn before removing somebody else's file.
* Add -M. Fix co -j bugs. Tune.
*
* Revision 5.7 1991/04/21 11:58:15 eggert
* Ensure that working file is newer than RCS file after co -[lu].
* Add -x, RCSINIT, MS-DOS support.
*
* Revision 5.6 1990/12/04 05:18:38 eggert
* Don't checkaccesslist() unless necessary.
* Use -I for prompts and -q for diagnostics.
*
* Revision 5.5 1990/11/01 05:03:26 eggert
* Fix -j. Add -I.
*
* Revision 5.4 1990/10/04 06:30:11 eggert
* Accumulate exit status across files.
*
* Revision 5.3 1990/09/11 02:41:09 eggert
* co -kv yields a readonly working file.
*
* Revision 5.2 1990/09/04 08:02:13 eggert
* Standardize yes-or-no procedure.
*
* Revision 5.0 1990/08/22 08:10:02 eggert
* Permit multiple locks by same user. Add setuid support.
* Remove compile-time limits; use malloc instead.
* Permit dates past 1999/12/31. Switch to GMT.
* Make lock and temp files faster and safer.
* Ansify and Posixate. Add -k, -V. Remove snooping. Tune.
*
* Revision 4.7 89/05/01 15:11:41 narten
* changed copyright header to reflect current distribution rules
*
* Revision 4.6 88/08/09 19:12:15 eggert
* Fix "co -d" core dump; rawdate wasn't always initialized.
* Use execv(), not system(); fix putchar('\0') and diagnose() botches; remove lint
*
* Revision 4.5 87/12/18 11:35:40 narten
* lint cleanups (from Guy Harris)
*
* Revision 4.4 87/10/18 10:20:53 narten
* Updating version numbers changes relative to 1.1, are actually
* relative to 4.2
*
* Revision 1.3 87/09/24 13:58:30 narten
* Sources now pass through lint (if you ignore printf/sprintf/fprintf
* warnings)
*
* Revision 1.2 87/03/27 14:21:38 jenkins
* Port to suns
*
* Revision 4.2 83/12/05 13:39:48 wft
* made rewriteflag external.
*
* Revision 4.1 83/05/10 16:52:55 wft
* Added option -u and -f.
* Added handling of default branch.
* Replaced getpwuid() with getcaller().
* Removed calls to stat(); now done by pairfilenames().
* Changed and renamed rmoldfile() to rmworkfile().
* Replaced catchints() calls with restoreints(), unlink()--link() with rename();
*
* Revision 3.7 83/02/15 15:27:07 wft
* Added call to fastcopy() to copy remainder of RCS file.
*
* Revision 3.6 83/01/15 14:37:50 wft
* Added ignoring of interrupts while RCS file is renamed; this avoids
* deletion of RCS files during the unlink/link window.
*
* Revision 3.5 82/12/08 21:40:11 wft
* changed processing of -d to use DATEFORM; removed actual from
* call to preparejoin; re-fixed printing of done at the end.
*
* Revision 3.4 82/12/04 18:40:00 wft
* Replaced getdelta() with gettree(), SNOOPDIR with SNOOPFILE.
* Fixed printing of "done".
*
* Revision 3.3 82/11/28 22:23:11 wft
* Replaced getlogin() with getpwuid(), flcose() with ffclose(),
* %02d with %.2d, mode generation for working file with WORKMODE.
* Fixed nil printing. Fixed -j combined with -l and -p, and exit
* for non-existing revisions in preparejoin().
*
* Revision 3.2 82/10/18 20:47:21 wft
* Mode of working file is now maintained even for co -l, but write permission
* is removed.
* The working file inherits its mode from the RCS file, plus write permission
* for the owner. The write permission is not given if locking is strict and
* co does not lock.
* An existing working file without write permission is deleted automatically.
* Otherwise, co asks (empty answer: abort co).
* Call to getfullRCSname() added, check for write error added, call
* for getlogin() fixed.
*
* Revision 3.1 82/10/13 16:01:30 wft
* fixed type of variables receiving from getc() (char -> int).
* removed unused variables.
*/
#include "rcsbase.h"
static char *addjoin P((char*));
static char const *getancestor P((char const*,char const*));
static int buildjoin P((char const*));
static int preparejoin P((char*));
static int rmlock P((struct hshentry const*));
static int rmworkfile P((void));
static void cleanup P((void));
static char const quietarg[] = "-q";
static char const *expandarg, *suffixarg, *versionarg, *zonearg;
static char const **joinlist; /* revisions to be joined */
static int joinlength;
static FILE *neworkptr;
static int exitstatus;
static int forceflag;
static int lastjoin; /* index of last element in joinlist */
static int lockflag; /* -1 -> unlock, 0 -> do nothing, 1 -> lock */
static int mtimeflag;
static struct hshentries *gendeltas; /* deltas to be generated */
static struct hshentry *targetdelta; /* final delta to be generated */
static struct stat workstat;
mainProg(coId, "co", "$FreeBSD$")
{
static char const cmdusage[] =
"\nco usage: co -{fIlMpqru}[rev] -ddate -jjoins -ksubst -sstate -T -w[who] -Vn -xsuff -zzone file ...";
char *a, *joinflag, **newargv;
char const *author, *date, *rev, *state;
char const *joinname, *newdate, *neworkname;
int changelock; /* 1 if a lock has been changed, -1 if error */
int expmode, r, tostdout, workstatstat;
int Ttimeflag;
struct buf numericrev; /* expanded revision number */
char finaldate[datesize];
# if OPEN_O_BINARY
int stdout_mode = 0;
# endif
setrid();
author = date = rev = state = 0;
joinflag = 0;
bufautobegin(&numericrev);
expmode = -1;
suffixes = X_DEFAULT;
tostdout = false;
Ttimeflag = false;
argc = getRCSINIT(argc, argv, &newargv);
argv = newargv;
while (a = *++argv, 0<--argc && *a++=='-') {
switch (*a++) {
case 'r':
revno:
if (*a) {
if (rev) warn("redefinition of revision number");
rev = a;
}
break;
case 'f':
forceflag=true;
goto revno;
case 'l':
if (lockflag < 0) {
warn("-u overridden by -l.");
}
lockflag = 1;
goto revno;
case 'u':
if (0 < lockflag) {
warn("-l overridden by -u.");
}
lockflag = -1;
goto revno;
case 'p':
tostdout = true;
goto revno;
case 'I':
interactiveflag = true;
goto revno;
case 'q':
quietflag=true;
goto revno;
case 'd':
if (date)
redefined('d');
str2date(a, finaldate);
date=finaldate;
break;
case 'j':
if (*a) {
if (joinflag) redefined('j');
joinflag = a;
}
break;
case 'M':
mtimeflag = true;
goto revno;
case 's':
if (*a) {
if (state) redefined('s');
state = a;
}
break;
case 'T':
if (*a)
goto unknown;
Ttimeflag = true;
break;
case 'w':
if (author) redefined('w');
if (*a)
author = a;
else
author = getcaller();
break;
case 'x':
suffixarg = *argv;
suffixes = a;
break;
case 'V':
versionarg = *argv;
setRCSversion(versionarg);
break;
case 'z':
zonearg = *argv;
zone_set(a);
break;
case 'k': /* set keyword expand mode */
expandarg = *argv;
if (0 <= expmode) redefined('k');
if (0 <= (expmode = str2expmode(a)))
break;
/* fall into */
default:
unknown:
error("unknown option: %s%s", *argv, cmdusage);
};
} /* end of option processing */
/* Now handle all pathnames. */
if (nerror) cleanup();
else if (argc < 1) faterror("no input file%s", cmdusage);
else for (; 0 < argc; cleanup(), ++argv, --argc) {
ffree();
if (pairnames(argc, argv, lockflag?rcswriteopen:rcsreadopen, true, false) <= 0)
continue;
/*
* RCSname contains the name of the RCS file, and finptr
* points at it. workname contains the name of the working file.
* Also, RCSstat has been set.
*/
diagnose("%s --> %s\n", RCSname, tostdout?"standard output":workname);
workstatstat = -1;
if (tostdout) {
# if OPEN_O_BINARY
int newmode = Expand==BINARY_EXPAND ? OPEN_O_BINARY : 0;
if (stdout_mode != newmode) {
stdout_mode = newmode;
oflush();
VOID setmode(STDOUT_FILENO, newmode);
}
# endif
neworkname = 0;
neworkptr = workstdout = stdout;
} else {
workstatstat = stat(workname, &workstat);
if (workstatstat == 0 && same_file(RCSstat, workstat, 0)) {
rcserror("RCS file is the same as working file %s.",
workname
);
continue;
}
neworkname = makedirtemp(1);
if (!(neworkptr = fopenSafer(neworkname, FOPEN_W_WORK))) {
if (errno == EACCES)
workerror("permission denied on parent directory");
else
eerror(neworkname);
continue;
}
}
gettree(); /* reads in the delta tree */
if (!Head) {
/* no revisions; create empty file */
diagnose("no revisions present; generating empty revision 0.0\n");
if (lockflag)
warn(
"no revisions, so nothing can be %slocked",
lockflag < 0 ? "un" : ""
);
Ozclose(&fcopy);
if (workstatstat == 0)
if (!rmworkfile()) continue;
changelock = 0;
newdate = 0;
} else {
int locks = lockflag ? findlock(false, &targetdelta) : 0;
if (rev) {
/* expand symbolic revision number */
if (!expandsym(rev, &numericrev))
continue;
} else {
switch (locks) {
default:
continue;
case 0:
bufscpy(&numericrev, Dbranch?Dbranch:"");
break;
case 1:
bufscpy(&numericrev, targetdelta->num);
break;
}
}
/* get numbers of deltas to be generated */
if (!(targetdelta=genrevs(numericrev.string,date,author,state,&gendeltas)))
continue;
/* check reservations */
changelock =
lockflag < 0 ?
rmlock(targetdelta)
: lockflag == 0 ?
0
:
addlock(targetdelta, true);
if (
changelock < 0
|| (changelock && !checkaccesslist())
|| dorewrite(lockflag, changelock) != 0
)
continue;
if (0 <= expmode)
Expand = expmode;
if (0 < lockflag && Expand == VAL_EXPAND) {
rcserror("cannot combine -kv and -l");
continue;
}
if (joinflag && !preparejoin(joinflag))
continue;
diagnose("revision %s%s\n",targetdelta->num,
0<lockflag ? " (locked)" :
lockflag<0 ? " (unlocked)" : "");
/* Prepare to remove old working file if necessary. */
if (workstatstat == 0)
if (!rmworkfile()) continue;
/* skip description */
getdesc(false); /* don't echo*/
locker_expansion = 0 < lockflag;
targetdelta->name = namedrev(rev, targetdelta);
joinname = buildrevision(
gendeltas, targetdelta,
joinflag&&tostdout ? (FILE*)0 : neworkptr,
Expand < MIN_UNEXPAND
);
# if !large_memory
if (fcopy == neworkptr)
fcopy = 0; /* Don't close it twice. */
# endif
if_advise_access(changelock && gendeltas->first!=targetdelta,
finptr, MADV_SEQUENTIAL
);
if (donerewrite(changelock,
Ttimeflag ? RCSstat.st_mtime : (time_t)-1
) != 0)
continue;
if (changelock) {
locks += lockflag;
if (1 < locks)
rcswarn("You now have %d locks.", locks);
}
newdate = targetdelta->date;
if (joinflag) {
newdate = 0;
if (!joinname) {
aflush(neworkptr);
joinname = neworkname;
}
if (Expand == BINARY_EXPAND)
workerror("merging binary files");
if (!buildjoin(joinname))
continue;
}
}
if (!tostdout) {
mode_t m = WORKMODE(RCSstat.st_mode,
! (Expand==VAL_EXPAND || (lockflag<=0 && StrictLocks))
);
time_t t = mtimeflag&&newdate ? date2time(newdate) : (time_t)-1;
aflush(neworkptr);
ignoreints();
r = chnamemod(&neworkptr, neworkname, workname, 1, m, t);
keepdirtemp(neworkname);
restoreints();
if (r != 0) {
eerror(workname);
error("see %s", neworkname);
continue;
}
diagnose("done\n");
}
}
tempunlink();
Ofclose(workstdout);
exitmain(exitstatus);
} /* end of main (co) */
static void
cleanup()
{
if (nerror) exitstatus = EXIT_FAILURE;
Izclose(&finptr);
ORCSclose();
# if !large_memory
if (fcopy!=workstdout) Ozclose(&fcopy);
# endif
if (neworkptr!=workstdout) Ozclose(&neworkptr);
dirtempunlink();
}
#if RCS_lint
# define exiterr coExit
#endif
void
exiterr()
{
ORCSerror();
dirtempunlink();
tempunlink();
_exit(EXIT_FAILURE);
}
/*****************************************************************
* The following routines are auxiliary routines
*****************************************************************/
static int
rmworkfile()
/*
* Prepare to remove workname, if it exists, and if
* it is read-only.
* Otherwise (file writable):
* if !quietmode asks the user whether to really delete it (default: fail);
* otherwise failure.
* Returns true if permission is gotten.
*/
{
if (workstat.st_mode&(S_IWUSR|S_IWGRP|S_IWOTH) && !forceflag) {
/* File is writable */
if (!yesorno(false, "writable %s exists%s; remove it? [ny](n): ",
workname,
myself(workstat.st_uid) ? "" : ", and you do not own it"
)) {
error(!quietflag && ttystdin()
? "checkout aborted"
: "writable %s exists; checkout aborted", workname);
return false;
}
}
/* Actual unlink is done later by caller. */
return true;
}
static int
rmlock(delta)
struct hshentry const *delta;
/* Function: removes the lock held by caller on delta.
* Returns -1 if someone else holds the lock,
* 0 if there is no lock on delta,
* and 1 if a lock was found and removed.
*/
{ register struct rcslock * next, * trail;
char const *num;
struct rcslock dummy;
int whomatch, nummatch;
num=delta->num;
dummy.nextlock=next=Locks;
trail = &dummy;
while (next) {
whomatch = strcmp(getcaller(), next->login);
nummatch=strcmp(num,next->delta->num);
if ((whomatch==0) && (nummatch==0)) break;
/*found a lock on delta by caller*/
if ((whomatch!=0)&&(nummatch==0)) {
rcserror("revision %s locked by %s; use co -r or rcs -u",
num, next->login
);
return -1;
}
trail=next;
next=next->nextlock;
}
if (next) {
/*found one; delete it */
trail->nextlock=next->nextlock;
Locks=dummy.nextlock;
next->delta->lockedby = 0;
return 1; /*success*/
} else return 0; /*no lock on delta*/
}
/*****************************************************************
* The rest of the routines are for handling joins
*****************************************************************/
static char *
addjoin(joinrev)
char *joinrev;
/* Add joinrev's number to joinlist, yielding address of char past joinrev,
* or 0 if no such revision exists.
*/
{
register char *j;
register struct hshentry *d;
char terminator;
struct buf numrev;
struct hshentries *joindeltas;
j = joinrev;
for (;;) {
switch (*j++) {
default:
continue;
case 0:
case ' ': case '\t': case '\n':
case ':': case ',': case ';':
break;
}
break;
}
terminator = *--j;
*j = 0;
bufautobegin(&numrev);
d = 0;
if (expandsym(joinrev, &numrev))
d = genrevs(numrev.string,(char*)0,(char*)0,(char*)0,&joindeltas);
bufautoend(&numrev);
*j = terminator;
if (d) {
joinlist[++lastjoin] = d->num;
return j;
}
return 0;
}
static int
preparejoin(j)
register char *j;
/* Parse join list J and place pointers to the
* revision numbers into joinlist.
*/
{
lastjoin= -1;
for (;;) {
while ((*j==' ')||(*j=='\t')||(*j==',')) j++;
if (*j=='\0') break;
if (lastjoin>=joinlength-2) {
joinlist =
(joinlength *= 2) == 0
? tnalloc(char const *, joinlength = 16)
: trealloc(char const *, joinlist, joinlength);
}
if (!(j = addjoin(j))) return false;
while ((*j==' ') || (*j=='\t')) j++;
if (*j == ':') {
j++;
while((*j==' ') || (*j=='\t')) j++;
if (*j!='\0') {
if (!(j = addjoin(j))) return false;
} else {
rcsfaterror("join pair incomplete");
}
} else {
if (lastjoin==0) { /* first pair */
/* common ancestor missing */
joinlist[1]=joinlist[0];
lastjoin=1;
/*derive common ancestor*/
if (!(joinlist[0] = getancestor(targetdelta->num,joinlist[1])))
return false;
} else {
rcsfaterror("join pair incomplete");
}
}
}
if (lastjoin < 1)
rcsfaterror("empty join");
return true;
}
static char const *
getancestor(r1, r2)
char const *r1, *r2;
/* Yield the common ancestor of r1 and r2 if successful, 0 otherwise.
* Work reliably only if r1 and r2 are not branch numbers.
*/
{
static struct buf t1, t2;
int l1, l2, l3;
char const *r;
l1 = countnumflds(r1);
l2 = countnumflds(r2);
if ((2<l1 || 2<l2) && cmpnum(r1,r2)!=0) {
/* not on main trunk or identical */
l3 = 0;
while (cmpnumfld(r1, r2, l3+1)==0 && cmpnumfld(r1, r2, l3+2)==0)
l3 += 2;
/* This will terminate since r1 and r2 are not the same; see above. */
if (l3==0) {
/* no common prefix; common ancestor on main trunk */
VOID partialno(&t1, r1, l1>2 ? 2 : l1);
VOID partialno(&t2, r2, l2>2 ? 2 : l2);
r = cmpnum(t1.string,t2.string)<0 ? t1.string : t2.string;
if (cmpnum(r,r1)!=0 && cmpnum(r,r2)!=0)
return r;
} else if (cmpnumfld(r1, r2, l3+1)!=0)
return partialno(&t1,r1,l3);
}
rcserror("common ancestor of %s and %s undefined", r1, r2);
return 0;
}
static int
buildjoin(initialfile)
char const *initialfile;
/* Function: merge pairs of elements in joinlist into initialfile
* If workstdout is set, copy result to stdout.
* All unlinking of initialfile, rev2, and rev3 should be done by tempunlink().
*/
{
struct buf commarg;
struct buf subs;
char const *rev2, *rev3;
int i;
char const *cov[10], *mergev[11];
char const **p;
bufautobegin(&commarg);
bufautobegin(&subs);
rev2 = maketemp(0);
rev3 = maketemp(3); /* buildrevision() may use 1 and 2 */
cov[1] = CO;
/* cov[2] setup below */
p = &cov[3];
if (expandarg) *p++ = expandarg;
if (suffixarg) *p++ = suffixarg;
if (versionarg) *p++ = versionarg;
if (zonearg) *p++ = zonearg;
*p++ = quietarg;
*p++ = RCSname;
*p = 0;
mergev[1] = MERGE;
mergev[2] = mergev[4] = "-L";
/* rest of mergev setup below */
i=0;
while (i<lastjoin) {
/*prepare marker for merge*/
if (i==0)
bufscpy(&subs, targetdelta->num);
else {
bufscat(&subs, ",");
bufscat(&subs, joinlist[i-2]);
bufscat(&subs, ":");
bufscat(&subs, joinlist[i-1]);
}
diagnose("revision %s\n",joinlist[i]);
bufscpy(&commarg, "-p");
bufscat(&commarg, joinlist[i]);
cov[2] = commarg.string;
if (runv(-1, rev2, cov))
goto badmerge;
diagnose("revision %s\n",joinlist[i+1]);
bufscpy(&commarg, "-p");
bufscat(&commarg, joinlist[i+1]);
cov[2] = commarg.string;
if (runv(-1, rev3, cov))
goto badmerge;
diagnose("merging...\n");
mergev[3] = subs.string;
mergev[5] = joinlist[i+1];
p = &mergev[6];
if (quietflag) *p++ = quietarg;
if (lastjoin<=i+2 && workstdout) *p++ = "-p";
*p++ = initialfile;
*p++ = rev2;
*p++ = rev3;
*p = 0;
switch (runv(-1, (char*)0, mergev)) {
case DIFF_FAILURE: case DIFF_SUCCESS:
break;
default:
goto badmerge;
}
i=i+2;
}
bufautoend(&commarg);
bufautoend(&subs);
return true;
badmerge:
nerror++;
bufautoend(&commarg);
bufautoend(&subs);
return false;
}

1518
gnu/usr.bin/rcs/doc/rcs.ms Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,95 @@
.SH
Functions of RCS (Revision Control System)
.PP
RCS manages software libraries. It greatly increases programmer productivity
by providing the following functions.
.IP 1.
RCS stores and retrieves multiple revisions of program and other text.
Thus, one can maintain one or more releases while developing the next
release, with a minimum of space overhead. Changes no longer destroy the
original -- previous revisions remain accessible.
.RS
.IP a.
Maintains each module as a tree of revisions.
.IP b.
Project libraries can
be organized centrally, decentralized, or any way you like.
.IP c.
RCS works for any type of text: programs, documentation, memos, papers,
graphics, VLSI layouts, form letters, etc.
.RE
.IP 2.
RCS maintains a complete history of changes.
Thus, one can find out what happened to a module easily
and quickly, without having to compare source listings or
having to track down colleagues.
.RS
.IP a.
RCS performs automatic record keeping.
.IP b.
RCS logs all changes automatically.
.IP c.
RCS guarantees project continuity.
.RE
.IP 3.
RCS manages multiple lines of development.
.IP 4.
RCS can merge multiple lines of development.
Thus, when several parallel lines of development must be consolidated
into one line, the merging of changes is automatic.
.IP 5.
RCS flags coding conflicts.
If two or more lines of development modify the same section of code,
RCS can alert programmers about overlapping changes.
.IP 6.
RCS resolves access conflicts.
When two or more programmers wish to modify the same revision,
RCS alerts the programmers and makes sure that one modification won't wipe
out the other one.
.IP 7.
RCS provides high-level retrieval functions.
Revisions can be retrieved according to ranges of revision numbers,
symbolic names, dates, authors, and states.
.IP 8.
RCS provides release and configuration control.
Revisions can be marked as released, stable, experimental, etc.
Configurations of modules can be described simply and directly.
.IP 9.
RCS performs automatic identification of modules with name, revision
number, creation time, author, etc.
Thus, it is always possible to determine which revisions of which
modules make up a given configuration.
.IP 10.
Provides high-level management visibility.
Thus, it is easy to track the status of a software project.
.RS
.IP a.
RCS provides a complete change history.
.IP b.
RCS records who did what when to which revision of which module.
.RE
.IP 11.
RCS is fully compatible with existing software development tools.
RCS is unobtrusive -- its interface to the file system is such that
all your existing software tools can be used as before.
.IP 12.
RCS' basic user interface is extremely simple. The novice need to learn
only two commands. Its more sophisticated features have been
tuned towards advanced software development environments and the
experienced software professional.
.IP 13.
RCS simplifies software distribution if customers
maintain sources with RCS also. This technique assures proper
identification of versions and configurations, and tracking of customer
modifications. Customer modifications can be merged into distributed
versions locally or by the development group.
.IP 14.
RCS needs little extra space for the revisions (only the differences).
If intermediate revisions are deleted, the corresponding
differences are compressed into the shortest possible form.
.IP 15.
RCS is implemented with reverse deltas. This means that
the latest revision, which is the one that is accessed most often,
is stored intact. All others are regenerated from the latest one
by applying reverse deltas (backward differences). This
results in fast access time for the revision needed most often.

View File

@ -0,0 +1,8 @@
PROG= ident
SRCS= ident.c
CFLAGS+= -I${.CURDIR}/../lib
LDADD= ${LIBRCS}
DPADD= ${LIBRCS}
.include "../../Makefile.inc"
.include <bsd.prog.mk>

View File

@ -0,0 +1,182 @@
.de Id
.ds Rv \\$3
.ds Dt \\$4
.ds iD \\$3 \\$4 \\$5 \\$6 \\$7
..
.Id $FreeBSD$
.ds r \&\s-1RCS\s0
.ds u \&\s-1UTC\s0
.if n .ds - \%--
.if t .ds - \(em
.TH IDENT 1 \*(Dt GNU
.SH NAME
ident \- identify RCS keyword strings in files
.SH SYNOPSIS
.B ident
[
.B \-q
] [
.B \-V
] [
.I file
\&.\|.\|. ]
.SH DESCRIPTION
.B ident
searches for all instances of the pattern
.BI $ keyword : "\ text\ " $
in the named files or, if no files are named, the standard input.
.PP
These patterns are normally inserted automatically by the \*r command
.BR co (1),
but can also be inserted manually.
The option
.B \-q
suppresses
the warning given if there are no patterns in a file.
The option
.B \-V
prints
.BR ident 's
version number.
.PP
.B ident
works on text files as well as object files and dumps.
For example, if the C program in
.B f.c
contains
.IP
.ft 3
#include <stdio.h>
.br
static char const rcsid[] =
.br
\&"$\&Id: f.c,v \*(iD $\&";
.br
int main() { return printf(\&"%s\en\&", rcsid) == EOF; }
.ft P
.LP
and
.B f.c
is compiled into
.BR f.o ,
then the command
.IP
.B "ident f.c f.o"
.LP
will output
.nf
.IP
.ft 3
f.c:
$\&Id: f.c,v \*(iD $
f.o:
$\&Id: f.c,v \*(iD $
.ft
.fi
.PP
If a C program defines a string like
.B rcsid
above but does not use it,
.BR lint (1)
may complain, and some C compilers will optimize away the string.
The most reliable solution is to have the program use the
.B rcsid
string, as shown in the example above.
.PP
.B ident
finds all instances of the
.BI $ keyword : "\ text\ " $
pattern, even if
.I keyword
is not actually an \*r-supported keyword.
This gives you information about nonstandard keywords like
.BR $\&XConsortium$ .
.SH KEYWORDS
Here is the list of keywords currently maintained by
.BR co (1).
All times are given in Coordinated Universal Time (\*u,
sometimes called \&\s-1GMT\s0) by default, but if the files
were checked out with
.BR co 's
.BI \-z zone
option, times are given with a numeric time zone indication appended.
.TP
.B $\&Author$
The login name of the user who checked in the revision.
.TP
.B $\&Date$
The date and time the revision was checked in.
.TP
.B $\&Header$
A standard header containing the full pathname of the \*r file, the
revision number, the date and time, the author, the state,
and the locker (if locked).
.TP
.B $\&Id$
Same as
.BR $\&Header$ ,
except that the \*r filename is without a path.
.TP
.B $\&Locker$
The login name of the user who locked the revision (empty if not locked).
.TP
.B $\&Log$
The log message supplied during checkin.
For
.BR ident 's
purposes, this is equivalent to
.BR $\&RCSfile$ .
.TP
.B $\&Name$
The symbolic name used to check out the revision, if any.
.TP
.B $\&RCSfile$
The name of the \*r file without a path.
.TP
.B $\&Revision$
The revision number assigned to the revision.
.TP
.B $\&Source$
The full pathname of the \*r file.
.TP
.B $\&State$
The state assigned to the revision with the
.B \-s
option of
.BR rcs (1)
or
.BR ci (1).
.PP
.BR co (1)
represents the following characters in keyword values by escape sequences
to keep keyword strings well-formed.
.LP
.RS
.nf
.ne 6
.ta \w'newline 'u
\f2char escape sequence\fP
tab \f3\et\fP
newline \f3\en\fP
space \f3\e040
$ \e044
\e \e\e\fP
.fi
.RE
.SH IDENTIFICATION
Author: Walter F. Tichy.
.br
Manual Page Revision: \*(Rv; Release Date: \*(Dt.
.br
Copyright \(co 1982, 1988, 1989 Walter F. Tichy.
.br
Copyright \(co 1990, 1992, 1993 Paul Eggert.
.SH "SEE ALSO"
ci(1), co(1), rcs(1), rcsdiff(1), rcsintro(1), rcsmerge(1), rlog(1),
rcsfile(5)
.br
Walter F. Tichy,
\*r\*-A System for Version Control,
.I "Software\*-Practice & Experience"
.BR 15 ,
7 (July 1985), 637-654.

View File

@ -0,0 +1,270 @@
/* Identify RCS keyword strings in files. */
/* Copyright 1982, 1988, 1989 Walter Tichy
Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert
Distributed under license by the Free Software Foundation, Inc.
This file is part of RCS.
RCS 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.
RCS 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 RCS; see the file COPYING.
If not, write to the Free Software Foundation,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
Report problems and direct all questions to:
rcs-bugs@cs.purdue.edu
*/
/*
* Revision 5.9 1995/06/16 06:19:24 eggert
* Update FSF address.
*
* Revision 5.8 1995/06/01 16:23:43 eggert
* (exiterr, reportError): New functions, needed for DOS and OS/2 ports.
* (scanfile): Use them.
*
* Revision 5.7 1994/03/20 04:52:58 eggert
* Remove `exiting' from identExit.
*
* Revision 5.6 1993/11/09 17:40:15 eggert
* Add -V.
*
* Revision 5.5 1993/11/03 17:42:27 eggert
* Test for char == EOF, not char < 0.
*
* Revision 5.4 1992/01/24 18:44:19 eggert
* lint -> RCS_lint
*
* Revision 5.3 1991/09/10 22:15:46 eggert
* Open files with FOPEN_R, not FOPEN_R_WORK,
* because they might be executables, not working files.
*
* Revision 5.2 1991/08/19 03:13:55 eggert
* Report read errors immediately.
*
* Revision 5.1 1991/02/25 07:12:37 eggert
* Don't report empty keywords. Check for I/O errors.
*
* Revision 5.0 1990/08/22 08:12:37 eggert
* Don't limit output to known keywords.
* Remove arbitrary limits and lint. Ansify and Posixate.
*
* Revision 4.5 89/05/01 15:11:54 narten
* changed copyright header to reflect current distribution rules
*
* Revision 4.4 87/10/23 17:09:57 narten
* added exit(0) so exit return code would be non random
*
* Revision 4.3 87/10/18 10:23:55 narten
* Updating version numbers. Changes relative to 1.1 are actually relative
* to 4.1
*
* Revision 1.3 87/07/09 09:20:52 trinkle
* Added check to make sure there is at least one arg before comparing argv[1]
* with "-q". This necessary on machines that don't allow dereferncing null
* pointers (i.e. Suns).
*
* Revision 1.2 87/03/27 14:21:47 jenkins
* Port to suns
*
* Revision 4.1 83/05/10 16:31:02 wft
* Added option -q and input from reading stdin.
* Marker matching is now done with trymatch() (independent of keywords).
*
* Revision 3.4 83/02/18 17:37:49 wft
* removed printing of new line after last file.
*
* Revision 3.3 82/12/04 12:48:55 wft
* Added LOCKER.
*
* Revision 3.2 82/11/28 18:24:17 wft
* removed Suffix; added ungetc to avoid skipping over trailing KDELIM.
*
* Revision 3.1 82/10/13 15:58:51 wft
* fixed type of variables receiving from getc() (char-->int).
*/
#include "rcsbase.h"
static int match P((FILE*));
static int scanfile P((FILE*,char const*,int));
static void reportError P((char const*));
mainProg(identId, "ident", "$FreeBSD$")
/* Ident searches the named files for all occurrences
* of the pattern $@: text $ where @ is a keyword.
*/
{
FILE *fp;
int quiet = 0;
int status = EXIT_SUCCESS;
char const *a;
while ((a = *++argv) && *a=='-')
while (*++a)
switch (*a) {
case 'q':
quiet = 1;
break;
case 'V':
VOID printf("RCS version %s\n", RCS_version_string);
quiet = -1;
break;
default:
VOID fprintf(stderr,
"ident: usage: ident -{qV} [file...]\n"
);
exitmain(EXIT_FAILURE);
break;
}
if (0 <= quiet)
if (!a)
VOID scanfile(stdin, (char*)0, quiet);
else
do {
if (!(fp = fopen(a, FOPEN_RB))) {
reportError(a);
status = EXIT_FAILURE;
} else if (
scanfile(fp, a, quiet) != 0
|| (argv[1] && putchar('\n') == EOF)
)
break;
} while ((a = *++argv));
if (ferror(stdout) || fclose(stdout)!=0) {
reportError("standard output");
status = EXIT_FAILURE;
}
exitmain(status);
}
#if RCS_lint
# define exiterr identExit
#endif
void
exiterr()
{
_exit(EXIT_FAILURE);
}
static void
reportError(s)
char const *s;
{
int e = errno;
VOID fprintf(stderr, "%s error: ", cmdid);
errno = e;
perror(s);
}
static int
scanfile(file, name, quiet)
register FILE *file;
char const *name;
int quiet;
/* Function: scan an open file with descriptor file for keywords.
* Return -1 if there's a write error; exit immediately on a read error.
*/
{
register int c;
if (name) {
VOID printf("%s:\n", name);
if (ferror(stdout))
return -1;
} else
name = "standard input";
c = 0;
while (c != EOF || ! (feof(file)|ferror(file))) {
if (c == KDELIM) {
if ((c = match(file)))
continue;
if (ferror(stdout))
return -1;
quiet = true;
}
c = getc(file);
}
if (ferror(file) || fclose(file) != 0) {
reportError(name);
/*
* The following is equivalent to exit(EXIT_FAILURE), but we invoke
* exiterr to keep lint happy. The DOS and OS/2 ports need exiterr.
*/
VOID fflush(stderr);
VOID fflush(stdout);
exiterr();
}
if (!quiet)
VOID fprintf(stderr, "%s warning: no id keywords in %s\n", cmdid, name);
return 0;
}
static int
match(fp) /* group substring between two KDELIM's; then do pattern match */
register FILE *fp;
{
char line[BUFSIZ];
register int c;
register char * tp;
tp = line;
while ((c = getc(fp)) != VDELIM) {
if (c == EOF && feof(fp) | ferror(fp))
return c;
switch (ctab[c]) {
case LETTER: case Letter: case DIGIT:
*tp++ = c;
if (tp < line+sizeof(line)-4)
break;
/* fall into */
default:
return c ? c : '\n'/* anything but 0 or KDELIM or EOF */;
}
}
if (tp == line)
return c;
*tp++ = c;
if ((c = getc(fp)) != ' ')
return c ? c : '\n';
*tp++ = c;
while( (c = getc(fp)) != KDELIM ) {
if (c == EOF && feof(fp) | ferror(fp))
return c;
switch (ctab[c]) {
default:
*tp++ = c;
if (tp < line+sizeof(line)-2)
break;
/* fall into */
case NEWLN: case UNKN:
return c ? c : '\n';
}
}
if (tp[-1] != ' ')
return c;
*tp++ = c; /*append trailing KDELIM*/
*tp = '\0';
VOID printf(" %c%s\n", KDELIM, line);
return 0;
}

View File

@ -0,0 +1,14 @@
# $FreeBSD$
# Define FSYNC_ALL to get slower but safer writes in case of crashes in
# the middle of CVS/RCS changes
#CFLAGS += -DFSYNC_ALL
LIB = rcs
SRCS = maketime.c partime.c rcsedit.c rcsfcmp.c rcsfnms.c rcsgen.c \
rcskeep.c rcskeys.c rcslex.c rcsmap.c rcsrev.c rcssyn.c rcstime.c \
rcsutil.c merger.c version.c
INTERNALLIB=
.include <bsd.lib.mk>

400
gnu/usr.bin/rcs/lib/conf.h Normal file
View File

@ -0,0 +1,400 @@
/* RCS compile-time configuration */
/* $FreeBSD$ */
/*
* This file is generated automatically.
* If you edit it by hand your changes may be lost.
* Instead, please try to fix conf.sh,
* and send your fixes to rcs-bugs@cs.purdue.edu.
*/
#define exitmain(n) return n /* how to exit from main() */
/* #define _POSIX_C_SOURCE 2147483647L */ /* if strict C + Posix 1003.1b-1993 or later */
/* #define _POSIX_SOURCE */ /* if strict C + Posix 1003.1-1990 */
#include <errno.h>
#include <stdio.h>
#include <time.h>
/* Comment out #include lines below that do not work. */
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <fcntl.h>
#include <limits.h>
/* #include <mach/mach.h> */
/* #include <net/errno.h> */
#include <pwd.h>
/* #include <siginfo.h> */
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/wait.h>
/* #include <ucontext.h> */
#include <unistd.h>
#include <utime.h>
/* #include <vfork.h> */
/* Define boolean symbols to be 0 (false, the default), or 1 (true). */
#define has_sys_param_h 1 /* Does #include <sys/param.h> work? */
/* extern int errno; */ /* Uncomment if <errno.h> doesn't declare errno. */
#define has_readlink 1 /* Does readlink() work? */
#define readlink_isreg_errno EINVAL /* errno after readlink on regular file */
#if has_readlink && !defined(MAXSYMLINKS)
# if has_sys_param_h
# include <sys/param.h>
# endif
# ifndef MAXSYMLINKS
# define MAXSYMLINKS 20 /* BSD; not standard yet */
# endif
#endif
/* Comment out the typedefs below if the types are already declared. */
/* Fix any uncommented typedefs that are wrong. */
/* typedef int mode_t; */
/* typedef long off_t; */
/* typedef int pid_t; */
/* typedef int sig_atomic_t; */
/* typedef unsigned size_t; */
/* typedef int ssize_t; */
/* typedef long time_t; */
/* typedef int uid_t; */
/* Comment out the keyword definitions below if the keywords work. */
/* #define const */
/* #define volatile */
/* Define boolean symbols to be 0 (false, the default), or 1 (true). */
#define has_prototypes 1 /* Do function prototypes work? */
#define has_stdarg 1 /* Does <stdarg.h> work? */
/* #define has_varargs ? */ /* Does <varargs.h> work? */
#define va_start_args 2 /* How many args does va_start() take? */
#if O_BINARY
/* Text and binary i/o behave differently. */
/* This is incompatible with Posix and Unix. */
# define FOPEN_RB "rb"
# define FOPEN_R_WORK (Expand==BINARY_EXPAND ? "r" : "rb")
# define FOPEN_WB "wb"
# define FOPEN_W_WORK (Expand==BINARY_EXPAND ? "w" : "wb")
# define FOPEN_WPLUS_WORK (Expand==BINARY_EXPAND ? "w+" : "w+b")
# define OPEN_O_BINARY O_BINARY
#else
/*
* Text and binary i/o behave the same.
* Omit "b", since some nonstandard hosts reject it.
*/
# define FOPEN_RB "r"
# define FOPEN_R_WORK "r"
# define FOPEN_WB "w"
# define FOPEN_W_WORK "w"
# define FOPEN_WPLUS_WORK "w+"
# define OPEN_O_BINARY 0
#endif
/* This may need changing on non-Unix systems (notably DOS). */
#define OPEN_CREAT_READONLY (S_IRUSR|S_IRGRP|S_IROTH) /* lock file mode */
#define OPEN_O_LOCK 0 /* extra open flags for creating lock file */
#define OPEN_O_WRONLY O_WRONLY /* main open flag for creating a lock file */
/* Define or comment out the following symbols as needed. */
#if has_prototypes
# define P(params) params
#else
# define P(params) ()
#endif
#if has_stdarg
# include <stdarg.h>
#else
# if has_varargs
# include <varargs.h>
# else
typedef char *va_list;
# define va_dcl int va_alist;
# define va_start(ap) ((ap) = (va_list)&va_alist)
# define va_arg(ap,t) (((t*) ((ap)+=sizeof(t))) [-1])
# define va_end(ap)
# endif
#endif
#if va_start_args == 2
# define vararg_start va_start
#else
# define vararg_start(ap,p) va_start(ap)
#endif
#define bad_chmod_close 0 /* Can chmod() close file descriptors? */
#define bad_creat0 0 /* Do writes fail after creat(f,0)? */
#define bad_fopen_wplus 0 /* Does fopen(f,"w+") fail to truncate f? */
#define getlogin_is_secure 0 /* Is getlogin() secure? Usually it's not. */
#define has_attribute_noreturn 1 /* Does __attribute__((noreturn)) work? */
#if has_attribute_noreturn
# define exiting __attribute__((noreturn))
#else
# define exiting
#endif
#define has_dirent 1 /* Do opendir(), readdir(), closedir() work? */
#define void_closedir 0 /* Does closedir() yield void? */
#define has_fchmod 1 /* Does fchmod() work? */
#define has_fflush_input 0 /* Does fflush() work on input files? */
#define has_fputs 1 /* Does fputs() work? */
#define has_ftruncate 1 /* Does ftruncate() work? */
#define has_getuid 1 /* Does getuid() work? */
#define has_getpwuid 1 /* Does getpwuid() work? */
#define has_memcmp 1 /* Does memcmp() work? */
#define has_memcpy 1 /* Does memcpy() work? */
#define has_memmove 1 /* Does memmove() work? */
#define has_map_fd 0 /* Does map_fd() work? */
#define has_mmap 1 /* Does mmap() work on regular files? */
#define has_madvise 0 /* Does madvise() work? */
#define mmap_signal SIGBUS /* signal received if you reference nonexistent part of mmapped file */
#define has_rename 1 /* Does rename() work? */
#define bad_a_rename 0 /* Does rename(A,B) fail if A is unwritable? */
#define bad_b_rename 0 /* Does rename(A,B) fail if B is unwritable? */
#define bad_NFS_rename 0 /* Can rename(A,B) falsely report success? */
/* typedef int void; */ /* Some ancient compilers need this. */
#define VOID (void) /* 'VOID e;' discards the value of an expression 'e'. */
#define has_seteuid 1 /* Does seteuid() work? See ../INSTALL.RCS. */
#define has_setreuid 0 /* Does setreuid() work? See ../INSTALL.RCS. */
#define has_setuid 1 /* Does setuid() exist? */
#define has_sigaction 1 /* Does struct sigaction work? */
#define has_sa_sigaction 1 /* Does struct sigaction have sa_sigaction? */
#define has_signal 1 /* Does signal() work? */
#define signal_type void /* type returned by signal handlers */
#define sig_zaps_handler 0 /* Must a signal handler reinvoke signal()? */
/* #define has_sigblock ? */ /* Does sigblock() work? */
/* #define sigmask(s) (1 << ((s)-1)) */ /* Yield mask for signal number. */
typedef size_t fread_type; /* type returned by fread() and fwrite() */
typedef size_t freadarg_type; /* type of their size arguments */
typedef void *malloc_type; /* type returned by malloc() */
#define has_getcwd 1 /* Does getcwd() work? */
/* #define has_getwd ? */ /* Does getwd() work? */
#define needs_getabsname 0 /* Must we define getabsname? */
#define has_mktemp 1 /* Does mktemp() work? */
#define has_mkstemp 1 /* Does mkstemp() work? */
#define has_NFS 1 /* Might NFS be used? */
#define has_psiginfo 0 /* Does psiginfo() work? */
#define has_psignal 1 /* Does psignal() work? */
/* #define has_si_errno ? */ /* Does siginfo_t have si_errno? */
/* #define has_sys_siglist ? */ /* Does sys_siglist[] work? */
/* #define strchr index */ /* Use old-fashioned name for strchr()? */
/* #define strrchr rindex */ /* Use old-fashioned name for strrchr()? */
#define bad_unlink 0 /* Does unlink() fail on unwritable files? */
#define has_vfork 1 /* Does vfork() work? */
#define has_fork 1 /* Does fork() work? */
#define has_spawn 0 /* Does spawn*() work? */
#define has_waitpid 1 /* Does waitpid() work? */
#define bad_wait_if_SIGCHLD_ignored 0 /* Does ignoring SIGCHLD break wait()? */
#define RCS_SHELL "/bin/sh" /* shell to run RCS subprograms */
#define has_printf_dot 1 /* Does "%.2d" print leading 0? */
#define has_vfprintf 1 /* Does vfprintf() work? */
#define has_attribute_format_printf 1 /* Does __attribute__((format(printf,N,N+1))) work? */
#if has_attribute_format_printf
# define printf_string(m, n) __attribute__((format(printf, m, n)))
#else
# define printf_string(m, n)
#endif
#if has_attribute_format_printf && has_attribute_noreturn
/* Work around a bug in GCC 2.5.x. */
# define printf_string_exiting(m, n) __attribute__((format(printf, m, n), noreturn))
#else
# define printf_string_exiting(m, n) printf_string(m, n) exiting
#endif
/* #define has__doprintf ? */ /* Does _doprintf() work? */
/* #define has__doprnt ? */ /* Does _doprnt() work? */
/* #undef EXIT_FAILURE */ /* Uncomment this if EXIT_FAILURE is broken. */
#define large_memory 1 /* Can main memory hold entire RCS files? */
#ifndef LONG_MAX
#define LONG_MAX 2147483647L /* long maximum */
#endif
/* Do struct stat s and t describe the same file? Answer d if unknown. */
#define same_file(s,t,d) ((s).st_ino==(t).st_ino && (s).st_dev==(t).st_dev)
#define has_utimbuf 1 /* Does struct utimbuf work? */
#define CO "/usr/bin/co" /* name of 'co' program */
#define COMPAT2 0 /* Are version 2 files supported? */
#define DIFF "/usr/bin/diff" /* name of 'diff' program */
#define DIFF3 "/usr/bin/diff3" /* name of 'diff3' program */
#define DIFF3_BIN 1 /* Is diff3 user-visible (not the /usr/lib auxiliary)? */
#define DIFFFLAGS "-an" /* Make diff output suitable for RCS. */
#define DIFF_L 1 /* Does diff -L work? */
#define DIFF_SUCCESS 0 /* DIFF status if no differences are found */
#define DIFF_FAILURE 1 /* DIFF status if differences are found */
#define DIFF_TROUBLE 2 /* DIFF status if trouble */
#define ED "/bin/ed" /* name of 'ed' program (used only if !DIFF3_BIN) */
#define MERGE "/usr/bin/merge" /* name of 'merge' program */
#define TMPDIR "/tmp" /* default directory for temporary files */
#define SLASH '/' /* principal filename separator */
#define SLASHes '/' /* `case SLASHes:' labels all filename separators */
#define isSLASH(c) ((c) == SLASH) /* Is arg a filename separator? */
#define ROOTPATH(p) isSLASH((p)[0]) /* Is p an absolute pathname? */
#define X_DEFAULT ",v/" /* default value for -x option */
#define SLASHSLASH_is_SLASH 1 /* Are // and / the same directory? */
#define ALL_ABSOLUTE 1 /* Do all subprograms satisfy ROOTPATH? */
#define DIFF_ABSOLUTE 1 /* Is ROOTPATH(DIFF) true? */
#define SENDMAIL "/usr/sbin/sendmail" /* how to send mail */
#define TZ_must_be_set 0 /* Must TZ be set for gmtime() to work? */
/* Adjust the following declarations as needed. */
/* The rest is for the benefit of non-standard, traditional hosts. */
/* Don't bother to declare functions that in traditional hosts do not appear, */
/* or are declared in .h files, or return int or void. */
/* traditional BSD */
#if has_sys_siglist && !defined(sys_siglist)
extern char const * const sys_siglist[];
#endif
/* Posix (ISO/IEC 9945-1: 1990 / IEEE Std 1003.1-1990) */
/* <fcntl.h> */
#ifdef O_CREAT
# define open_can_creat 1
#else
# define open_can_creat 0
# define O_RDONLY 0
# define O_WRONLY 1
# define O_RDWR 2
# define O_CREAT 01000
# define O_TRUNC 02000
#endif
#ifndef O_EXCL
#define O_EXCL 0
#endif
/* <sys/stat.h> */
#ifndef S_IRUSR
# ifdef S_IREAD
# define S_IRUSR S_IREAD
# else
# define S_IRUSR 0400
# endif
# ifdef S_IWRITE
# define S_IWUSR S_IWRITE
# else
# define S_IWUSR (S_IRUSR/2)
# endif
#endif
#ifndef S_IRGRP
# if has_getuid
# define S_IRGRP (S_IRUSR / 0010)
# define S_IWGRP (S_IWUSR / 0010)
# define S_IROTH (S_IRUSR / 0100)
# define S_IWOTH (S_IWUSR / 0100)
# else
/* single user OS -- not Posix or Unix */
# define S_IRGRP 0
# define S_IWGRP 0
# define S_IROTH 0
# define S_IWOTH 0
# endif
#endif
#ifndef S_ISREG
#define S_ISREG(n) (((n) & S_IFMT) == S_IFREG)
#endif
/* <sys/wait.h> */
#ifndef WEXITSTATUS
#define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
#undef WIFEXITED /* Avoid 4.3BSD incompatibility with Posix. */
#endif
#ifndef WIFEXITED
#define WIFEXITED(stat_val) (((stat_val) & 0377) == 0)
#endif
#ifndef WTERMSIG
#define WTERMSIG(stat_val) ((stat_val) & 0177)
#undef WIFSIGNALED /* Avoid 4.3BSD incompatibility with Posix. */
#endif
#ifndef WIFSIGNALED
#define WIFSIGNALED(stat_val) ((unsigned)(stat_val) - 1 < 0377)
#endif
/* <unistd.h> */
char *getlogin P((void));
#ifndef STDIN_FILENO
# define STDIN_FILENO 0
# define STDOUT_FILENO 1
# define STDERR_FILENO 2
#endif
#if has_fork && !has_vfork
# undef vfork
# define vfork fork
#endif
#if has_getcwd || !has_getwd
char *getcwd P((char*,size_t));
#else
char *getwd P((char*));
#endif
#if has_setuid && !has_seteuid
# undef seteuid
# define seteuid setuid
#endif
#if has_spawn
# if ALL_ABSOLUTE
# define spawn_RCS spawnv
# else
# define spawn_RCS spawnvp
# endif
#else
# if ALL_ABSOLUTE
# define exec_RCS execv
# else
# define exec_RCS execvp
# endif
#endif
/* utime.h */
#if !has_utimbuf
struct utimbuf { time_t actime, modtime; };
#endif
/* Standard C library */
/* <stdio.h> */
#ifndef L_tmpnam
#define L_tmpnam 32 /* power of 2 > sizeof("/usr/tmp/xxxxxxxxxxxxxxx") */
#endif
#ifndef SEEK_SET
#define SEEK_SET 0
#endif
#ifndef SEEK_CUR
#define SEEK_CUR 1
#endif
#if has_mktemp
char *mktemp P((char*)); /* traditional */
#else
char *tmpnam P((char*));
#endif
/* <stdlib.h> */
char *getenv P((char const*));
void _exit P((int)) exiting;
void exit P((int)) exiting;
malloc_type malloc P((size_t));
malloc_type realloc P((malloc_type,size_t));
#ifndef EXIT_FAILURE
#define EXIT_FAILURE 1
#endif
#ifndef EXIT_SUCCESS
#define EXIT_SUCCESS 0
#endif
/* <string.h> */
char *strcpy P((char*,char const*));
char *strchr P((char const*,int));
char *strrchr P((char const*,int));
void *memcpy P((void*,void const*,size_t));
#if has_memmove
void *memmove P((void*,void const*,size_t));
#endif
/* <time.h> */
time_t time P((time_t*));

View File

@ -0,0 +1,344 @@
/* Convert struct partime into time_t. */
/* Copyright 1992, 1993, 1994, 1995 Paul Eggert
Distributed under license by the Free Software Foundation, Inc.
This file is part of RCS.
RCS 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.
RCS 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 RCS; see the file COPYING.
If not, write to the Free Software Foundation,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
Report problems and direct all questions to:
rcs-bugs@cs.purdue.edu
*/
#if has_conf_h
# include "conf.h"
#else
# ifdef __STDC__
# define P(x) x
# else
# define const
# define P(x) ()
# endif
# include <stdlib.h>
# include <time.h>
#endif
#include "partime.h"
#include "maketime.h"
char const maketId[]
= "$FreeBSD$";
static int isleap P((int));
static int month_days P((struct tm const*));
static time_t maketime P((struct partime const*,time_t));
/*
* For maximum portability, use only localtime and gmtime.
* Make no assumptions about the time_t epoch or the range of time_t values.
* Avoid mktime because it's not universal and because there's no easy,
* portable way for mktime to yield the inverse of gmtime.
*/
#define TM_YEAR_ORIGIN 1900
static int
isleap(y)
int y;
{
return (y&3) == 0 && (y%100 != 0 || y%400 == 0);
}
static int const month_yday[] = {
/* days in year before start of months 0-12 */
0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365
};
/* Yield the number of days in TM's month. */
static int
month_days(tm)
struct tm const *tm;
{
int m = tm->tm_mon;
return month_yday[m+1] - month_yday[m]
+ (m==1 && isleap(tm->tm_year + TM_YEAR_ORIGIN));
}
/*
* Convert UNIXTIME to struct tm form.
* Use gmtime if available and if !LOCALZONE, localtime otherwise.
*/
struct tm *
time2tm(unixtime, localzone)
time_t unixtime;
int localzone;
{
struct tm *tm;
# if TZ_must_be_set
static char const *TZ;
if (!TZ && !(TZ = getenv("TZ")))
faterror("The TZ environment variable is not set; please set it to your timezone");
# endif
if (localzone || !(tm = gmtime(&unixtime)))
tm = localtime(&unixtime);
return tm;
}
/* Yield A - B, measured in seconds. */
time_t
difftm(a, b)
struct tm const *a, *b;
{
int ay = a->tm_year + (TM_YEAR_ORIGIN - 1);
int by = b->tm_year + (TM_YEAR_ORIGIN - 1);
int difference_in_day_of_year = a->tm_yday - b->tm_yday;
int intervening_leap_days = (
((ay >> 2) - (by >> 2))
- (ay/100 - by/100)
+ ((ay/100 >> 2) - (by/100 >> 2))
);
time_t difference_in_years = ay - by;
time_t difference_in_days = (
difference_in_years*365
+ (intervening_leap_days + difference_in_day_of_year)
);
return
(
(
24*difference_in_days
+ (a->tm_hour - b->tm_hour)
)*60 + (a->tm_min - b->tm_min)
)*60 + (a->tm_sec - b->tm_sec);
}
/*
* Adjust time T by adding SECONDS. SECONDS must be at most 24 hours' worth.
* Adjust only T's year, mon, mday, hour, min and sec members;
* plus adjust wday if it is defined.
*/
void
adjzone(t, seconds)
register struct tm *t;
long seconds;
{
/*
* This code can be off by a second if SECONDS is not a multiple of 60,
* if T is local time, and if a leap second happens during this minute.
* But this bug has never occurred, and most likely will not ever occur.
* Liberia, the last country for which SECONDS % 60 was nonzero,
* switched to UTC in May 1972; the first leap second was in June 1972.
*/
int leap_second = t->tm_sec == 60;
long sec = seconds + (t->tm_sec - leap_second);
if (sec < 0) {
if ((t->tm_min -= (59-sec)/60) < 0) {
if ((t->tm_hour -= (59-t->tm_min)/60) < 0) {
t->tm_hour += 24;
if (TM_DEFINED(t->tm_wday) && --t->tm_wday < 0)
t->tm_wday = 6;
if (--t->tm_mday <= 0) {
if (--t->tm_mon < 0) {
--t->tm_year;
t->tm_mon = 11;
}
t->tm_mday = month_days(t);
}
}
t->tm_min += 24 * 60;
}
sec += 24L * 60 * 60;
} else
if (60 <= (t->tm_min += sec/60))
if (24 <= (t->tm_hour += t->tm_min/60)) {
t->tm_hour -= 24;
if (TM_DEFINED(t->tm_wday) && ++t->tm_wday == 7)
t->tm_wday = 0;
if (month_days(t) < ++t->tm_mday) {
if (11 < ++t->tm_mon) {
++t->tm_year;
t->tm_mon = 0;
}
t->tm_mday = 1;
}
}
t->tm_min %= 60;
t->tm_sec = (int) (sec%60) + leap_second;
}
/*
* Convert TM to time_t, using localtime if LOCALZONE and gmtime otherwise.
* Use only TM's year, mon, mday, hour, min, and sec members.
* Ignore TM's old tm_yday and tm_wday, but fill in their correct values.
* Yield -1 on failure (e.g. a member out of range).
* Posix 1003.1-1990 doesn't allow leap seconds, but some implementations
* have them anyway, so allow them if localtime/gmtime does.
*/
time_t
tm2time(tm, localzone)
struct tm *tm;
int localzone;
{
/* Cache the most recent t,tm pairs; 1 for gmtime, 1 for localtime. */
static time_t t_cache[2];
static struct tm tm_cache[2];
time_t d, gt;
struct tm const *gtm;
/*
* The maximum number of iterations should be enough to handle any
* combinations of leap seconds, time zone rule changes, and solar time.
* 4 is probably enough; we use a bigger number just to be safe.
*/
int remaining_tries = 8;
/* Avoid subscript errors. */
if (12 <= (unsigned)tm->tm_mon)
return -1;
tm->tm_yday = month_yday[tm->tm_mon] + tm->tm_mday
- (tm->tm_mon<2 || ! isleap(tm->tm_year + TM_YEAR_ORIGIN));
/* Make a first guess. */
gt = t_cache[localzone];
gtm = gt ? &tm_cache[localzone] : time2tm(gt,localzone);
/* Repeatedly use the error from the guess to improve the guess. */
while ((d = difftm(tm, gtm)) != 0) {
if (--remaining_tries == 0)
return -1;
gt += d;
gtm = time2tm(gt,localzone);
}
t_cache[localzone] = gt;
tm_cache[localzone] = *gtm;
/*
* Check that the guess actually matches;
* overflow can cause difftm to yield 0 even on differing times,
* or tm may have members out of range (e.g. bad leap seconds).
*/
if ( (tm->tm_year ^ gtm->tm_year)
| (tm->tm_mon ^ gtm->tm_mon)
| (tm->tm_mday ^ gtm->tm_mday)
| (tm->tm_hour ^ gtm->tm_hour)
| (tm->tm_min ^ gtm->tm_min)
| (tm->tm_sec ^ gtm->tm_sec))
return -1;
tm->tm_wday = gtm->tm_wday;
return gt;
}
/*
* Check *PT and convert it to time_t.
* If it is incompletely specified, use DEFAULT_TIME to fill it out.
* Use localtime if PT->zone is the special value TM_LOCAL_ZONE.
* Yield -1 on failure.
* ISO 8601 day-of-year and week numbers are not yet supported.
*/
static time_t
maketime(pt, default_time)
struct partime const *pt;
time_t default_time;
{
int localzone, wday;
struct tm tm;
struct tm *tm0 = 0;
time_t r;
tm0 = 0; /* Keep gcc -Wall happy. */
localzone = pt->zone==TM_LOCAL_ZONE;
tm = pt->tm;
if (TM_DEFINED(pt->ymodulus) || !TM_DEFINED(tm.tm_year)) {
/* Get tm corresponding to current time. */
tm0 = time2tm(default_time, localzone);
if (!localzone)
adjzone(tm0, pt->zone);
}
if (TM_DEFINED(pt->ymodulus))
tm.tm_year +=
(tm0->tm_year + TM_YEAR_ORIGIN)/pt->ymodulus * pt->ymodulus;
else if (!TM_DEFINED(tm.tm_year)) {
/* Set default year, month, day from current time. */
tm.tm_year = tm0->tm_year + TM_YEAR_ORIGIN;
if (!TM_DEFINED(tm.tm_mon)) {
tm.tm_mon = tm0->tm_mon;
if (!TM_DEFINED(tm.tm_mday))
tm.tm_mday = tm0->tm_mday;
}
}
/* Convert from partime year (Gregorian) to Posix year. */
tm.tm_year -= TM_YEAR_ORIGIN;
/* Set remaining default fields to be their minimum values. */
if (!TM_DEFINED(tm.tm_mon)) tm.tm_mon = 0;
if (!TM_DEFINED(tm.tm_mday)) tm.tm_mday = 1;
if (!TM_DEFINED(tm.tm_hour)) tm.tm_hour = 0;
if (!TM_DEFINED(tm.tm_min)) tm.tm_min = 0;
if (!TM_DEFINED(tm.tm_sec)) tm.tm_sec = 0;
if (!localzone)
adjzone(&tm, -pt->zone);
wday = tm.tm_wday;
/* Convert and fill in the rest of the tm. */
r = tm2time(&tm, localzone);
/* Check weekday. */
if (r != -1 && TM_DEFINED(wday) && wday != tm.tm_wday)
return -1;
return r;
}
/* Parse a free-format date in SOURCE, yielding a Unix format time. */
time_t
str2time(source, default_time, default_zone)
char const *source;
time_t default_time;
long default_zone;
{
struct partime pt;
if (*partime(source, &pt))
return -1;
if (pt.zone == TM_UNDEFINED_ZONE)
pt.zone = default_zone;
return maketime(&pt, default_time);
}
#if TEST
#include <stdio.h>
int
main(argc, argv) int argc; char **argv;
{
time_t default_time = time((time_t *)0);
long default_zone = argv[1] ? atol(argv[1]) : 0;
char buf[1000];
while (fgets(buf, 1000, stdin)) {
time_t t = str2time(buf, default_time, default_zone);
printf("%s", asctime(gmtime(&t)));
}
return 0;
}
#endif

View File

@ -0,0 +1,39 @@
/* Yield time_t from struct partime yielded by partime. */
/* Copyright 1993, 1994, 1995 Paul Eggert
Distributed under license by the Free Software Foundation, Inc.
This file is part of RCS.
RCS 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.
RCS 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 RCS; see the file COPYING.
If not, write to the Free Software Foundation,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
Report problems and direct all questions to:
rcs-bugs@cs.purdue.edu
*/
#if defined(__STDC__) || has_prototypes
# define __MAKETIME_P(x) x
#else
# define __MAKETIME_P(x) ()
#endif
struct tm *time2tm __MAKETIME_P((time_t,int));
time_t difftm __MAKETIME_P((struct tm const *, struct tm const *));
time_t str2time __MAKETIME_P((char const *, time_t, long));
time_t tm2time __MAKETIME_P((struct tm *, int));
void adjzone __MAKETIME_P((struct tm *, long));

View File

@ -0,0 +1,148 @@
/* three-way file merge internals */
/* Copyright 1991, 1992, 1993, 1994, 1995 Paul Eggert
Distributed under license by the Free Software Foundation, Inc.
This file is part of RCS.
RCS 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.
RCS 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 RCS; see the file COPYING.
If not, write to the Free Software Foundation,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
Report problems and direct all questions to:
rcs-bugs@cs.purdue.edu
*/
#include "rcsbase.h"
libId(mergerId, "$FreeBSD$")
static char const *normalize_arg P((char const*,char**));
static char const *
normalize_arg(s, b)
char const *s;
char **b;
/*
* If S looks like an option, prepend ./ to it. Yield the result.
* Set *B to the address of any storage that was allocated.
*/
{
char *t;
if (*s == '-') {
*b = t = testalloc(strlen(s) + 3);
VOID sprintf(t, ".%c%s", SLASH, s);
return t;
} else {
*b = 0;
return s;
}
}
int
merge(tostdout, edarg, label, argv)
int tostdout;
char const *edarg;
char const *const label[3];
char const *const argv[3];
/*
* Do `merge [-p] EDARG -L l0 -L l1 -L l2 a0 a1 a2',
* where TOSTDOUT specifies whether -p is present,
* EDARG gives the editing type (e.g. "-A", or null for the default),
* LABEL gives l0, l1 and l2, and ARGV gives a0, a1 and a2.
* Yield DIFF_SUCCESS or DIFF_FAILURE.
*/
{
register int i;
FILE *f;
RILE *rt;
char const *a[3], *t;
char *b[3];
int s;
#if !DIFF3_BIN
char const *d[2];
#endif
for (i=3; 0<=--i; )
a[i] = normalize_arg(argv[i], &b[i]);
if (!edarg)
edarg = "-E";
#if DIFF3_BIN
t = 0;
if (!tostdout)
t = maketemp(0);
s = run(
-1, t,
DIFF3, edarg, "-am",
"-L", label[0],
"-L", label[1],
"-L", label[2],
a[0], a[1], a[2], (char*)0
);
switch (s) {
case DIFF_SUCCESS:
break;
case DIFF_FAILURE:
warn("conflicts during merge");
break;
default:
exiterr();
}
if (t) {
if (!(f = fopenSafer(argv[0], "w")))
efaterror(argv[0]);
if (!(rt = Iopen(t, "r", (struct stat*)0)))
efaterror(t);
fastcopy(rt, f);
Ifclose(rt);
Ofclose(f);
}
#else
for (i=0; i<2; i++)
switch (run(
-1, d[i]=maketemp(i),
DIFF, a[i], a[2], (char*)0
)) {
case DIFF_FAILURE: case DIFF_SUCCESS: break;
default: faterror("diff failed");
}
t = maketemp(2);
s = run(
-1, t,
DIFF3, edarg, d[0], d[1], a[0], a[1], a[2],
label[0], label[2], (char*)0
);
if (s != DIFF_SUCCESS) {
s = DIFF_FAILURE;
warn("overlaps or other problems during merge");
}
if (!(f = fopenSafer(t, "a+")))
efaterror(t);
aputs(tostdout ? "1,$p\n" : "w\n", f);
Orewind(f);
aflush(f);
if (run(fileno(f), (char*)0, ED, "-", a[0], (char*)0))
exiterr();
Ofclose(f);
#endif
tempunlink();
for (i=3; 0<=--i; )
if (b[i])
tfree(b[i]);
return s;
}

View File

@ -0,0 +1,701 @@
/* Parse a string, yielding a struct partime that describes it. */
/* Copyright 1993, 1994, 1995 Paul Eggert
Distributed under license by the Free Software Foundation, Inc.
This file is part of RCS.
RCS 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.
RCS 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 RCS; see the file COPYING.
If not, write to the Free Software Foundation,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
Report problems and direct all questions to:
rcs-bugs@cs.purdue.edu
*/
#if has_conf_h
# include "conf.h"
#else
# ifdef __STDC__
# define P(x) x
# else
# define const
# define P(x) ()
# endif
# include <limits.h>
# include <time.h>
#endif
#include <ctype.h>
#undef isdigit
#define isdigit(c) (((unsigned)(c)-'0') <= 9) /* faster than stock */
#include "partime.h"
char const partimeId[]
= "$FreeBSD$";
/* Lookup tables for names of months, weekdays, time zones. */
#define NAME_LENGTH_MAXIMUM 4
struct name_val {
char name[NAME_LENGTH_MAXIMUM];
int val;
};
static char const *parse_decimal P((char const*,int,int,int,int,int*,int*));
static char const *parse_fixed P((char const*,int,int*));
static char const *parse_pattern_letter P((char const*,int,struct partime*));
static char const *parse_prefix P((char const*,struct partime*,int*));
static char const *parse_ranged P((char const*,int,int,int,int*));
static int lookup P((char const*,struct name_val const[]));
static int merge_partime P((struct partime*, struct partime const*));
static void undefine P((struct partime*));
static struct name_val const month_names[] = {
{"jan",0}, {"feb",1}, {"mar",2}, {"apr",3}, {"may",4}, {"jun",5},
{"jul",6}, {"aug",7}, {"sep",8}, {"oct",9}, {"nov",10}, {"dec",11},
{"", TM_UNDEFINED}
};
static struct name_val const weekday_names[] = {
{"sun",0}, {"mon",1}, {"tue",2}, {"wed",3}, {"thu",4}, {"fri",5}, {"sat",6},
{"", TM_UNDEFINED}
};
#define hr60nonnegative(t) ((t)/100 * 60 + (t)%100)
#define hr60(t) ((t)<0 ? -hr60nonnegative(-(t)) : hr60nonnegative(t))
#define zs(t,s) {s, hr60(t)}
#define zd(t,s,d) zs(t, s), zs((t)+100, d)
static struct name_val const zone_names[] = {
zs(-1000, "hst"), /* Hawaii */
zd(-1000,"hast","hadt"),/* Hawaii-Aleutian */
zd(- 900,"akst","akdt"),/* Alaska */
zd(- 800, "pst", "pdt"),/* Pacific */
zd(- 700, "mst", "mdt"),/* Mountain */
zd(- 600, "cst", "cdt"),/* Central */
zd(- 500, "est", "edt"),/* Eastern */
zd(- 400, "ast", "adt"),/* Atlantic */
zd(- 330, "nst", "ndt"),/* Newfoundland */
zs( 000, "utc"), /* Coordinated Universal */
zs( 000, "cut"), /* " */
zs( 000, "ut"), /* Universal */
zs( 000, "z"), /* Zulu (required by ISO 8601) */
zd( 000, "gmt", "bst"),/* Greenwich Mean, British Summer */
zs( 000, "wet"), /* Western Europe */
zs( 100, "met"), /* Middle Europe */
zs( 100, "cet"), /* Central Europe */
zs( 200, "eet"), /* Eastern Europe */
zs( 530, "ist"), /* India */
zd( 900, "jst", "jdt"),/* Japan */
zd( 900, "kst", "kdt"),/* Korea */
zd( 1200,"nzst","nzdt"),/* New Zealand */
{ "lt", 1 },
#if 0
/* The following names are duplicates or are not well attested. */
zs(-1100, "sst"), /* Samoa */
zs(-1000, "tht"), /* Tahiti */
zs(- 930, "mqt"), /* Marquesas */
zs(- 900, "gbt"), /* Gambier */
zd(- 900, "yst", "ydt"),/* Yukon - name is no longer used */
zs(- 830, "pit"), /* Pitcairn */
zd(- 500, "cst", "cdt"),/* Cuba */
zd(- 500, "ast", "adt"),/* Acre */
zd(- 400, "wst", "wdt"),/* Western Brazil */
zd(- 400, "ast", "adt"),/* Andes */
zd(- 400, "cst", "cdt"),/* Chile */
zs(- 300, "wgt"), /* Western Greenland */
zd(- 300, "est", "edt"),/* Eastern South America */
zs(- 300, "mgt"), /* Middle Greenland */
zd(- 200, "fst", "fdt"),/* Fernando de Noronha */
zs(- 100, "egt"), /* Eastern Greenland */
zs(- 100, "aat"), /* Atlantic Africa */
zs(- 100, "act"), /* Azores and Canaries */
zs( 000, "wat"), /* West Africa */
zs( 100, "cat"), /* Central Africa */
zd( 100, "mez","mesz"),/* Mittel-Europaeische Zeit */
zs( 200, "sat"), /* South Africa */
zd( 200, "ist", "idt"),/* Israel */
zs( 300, "eat"), /* East Africa */
zd( 300, "ast", "adt"),/* Arabia */
zd( 300, "msk", "msd"),/* Moscow */
zd( 330, "ist", "idt"),/* Iran */
zs( 400, "gst"), /* Gulf */
zs( 400, "smt"), /* Seychelles & Mascarene */
zd( 400, "esk", "esd"),/* Yekaterinburg */
zd( 400, "bsk", "bsd"),/* Baku */
zs( 430, "aft"), /* Afghanistan */
zd( 500, "osk", "osd"),/* Omsk */
zs( 500, "pkt"), /* Pakistan */
zd( 500, "tsk", "tsd"),/* Tashkent */
zs( 545, "npt"), /* Nepal */
zs( 600, "bgt"), /* Bangladesh */
zd( 600, "nsk", "nsd"),/* Novosibirsk */
zs( 630, "bmt"), /* Burma */
zs( 630, "cct"), /* Cocos */
zs( 700, "ict"), /* Indochina */
zs( 700, "jvt"), /* Java */
zd( 700, "isk", "isd"),/* Irkutsk */
zs( 800, "hkt"), /* Hong Kong */
zs( 800, "pst"), /* Philippines */
zs( 800, "sgt"), /* Singapore */
zd( 800, "cst", "cdt"),/* China */
zd( 800, "ust", "udt"),/* Ulan Bator */
zd( 800, "wst", "wst"),/* Western Australia */
zd( 800, "ysk", "ysd"),/* Yakutsk */
zs( 900, "blt"), /* Belau */
zs( 900, "mlt"), /* Moluccas */
zd( 900, "vsk", "vsd"),/* Vladivostok */
zd( 930, "cst", "cst"),/* Central Australia */
zs( 1000, "gst"), /* Guam */
zd( 1000, "gsk", "gsd"),/* Magadan */
zd( 1000, "est", "est"),/* Eastern Australia */
zd( 1100,"lhst","lhst"),/* Lord Howe */
zd( 1100, "psk", "psd"),/* Petropavlovsk-Kamchatski */
zs( 1100,"ncst"), /* New Caledonia */
zs( 1130,"nrft"), /* Norfolk */
zd( 1200, "ask", "asd"),/* Anadyr */
zs( 1245,"nz-chat"), /* Chatham */
zs( 1300, "tgt"), /* Tongatapu */
#endif
{"", -1}
};
static int
lookup (s, table)
char const *s;
struct name_val const table[];
/* Look for a prefix of S in TABLE, returning val for first matching entry. */
{
int j;
char buf[NAME_LENGTH_MAXIMUM];
for (j = 0; j < NAME_LENGTH_MAXIMUM; j++) {
unsigned char c = *s++;
buf[j] = isupper (c) ? tolower (c) : c;
if (!isalpha (c))
break;
}
for (; table[0].name[0]; table++)
for (j = 0; buf[j] == table[0].name[j]; )
if (++j == NAME_LENGTH_MAXIMUM || !table[0].name[j])
goto done;
done:
return table[0].val;
}
static void
undefine (t) struct partime *t;
/* Set *T to ``undefined'' values. */
{
t->tm.tm_sec = t->tm.tm_min = t->tm.tm_hour = t->tm.tm_mday = t->tm.tm_mon
= t->tm.tm_year = t->tm.tm_wday = t->tm.tm_yday
= t->ymodulus = t->yweek
= TM_UNDEFINED;
t->zone = TM_UNDEFINED_ZONE;
}
/*
* Array of patterns to look for in a date string.
* Order is important: we look for the first matching pattern
* whose values do not contradict values that we already know about.
* See `parse_pattern_letter' below for the meaning of the pattern codes.
*/
static char const * const patterns[] = {
/*
* These traditional patterns must come first,
* to prevent an ISO 8601 format from misinterpreting their prefixes.
*/
"E_n_y", "x", /* RFC 822 */
"E_n", "n_E", "n", "t:m:s_A", "t:m_A", "t_A", /* traditional */
"y/N/D$", /* traditional RCS */
/* ISO 8601:1988 formats, generalized a bit. */
"y-N-D$", "4ND$", "Y-N$",
"RND$", "-R=N$", "-R$", "--N=D$", "N=DT",
"--N$", "---D$", "DT",
"Y-d$", "4d$", "R=d$", "-d$", "dT",
"y-W-X", "yWX", "y=W",
"-r-W-X", "r-W-XT", "-rWX", "rWXT", "-W=X", "W=XT", "-W",
"-w-X", "w-XT", "---X$", "XT", "4$",
"T",
"h:m:s$", "hms$", "h:m$", "hm$", "h$", "-m:s$", "-ms$", "-m$", "--s$",
"Y", "Z",
0
};
static char const *
parse_prefix (str, t, pi) char const *str; struct partime *t; int *pi;
/*
* Parse an initial prefix of STR, setting *T accordingly.
* Return the first character after the prefix, or 0 if it couldn't be parsed.
* Start with pattern *PI; if success, set *PI to the next pattern to try.
* Set *PI to -1 if we know there are no more patterns to try;
* if *PI is initially negative, give up immediately.
*/
{
int i = *pi;
char const *pat;
unsigned char c;
if (i < 0)
return 0;
/* Remove initial noise. */
while (!isalnum (c = *str) && c != '-' && c != '+') {
if (!c) {
undefine (t);
*pi = -1;
return str;
}
str++;
}
/* Try a pattern until one succeeds. */
while ((pat = patterns[i++]) != 0) {
char const *s = str;
undefine (t);
do {
if (!(c = *pat++)) {
*pi = i;
return s;
}
} while ((s = parse_pattern_letter (s, c, t)) != 0);
}
return 0;
}
static char const *
parse_fixed (s, digits, res) char const *s; int digits, *res;
/*
* Parse an initial prefix of S of length DIGITS; it must be a number.
* Store the parsed number into *RES.
* Return the first character after the prefix, or 0 if it couldn't be parsed.
*/
{
int n = 0;
char const *lim = s + digits;
while (s < lim) {
unsigned d = *s++ - '0';
if (9 < d)
return 0;
n = 10*n + d;
}
*res = n;
return s;
}
static char const *
parse_ranged (s, digits, lo, hi, res) char const *s; int digits, lo, hi, *res;
/*
* Parse an initial prefix of S of length DIGITS;
* it must be a number in the range LO through HI.
* Store the parsed number into *RES.
* Return the first character after the prefix, or 0 if it couldn't be parsed.
*/
{
s = parse_fixed (s, digits, res);
return s && lo<=*res && *res<=hi ? s : 0;
}
static char const *
parse_decimal (s, digits, lo, hi, resolution, res, fres)
char const *s;
int digits, lo, hi, resolution, *res, *fres;
/*
* Parse an initial prefix of S of length DIGITS;
* it must be a number in the range LO through HI
* and it may be followed by a fraction that is to be computed using RESOLUTION.
* Store the parsed number into *RES; store the fraction times RESOLUTION,
* rounded to the nearest integer, into *FRES.
* Return the first character after the prefix, or 0 if it couldn't be parsed.
*/
{
s = parse_fixed (s, digits, res);
if (s && lo<=*res && *res<=hi) {
int f = 0;
if ((s[0]==',' || s[0]=='.') && isdigit ((unsigned char) s[1])) {
char const *s1 = ++s;
int num10 = 0, denom10 = 10, product;
while (isdigit ((unsigned char) *++s))
denom10 *= 10;
s = parse_fixed (s1, s - s1, &num10);
product = num10*resolution;
f = (product + (denom10>>1)) / denom10;
f -= f & (product%denom10 == denom10>>1); /* round to even */
if (f < 0 || product/resolution != num10)
return 0; /* overflow */
}
*fres = f;
return s;
}
return 0;
}
char *
parzone (s, zone) char const *s; long *zone;
/*
* Parse an initial prefix of S; it must denote a time zone.
* Set *ZONE to the number of seconds east of GMT,
* or to TM_LOCAL_ZONE if it is the local time zone.
* Return the first character after the prefix, or 0 if it couldn't be parsed.
*/
{
char sign;
int hh, mm, ss;
int minutesEastOfUTC;
long offset, z;
/*
* The formats are LT, n, n DST, nDST, no, o
* where n is a time zone name
* and o is a time zone offset of the form [-+]hh[:mm[:ss]].
*/
switch (*s) {
case '-': case '+':
z = 0;
break;
default:
minutesEastOfUTC = lookup (s, zone_names);
if (minutesEastOfUTC == -1)
return 0;
/* Don't bother to check rest of spelling. */
while (isalpha ((unsigned char) *s))
s++;
/* Don't modify LT. */
if (minutesEastOfUTC == 1) {
*zone = TM_LOCAL_ZONE;
return (char *) s;
}
z = minutesEastOfUTC * 60L;
/* Look for trailing " DST". */
if (
(s[-1]=='T' || s[-1]=='t') &&
(s[-2]=='S' || s[-2]=='s') &&
(s[-3]=='D' || s[-3]=='t')
)
goto trailing_dst;
while (isspace ((unsigned char) *s))
s++;
if (
(s[0]=='D' || s[0]=='d') &&
(s[1]=='S' || s[1]=='s') &&
(s[2]=='T' || s[2]=='t')
) {
s += 3;
trailing_dst:
*zone = z + 60*60;
return (char *) s;
}
switch (*s) {
case '-': case '+': break;
default: return (char *) s;
}
}
sign = *s++;
if (!(s = parse_ranged (s, 2, 0, 23, &hh)))
return 0;
mm = ss = 0;
if (*s == ':')
s++;
if (isdigit ((unsigned char) *s)) {
if (!(s = parse_ranged (s, 2, 0, 59, &mm)))
return 0;
if (*s==':' && s[-3]==':' && isdigit ((unsigned char) s[1])) {
if (!(s = parse_ranged (s + 1, 2, 0, 59, &ss)))
return 0;
}
}
if (isdigit ((unsigned char) *s))
return 0;
offset = (hh*60 + mm)*60L + ss;
*zone = z + (sign=='-' ? -offset : offset);
/*
* ?? Are fractions allowed here?
* If so, they're not implemented.
*/
return (char *) s;
}
static char const *
parse_pattern_letter (s, c, t) char const *s; int c; struct partime *t;
/*
* Parse an initial prefix of S, matching the pattern whose code is C.
* Set *T accordingly.
* Return the first character after the prefix, or 0 if it couldn't be parsed.
*/
{
switch (c) {
case '$': /* The next character must be a non-digit. */
if (isdigit ((unsigned char) *s))
return 0;
break;
case '-': case '/': case ':':
/* These characters stand for themselves. */
if (*s++ != c)
return 0;
break;
case '4': /* 4-digit year */
s = parse_fixed (s, 4, &t->tm.tm_year);
break;
case '=': /* optional '-' */
s += *s == '-';
break;
case 'A': /* AM or PM */
/*
* This matches the regular expression [AaPp][Mm]?.
* It must not be followed by a letter or digit;
* otherwise it would match prefixes of strings like "PST".
*/
switch (*s++) {
case 'A': case 'a':
if (t->tm.tm_hour == 12)
t->tm.tm_hour = 0;
break;
case 'P': case 'p':
if (t->tm.tm_hour != 12)
t->tm.tm_hour += 12;
break;
default: return 0;
}
switch (*s) {
case 'M': case 'm': s++; break;
}
if (isalnum (*s))
return 0;
break;
case 'D': /* day of month [01-31] */
s = parse_ranged (s, 2, 1, 31, &t->tm.tm_mday);
break;
case 'd': /* day of year [001-366] */
s = parse_ranged (s, 3, 1, 366, &t->tm.tm_yday);
t->tm.tm_yday--;
break;
case 'E': /* extended day of month [1-9, 01-31] */
s = parse_ranged (s, (
isdigit ((unsigned char) s[0]) &&
isdigit ((unsigned char) s[1])
) + 1, 1, 31, &t->tm.tm_mday);
break;
case 'h': /* hour [00-23 followed by optional fraction] */
{
int frac;
s = parse_decimal (s, 2, 0, 23, 60*60, &t->tm.tm_hour, &frac);
t->tm.tm_min = frac / 60;
t->tm.tm_sec = frac % 60;
}
break;
case 'm': /* minute [00-59 followed by optional fraction] */
s = parse_decimal (s, 2, 0, 59, 60, &t->tm.tm_min, &t->tm.tm_sec);
break;
case 'n': /* month name [e.g. "Jan"] */
if (!TM_DEFINED (t->tm.tm_mon = lookup (s, month_names)))
return 0;
/* Don't bother to check rest of spelling. */
while (isalpha ((unsigned char) *s))
s++;
break;
case 'N': /* month [01-12] */
s = parse_ranged (s, 2, 1, 12, &t->tm.tm_mon);
t->tm.tm_mon--;
break;
case 'r': /* year % 10 (remainder in origin-0 decade) [0-9] */
s = parse_fixed (s, 1, &t->tm.tm_year);
t->ymodulus = 10;
break;
case_R:
case 'R': /* year % 100 (remainder in origin-0 century) [00-99] */
s = parse_fixed (s, 2, &t->tm.tm_year);
t->ymodulus = 100;
break;
case 's': /* second [00-60 followed by optional fraction] */
{
int frac;
s = parse_decimal (s, 2, 0, 60, 1, &t->tm.tm_sec, &frac);
t->tm.tm_sec += frac;
}
break;
case 'T': /* 'T' or 't' */
switch (*s++) {
case 'T': case 't': break;
default: return 0;
}
break;
case 't': /* traditional hour [1-9 or 01-12] */
s = parse_ranged (s, (
isdigit ((unsigned char) s[0]) && isdigit ((unsigned char) s[1])
) + 1, 1, 12, &t->tm.tm_hour);
break;
case 'w': /* 'W' or 'w' only (stands for current week) */
switch (*s++) {
case 'W': case 'w': break;
default: return 0;
}
break;
case 'W': /* 'W' or 'w', followed by a week of year [00-53] */
switch (*s++) {
case 'W': case 'w': break;
default: return 0;
}
s = parse_ranged (s, 2, 0, 53, &t->yweek);
break;
case 'X': /* weekday (1=Mon ... 7=Sun) [1-7] */
s = parse_ranged (s, 1, 1, 7, &t->tm.tm_wday);
t->tm.tm_wday--;
break;
case 'x': /* weekday name [e.g. "Sun"] */
if (!TM_DEFINED (t->tm.tm_wday = lookup (s, weekday_names)))
return 0;
/* Don't bother to check rest of spelling. */
while (isalpha ((unsigned char) *s))
s++;
break;
case 'y': /* either R or Y */
if (
isdigit ((unsigned char) s[0]) &&
isdigit ((unsigned char) s[1]) &&
!isdigit ((unsigned char) s[2])
)
goto case_R;
/* fall into */
case 'Y': /* year in full [4 or more digits] */
{
int len = 0;
while (isdigit ((unsigned char) s[len]))
len++;
if (len < 4)
return 0;
s = parse_fixed (s, len, &t->tm.tm_year);
}
break;
case 'Z': /* time zone */
s = parzone (s, &t->zone);
break;
case '_': /* possibly empty sequence of non-alphanumerics */
while (!isalnum (*s) && *s)
s++;
break;
default: /* bad pattern */
return 0;
}
return s;
}
static int
merge_partime (t, u) struct partime *t; struct partime const *u;
/*
* If there is no conflict, merge into *T the additional information in *U
* and return 0. Otherwise do nothing and return -1.
*/
{
# define conflict(a,b) ((a) != (b) && TM_DEFINED (a) && TM_DEFINED (b))
if (
conflict (t->tm.tm_sec, u->tm.tm_sec) ||
conflict (t->tm.tm_min, u->tm.tm_min) ||
conflict (t->tm.tm_hour, u->tm.tm_hour) ||
conflict (t->tm.tm_mday, u->tm.tm_mday) ||
conflict (t->tm.tm_mon, u->tm.tm_mon) ||
conflict (t->tm.tm_year, u->tm.tm_year) ||
conflict (t->tm.tm_wday, u->tm.tm_yday) ||
conflict (t->ymodulus, u->ymodulus) ||
conflict (t->yweek, u->yweek) ||
(
t->zone != u->zone &&
t->zone != TM_UNDEFINED_ZONE &&
u->zone != TM_UNDEFINED_ZONE
)
)
return -1;
# undef conflict
# define merge_(a,b) if (TM_DEFINED (b)) (a) = (b);
merge_ (t->tm.tm_sec, u->tm.tm_sec)
merge_ (t->tm.tm_min, u->tm.tm_min)
merge_ (t->tm.tm_hour, u->tm.tm_hour)
merge_ (t->tm.tm_mday, u->tm.tm_mday)
merge_ (t->tm.tm_mon, u->tm.tm_mon)
merge_ (t->tm.tm_year, u->tm.tm_year)
merge_ (t->tm.tm_wday, u->tm.tm_yday)
merge_ (t->ymodulus, u->ymodulus)
merge_ (t->yweek, u->yweek)
# undef merge_
if (u->zone != TM_UNDEFINED_ZONE) t->zone = u->zone;
return 0;
}
char *
partime (s, t) char const *s; struct partime *t;
/*
* Parse a date/time prefix of S, putting the parsed result into *T.
* Return the first character after the prefix.
* The prefix may contain no useful information;
* in that case, *T will contain only undefined values.
*/
{
struct partime p;
undefine (t);
while (*s) {
int i = 0;
char const *s1;
do {
if (!(s1 = parse_prefix (s, &p, &i)))
return (char *) s;
} while (merge_partime (t, &p) != 0);
s = s1;
}
return (char *) s;
}

View File

@ -0,0 +1,71 @@
/* Parse a string, yielding a struct partime that describes it. */
/* Copyright 1993, 1994, 1995 Paul Eggert
Distributed under license by the Free Software Foundation, Inc.
This file is part of RCS.
RCS 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.
RCS 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 RCS; see the file COPYING.
If not, write to the Free Software Foundation,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
Report problems and direct all questions to:
rcs-bugs@cs.purdue.edu
*/
#define TM_UNDEFINED (-1)
#define TM_DEFINED(x) (0 <= (x))
#define TM_UNDEFINED_ZONE ((long) -24 * 60 * 60)
#define TM_LOCAL_ZONE (TM_UNDEFINED_ZONE - 1)
struct partime {
/*
* This structure describes the parsed time.
* Only the following tm_* values in it are used:
* sec, min, hour, mday, mon, year, wday, yday.
* If TM_UNDEFINED(value), the parser never found the value.
* The tm_year field is the actual year, not the year - 1900;
* but see ymodulus below.
*/
struct tm tm;
/*
* If !TM_UNDEFINED(ymodulus),
* then tm.tm_year is actually modulo ymodulus.
*/
int ymodulus;
/*
* Week of year, ISO 8601 style.
* If TM_UNDEFINED(yweek), the parser never found yweek.
* Weeks start on Mondays.
* Week 1 includes Jan 4.
*/
int yweek;
/* Seconds east of UTC; or TM_LOCAL_ZONE or TM_UNDEFINED_ZONE. */
long zone;
};
#if defined(__STDC__) || has_prototypes
# define __PARTIME_P(x) x
#else
# define __PARTIME_P(x) ()
#endif
char *partime __PARTIME_P((char const *, struct partime *));
char *parzone __PARTIME_P((char const *, long *));

View File

@ -0,0 +1,762 @@
/* RCS common definitions and data structures */
#define RCSBASE "$FreeBSD$"
/* Copyright 1982, 1988, 1989 Walter Tichy
Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert
Distributed under license by the Free Software Foundation, Inc.
This file is part of RCS.
RCS 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.
RCS 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 RCS; see the file COPYING.
If not, write to the Free Software Foundation,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
Report problems and direct all questions to:
rcs-bugs@cs.purdue.edu
*/
/*
* Revision 5.20 1995/06/16 06:19:24 eggert
* Update FSF address.
*
* Revision 5.19 1995/06/01 16:23:43 eggert
* (SIZEABLE_PATH): Don't depend on PATH_MAX: it's not worth configuring.
* (Ioffset_type,BINARY_EXPAND,MIN_UNEXPAND,MIN_UNCHANGED_EXPAND): New macros.
* (maps_memory): New macro; replaces many instances of `has_mmap'.
* (cacheptr): Renamed from cachetell.
* (struct RILE): New alternate name for RILE; the type is now recursive.
* (deallocate): New member for RILE, used for generic buffer deallocation.
* (cacheunget_): No longer take a failure arg; just call Ierror on failure.
* (struct rcslock): Renamed from struct lock, to avoid collisions with
* system headers on some hosts. All users changed.
* (basefilename): Renamed from basename, likewise.
* (dirtpname): Remove; no longer external.
* (dirlen, dateform): Remove; no longer used.
* (cmpdate, fopenSafer, fdSafer, readAccessFilenameBuffer): New functions.
* (zonelenmax): Increase to 9 for full ISO 8601 format.
* (catchmmapints): Depend on has_NFS.
*
* Revision 5.18 1994/03/17 14:05:48 eggert
* Add primitives for reading backwards from a RILE;
* this is needed to go back and find the $Log prefix.
* Specify subprocess input via file descriptor, not file name. Remove lint.
*
* Revision 5.17 1993/11/09 17:40:15 eggert
* Move RCS-specific time handling into rcstime.c.
* printf_string now takes two arguments, alas.
*
* Revision 5.16 1993/11/03 17:42:27 eggert
* Don't arbitrarily limit the number of joins. Remove `nil'.
* Add Name keyword. Don't discard ignored phrases.
* Add support for merge -A vs -E, and allow up to three labels.
* Improve quality of diagnostics and prototypes.
*
* Revision 5.15 1992/07/28 16:12:44 eggert
* Statement macro names now end in _.
*
* Revision 5.14 1992/02/17 23:02:22 eggert
* Add -T support. Work around NFS mmap SIGBUS problem.
*
* Revision 5.13 1992/01/24 18:44:19 eggert
* Add support for bad_creat0. lint -> RCS_lint
*
* Revision 5.12 1992/01/06 02:42:34 eggert
* while (E) ; -> while (E) continue;
*
* Revision 5.11 1991/10/07 17:32:46 eggert
* Support piece tables even if !has_mmap.
*
* Revision 5.10 1991/09/24 00:28:39 eggert
* Remove unexported functions.
*
* Revision 5.9 1991/08/19 03:13:55 eggert
* Add piece tables and other tuneups, and NFS workarounds.
*
* Revision 5.8 1991/04/21 11:58:20 eggert
* Add -x, RCSINIT, MS-DOS support.
*
* Revision 5.7 1991/02/28 19:18:50 eggert
* Try setuid() if seteuid() doesn't work.
*
* Revision 5.6 1991/02/26 17:48:37 eggert
* Support new link behavior. Move ANSI C / Posix declarations into conf.sh.
*
* Revision 5.5 1990/12/04 05:18:43 eggert
* Use -I for prompts and -q for diagnostics.
*
* Revision 5.4 1990/11/01 05:03:35 eggert
* Don't assume that builtins are functions; they may be macros.
* Permit arbitrary data in logs.
*
* Revision 5.3 1990/09/26 23:36:58 eggert
* Port wait() to non-Posix ANSI C hosts.
*
* Revision 5.2 1990/09/04 08:02:20 eggert
* Don't redefine NAME_MAX, PATH_MAX.
* Improve incomplete line handling. Standardize yes-or-no procedure.
*
* Revision 5.1 1990/08/29 07:13:53 eggert
* Add -kkvl. Fix type typos exposed by porting. Clean old log messages too.
*
* Revision 5.0 1990/08/22 08:12:44 eggert
* Adjust ANSI C / Posix support. Add -k, -V, setuid. Don't call access().
* Remove compile-time limits; use malloc instead.
* Ansify and Posixate. Add support for ISO 8859.
* Remove snoop and v2 support.
*
* Revision 4.9 89/05/01 15:17:14 narten
* botched previous USG fix
*
* Revision 4.8 89/05/01 14:53:05 narten
* changed #include <strings.h> -> string.h for USG systems.
*
* Revision 4.7 88/11/08 15:58:45 narten
* removed defs for functions loaded from libraries
*
* Revision 4.6 88/08/09 19:12:36 eggert
* Shrink stdio code size; remove lint; permit -Dhshsize=nn.
*
* Revision 4.5 87/12/18 17:06:41 narten
* made removed BSD ifdef, now uses V4_2BSD
*
* Revision 4.4 87/10/18 10:29:49 narten
* Updating version numbers
* Changes relative to 1.1 are actually relative to 4.2
*
* Revision 1.3 87/09/24 14:02:25 narten
* changes for lint
*
* Revision 1.2 87/03/27 14:22:02 jenkins
* Port to suns
*
* Revision 4.2 83/12/20 16:04:20 wft
* merged 3.6.1.1 and 4.1 (SMALLOG, logsize).
* moved setting of STRICT_LOCKING to Makefile.
* changed DOLLAR to UNKN (conflict with KDELIM).
*
* Revision 4.1 83/05/04 09:12:41 wft
* Added markers Id and RCSfile.
* Added Dbranch for default branches.
*
* Revision 3.6.1.1 83/12/02 21:56:22 wft
* Increased logsize, added macro SMALLOG.
*
* Revision 3.6 83/01/15 16:43:28 wft
* 4.2 prerelease
*
* Revision 3.6 83/01/15 16:43:28 wft
* Replaced dbm.h with BYTESIZ, fixed definition of rindex().
* Added variants of NCPFN and NCPPN for bsd 4.2, selected by defining V4_2BSD.
* Added macro DELNUMFORM to have uniform format for printing delta text nodes.
* Added macro DELETE to mark deleted deltas.
*
* Revision 3.5 82/12/10 12:16:56 wft
* Added two forms of DATEFORM, one using %02d, the other %.2d.
*
* Revision 3.4 82/12/04 20:01:25 wft
* added LOCKER, Locker, and USG (redefinition of rindex).
*
* Revision 3.3 82/12/03 12:22:04 wft
* Added dbm.h, stdio.h, RCSBASE, RCSSEP, RCSSUF, WORKMODE, TMPFILE3,
* PRINTDATE, PRINTTIME, map, and ctab; removed Suffix. Redefined keyvallength
* using NCPPN. Changed putc() to abort on write error.
*
* Revision 3.2 82/10/18 15:03:52 wft
* added macro STRICT_LOCKING, removed RCSUMASK.
* renamed JOINFILE[1,2] to JOINFIL[1,2].
*
* Revision 3.1 82/10/11 19:41:17 wft
* removed NBPW, NBPC, NCPW.
* added typdef int void to aid compiling
*/
#include "conf.h"
#define EXIT_TROUBLE DIFF_TROUBLE
#ifdef _POSIX_PATH_MAX
# define SIZEABLE_PATH _POSIX_PATH_MAX
#else
# define SIZEABLE_PATH 255 /* size of a large path; not a hard limit */
#endif
/* for traditional C hosts with unusual size arguments */
#define Fread(p,s,n,f) fread(p, (freadarg_type)(s), (freadarg_type)(n), f)
#define Fwrite(p,s,n,f) fwrite(p, (freadarg_type)(s), (freadarg_type)(n), f)
/*
* Parameters
*/
/* backwards compatibility with old versions of RCS */
#define VERSION_min 3 /* old output RCS format supported */
#define VERSION_max 5 /* newest output RCS format supported */
#ifndef VERSION_DEFAULT /* default RCS output format */
# define VERSION_DEFAULT VERSION_max
#endif
#define VERSION(n) ((n) - VERSION_DEFAULT) /* internally, 0 is the default */
#ifndef STRICT_LOCKING
#define STRICT_LOCKING 1
#endif
/* 0 sets the default locking to non-strict; */
/* used in experimental environments. */
/* 1 sets the default locking to strict; */
/* used in production environments. */
#define yearlength 16 /* (good through AD 9,999,999,999,999,999) */
#define datesize (yearlength+16) /* size of output of time2date */
#define RCSTMPPREFIX '_' /* prefix for temp files in working dir */
#define KDELIM '$' /* delimiter for keywords */
#define VDELIM ':' /* separates keywords from values */
#define DEFAULTSTATE "Exp" /* default state of revisions */
#define true 1
#define false 0
/*
* RILE - readonly file
* declarecache; - declares local cache for RILE variable(s)
* setupcache - sets up the local RILE cache, but does not initialize it
* cache, uncache - caches and uncaches the local RILE;
* (uncache,cache) is needed around functions that advance the RILE pointer
* Igeteof_(f,c,s) - get a char c from f, executing statement s at EOF
* cachegeteof_(c,s) - Igeteof_ applied to the local RILE
* Iget_(f,c) - like Igeteof_, except EOF is an error
* cacheget_(c) - Iget_ applied to the local RILE
* cacheunget_(f,c,s) - read c backwards from cached f, executing s at BOF
* Ifileno, Ioffset_type, Irewind, Itell - analogs to stdio routines
*
* By conventions, macros whose names end in _ are statements, not expressions.
* Following such macros with `; else' results in a syntax error.
*/
#define maps_memory (has_map_fd || has_mmap)
#if large_memory
typedef unsigned char const *Iptr_type;
typedef struct RILE {
Iptr_type ptr, lim;
unsigned char *base; /* not Iptr_type for lint's sake */
unsigned char *readlim;
int fd;
# if maps_memory
void (*deallocate) P((struct RILE *));
# else
FILE *stream;
# endif
} RILE;
# if maps_memory
# define declarecache register Iptr_type ptr, lim
# define setupcache(f) (lim = (f)->lim)
# define Igeteof_(f,c,s) if ((f)->ptr==(f)->lim) s else (c)= *(f)->ptr++;
# define cachegeteof_(c,s) if (ptr==lim) s else (c)= *ptr++;
# else
int Igetmore P((RILE*));
# define declarecache register Iptr_type ptr; register RILE *rRILE
# define setupcache(f) (rRILE = (f))
# define Igeteof_(f,c,s) if ((f)->ptr==(f)->readlim && !Igetmore(f)) s else (c)= *(f)->ptr++;
# define cachegeteof_(c,s) if (ptr==rRILE->readlim && !Igetmore(rRILE)) s else (c)= *ptr++;
# endif
# define uncache(f) ((f)->ptr = ptr)
# define cache(f) (ptr = (f)->ptr)
# define Iget_(f,c) Igeteof_(f,c,Ieof();)
# define cacheget_(c) cachegeteof_(c,Ieof();)
# define cacheunget_(f,c) (c)=(--ptr)[-1];
# define Ioffset_type size_t
# define Itell(f) ((f)->ptr - (f)->base)
# define Irewind(f) ((f)->ptr = (f)->base)
# define cacheptr() ptr
# define Ifileno(f) ((f)->fd)
#else
# define RILE FILE
# define declarecache register FILE *ptr
# define setupcache(f) (ptr = (f))
# define uncache(f)
# define cache(f)
# define Igeteof_(f,c,s) {if(((c)=getc(f))==EOF){testIerror(f);if(feof(f))s}}
# define cachegeteof_(c,s) Igeteof_(ptr,c,s)
# define Iget_(f,c) { if (((c)=getc(f))==EOF) testIeof(f); }
# define cacheget_(c) Iget_(ptr,c)
# define cacheunget_(f,c) if(fseek(ptr,-2L,SEEK_CUR))Ierror();else cacheget_(c)
# define Ioffset_type long
# define Itell(f) ftell(f)
# define Ifileno(f) fileno(f)
#endif
/* Print a char, but abort on write error. */
#define aputc_(c,o) { if (putc(c,o)==EOF) testOerror(o); }
/* Get a character from an RCS file, perhaps copying to a new RCS file. */
#define GETCeof_(o,c,s) { cachegeteof_(c,s) if (o) aputc_(c,o) }
#define GETC_(o,c) { cacheget_(c) if (o) aputc_(c,o) }
#define WORKMODE(RCSmode, writable) (((RCSmode)&(mode_t)~(S_IWUSR|S_IWGRP|S_IWOTH)) | ((writable)?S_IWUSR:0))
/* computes mode of working file: same as RCSmode, but write permission */
/* determined by writable */
/* character classes and token codes */
enum tokens {
/* classes */ DELIM, DIGIT, IDCHAR, NEWLN, LETTER, Letter,
PERIOD, SBEGIN, SPACE, UNKN,
/* tokens */ COLON, ID, NUM, SEMI, STRING
};
#define SDELIM '@' /* the actual character is needed for string handling*/
/* SDELIM must be consistent with ctab[], so that ctab[SDELIM]==SBEGIN.
* there should be no overlap among SDELIM, KDELIM, and VDELIM
*/
#define isdigit(c) (((unsigned)(c)-'0') <= 9) /* faster than ctab[c]==DIGIT */
/***************************************
* Data structures for the symbol table
***************************************/
/* Buffer of arbitrary data */
struct buf {
char *string;
size_t size;
};
struct cbuf {
char const *string;
size_t size;
};
/* Hash table entry */
struct hshentry {
char const * num; /* pointer to revision number (ASCIZ) */
char const * date; /* pointer to date of checkin */
char const * author; /* login of person checking in */
char const * lockedby; /* who locks the revision */
char const * state; /* state of revision (Exp by default) */
char const * name; /* name (if any) by which retrieved */
struct cbuf log; /* log message requested at checkin */
struct branchhead * branches; /* list of first revisions on branches*/
struct cbuf ig; /* ignored phrases in admin part */
struct cbuf igtext; /* ignored phrases in deltatext part */
struct hshentry * next; /* next revision on same branch */
struct hshentry * nexthsh; /* next revision with same hash value */
long insertlns;/* lines inserted (computed by rlog) */
long deletelns;/* lines deleted (computed by rlog) */
char selector; /* true if selected, false if deleted */
};
/* list of hash entries */
struct hshentries {
struct hshentries *rest;
struct hshentry *first;
};
/* list element for branch lists */
struct branchhead {
struct hshentry * hsh;
struct branchhead * nextbranch;
};
/* accesslist element */
struct access {
char const * login;
struct access * nextaccess;
};
/* list element for locks */
struct rcslock {
char const * login;
struct hshentry * delta;
struct rcslock * nextlock;
};
/* list element for symbolic names */
struct assoc {
char const * symbol;
char const * num;
struct assoc * nextassoc;
};
#define mainArgs (argc,argv) int argc; char **argv;
#if RCS_lint
# define libId(name,rcsid)
# define mainProg(name,cmd,rcsid) int name mainArgs
#else
# define libId(name,rcsid) char const name[] = rcsid;
# define mainProg(n,c,i) char const Copyright[] = "Copyright 1982,1988,1989 Walter F. Tichy, Purdue CS\nCopyright 1990,1991,1992,1993,1994,1995 Paul Eggert", baseid[] = RCSBASE, cmdid[] = c; libId(n,i) int main P((int,char**)); int main mainArgs
#endif
/*
* Markers for keyword expansion (used in co and ident)
* Every byte must have class LETTER or Letter.
*/
#define AUTHOR "Author"
#define DATE "Date"
#define HEADER "Header"
#define IDH "Id"
#define LOCKER "Locker"
#define LOG "Log"
#define NAME "Name"
#define RCSFILE "RCSfile"
#define REVISION "Revision"
#define SOURCE "Source"
#define STATE "State"
#define CVSHEADER "CVSHeader"
#define keylength 9 /* max length of any of the above keywords */
enum markers { Nomatch, Author, Date, Header, Id,
Locker, Log, Name, RCSfile, Revision, Source, State, CVSHeader,
LocalId };
/* This must be in the same order as rcskeys.c's Keyword[] array. */
#define DELNUMFORM "\n\n%s\n%s\n"
/* used by putdtext and scanlogtext */
#define EMPTYLOG "*** empty log message ***" /* used by ci and rlog */
/* main program */
extern char const cmdid[];
void exiterr P((void)) exiting;
/* merge */
int merge P((int,char const*,char const*const[3],char const*const[3]));
/* rcsedit */
#define ciklogsize 23 /* sizeof("checked in with -k by ") */
extern FILE *fcopy;
extern char const *resultname;
extern char const ciklog[ciklogsize];
extern int locker_expansion;
RILE *rcswriteopen P((struct buf*,struct stat*,int));
char const *makedirtemp P((int));
char const *getcaller P((void));
int addlock P((struct hshentry*,int));
int addsymbol P((char const*,char const*,int));
int checkaccesslist P((void));
int chnamemod P((FILE**,char const*,char const*,int,mode_t,time_t));
int donerewrite P((int,time_t));
int dorewrite P((int,int));
int expandline P((RILE*,FILE*,struct hshentry const*,int,FILE*,int));
int findlock P((int,struct hshentry**));
int setmtime P((char const*,time_t));
void ORCSclose P((void));
void ORCSerror P((void));
void copystring P((void));
void dirtempunlink P((void));
void enterstring P((void));
void finishedit P((struct hshentry const*,FILE*,int));
void keepdirtemp P((char const*));
void openfcopy P((FILE*));
void snapshotedit P((FILE*));
void xpandstring P((struct hshentry const*));
#if has_NFS || bad_unlink
int un_link P((char const*));
#else
# define un_link(s) unlink(s)
#endif
#if large_memory
void edit_string P((void));
# define editstring(delta) edit_string()
#else
void editstring P((struct hshentry const*));
#endif
/* rcsfcmp */
int rcsfcmp P((RILE*,struct stat const*,char const*,struct hshentry const*));
/* rcsfnms */
#define bufautobegin(b) clear_buf(b)
#define clear_buf(b) (VOID ((b)->string = 0, (b)->size = 0))
extern FILE *workstdout;
extern char *workname;
extern char const *RCSname;
extern char const *suffixes;
extern int fdlock;
extern struct stat RCSstat;
RILE *rcsreadopen P((struct buf*,struct stat*,int));
char *bufenlarge P((struct buf*,char const**));
char const *basefilename P((char const*));
char const *getfullRCSname P((void));
char const *getfullCVSname P((void));
char const *maketemp P((int));
char const *rcssuffix P((char const*));
int pairnames P((int,char**,RILE*(*)P((struct buf*,struct stat*,int)),int,int));
struct cbuf bufremember P((struct buf*,size_t));
void bufalloc P((struct buf*,size_t));
void bufautoend P((struct buf*));
void bufrealloc P((struct buf*,size_t));
void bufscat P((struct buf*,char const*));
void bufscpy P((struct buf*,char const*));
void tempunlink P((void));
/* rcsgen */
extern int interactiveflag;
extern struct buf curlogbuf;
char const *buildrevision P((struct hshentries const*,struct hshentry*,FILE*,int));
int getcstdin P((void));
int putdtext P((struct hshentry const*,char const*,FILE*,int));
int ttystdin P((void));
int yesorno P((int,char const*,...)) printf_string(2,3);
struct cbuf cleanlogmsg P((char*,size_t));
struct cbuf getsstdin P((char const*,char const*,char const*,struct buf*));
void putdesc P((int,char*));
void putdftext P((struct hshentry const*,RILE*,FILE*,int));
/* rcskeep */
extern int prevkeys;
extern struct buf prevauthor, prevdate, prevname, prevrev, prevstate;
int getoldkeys P((RILE*));
/* rcskeys */
extern char const *Keyword[];
extern enum markers LocalIdMode;
enum markers trymatch P((char const*));
void setRCSLocalId(char const *);
void setIncExc(char const *);
/* rcslex */
extern FILE *foutptr;
extern FILE *frewrite;
extern RILE *finptr;
extern char const *NextString;
extern enum tokens nexttok;
extern int hshenter;
extern int nerror;
extern int nextc;
extern int quietflag;
extern long rcsline;
char const *getid P((void));
void efaterror P((char const*)) exiting;
void enfaterror P((int,char const*)) exiting;
void fatcleanup P((int)) exiting;
void faterror P((char const*,...)) printf_string_exiting(1,2);
void fatserror P((char const*,...)) printf_string_exiting(1,2);
void rcsfaterror P((char const*,...)) printf_string_exiting(1,2);
void Ieof P((void)) exiting;
void Ierror P((void)) exiting;
void Oerror P((void)) exiting;
char *checkid P((char*,int));
char *checksym P((char*,int));
int eoflex P((void));
int getkeyopt P((char const*));
int getlex P((enum tokens));
struct cbuf getphrases P((char const*));
struct cbuf savestring P((struct buf*));
struct hshentry *getnum P((void));
void Ifclose P((RILE*));
void Izclose P((RILE**));
void Lexinit P((void));
void Ofclose P((FILE*));
void Orewind P((FILE*));
void Ozclose P((FILE**));
void aflush P((FILE*));
void afputc P((int,FILE*));
void aprintf P((FILE*,char const*,...)) printf_string(2,3);
void aputs P((char const*,FILE*));
void checksid P((char*));
void checkssym P((char*));
void diagnose P((char const*,...)) printf_string(1,2);
void eerror P((char const*));
void eflush P((void));
void enerror P((int,char const*));
void error P((char const*,...)) printf_string(1,2);
void fvfprintf P((FILE*,char const*,va_list));
void getkey P((char const*));
void getkeystring P((char const*));
void nextlex P((void));
void oflush P((void));
void printstring P((void));
void readstring P((void));
void redefined P((int));
void rcserror P((char const*,...)) printf_string(1,2);
void rcswarn P((char const*,...)) printf_string(1,2);
void testIerror P((FILE*));
void testOerror P((FILE*));
void warn P((char const*,...)) printf_string(1,2);
void warnignore P((void));
void workerror P((char const*,...)) printf_string(1,2);
void workwarn P((char const*,...)) printf_string(1,2);
#if has_madvise && has_mmap && large_memory
void advise_access P((RILE*,int));
# define if_advise_access(p,f,advice) if (p) advise_access(f,advice)
#else
# define advise_access(f,advice)
# define if_advise_access(p,f,advice)
#endif
#if large_memory && maps_memory
RILE *I_open P((char const*,struct stat*));
# define Iopen(f,m,s) I_open(f,s)
#else
RILE *Iopen P((char const*,char const*,struct stat*));
#endif
#if !large_memory
void testIeof P((FILE*));
void Irewind P((RILE*));
#endif
/* rcsmap */
extern enum tokens const ctab[];
/* rcsrev */
char *partialno P((struct buf*,char const*,int));
char const *namedrev P((char const*,struct hshentry*));
char const *tiprev P((void));
int cmpdate P((char const*,char const*));
int cmpnum P((char const*,char const*));
int cmpnumfld P((char const*,char const*,int));
int compartial P((char const*,char const*,int));
int expandsym P((char const*,struct buf*));
int fexpandsym P((char const*,struct buf*,RILE*));
struct hshentry *genrevs P((char const*,char const*,char const*,char const*,struct hshentries**));
int countnumflds P((char const*));
void getbranchno P((char const*,struct buf*));
/* rcssyn */
/* These expand modes must agree with Expand_names[] in rcssyn.c. */
#define KEYVAL_EXPAND 0 /* -kkv `$Keyword: value $' */
#define KEYVALLOCK_EXPAND 1 /* -kkvl `$Keyword: value locker $' */
#define KEY_EXPAND 2 /* -kk `$Keyword$' */
#define VAL_EXPAND 3 /* -kv `value' */
#define OLD_EXPAND 4 /* -ko use old string, omitting expansion */
#define BINARY_EXPAND 5 /* -kb like -ko, but use binary mode I/O */
#define MIN_UNEXPAND OLD_EXPAND /* min value for no logical expansion */
#define MIN_UNCHANGED_EXPAND (OPEN_O_BINARY ? BINARY_EXPAND : OLD_EXPAND)
/* min value guaranteed to yield an identical file */
struct diffcmd {
long
line1, /* number of first line */
nlines, /* number of lines affected */
adprev, /* previous 'a' line1+1 or 'd' line1 */
dafter; /* sum of previous 'd' line1 and previous 'd' nlines */
};
extern char const * Dbranch;
extern struct access * AccessList;
extern struct assoc * Symbols;
extern struct cbuf Comment;
extern struct cbuf Ignored;
extern struct rcslock *Locks;
extern struct hshentry * Head;
extern int Expand;
extern int StrictLocks;
extern int TotalDeltas;
extern char const *const expand_names[];
extern char const
Kaccess[], Kauthor[], Kbranch[], Kcomment[],
Kdate[], Kdesc[], Kexpand[], Khead[], Klocks[], Klog[],
Knext[], Kstate[], Kstrict[], Ksymbols[], Ktext[];
void unexpected_EOF P((void)) exiting;
int getdiffcmd P((RILE*,int,FILE*,struct diffcmd*));
int str2expmode P((char const*));
void getadmin P((void));
void getdesc P((int));
void gettree P((void));
void ignorephrases P((char const*));
void initdiffcmd P((struct diffcmd*));
void putadmin P((void));
void putstring P((FILE*,int,struct cbuf,int));
void puttree P((struct hshentry const*,FILE*));
/* rcstime */
#define zonelenmax 9 /* maxiumum length of time zone string, e.g. "+12:34:56" */
char const *date2str P((char const[datesize],char[datesize + zonelenmax]));
time_t date2time P((char const[datesize]));
void str2date P((char const*,char[datesize]));
void time2date P((time_t,char[datesize]));
void zone_set P((char const*));
/* rcsutil */
extern int RCSversion;
FILE *fopenSafer P((char const*,char const*));
char *cgetenv P((char const*));
char *fstr_save P((char const*));
char *str_save P((char const*));
char const *getusername P((int));
int fdSafer P((int));
int getRCSINIT P((int,char**,char***));
int run P((int,char const*,...));
int runv P((int,char const*,char const**));
malloc_type fremember P((malloc_type));
malloc_type ftestalloc P((size_t));
malloc_type testalloc P((size_t));
malloc_type testrealloc P((malloc_type,size_t));
#define ftalloc(T) ftnalloc(T,1)
#define talloc(T) tnalloc(T,1)
#if RCS_lint
extern malloc_type lintalloc;
# define ftnalloc(T,n) (lintalloc = ftestalloc(sizeof(T)*(n)), (T*)0)
# define tnalloc(T,n) (lintalloc = testalloc(sizeof(T)*(n)), (T*)0)
# define trealloc(T,p,n) (lintalloc = testrealloc((malloc_type)0, sizeof(T)*(n)), p)
# define tfree(p)
#else
# define ftnalloc(T,n) ((T*) ftestalloc(sizeof(T)*(n)))
# define tnalloc(T,n) ((T*) testalloc(sizeof(T)*(n)))
# define trealloc(T,p,n) ((T*) testrealloc((malloc_type)(p), sizeof(T)*(n)))
# define tfree(p) free((malloc_type)(p))
#endif
time_t now P((void));
void awrite P((char const*,size_t,FILE*));
void fastcopy P((RILE*,FILE*));
void ffree P((void));
void ffree1 P((char const*));
void setRCSversion P((char const*));
#if has_signal
void catchints P((void));
void ignoreints P((void));
void restoreints P((void));
#else
# define catchints()
# define ignoreints()
# define restoreints()
#endif
#if has_mmap && large_memory
# if has_NFS && mmap_signal
void catchmmapints P((void));
void readAccessFilenameBuffer P((char const*,unsigned char const*));
# else
# define catchmmapints()
# endif
#endif
#if has_getuid
uid_t ruid P((void));
# define myself(u) ((u) == ruid())
#else
# define myself(u) true
#endif
#if has_setuid
uid_t euid P((void));
void nosetid P((void));
void seteid P((void));
void setrid P((void));
#else
# define nosetid()
# define seteid()
# define setrid()
#endif
/* version */
extern char const RCS_version_string[];

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,354 @@
/* Compare working files, ignoring RCS keyword strings. */
/*****************************************************************************
* rcsfcmp()
* Testprogram: define FCMPTEST
*****************************************************************************
*/
/* Copyright 1982, 1988, 1989 Walter Tichy
Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert
Distributed under license by the Free Software Foundation, Inc.
This file is part of RCS.
RCS 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.
RCS 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 RCS; see the file COPYING.
If not, write to the Free Software Foundation,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
Report problems and direct all questions to:
rcs-bugs@cs.purdue.edu
*/
/*
* Revision 5.14 1995/06/16 06:19:24 eggert
* Update FSF address.
*
* Revision 5.13 1995/06/01 16:23:43 eggert
* (rcsfcmp): Add -kb support.
*
* Revision 5.12 1994/03/17 14:05:48 eggert
* Normally calculate the $Log prefix from context, not from RCS file.
* Calculate line numbers correctly even if the $Log prefix contains newlines.
* Remove lint.
*
* Revision 5.11 1993/11/03 17:42:27 eggert
* Fix yet another off-by-one error when comparing Log string expansions.
*
* Revision 5.10 1992/07/28 16:12:44 eggert
* Statement macro names now end in _.
*
* Revision 5.9 1991/10/07 17:32:46 eggert
* Count log lines correctly.
*
* Revision 5.8 1991/08/19 03:13:55 eggert
* Tune.
*
* Revision 5.7 1991/04/21 11:58:22 eggert
* Fix errno bug. Add MS-DOS support.
*
* Revision 5.6 1991/02/28 19:18:47 eggert
* Open work file at most once.
*
* Revision 5.5 1990/11/27 09:26:05 eggert
* Fix comment leader bug.
*
* Revision 5.4 1990/11/01 05:03:42 eggert
* Permit arbitrary data in logs and comment leaders.
*
* Revision 5.3 1990/09/11 02:41:15 eggert
* Don't ignore differences inside keyword strings if -ko is set.
*
* Revision 5.1 1990/08/29 07:13:58 eggert
* Clean old log messages too.
*
* Revision 5.0 1990/08/22 08:12:49 eggert
* Don't append "checked in with -k by " log to logs,
* so that checking in a program with -k doesn't change it.
* Ansify and Posixate. Remove lint.
*
* Revision 4.5 89/05/01 15:12:42 narten
* changed copyright header to reflect current distribution rules
*
* Revision 4.4 88/08/09 19:12:50 eggert
* Shrink stdio code size.
*
* Revision 4.3 87/12/18 11:40:02 narten
* lint cleanups (Guy Harris)
*
* Revision 4.2 87/10/18 10:33:06 narten
* updting version number. Changes relative to 1.1 actually relative to
* 4.1
*
* Revision 1.2 87/03/27 14:22:19 jenkins
* Port to suns
*
* Revision 4.1 83/05/10 16:24:04 wft
* Marker matching now uses trymatch(). Marker pattern is now
* checked precisely.
*
* Revision 3.1 82/12/04 13:21:40 wft
* Initial revision.
*
*/
/*
#define FCMPTEST
*/
/* Testprogram; prints out whether two files are identical,
* except for keywords
*/
#include "rcsbase.h"
libId(fcmpId, "$FreeBSD$")
static int discardkeyval P((int,RILE*));
static int
discardkeyval(c, f)
register int c;
register RILE *f;
{
for (;;)
switch (c) {
case KDELIM:
case '\n':
return c;
default:
Igeteof_(f, c, return EOF;)
break;
}
}
int
rcsfcmp(xfp, xstatp, uname, delta)
register RILE *xfp;
struct stat const *xstatp;
char const *uname;
struct hshentry const *delta;
/* Compare the files xfp and uname. Return zero
* if xfp has the same contents as uname and neither has keywords,
* otherwise -1 if they are the same ignoring keyword values,
* and 1 if they differ even ignoring
* keyword values. For the LOG-keyword, rcsfcmp skips the log message
* given by the parameter delta in xfp. Thus, rcsfcmp returns nonpositive
* if xfp contains the same as uname, with the keywords expanded.
* Implementation: character-by-character comparison until $ is found.
* If a $ is found, read in the marker keywords; if they are real keywords
* and identical, read in keyword value. If value is terminated properly,
* disregard it and optionally skip log message; otherwise, compare value.
*/
{
register int xc, uc;
char xkeyword[keylength+2];
int eqkeyvals;
register RILE *ufp;
register int xeof, ueof;
register char * tp;
register char const *sp;
register size_t leaderlen;
int result;
enum markers match1;
struct stat ustat;
if (!(ufp = Iopen(uname, FOPEN_R_WORK, &ustat))) {
efaterror(uname);
}
xeof = ueof = false;
if (MIN_UNEXPAND <= Expand) {
if (!(result = xstatp->st_size!=ustat.st_size)) {
# if large_memory && maps_memory
result = !!memcmp(xfp->base,ufp->base,(size_t)xstatp->st_size);
# else
for (;;) {
/* get the next characters */
Igeteof_(xfp, xc, xeof=true;)
Igeteof_(ufp, uc, ueof=true;)
if (xeof | ueof)
goto eof;
if (xc != uc)
goto return1;
}
# endif
}
} else {
xc = 0;
uc = 0; /* Keep lint happy. */
leaderlen = 0;
result = 0;
for (;;) {
if (xc != KDELIM) {
/* get the next characters */
Igeteof_(xfp, xc, xeof=true;)
Igeteof_(ufp, uc, ueof=true;)
if (xeof | ueof)
goto eof;
} else {
/* try to get both keywords */
tp = xkeyword;
for (;;) {
Igeteof_(xfp, xc, xeof=true;)
Igeteof_(ufp, uc, ueof=true;)
if (xeof | ueof)
goto eof;
if (xc != uc)
break;
switch (xc) {
default:
if (xkeyword+keylength <= tp)
break;
*tp++ = xc;
continue;
case '\n': case KDELIM: case VDELIM:
break;
}
break;
}
if (
(xc==KDELIM || xc==VDELIM) && (uc==KDELIM || uc==VDELIM) &&
(*tp = xc, (match1 = trymatch(xkeyword)) != Nomatch)
) {
#ifdef FCMPTEST
VOID printf("found common keyword %s\n",xkeyword);
#endif
result = -1;
for (;;) {
if (xc != uc) {
xc = discardkeyval(xc, xfp);
uc = discardkeyval(uc, ufp);
if ((xeof = xc==EOF) | (ueof = uc==EOF))
goto eof;
eqkeyvals = false;
break;
}
switch (xc) {
default:
Igeteof_(xfp, xc, xeof=true;)
Igeteof_(ufp, uc, ueof=true;)
if (xeof | ueof)
goto eof;
continue;
case '\n': case KDELIM:
eqkeyvals = true;
break;
}
break;
}
if (xc != uc)
goto return1;
if (xc==KDELIM) {
/* Skip closing KDELIM. */
Igeteof_(xfp, xc, xeof=true;)
Igeteof_(ufp, uc, ueof=true;)
if (xeof | ueof)
goto eof;
/* if the keyword is LOG, also skip the log message in xfp*/
if (match1==Log) {
/* first, compute the number of line feeds in log msg */
int lncnt;
size_t ls, ccnt;
sp = delta->log.string;
ls = delta->log.size;
if (ls<sizeof(ciklog)-1 || memcmp(sp,ciklog,sizeof(ciklog)-1)) {
/*
* This log message was inserted. Skip its header.
* The number of newlines to skip is
* 1 + (C+1)*(1+L+1), where C is the number of newlines
* in the comment leader, and L is the number of
* newlines in the log string.
*/
int c1 = 1;
for (ccnt=Comment.size; ccnt--; )
c1 += Comment.string[ccnt] == '\n';
lncnt = 2*c1 + 1;
while (ls--) if (*sp++=='\n') lncnt += c1;
for (;;) {
if (xc=='\n')
if(--lncnt==0) break;
Igeteof_(xfp, xc, goto returnresult;)
}
/* skip last comment leader */
/* Can't just skip another line here, because there may be */
/* additional characters on the line (after the Log....$) */
ccnt = RCSversion<VERSION(5) ? Comment.size : leaderlen;
do {
Igeteof_(xfp, xc, goto returnresult;)
/*
* Read to the end of the comment leader or '\n',
* whatever comes first, because the leader's
* trailing white space was probably stripped.
*/
} while (ccnt-- && (xc!='\n' || --c1));
}
}
} else {
/* both end in the same character, but not a KDELIM */
/* must compare string values.*/
#ifdef FCMPTEST
VOID printf("non-terminated keywords %s, potentially different values\n",xkeyword);
#endif
if (!eqkeyvals)
goto return1;
}
}
}
if (xc != uc)
goto return1;
if (xc == '\n')
leaderlen = 0;
else
leaderlen++;
}
}
eof:
if (xeof==ueof)
goto returnresult;
return1:
result = 1;
returnresult:
Ifclose(ufp);
return result;
}
#ifdef FCMPTEST
char const cmdid[] = "rcsfcmp";
main(argc, argv)
int argc; char *argv[];
/* first argument: comment leader; 2nd: log message, 3rd: expanded file,
* 4th: unexpanded file
*/
{ struct hshentry delta;
Comment.string = argv[1];
Comment.size = strlen(argv[1]);
delta.log.string = argv[2];
delta.log.size = strlen(argv[2]);
if (rcsfcmp(Iopen(argv[3], FOPEN_R_WORK, (struct stat*)0), argv[4], &delta))
VOID printf("files are the same\n");
else VOID printf("files are different\n");
}
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,681 @@
/* Generate RCS revisions. */
/* Copyright 1982, 1988, 1989 Walter Tichy
Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert
Distributed under license by the Free Software Foundation, Inc.
This file is part of RCS.
RCS 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.
RCS 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 RCS; see the file COPYING.
If not, write to the Free Software Foundation,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
Report problems and direct all questions to:
rcs-bugs@cs.purdue.edu
*/
/*
* Revision 5.16 1995/06/16 06:19:24 eggert
* Update FSF address.
*
* Revision 5.15 1995/06/01 16:23:43 eggert
* (putadmin): Open RCS file with FOPEN_WB.
*
* Revision 5.14 1994/03/17 14:05:48 eggert
* Work around SVR4 stdio performance bug.
* Flush stderr after prompt. Remove lint.
*
* Revision 5.13 1993/11/03 17:42:27 eggert
* Don't discard ignored phrases. Improve quality of diagnostics.
*
* Revision 5.12 1992/07/28 16:12:44 eggert
* Statement macro names now end in _.
* Be consistent about pathnames vs filenames.
*
* Revision 5.11 1992/01/24 18:44:19 eggert
* Move put routines here from rcssyn.c.
* Add support for bad_creat0.
*
* Revision 5.10 1991/10/07 17:32:46 eggert
* Fix log bugs, e.g. ci -t/dev/null when has_mmap.
*
* Revision 5.9 1991/09/10 22:15:46 eggert
* Fix test for redirected stdin.
*
* Revision 5.8 1991/08/19 03:13:55 eggert
* Add piece tables. Tune.
*
* Revision 5.7 1991/04/21 11:58:24 eggert
* Add MS-DOS support.
*
* Revision 5.6 1990/12/27 19:54:26 eggert
* Fix bug: rcs -t inserted \n, making RCS file grow.
*
* Revision 5.5 1990/12/04 05:18:45 eggert
* Use -I for prompts and -q for diagnostics.
*
* Revision 5.4 1990/11/01 05:03:47 eggert
* Add -I and new -t behavior. Permit arbitrary data in logs.
*
* Revision 5.3 1990/09/21 06:12:43 hammer
* made putdesc() treat stdin the same whether or not it was from a terminal
* by making it recognize that a single '.' was then end of the
* description always
*
* Revision 5.2 1990/09/04 08:02:25 eggert
* Fix `co -p1.1 -ko' bug. Standardize yes-or-no procedure.
*
* Revision 5.1 1990/08/29 07:14:01 eggert
* Clean old log messages too.
*
* Revision 5.0 1990/08/22 08:12:52 eggert
* Remove compile-time limits; use malloc instead.
* Ansify and Posixate.
*
* Revision 4.7 89/05/01 15:12:49 narten
* changed copyright header to reflect current distribution rules
*
* Revision 4.6 88/08/28 14:59:10 eggert
* Shrink stdio code size; allow cc -R; remove lint; isatty() -> ttystdin()
*
* Revision 4.5 87/12/18 11:43:25 narten
* additional lint cleanups, and a bug fix from the 4.3BSD version that
* keeps "ci" from sticking a '\377' into the description if you run it
* with a zero-length file as the description. (Guy Harris)
*
* Revision 4.4 87/10/18 10:35:10 narten
* Updating version numbers. Changes relative to 1.1 actually relative to
* 4.2
*
* Revision 1.3 87/09/24 13:59:51 narten
* Sources now pass through lint (if you ignore printf/sprintf/fprintf
* warnings)
*
* Revision 1.2 87/03/27 14:22:27 jenkins
* Port to suns
*
* Revision 4.2 83/12/02 23:01:39 wft
* merged 4.1 and 3.3.1.1 (clearerr(stdin)).
*
* Revision 4.1 83/05/10 16:03:33 wft
* Changed putamin() to abort if trying to reread redirected stdin.
* Fixed getdesc() to output a prompt on initial newline.
*
* Revision 3.3.1.1 83/10/19 04:21:51 lepreau
* Added clearerr(stdin) for re-reading description from stdin.
*
* Revision 3.3 82/11/28 21:36:49 wft
* 4.2 prerelease
*
* Revision 3.3 82/11/28 21:36:49 wft
* Replaced ferror() followed by fclose() with ffclose().
* Putdesc() now suppresses the prompts if stdin
* is not a terminal. A pointer to the current log message is now
* inserted into the corresponding delta, rather than leaving it in a
* global variable.
*
* Revision 3.2 82/10/18 21:11:26 wft
* I added checks for write errors during editing, and improved
* the prompt on putdesc().
*
* Revision 3.1 82/10/13 15:55:09 wft
* corrected type of variables assigned to by getc (char --> int)
*/
#include "rcsbase.h"
libId(genId, "$FreeBSD$")
int interactiveflag; /* Should we act as if stdin is a tty? */
struct buf curlogbuf; /* buffer for current log message */
enum stringwork { enter, copy, edit, expand, edit_expand };
static void putdelta P((struct hshentry const*,FILE*));
static void scandeltatext P((struct hshentry*,enum stringwork,int));
char const *
buildrevision(deltas, target, outfile, expandflag)
struct hshentries const *deltas;
struct hshentry *target;
FILE *outfile;
int expandflag;
/* Function: Generates the revision given by target
* by retrieving all deltas given by parameter deltas and combining them.
* If outfile is set, the revision is output to it,
* otherwise written into a temporary file.
* Temporary files are allocated by maketemp().
* if expandflag is set, keyword expansion is performed.
* Return 0 if outfile is set, the name of the temporary file otherwise.
*
* Algorithm: Copy initial revision unchanged. Then edit all revisions but
* the last one into it, alternating input and output files (resultname and
* editname). The last revision is then edited in, performing simultaneous
* keyword substitution (this saves one extra pass).
* All this simplifies if only one revision needs to be generated,
* or no keyword expansion is necessary, or if output goes to stdout.
*/
{
if (deltas->first == target) {
/* only latest revision to generate */
openfcopy(outfile);
scandeltatext(target, expandflag?expand:copy, true);
if (outfile)
return 0;
else {
Ozclose(&fcopy);
return resultname;
}
} else {
/* several revisions to generate */
/* Get initial revision without keyword expansion. */
scandeltatext(deltas->first, enter, false);
while ((deltas=deltas->rest)->rest) {
/* do all deltas except last one */
scandeltatext(deltas->first, edit, false);
}
if (expandflag || outfile) {
/* first, get to beginning of file*/
finishedit((struct hshentry*)0, outfile, false);
}
scandeltatext(target, expandflag?edit_expand:edit, true);
finishedit(
expandflag ? target : (struct hshentry*)0,
outfile, true
);
if (outfile)
return 0;
Ozclose(&fcopy);
return resultname;
}
}
static void
scandeltatext(delta, func, needlog)
struct hshentry *delta;
enum stringwork func;
int needlog;
/* Function: Scans delta text nodes up to and including the one given
* by delta. For the one given by delta, the log message is saved into
* delta->log if needlog is set; func specifies how to handle the text.
* Similarly, if needlog, delta->igtext is set to the ignored phrases.
* Assumes the initial lexeme must be read in first.
* Does not advance nexttok after it is finished.
*/
{
struct hshentry const *nextdelta;
struct cbuf cb;
for (;;) {
if (eoflex())
fatserror("can't find delta for revision %s", delta->num);
nextlex();
if (!(nextdelta=getnum())) {
fatserror("delta number corrupted");
}
getkeystring(Klog);
if (needlog && delta==nextdelta) {
cb = savestring(&curlogbuf);
delta->log = cleanlogmsg(curlogbuf.string, cb.size);
nextlex();
delta->igtext = getphrases(Ktext);
} else {readstring();
ignorephrases(Ktext);
}
getkeystring(Ktext);
if (delta==nextdelta)
break;
readstring(); /* skip over it */
}
switch (func) {
case enter: enterstring(); break;
case copy: copystring(); break;
case expand: xpandstring(delta); break;
case edit: editstring((struct hshentry *)0); break;
case edit_expand: editstring(delta); break;
}
}
struct cbuf
cleanlogmsg(m, s)
char *m;
size_t s;
{
register char *t = m;
register char const *f = t;
struct cbuf r;
while (s) {
--s;
if ((*t++ = *f++) == '\n')
while (m < --t)
if (t[-1]!=' ' && t[-1]!='\t') {
*t++ = '\n';
break;
}
}
while (m < t && (t[-1]==' ' || t[-1]=='\t' || t[-1]=='\n'))
--t;
r.string = m;
r.size = t - m;
return r;
}
int ttystdin()
{
static int initialized;
if (!initialized) {
if (!interactiveflag)
interactiveflag = isatty(STDIN_FILENO);
initialized = true;
}
return interactiveflag;
}
int
getcstdin()
{
register FILE *in;
register int c;
in = stdin;
if (feof(in) && ttystdin())
clearerr(in);
c = getc(in);
if (c == EOF) {
testIerror(in);
if (feof(in) && ttystdin())
afputc('\n',stderr);
}
return c;
}
#if has_prototypes
int
yesorno(int default_answer, char const *question, ...)
#else
/*VARARGS2*/ int
yesorno(default_answer, question, va_alist)
int default_answer; char const *question; va_dcl
#endif
{
va_list args;
register int c, r;
if (!quietflag && ttystdin()) {
oflush();
vararg_start(args, question);
fvfprintf(stderr, question, args);
va_end(args);
eflush();
r = c = getcstdin();
while (c!='\n' && !feof(stdin))
c = getcstdin();
if (r=='y' || r=='Y')
return true;
if (r=='n' || r=='N')
return false;
}
return default_answer;
}
void
putdesc(textflag, textfile)
int textflag;
char *textfile;
/* Function: puts the descriptive text into file frewrite.
* if finptr && !textflag, the text is copied from the old description.
* Otherwise, if textfile, the text is read from that
* file, or from stdin, if !textfile.
* A textfile with a leading '-' is treated as a string, not a pathname.
* If finptr, the old descriptive text is discarded.
* Always clears foutptr.
*/
{
static struct buf desc;
static struct cbuf desclean;
register FILE *txt;
register int c;
register FILE * frew;
register char *p;
register size_t s;
char const *plim;
frew = frewrite;
if (finptr && !textflag) {
/* copy old description */
aprintf(frew, "\n\n%s%c", Kdesc, nextc);
foutptr = frewrite;
getdesc(false);
foutptr = 0;
} else {
foutptr = 0;
/* get new description */
if (finptr) {
/*skip old description*/
getdesc(false);
}
aprintf(frew,"\n\n%s\n%c",Kdesc,SDELIM);
if (!textfile)
desclean = getsstdin(
"t-", "description",
"NOTE: This is NOT the log message!\n", &desc
);
else if (!desclean.string) {
if (*textfile == '-') {
p = textfile + 1;
s = strlen(p);
} else {
if (!(txt = fopenSafer(textfile, "r")))
efaterror(textfile);
bufalloc(&desc, 1);
p = desc.string;
plim = p + desc.size;
for (;;) {
if ((c=getc(txt)) == EOF) {
testIerror(txt);
if (feof(txt))
break;
}
if (plim <= p)
p = bufenlarge(&desc, &plim);
*p++ = c;
}
if (fclose(txt) != 0)
Ierror();
s = p - desc.string;
p = desc.string;
}
desclean = cleanlogmsg(p, s);
}
putstring(frew, false, desclean, true);
aputc_('\n', frew)
}
}
struct cbuf
getsstdin(option, name, note, buf)
char const *option, *name, *note;
struct buf *buf;
{
register int c;
register char *p;
register size_t i;
register int tty = ttystdin();
if (tty) {
aprintf(stderr,
"enter %s, terminated with single '.' or end of file:\n%s>> ",
name, note
);
eflush();
} else if (feof(stdin))
rcsfaterror("can't reread redirected stdin for %s; use -%s<%s>",
name, option, name
);
for (
i = 0, p = 0;
c = getcstdin(), !feof(stdin);
bufrealloc(buf, i+1), p = buf->string, p[i++] = c
)
if (c == '\n')
if (i && p[i-1]=='.' && (i==1 || p[i-2]=='\n')) {
/* Remove trailing '.'. */
--i;
break;
} else if (tty) {
aputs(">> ", stderr);
eflush();
}
return cleanlogmsg(p, i);
}
void
putadmin()
/* Output the admin node. */
{
register FILE *fout;
struct assoc const *curassoc;
struct rcslock const *curlock;
struct access const *curaccess;
if (!(fout = frewrite)) {
# if bad_creat0
ORCSclose();
fout = fopenSafer(makedirtemp(0), FOPEN_WB);
# else
int fo = fdlock;
fdlock = -1;
fout = fdopen(fo, FOPEN_WB);
# endif
if (!(frewrite = fout))
efaterror(RCSname);
}
/*
* Output the first character with putc, not printf.
* Otherwise, an SVR4 stdio bug buffers output inefficiently.
*/
aputc_(*Khead, fout)
aprintf(fout, "%s\t%s;\n", Khead + 1, Head?Head->num:"");
if (Dbranch && VERSION(4)<=RCSversion)
aprintf(fout, "%s\t%s;\n", Kbranch, Dbranch);
aputs(Kaccess, fout);
curaccess = AccessList;
while (curaccess) {
aprintf(fout, "\n\t%s", curaccess->login);
curaccess = curaccess->nextaccess;
}
aprintf(fout, ";\n%s", Ksymbols);
curassoc = Symbols;
while (curassoc) {
aprintf(fout, "\n\t%s:%s", curassoc->symbol, curassoc->num);
curassoc = curassoc->nextassoc;
}
aprintf(fout, ";\n%s", Klocks);
curlock = Locks;
while (curlock) {
aprintf(fout, "\n\t%s:%s", curlock->login, curlock->delta->num);
curlock = curlock->nextlock;
}
if (StrictLocks) aprintf(fout, "; %s", Kstrict);
aprintf(fout, ";\n");
if (Comment.size) {
aprintf(fout, "%s\t", Kcomment);
putstring(fout, true, Comment, false);
aprintf(fout, ";\n");
}
if (Expand != KEYVAL_EXPAND)
aprintf(fout, "%s\t%c%s%c;\n",
Kexpand, SDELIM, expand_names[Expand], SDELIM
);
awrite(Ignored.string, Ignored.size, fout);
aputc_('\n', fout)
}
static void
putdelta(node, fout)
register struct hshentry const *node;
register FILE * fout;
/* Output the delta NODE to FOUT. */
{
struct branchhead const *nextbranch;
if (!node) return;
aprintf(fout, "\n%s\n%s\t%s;\t%s %s;\t%s %s;\nbranches",
node->num,
Kdate, node->date,
Kauthor, node->author,
Kstate, node->state?node->state:""
);
nextbranch = node->branches;
while (nextbranch) {
aprintf(fout, "\n\t%s", nextbranch->hsh->num);
nextbranch = nextbranch->nextbranch;
}
aprintf(fout, ";\n%s\t%s;\n", Knext, node->next?node->next->num:"");
awrite(node->ig.string, node->ig.size, fout);
}
void
puttree(root, fout)
struct hshentry const *root;
register FILE *fout;
/* Output the delta tree with base ROOT in preorder to FOUT. */
{
struct branchhead const *nextbranch;
if (!root) return;
if (root->selector)
putdelta(root, fout);
puttree(root->next, fout);
nextbranch = root->branches;
while (nextbranch) {
puttree(nextbranch->hsh, fout);
nextbranch = nextbranch->nextbranch;
}
}
int
putdtext(delta, srcname, fout, diffmt)
struct hshentry const *delta;
char const *srcname;
FILE *fout;
int diffmt;
/*
* Output a deltatext node with delta number DELTA->num, log message DELTA->log,
* ignored phrases DELTA->igtext and text SRCNAME to FOUT.
* Double up all SDELIMs in both the log and the text.
* Make sure the log message ends in \n.
* Return false on error.
* If DIFFMT, also check that the text is valid diff -n output.
*/
{
RILE *fin;
if (!(fin = Iopen(srcname, "r", (struct stat*)0))) {
eerror(srcname);
return false;
}
putdftext(delta, fin, fout, diffmt);
Ifclose(fin);
return true;
}
void
putstring(out, delim, s, log)
register FILE *out;
struct cbuf s;
int delim, log;
/*
* Output to OUT one SDELIM if DELIM, then the string S with SDELIMs doubled.
* If LOG is set then S is a log string; append a newline if S is nonempty.
*/
{
register char const *sp;
register size_t ss;
if (delim)
aputc_(SDELIM, out)
sp = s.string;
for (ss = s.size; ss; --ss) {
if (*sp == SDELIM)
aputc_(SDELIM, out)
aputc_(*sp++, out)
}
if (s.size && log)
aputc_('\n', out)
aputc_(SDELIM, out)
}
void
putdftext(delta, finfile, foutfile, diffmt)
struct hshentry const *delta;
RILE *finfile;
FILE *foutfile;
int diffmt;
/* like putdtext(), except the source file is already open */
{
declarecache;
register FILE *fout;
register int c;
register RILE *fin;
int ed;
struct diffcmd dc;
fout = foutfile;
aprintf(fout, DELNUMFORM, delta->num, Klog);
/* put log */
putstring(fout, true, delta->log, true);
aputc_('\n', fout)
/* put ignored phrases */
awrite(delta->igtext.string, delta->igtext.size, fout);
/* put text */
aprintf(fout, "%s\n%c", Ktext, SDELIM);
fin = finfile;
setupcache(fin);
if (!diffmt) {
/* Copy the file */
cache(fin);
for (;;) {
cachegeteof_(c, break;)
if (c==SDELIM) aputc_(SDELIM, fout) /*double up SDELIM*/
aputc_(c, fout)
}
} else {
initdiffcmd(&dc);
while (0 <= (ed = getdiffcmd(fin, false, fout, &dc)))
if (ed) {
cache(fin);
while (dc.nlines--)
do {
cachegeteof_(c, { if (!dc.nlines) goto OK_EOF; unexpected_EOF(); })
if (c == SDELIM)
aputc_(SDELIM, fout)
aputc_(c, fout)
} while (c != '\n');
uncache(fin);
}
}
OK_EOF:
aprintf(fout, "%c\n", SDELIM);
}

View File

@ -0,0 +1,452 @@
/* Extract RCS keyword string values from working files. */
/* Copyright 1982, 1988, 1989 Walter Tichy
Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert
Distributed under license by the Free Software Foundation, Inc.
This file is part of RCS.
RCS 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.
RCS 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 RCS; see the file COPYING.
If not, write to the Free Software Foundation,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
Report problems and direct all questions to:
rcs-bugs@cs.purdue.edu
*/
/*
* Revision 5.10 1995/06/16 06:19:24 eggert
* Update FSF address.
*
* Revision 5.9 1995/06/01 16:23:43 eggert
* (getoldkeys): Don't panic if a Name: is empty.
*
* Revision 5.8 1994/03/17 14:05:48 eggert
* Remove lint.
*
* Revision 5.7 1993/11/09 17:40:15 eggert
* Use simpler timezone parsing strategy now that we're using ISO 8601 format.
*
* Revision 5.6 1993/11/03 17:42:27 eggert
* Scan for Name keyword. Improve quality of diagnostics.
*
* Revision 5.5 1992/07/28 16:12:44 eggert
* Statement macro names now end in _.
*
* Revision 5.4 1991/08/19 03:13:55 eggert
* Tune.
*
* Revision 5.3 1991/04/21 11:58:25 eggert
* Shorten names to keep them distinct on shortname hosts.
*
* Revision 5.2 1990/10/04 06:30:20 eggert
* Parse time zone offsets; future RCS versions may output them.
*
* Revision 5.1 1990/09/20 02:38:56 eggert
* ci -k now checks dates more thoroughly.
*
* Revision 5.0 1990/08/22 08:12:53 eggert
* Retrieve old log message if there is one.
* Don't require final newline.
* Remove compile-time limits; use malloc instead. Tune.
* Permit dates past 1999/12/31. Ansify and Posixate.
*
* Revision 4.6 89/05/01 15:12:56 narten
* changed copyright header to reflect current distribution rules
*
* Revision 4.5 88/08/09 19:13:03 eggert
* Remove lint and speed up by making FILE *fp local, not global.
*
* Revision 4.4 87/12/18 11:44:21 narten
* more lint cleanups (Guy Harris)
*
* Revision 4.3 87/10/18 10:35:50 narten
* Updating version numbers. Changes relative to 1.1 actually relative
* to 4.1
*
* Revision 1.3 87/09/24 14:00:00 narten
* Sources now pass through lint (if you ignore printf/sprintf/fprintf
* warnings)
*
* Revision 1.2 87/03/27 14:22:29 jenkins
* Port to suns
*
* Revision 4.1 83/05/10 16:26:44 wft
* Added new markers Id and RCSfile; extraction added.
* Marker matching with trymatch().
*
* Revision 3.2 82/12/24 12:08:26 wft
* added missing #endif.
*
* Revision 3.1 82/12/04 13:22:41 wft
* Initial revision.
*
*/
#include "rcsbase.h"
libId(keepId, "$FreeBSD$")
static int badly_terminated P((void));
static int checknum P((char const*));
static int get0val P((int,RILE*,struct buf*,int));
static int getval P((RILE*,struct buf*,int));
static int keepdate P((RILE*));
static int keepid P((int,RILE*,struct buf*));
static int keeprev P((RILE*));
int prevkeys;
struct buf prevauthor, prevdate, prevname, prevrev, prevstate;
int
getoldkeys(fp)
register RILE *fp;
/* Function: Tries to read keyword values for author, date,
* revision number, and state out of the file fp.
* If fp is null, workname is opened and closed instead of using fp.
* The results are placed into
* prevauthor, prevdate, prevname, prevrev, prevstate.
* Aborts immediately if it finds an error and returns false.
* If it returns true, it doesn't mean that any of the
* values were found; instead, check to see whether the corresponding arrays
* contain the empty string.
*/
{
register int c;
char keyword[keylength+1];
register char * tp;
int needs_closing;
int prevname_found;
if (prevkeys)
return true;
needs_closing = false;
if (!fp) {
if (!(fp = Iopen(workname, FOPEN_R_WORK, (struct stat*)0))) {
eerror(workname);
return false;
}
needs_closing = true;
}
/* initialize to empty */
bufscpy(&prevauthor, "");
bufscpy(&prevdate, "");
bufscpy(&prevname, ""); prevname_found = 0;
bufscpy(&prevrev, "");
bufscpy(&prevstate, "");
c = '\0'; /* anything but KDELIM */
for (;;) {
if ( c==KDELIM) {
do {
/* try to get keyword */
tp = keyword;
for (;;) {
Igeteof_(fp, c, goto ok;)
switch (c) {
default:
if (keyword+keylength <= tp)
break;
*tp++ = c;
continue;
case '\n': case KDELIM: case VDELIM:
break;
}
break;
}
} while (c==KDELIM);
if (c!=VDELIM) continue;
*tp = c;
Igeteof_(fp, c, break;)
switch (c) {
case ' ': case '\t': break;
default: continue;
}
switch (trymatch(keyword)) {
case Author:
if (!keepid(0, fp, &prevauthor))
return false;
c = 0;
break;
case Date:
if (!(c = keepdate(fp)))
return false;
break;
case Header:
case Id:
case LocalId:
if (!(
getval(fp, (struct buf*)0, false) &&
keeprev(fp) &&
(c = keepdate(fp)) &&
keepid(c, fp, &prevauthor) &&
keepid(0, fp, &prevstate)
))
return false;
/* Skip either ``who'' (new form) or ``Locker: who'' (old). */
if (getval(fp, (struct buf*)0, true) &&
getval(fp, (struct buf*)0, true))
c = 0;
else if (nerror)
return false;
else
c = KDELIM;
break;
case Locker:
(void) getval(fp, (struct buf*)0, false);
c = 0;
break;
case Log:
case RCSfile:
case Source:
if (!getval(fp, (struct buf*)0, false))
return false;
c = 0;
break;
case Name:
if (getval(fp, &prevname, false)) {
if (*prevname.string)
checkssym(prevname.string);
prevname_found = 1;
}
c = 0;
break;
case Revision:
if (!keeprev(fp))
return false;
c = 0;
break;
case State:
if (!keepid(0, fp, &prevstate))
return false;
c = 0;
break;
default:
continue;
}
if (!c)
Igeteof_(fp, c, c=0;)
if (c != KDELIM) {
workerror("closing %c missing on keyword", KDELIM);
return false;
}
if (prevname_found &&
*prevauthor.string && *prevdate.string &&
*prevrev.string && *prevstate.string
)
break;
}
Igeteof_(fp, c, break;)
}
ok:
if (needs_closing)
Ifclose(fp);
else
Irewind(fp);
prevkeys = true;
return true;
}
static int
badly_terminated()
{
workerror("badly terminated keyword value");
return false;
}
static int
getval(fp, target, optional)
register RILE *fp;
struct buf *target;
int optional;
/* Reads a keyword value from FP into TARGET.
* Returns true if one is found, false otherwise.
* Does not modify target if it is 0.
* Do not report an error if OPTIONAL is set and KDELIM is found instead.
*/
{
int c;
Igeteof_(fp, c, return badly_terminated();)
return get0val(c, fp, target, optional);
}
static int
get0val(c, fp, target, optional)
register int c;
register RILE *fp;
struct buf *target;
int optional;
/* Reads a keyword value from C+FP into TARGET, perhaps OPTIONALly.
* Same as getval, except C is the lookahead character.
*/
{ register char * tp;
char const *tlim;
register int got1;
if (target) {
bufalloc(target, 1);
tp = target->string;
tlim = tp + target->size;
} else
tlim = tp = 0;
got1 = false;
for (;;) {
switch (c) {
default:
got1 = true;
if (tp) {
*tp++ = c;
if (tlim <= tp)
tp = bufenlarge(target, &tlim);
}
break;
case ' ':
case '\t':
if (tp) {
*tp = 0;
# ifdef KEEPTEST
VOID printf("getval: %s\n", target);
# endif
}
return got1;
case KDELIM:
if (!got1 && optional)
return false;
/* fall into */
case '\n':
case 0:
return badly_terminated();
}
Igeteof_(fp, c, return badly_terminated();)
}
}
static int
keepdate(fp)
RILE *fp;
/* Function: reads a date prevdate; checks format
* Return 0 on error, lookahead character otherwise.
*/
{
struct buf prevday, prevtime;
register int c;
c = 0;
bufautobegin(&prevday);
if (getval(fp,&prevday,false)) {
bufautobegin(&prevtime);
if (getval(fp,&prevtime,false)) {
Igeteof_(fp, c, c=0;)
if (c) {
register char const *d = prevday.string, *t = prevtime.string;
bufalloc(&prevdate, strlen(d) + strlen(t) + 9);
VOID sprintf(prevdate.string, "%s%s %s%s",
/* Parse dates put out by old versions of RCS. */
isdigit(d[0]) && isdigit(d[1]) && !isdigit(d[2])
? "19" : "",
d, t,
strchr(t,'-') || strchr(t,'+') ? "" : "+0000"
);
}
}
bufautoend(&prevtime);
}
bufautoend(&prevday);
return c;
}
static int
keepid(c, fp, b)
int c;
RILE *fp;
struct buf *b;
/* Get previous identifier from C+FP into B. */
{
if (!c)
Igeteof_(fp, c, return false;)
if (!get0val(c, fp, b, false))
return false;
checksid(b->string);
return !nerror;
}
static int
keeprev(fp)
RILE *fp;
/* Get previous revision from FP into prevrev. */
{
return getval(fp,&prevrev,false) && checknum(prevrev.string);
}
static int
checknum(s)
char const *s;
{
register char const *sp;
register int dotcount = 0;
for (sp=s; ; sp++) {
switch (*sp) {
case 0:
if (dotcount & 1)
return true;
else
break;
case '.':
dotcount++;
continue;
default:
if (isdigit(*sp))
continue;
break;
}
break;
}
workerror("%s is not a revision number", s);
return false;
}
#ifdef KEEPTEST
/* Print the keyword values found. */
char const cmdid[] ="keeptest";
int
main(argc, argv)
int argc; char *argv[];
{
while (*(++argv)) {
workname = *argv;
getoldkeys((RILE*)0);
VOID printf("%s: revision: %s, date: %s, author: %s, name: %s, state: %s\n",
*argv, prevrev.string, prevdate.string, prevauthor.string, prevname.string, prevstate.string);
}
exitmain(EXIT_SUCCESS);
}
#endif

View File

@ -0,0 +1,186 @@
/* RCS keyword table and match operation */
/* Copyright 1982, 1988, 1989 Walter Tichy
Copyright 1990, 1991, 1992, 1993, 1995 Paul Eggert
Distributed under license by the Free Software Foundation, Inc.
This file is part of RCS.
RCS 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.
RCS 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 RCS; see the file COPYING.
If not, write to the Free Software Foundation,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
Report problems and direct all questions to:
rcs-bugs@cs.purdue.edu
*/
/*
* Revision 5.4 1995/06/16 06:19:24 eggert
* Update FSF address.
*
* Revision 5.3 1993/11/03 17:42:27 eggert
* Add Name keyword.
*
* Revision 5.2 1991/08/19 03:13:55 eggert
* Say `T const' instead of `const T'; it's less confusing for pointer types.
* (This change was made in other source files too.)
*
* Revision 5.1 1991/04/21 11:58:25 eggert
* Don't put , just before } in initializer.
*
* Revision 5.0 1990/08/22 08:12:54 eggert
* Add -k. Ansify and Posixate.
*
* Revision 4.3 89/05/01 15:13:02 narten
* changed copyright header to reflect current distribution rules
*
* Revision 4.2 87/10/18 10:36:33 narten
* Updating version numbers. Changes relative to 1.1 actuallyt
* relative to 4.1
*
* Revision 1.2 87/09/24 14:00:10 narten
* Sources now pass through lint (if you ignore printf/sprintf/fprintf
* warnings)
*
* Revision 4.1 83/05/04 10:06:53 wft
* Initial revision.
*
*/
#include "rcsbase.h"
libId(keysId, "$FreeBSD$")
char const *Keyword[] = {
/* This must be in the same order as rcsbase.h's enum markers type. */
0,
AUTHOR, DATE, HEADER, IDH,
LOCKER, LOG, NAME, RCSFILE, REVISION, SOURCE, STATE, CVSHEADER,
NULL
};
/* Expand all keywords by default */
static int ExpandKeyword[] = {
false,
true, true, true, true,
true, true, true, true, true, true, true, true,
true
};
enum markers LocalIdMode = Id;
enum markers
trymatch(string)
char const *string;
/* function: Checks whether string starts with a keyword followed
* by a KDELIM or a VDELIM.
* If successful, returns the appropriate marker, otherwise Nomatch.
*/
{
register int j;
register char const *p, *s;
for (j = sizeof(Keyword)/sizeof(*Keyword); (--j); ) {
if (!ExpandKeyword[j])
continue;
/* try next keyword */
p = Keyword[j];
if (p == NULL)
continue;
s = string;
while (*p++ == *s++) {
if (!*p)
switch (*s) {
case KDELIM:
case VDELIM:
return (enum markers)j;
default:
return Nomatch;
}
}
}
return(Nomatch);
}
void
setIncExc(arg)
char const *arg;
/* Sets up the ExpandKeyword table according to command-line flags */
{
char *key;
char *copy, *next;
int include = 0, j;
copy = strdup(arg);
next = copy;
switch (*next++) {
case 'e':
include = false;
break;
case 'i':
include = true;
break;
default:
free(copy);
return;
}
if (include)
for (j = sizeof(Keyword)/sizeof(*Keyword); (--j); )
ExpandKeyword[j] = false;
key = strtok(next, ",");
while (key) {
for (j = sizeof(Keyword)/sizeof(*Keyword); (--j); ) {
if (Keyword[j] == NULL)
continue;
if (!strcmp(key, Keyword[j]))
ExpandKeyword[j] = include;
}
key = strtok(NULL, ",");
}
free(copy);
return;
}
void
setRCSLocalId(string)
char const *string;
/* function: sets local RCS id and RCSLOCALID envariable */
{
static char local_id[keylength+1];
char *copy, *next, *key;
int j;
copy = strdup(string);
next = copy;
key = strtok(next, "=");
if (strlen(key) > keylength)
faterror("LocalId is too long");
VOID strcpy(local_id, key);
Keyword[LocalId] = local_id;
/* options? */
while (key = strtok(NULL, ",")) {
if (!strcmp(key, Keyword[Id]))
LocalIdMode=Id;
else if (!strcmp(key, Keyword[Header]))
LocalIdMode=Header;
else if (!strcmp(key, Keyword[CVSHeader]))
LocalIdMode=CVSHeader;
else
error("Unknown LocalId mode");
}
free(copy);
}

1568
gnu/usr.bin/rcs/lib/rcslex.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,69 @@
/* RCS map of character types */
/* Copyright (C) 1982, 1988, 1989 Walter Tichy
Copyright 1990, 1991, 1995 by Paul Eggert
Distributed under license by the Free Software Foundation, Inc.
This file is part of RCS.
RCS 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.
RCS 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 RCS; see the file COPYING.
If not, write to the Free Software Foundation,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
Report problems and direct all questions to:
rcs-bugs@cs.purdue.edu
*/
#include "rcsbase.h"
libId(mapId, "$FreeBSD$")
/* map of character types */
/* ISO 8859/1 (Latin-1) */
enum tokens const ctab[] = {
UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN,
SPACE, SPACE, NEWLN, SPACE, SPACE, SPACE, UNKN, UNKN,
UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN,
UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN,
SPACE, IDCHAR, IDCHAR, IDCHAR, DELIM, IDCHAR, IDCHAR, IDCHAR,
IDCHAR, IDCHAR, IDCHAR, IDCHAR, DELIM, IDCHAR, PERIOD, IDCHAR,
DIGIT, DIGIT, DIGIT, DIGIT, DIGIT, DIGIT, DIGIT, DIGIT,
DIGIT, DIGIT, COLON, SEMI, IDCHAR, IDCHAR, IDCHAR, IDCHAR,
SBEGIN, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER,
LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER,
LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER,
LETTER, LETTER, LETTER, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR,
IDCHAR, Letter, Letter, Letter, Letter, Letter, Letter, Letter,
Letter, Letter, Letter, Letter, Letter, Letter, Letter, Letter,
Letter, Letter, Letter, Letter, Letter, Letter, Letter, Letter,
Letter, Letter, Letter, IDCHAR, IDCHAR, IDCHAR, IDCHAR, UNKN,
UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN,
UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN,
UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN,
UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN,
IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR,
IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR,
IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR,
IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR,
LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER,
LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER,
LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, IDCHAR,
LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, Letter,
Letter, Letter, Letter, Letter, Letter, Letter, Letter, Letter,
Letter, Letter, Letter, Letter, Letter, Letter, Letter, Letter,
Letter, Letter, Letter, Letter, Letter, Letter, Letter, IDCHAR,
Letter, Letter, Letter, Letter, Letter, Letter, Letter, Letter
};

View File

@ -0,0 +1,911 @@
/* Handle RCS revision numbers. */
/* Copyright 1982, 1988, 1989 Walter Tichy
Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert
Distributed under license by the Free Software Foundation, Inc.
This file is part of RCS.
RCS 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.
RCS 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 RCS; see the file COPYING.
If not, write to the Free Software Foundation,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
Report problems and direct all questions to:
rcs-bugs@cs.purdue.edu
*/
/*
* Revision 5.10 1995/06/16 06:19:24 eggert
* Update FSF address.
*
* Revision 5.9 1995/06/01 16:23:43 eggert
* (cmpdate, normalizeyear): New functions work around MKS RCS incompatibility.
* (cmpnum, compartial): s[d] -> *(s+d) to work around Cray compiler bug.
* (genrevs, genbranch): cmpnum -> cmpdate
*
* Revision 5.8 1994/03/17 14:05:48 eggert
* Remove lint.
*
* Revision 5.7 1993/11/09 17:40:15 eggert
* Fix format string typos.
*
* Revision 5.6 1993/11/03 17:42:27 eggert
* Revision number `.N' now stands for `D.N', where D is the default branch.
* Add -z. Improve quality of diagnostics. Add `namedrev' for Name support.
*
* Revision 5.5 1992/07/28 16:12:44 eggert
* Identifiers may now start with a digit. Avoid `unsigned'.
*
* Revision 5.4 1992/01/06 02:42:34 eggert
* while (E) ; -> while (E) continue;
*
* Revision 5.3 1991/08/19 03:13:55 eggert
* Add `-r$', `-rB.'. Remove botches like `<now>' from messages. Tune.
*
* Revision 5.2 1991/04/21 11:58:28 eggert
* Add tiprev().
*
* Revision 5.1 1991/02/25 07:12:43 eggert
* Avoid overflow when comparing revision numbers.
*
* Revision 5.0 1990/08/22 08:13:43 eggert
* Remove compile-time limits; use malloc instead.
* Ansify and Posixate. Tune.
* Remove possibility of an internal error. Remove lint.
*
* Revision 4.5 89/05/01 15:13:22 narten
* changed copyright header to reflect current distribution rules
*
* Revision 4.4 87/12/18 11:45:22 narten
* more lint cleanups. Also, the NOTREACHED comment is no longer necessary,
* since there's now a return value there with a value. (Guy Harris)
*
* Revision 4.3 87/10/18 10:38:42 narten
* Updating version numbers. Changes relative to version 1.1 actually
* relative to 4.1
*
* Revision 1.3 87/09/24 14:00:37 narten
* Sources now pass through lint (if you ignore printf/sprintf/fprintf
* warnings)
*
* Revision 1.2 87/03/27 14:22:37 jenkins
* Port to suns
*
* Revision 4.1 83/03/25 21:10:45 wft
* Only changed $Header to $Id.
*
* Revision 3.4 82/12/04 13:24:08 wft
* Replaced getdelta() with gettree().
*
* Revision 3.3 82/11/28 21:33:15 wft
* fixed compartial() and compnum() for nil-parameters; fixed nils
* in error messages. Testprogram output shortenend.
*
* Revision 3.2 82/10/18 21:19:47 wft
* renamed compnum->cmpnum, compnumfld->cmpnumfld,
* numericrevno->numricrevno.
*
* Revision 3.1 82/10/11 19:46:09 wft
* changed expandsym() to check for source==nil; returns zero length string
* in that case.
*/
#include "rcsbase.h"
libId(revId, "$FreeBSD$")
static char const *branchtip P((char const*));
static char const *lookupsym P((char const*));
static char const *normalizeyear P((char const*,char[5]));
static struct hshentry *genbranch P((struct hshentry const*,char const*,int,char const*,char const*,char const*,struct hshentries**));
static void absent P((char const*,int));
static void cantfindbranch P((char const*,char const[datesize],char const*,char const*));
static void store1 P((struct hshentries***,struct hshentry*));
int
countnumflds(s)
char const *s;
/* Given a pointer s to a dotted number (date or revision number),
* countnumflds returns the number of digitfields in s.
*/
{
register char const *sp;
register int count;
if (!(sp=s) || !*sp)
return 0;
count = 1;
do {
if (*sp++ == '.') count++;
} while (*sp);
return(count);
}
void
getbranchno(revno,branchno)
char const *revno;
struct buf *branchno;
/* Given a revision number revno, getbranchno copies the number of the branch
* on which revno is into branchno. If revno itself is a branch number,
* it is copied unchanged.
*/
{
register int numflds;
register char *tp;
bufscpy(branchno, revno);
numflds=countnumflds(revno);
if (!(numflds & 1)) {
tp = branchno->string;
while (--numflds)
while (*tp++ != '.')
continue;
*(tp-1)='\0';
}
}
int cmpnum(num1, num2)
char const *num1, *num2;
/* compares the two dotted numbers num1 and num2 lexicographically
* by field. Individual fields are compared numerically.
* returns <0, 0, >0 if num1<num2, num1==num2, and num1>num2, resp.
* omitted fields are assumed to be higher than the existing ones.
*/
{
register char const *s1, *s2;
register size_t d1, d2;
register int r;
s1 = num1 ? num1 : "";
s2 = num2 ? num2 : "";
for (;;) {
/* Give precedence to shorter one. */
if (!*s1)
return (unsigned char)*s2;
if (!*s2)
return -1;
/* Strip leading zeros, then find number of digits. */
while (*s1=='0') ++s1;
while (*s2=='0') ++s2;
for (d1=0; isdigit(*(s1+d1)); d1++) continue;
for (d2=0; isdigit(*(s2+d2)); d2++) continue;
/* Do not convert to integer; it might overflow! */
if (d1 != d2)
return d1<d2 ? -1 : 1;
if ((r = memcmp(s1, s2, d1)))
return r;
s1 += d1;
s2 += d1;
/* skip '.' */
if (*s1) s1++;
if (*s2) s2++;
}
}
int cmpnumfld(num1, num2, fld)
char const *num1, *num2;
int fld;
/* Compare the two dotted numbers at field fld.
* num1 and num2 must have at least fld fields.
* fld must be positive.
*/
{
register char const *s1, *s2;
register size_t d1, d2;
s1 = num1;
s2 = num2;
/* skip fld-1 fields */
while (--fld) {
while (*s1++ != '.')
continue;
while (*s2++ != '.')
continue;
}
/* Now s1 and s2 point to the beginning of the respective fields */
while (*s1=='0') ++s1; for (d1=0; isdigit(*(s1+d1)); d1++) continue;
while (*s2=='0') ++s2; for (d2=0; isdigit(*(s2+d2)); d2++) continue;
return d1<d2 ? -1 : d1==d2 ? memcmp(s1,s2,d1) : 1;
}
int
cmpdate(d1, d2)
char const *d1, *d2;
/*
* Compare the two dates. This is just like cmpnum,
* except that for compatibility with old versions of RCS,
* 1900 is added to dates with two-digit years.
*/
{
char year1[5], year2[5];
int r = cmpnumfld(normalizeyear(d1,year1), normalizeyear(d2,year2), 1);
if (r)
return r;
else {
while (isdigit(*d1)) d1++; d1 += *d1=='.';
while (isdigit(*d2)) d2++; d2 += *d2=='.';
return cmpnum(d1, d2);
}
}
static char const *
normalizeyear(date, year)
char const *date;
char year[5];
{
if (isdigit(date[0]) && isdigit(date[1]) && !isdigit(date[2])) {
year[0] = '1';
year[1] = '9';
year[2] = date[0];
year[3] = date[1];
year[4] = 0;
return year;
} else
return date;
}
static void
cantfindbranch(revno, date, author, state)
char const *revno, date[datesize], *author, *state;
{
char datebuf[datesize + zonelenmax];
rcserror("No revision on branch %s has%s%s%s%s%s%s.",
revno,
date ? " a date before " : "",
date ? date2str(date,datebuf) : "",
author ? " and author "+(date?0:4) : "",
author ? author : "",
state ? " and state "+(date||author?0:4) : "",
state ? state : ""
);
}
static void
absent(revno, field)
char const *revno;
int field;
{
struct buf t;
bufautobegin(&t);
rcserror("%s %s absent", field&1?"revision":"branch",
partialno(&t,revno,field)
);
bufautoend(&t);
}
int
compartial(num1, num2, length)
char const *num1, *num2;
int length;
/* compare the first "length" fields of two dot numbers;
the omitted field is considered to be larger than any number */
/* restriction: at least one number has length or more fields */
{
register char const *s1, *s2;
register size_t d1, d2;
register int r;
s1 = num1; s2 = num2;
if (!s1) return 1;
if (!s2) return -1;
for (;;) {
if (!*s1) return 1;
if (!*s2) return -1;
while (*s1=='0') ++s1; for (d1=0; isdigit(*(s1+d1)); d1++) continue;
while (*s2=='0') ++s2; for (d2=0; isdigit(*(s2+d2)); d2++) continue;
if (d1 != d2)
return d1<d2 ? -1 : 1;
if ((r = memcmp(s1, s2, d1)))
return r;
if (!--length)
return 0;
s1 += d1;
s2 += d1;
if (*s1 == '.') s1++;
if (*s2 == '.') s2++;
}
}
char * partialno(rev1,rev2,length)
struct buf *rev1;
char const *rev2;
register int length;
/* Function: Copies length fields of revision number rev2 into rev1.
* Return rev1's string.
*/
{
register char *r1;
bufscpy(rev1, rev2);
r1 = rev1->string;
while (length) {
while (*r1!='.' && *r1)
++r1;
++r1;
length--;
}
/* eliminate last '.'*/
*(r1-1)='\0';
return rev1->string;
}
static void
store1(store, next)
struct hshentries ***store;
struct hshentry *next;
/*
* Allocate a new list node that addresses NEXT.
* Append it to the list that **STORE is the end pointer of.
*/
{
register struct hshentries *p;
p = ftalloc(struct hshentries);
p->first = next;
**store = p;
*store = &p->rest;
}
struct hshentry * genrevs(revno,date,author,state,store)
char const *revno, *date, *author, *state;
struct hshentries **store;
/* Function: finds the deltas needed for reconstructing the
* revision given by revno, date, author, and state, and stores pointers
* to these deltas into a list whose starting address is given by store.
* The last delta (target delta) is returned.
* If the proper delta could not be found, 0 is returned.
*/
{
int length;
register struct hshentry * next;
int result;
char const *branchnum;
struct buf t;
char datebuf[datesize + zonelenmax];
bufautobegin(&t);
if (!(next = Head)) {
rcserror("RCS file empty");
goto norev;
}
length = countnumflds(revno);
if (length >= 1) {
/* at least one field; find branch exactly */
while ((result=cmpnumfld(revno,next->num,1)) < 0) {
store1(&store, next);
next = next->next;
if (!next) {
rcserror("branch number %s too low", partialno(&t,revno,1));
goto norev;
}
}
if (result>0) {
absent(revno, 1);
goto norev;
}
}
if (length<=1){
/* pick latest one on given branch */
branchnum = next->num; /* works even for empty revno*/
while (next &&
cmpnumfld(branchnum,next->num,1) == 0 &&
(
(date && cmpdate(date,next->date) < 0) ||
(author && strcmp(author,next->author) != 0) ||
(state && strcmp(state,next->state) != 0)
)
)
{
store1(&store, next);
next=next->next;
}
if (!next ||
(cmpnumfld(branchnum,next->num,1)!=0))/*overshot*/ {
cantfindbranch(
length ? revno : partialno(&t,branchnum,1),
date, author, state
);
goto norev;
} else {
store1(&store, next);
}
*store = 0;
return next;
}
/* length >=2 */
/* find revision; may go low if length==2*/
while ((result=cmpnumfld(revno,next->num,2)) < 0 &&
(cmpnumfld(revno,next->num,1)==0) ) {
store1(&store, next);
next = next->next;
if (!next)
break;
}
if (!next || cmpnumfld(revno,next->num,1) != 0) {
rcserror("revision number %s too low", partialno(&t,revno,2));
goto norev;
}
if ((length>2) && (result!=0)) {
absent(revno, 2);
goto norev;
}
/* print last one */
store1(&store, next);
if (length>2)
return genbranch(next,revno,length,date,author,state,store);
else { /* length == 2*/
if (date && cmpdate(date,next->date)<0) {
rcserror("Revision %s has date %s.",
next->num,
date2str(next->date, datebuf)
);
return 0;
}
if (author && strcmp(author,next->author)!=0) {
rcserror("Revision %s has author %s.",
next->num, next->author
);
return 0;
}
if (state && strcmp(state,next->state)!=0) {
rcserror("Revision %s has state %s.",
next->num,
next->state ? next->state : "<empty>"
);
return 0;
}
*store = 0;
return next;
}
norev:
bufautoend(&t);
return 0;
}
static struct hshentry *
genbranch(bpoint, revno, length, date, author, state, store)
struct hshentry const *bpoint;
char const *revno;
int length;
char const *date, *author, *state;
struct hshentries **store;
/* Function: given a branchpoint, a revision number, date, author, and state,
* genbranch finds the deltas necessary to reconstruct the given revision
* from the branch point on.
* Pointers to the found deltas are stored in a list beginning with store.
* revno must be on a side branch.
* Return 0 on error.
*/
{
int field;
register struct hshentry * next, * trail;
register struct branchhead const *bhead;
int result;
struct buf t;
char datebuf[datesize + zonelenmax];
field = 3;
bhead = bpoint->branches;
do {
if (!bhead) {
bufautobegin(&t);
rcserror("no side branches present for %s",
partialno(&t,revno,field-1)
);
bufautoend(&t);
return 0;
}
/*find branch head*/
/*branches are arranged in increasing order*/
while (0 < (result=cmpnumfld(revno,bhead->hsh->num,field))) {
bhead = bhead->nextbranch;
if (!bhead) {
bufautobegin(&t);
rcserror("branch number %s too high",
partialno(&t,revno,field)
);
bufautoend(&t);
return 0;
}
}
if (result<0) {
absent(revno, field);
return 0;
}
next = bhead->hsh;
if (length==field) {
/* pick latest one on that branch */
trail = 0;
do { if ((!date || cmpdate(date,next->date)>=0) &&
(!author || strcmp(author,next->author)==0) &&
(!state || strcmp(state,next->state)==0)
) trail = next;
next=next->next;
} while (next);
if (!trail) {
cantfindbranch(revno, date, author, state);
return 0;
} else { /* print up to last one suitable */
next = bhead->hsh;
while (next!=trail) {
store1(&store, next);
next=next->next;
}
store1(&store, next);
}
*store = 0;
return next;
}
/* length > field */
/* find revision */
/* check low */
if (cmpnumfld(revno,next->num,field+1)<0) {
bufautobegin(&t);
rcserror("revision number %s too low",
partialno(&t,revno,field+1)
);
bufautoend(&t);
return 0;
}
do {
store1(&store, next);
trail = next;
next = next->next;
} while (next && cmpnumfld(revno,next->num,field+1)>=0);
if ((length>field+1) && /*need exact hit */
(cmpnumfld(revno,trail->num,field+1) !=0)){
absent(revno, field+1);
return 0;
}
if (length == field+1) {
if (date && cmpdate(date,trail->date)<0) {
rcserror("Revision %s has date %s.",
trail->num,
date2str(trail->date, datebuf)
);
return 0;
}
if (author && strcmp(author,trail->author)!=0) {
rcserror("Revision %s has author %s.",
trail->num, trail->author
);
return 0;
}
if (state && strcmp(state,trail->state)!=0) {
rcserror("Revision %s has state %s.",
trail->num,
trail->state ? trail->state : "<empty>"
);
return 0;
}
}
bhead = trail->branches;
} while ((field+=2) <= length);
*store = 0;
return trail;
}
static char const *
lookupsym(id)
char const *id;
/* Function: looks up id in the list of symbolic names starting
* with pointer SYMBOLS, and returns a pointer to the corresponding
* revision number. Return 0 if not present.
*/
{
register struct assoc const *next;
for (next = Symbols; next; next = next->nextassoc)
if (strcmp(id, next->symbol)==0)
return next->num;
return 0;
}
int expandsym(source, target)
char const *source;
struct buf *target;
/* Function: Source points to a revision number. Expandsym copies
* the number to target, but replaces all symbolic fields in the
* source number with their numeric values.
* Expand a branch followed by `.' to the latest revision on that branch.
* Ignore `.' after a revision. Remove leading zeros.
* returns false on error;
*/
{
return fexpandsym(source, target, (RILE*)0);
}
int
fexpandsym(source, target, fp)
char const *source;
struct buf *target;
RILE *fp;
/* Same as expandsym, except if FP is nonzero, it is used to expand KDELIM. */
{
register char const *sp, *bp;
register char *tp;
char const *tlim;
int dots;
sp = source;
bufalloc(target, 1);
tp = target->string;
if (!sp || !*sp) { /* Accept 0 pointer as a legal value. */
*tp='\0';
return true;
}
if (sp[0] == KDELIM && !sp[1]) {
if (!getoldkeys(fp))
return false;
if (!*prevrev.string) {
workerror("working file lacks revision number");
return false;
}
bufscpy(target, prevrev.string);
return true;
}
tlim = tp + target->size;
dots = 0;
for (;;) {
register char *p = tp;
size_t s = tp - target->string;
int id = false;
for (;;) {
switch (ctab[(unsigned char)*sp]) {
case IDCHAR:
case LETTER:
case Letter:
id = true;
/* fall into */
case DIGIT:
if (tlim <= p)
p = bufenlarge(target, &tlim);
*p++ = *sp++;
continue;
default:
break;
}
break;
}
if (tlim <= p)
p = bufenlarge(target, &tlim);
*p = 0;
tp = target->string + s;
if (id) {
bp = lookupsym(tp);
if (!bp) {
rcserror("Symbolic name `%s' is undefined.",tp);
return false;
}
} else {
/* skip leading zeros */
for (bp = tp; *bp=='0' && isdigit(bp[1]); bp++)
continue;
if (!*bp)
if (s || *sp!='.')
break;
else {
/* Insert default branch before initial `.'. */
char const *b;
if (Dbranch)
b = Dbranch;
else if (Head)
b = Head->num;
else
break;
getbranchno(b, target);
bp = tp = target->string;
tlim = tp + target->size;
}
}
while ((*tp++ = *bp++))
if (tlim <= tp)
tp = bufenlarge(target, &tlim);
switch (*sp++) {
case '\0':
return true;
case '.':
if (!*sp) {
if (dots & 1)
break;
if (!(bp = branchtip(target->string)))
return false;
bufscpy(target, bp);
return true;
}
++dots;
tp[-1] = '.';
continue;
}
break;
}
rcserror("improper revision number: %s", source);
return false;
}
char const *
namedrev(name, delta)
char const *name;
struct hshentry *delta;
/* Yield NAME if it names DELTA, 0 otherwise. */
{
if (name) {
char const *id = 0, *p, *val;
for (p = name; ; p++)
switch (ctab[(unsigned char)*p]) {
case IDCHAR:
case LETTER:
case Letter:
id = name;
break;
case DIGIT:
break;
case UNKN:
if (!*p && id &&
(val = lookupsym(id)) &&
strcmp(val, delta->num) == 0
)
return id;
/* fall into */
default:
return 0;
}
}
return 0;
}
static char const *
branchtip(branch)
char const *branch;
{
struct hshentry *h;
struct hshentries *hs;
h = genrevs(branch, (char*)0, (char*)0, (char*)0, &hs);
return h ? h->num : (char const*)0;
}
char const *
tiprev()
{
return Dbranch ? branchtip(Dbranch) : Head ? Head->num : (char const*)0;
}
#ifdef REVTEST
/*
* Test the routines that generate a sequence of delta numbers
* needed to regenerate a given delta.
*/
char const cmdid[] = "revtest";
int
main(argc,argv)
int argc; char * argv[];
{
static struct buf numricrevno;
char symrevno[100]; /* used for input of revision numbers */
char author[20];
char state[20];
char date[20];
struct hshentries *gendeltas;
struct hshentry * target;
int i;
if (argc<2) {
aputs("No input file\n",stderr);
exitmain(EXIT_FAILURE);
}
if (!(finptr=Iopen(argv[1], FOPEN_R, (struct stat*)0))) {
faterror("can't open input file %s", argv[1]);
}
Lexinit();
getadmin();
gettree();
getdesc(false);
do {
/* all output goes to stderr, to have diagnostics and */
/* errors in sequence. */
aputs("\nEnter revision number or <return> or '.': ",stderr);
if (!fgets(symrevno, 100, stdin)) break;
if (*symrevno == '.') break;
aprintf(stderr,"%s;\n",symrevno);
expandsym(symrevno,&numricrevno);
aprintf(stderr,"expanded number: %s; ",numricrevno.string);
aprintf(stderr,"Date: ");
fgets(date, 20, stdin); aprintf(stderr,"%s; ",date);
aprintf(stderr,"Author: ");
fgets(author, 20, stdin); aprintf(stderr,"%s; ",author);
aprintf(stderr,"State: ");
fgets(state, 20, stdin); aprintf(stderr, "%s;\n", state);
target = genrevs(numricrevno.string, *date?date:(char *)0, *author?author:(char *)0,
*state?state:(char*)0, &gendeltas);
if (target) {
while (gendeltas) {
aprintf(stderr,"%s\n",gendeltas->first->num);
gendeltas = gendeltas->next;
}
}
} while (true);
aprintf(stderr,"done\n");
exitmain(EXIT_SUCCESS);
}
void exiterr() { _exit(EXIT_FAILURE); }
#endif

View File

@ -0,0 +1,681 @@
/* RCS file syntactic analysis */
/******************************************************************************
* Syntax Analysis.
* Keyword table
* Testprogram: define SYNTEST
* Compatibility with Release 2: define COMPAT2=1
******************************************************************************
*/
/* Copyright 1982, 1988, 1989 Walter Tichy
Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert
Distributed under license by the Free Software Foundation, Inc.
This file is part of RCS.
RCS 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.
RCS 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 RCS; see the file COPYING.
If not, write to the Free Software Foundation,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
Report problems and direct all questions to:
rcs-bugs@cs.purdue.edu
*/
/*
* Revision 5.15 1995/06/16 06:19:24 eggert
* Update FSF address.
*
* Revision 5.14 1995/06/01 16:23:43 eggert
* (expand_names): Add "b" for -kb.
* (getdelta): Don't strip leading "19" from MKS RCS dates; see cmpdate.
*
* Revision 5.13 1994/03/20 04:52:58 eggert
* Remove lint.
*
* Revision 5.12 1993/11/03 17:42:27 eggert
* Parse MKS RCS dates; ignore \r in diff control lines.
* Don't discard ignored phrases. Improve quality of diagnostics.
*
* Revision 5.11 1992/07/28 16:12:44 eggert
* Avoid `unsigned'. Statement macro names now end in _.
*
* Revision 5.10 1992/01/24 18:44:19 eggert
* Move put routines to rcsgen.c.
*
* Revision 5.9 1992/01/06 02:42:34 eggert
* ULONG_MAX/10 -> ULONG_MAX_OVER_10
* while (E) ; -> while (E) continue;
*
* Revision 5.8 1991/08/19 03:13:55 eggert
* Tune.
*
* Revision 5.7 1991/04/21 11:58:29 eggert
* Disambiguate names on shortname hosts.
* Fix errno bug. Add MS-DOS support.
*
* Revision 5.6 1991/02/28 19:18:51 eggert
* Fix null termination bug in reporting keyword expansion.
*
* Revision 5.5 1991/02/25 07:12:44 eggert
* Check diff output more carefully; avoid overflow.
*
* Revision 5.4 1990/11/01 05:28:48 eggert
* When ignoring unknown phrases, copy them to the output RCS file.
* Permit arbitrary data in logs and comment leaders.
* Don't check for nontext on initial checkin.
*
* Revision 5.3 1990/09/20 07:58:32 eggert
* Remove the test for non-text bytes; it caused more pain than it cured.
*
* Revision 5.2 1990/09/04 08:02:30 eggert
* Parse RCS files with no revisions.
* Don't strip leading white space from diff commands. Count RCS lines better.
*
* Revision 5.1 1990/08/29 07:14:06 eggert
* Add -kkvl. Clean old log messages too.
*
* Revision 5.0 1990/08/22 08:13:44 eggert
* Try to parse future RCS formats without barfing.
* Add -k. Don't require final newline.
* Remove compile-time limits; use malloc instead.
* Don't output branch keyword if there's no default branch,
* because RCS version 3 doesn't understand it.
* Tune. Remove lint.
* Add support for ISO 8859. Ansify and Posixate.
* Check that a newly checked-in file is acceptable as input to 'diff'.
* Check diff's output.
*
* Revision 4.6 89/05/01 15:13:32 narten
* changed copyright header to reflect current distribution rules
*
* Revision 4.5 88/08/09 19:13:21 eggert
* Allow cc -R; remove lint.
*
* Revision 4.4 87/12/18 11:46:16 narten
* more lint cleanups (Guy Harris)
*
* Revision 4.3 87/10/18 10:39:36 narten
* Updating version numbers. Changes relative to 1.1 actually relative to
* 4.1
*
* Revision 1.3 87/09/24 14:00:49 narten
* Sources now pass through lint (if you ignore printf/sprintf/fprintf
* warnings)
*
* Revision 1.2 87/03/27 14:22:40 jenkins
* Port to suns
*
* Revision 4.1 83/03/28 11:38:49 wft
* Added parsing and printing of default branch.
*
* Revision 3.6 83/01/15 17:46:50 wft
* Changed readdelta() to initialize selector and log-pointer.
* Changed puttree to check for selector==DELETE; putdtext() uses DELNUMFORM.
*
* Revision 3.5 82/12/08 21:58:58 wft
* renamed Commentleader to Commleader.
*
* Revision 3.4 82/12/04 13:24:40 wft
* Added routine gettree(), which updates keeplock after reading the
* delta tree.
*
* Revision 3.3 82/11/28 21:30:11 wft
* Reading and printing of Suffix removed; version COMPAT2 skips the
* Suffix for files of release 2 format. Fixed problems with printing nil.
*
* Revision 3.2 82/10/18 21:18:25 wft
* renamed putdeltatext to putdtext.
*
* Revision 3.1 82/10/11 19:45:11 wft
* made sure getc() returns into an integer.
*/
/* version COMPAT2 reads files of the format of release 2 and 3, but
* generates files of release 3 format. Need not be defined if no
* old RCS files generated with release 2 exist.
*/
#include "rcsbase.h"
libId(synId, "$FreeBSD$")
static char const *getkeyval P((char const*,enum tokens,int));
static int getdelta P((void));
static int strn2expmode P((char const*,size_t));
static struct hshentry *getdnum P((void));
static void badDiffOutput P((char const*)) exiting;
static void diffLineNumberTooLarge P((char const*)) exiting;
static void getsemi P((char const*));
/* keyword table */
char const
Kaccess[] = "access",
Kauthor[] = "author",
Kbranch[] = "branch",
Kcomment[] = "comment",
Kdate[] = "date",
Kdesc[] = "desc",
Kexpand[] = "expand",
Khead[] = "head",
Klocks[] = "locks",
Klog[] = "log",
Knext[] = "next",
Kstate[] = "state",
Kstrict[] = "strict",
Ksymbols[] = "symbols",
Ktext[] = "text";
static char const
#if COMPAT2
Ksuffix[] = "suffix",
#endif
K_branches[]= "branches";
static struct buf Commleader;
struct cbuf Comment;
struct cbuf Ignored;
struct access * AccessList;
struct assoc * Symbols;
struct rcslock *Locks;
int Expand;
int StrictLocks;
struct hshentry * Head;
char const * Dbranch;
int TotalDeltas;
static void
getsemi(key)
char const *key;
/* Get a semicolon to finish off a phrase started by KEY. */
{
if (!getlex(SEMI))
fatserror("missing ';' after '%s'", key);
}
static struct hshentry *
getdnum()
/* Get a delta number. */
{
register struct hshentry *delta = getnum();
if (delta && countnumflds(delta->num)&1)
fatserror("%s isn't a delta number", delta->num);
return delta;
}
void
getadmin()
/* Read an <admin> and initialize the appropriate global variables. */
{
register char const *id;
struct access * newaccess;
struct assoc * newassoc;
struct rcslock *newlock;
struct hshentry * delta;
struct access **LastAccess;
struct assoc **LastSymbol;
struct rcslock **LastLock;
struct buf b;
struct cbuf cb;
TotalDeltas=0;
getkey(Khead);
Head = getdnum();
getsemi(Khead);
Dbranch = 0;
if (getkeyopt(Kbranch)) {
if ((delta = getnum()))
Dbranch = delta->num;
getsemi(Kbranch);
}
#if COMPAT2
/* read suffix. Only in release 2 format */
if (getkeyopt(Ksuffix)) {
if (nexttok==STRING) {
readstring(); nextlex(); /* Throw away the suffix. */
} else if (nexttok==ID) {
nextlex();
}
getsemi(Ksuffix);
}
#endif
getkey(Kaccess);
LastAccess = &AccessList;
while ((id = getid())) {
newaccess = ftalloc(struct access);
newaccess->login = id;
*LastAccess = newaccess;
LastAccess = &newaccess->nextaccess;
}
*LastAccess = 0;
getsemi(Kaccess);
getkey(Ksymbols);
LastSymbol = &Symbols;
while ((id = getid())) {
if (!getlex(COLON))
fatserror("missing ':' in symbolic name definition");
if (!(delta=getnum())) {
fatserror("missing number in symbolic name definition");
} else { /*add new pair to association list*/
newassoc = ftalloc(struct assoc);
newassoc->symbol=id;
newassoc->num = delta->num;
*LastSymbol = newassoc;
LastSymbol = &newassoc->nextassoc;
}
}
*LastSymbol = 0;
getsemi(Ksymbols);
getkey(Klocks);
LastLock = &Locks;
while ((id = getid())) {
if (!getlex(COLON))
fatserror("missing ':' in lock");
if (!(delta=getdnum())) {
fatserror("missing number in lock");
} else { /*add new pair to lock list*/
newlock = ftalloc(struct rcslock);
newlock->login=id;
newlock->delta=delta;
*LastLock = newlock;
LastLock = &newlock->nextlock;
}
}
*LastLock = 0;
getsemi(Klocks);
if ((StrictLocks = getkeyopt(Kstrict)))
getsemi(Kstrict);
clear_buf(&Comment);
if (getkeyopt(Kcomment)) {
if (nexttok==STRING) {
Comment = savestring(&Commleader);
nextlex();
}
getsemi(Kcomment);
}
Expand = KEYVAL_EXPAND;
if (getkeyopt(Kexpand)) {
if (nexttok==STRING) {
bufautobegin(&b);
cb = savestring(&b);
if ((Expand = strn2expmode(cb.string,cb.size)) < 0)
fatserror("unknown expand mode %.*s",
(int)cb.size, cb.string
);
bufautoend(&b);
nextlex();
}
getsemi(Kexpand);
}
Ignored = getphrases(Kdesc);
}
char const *const expand_names[] = {
/* These must agree with *_EXPAND in rcsbase.h. */
"kv", "kvl", "k", "v", "o", "b",
0
};
int
str2expmode(s)
char const *s;
/* Yield expand mode corresponding to S, or -1 if bad. */
{
return strn2expmode(s, strlen(s));
}
static int
strn2expmode(s, n)
char const *s;
size_t n;
{
char const *const *p;
for (p = expand_names; *p; ++p)
if (memcmp(*p,s,n) == 0 && !(*p)[n])
return p - expand_names;
return -1;
}
void
ignorephrases(key)
const char *key;
/*
* Ignore a series of phrases that do not start with KEY.
* Stop when the next phrase starts with a token that is not an identifier,
* or is KEY.
*/
{
for (;;) {
nextlex();
if (nexttok != ID || strcmp(NextString,key) == 0)
break;
warnignore();
hshenter=false;
for (;; nextlex()) {
switch (nexttok) {
case SEMI: hshenter=true; break;
case ID:
case NUM: ffree1(NextString); continue;
case STRING: readstring(); continue;
default: continue;
}
break;
}
}
}
static int
getdelta()
/* Function: reads a delta block.
* returns false if the current block does not start with a number.
*/
{
register struct hshentry * Delta, * num;
struct branchhead **LastBranch, *NewBranch;
if (!(Delta = getdnum()))
return false;
hshenter = false; /*Don't enter dates into hashtable*/
Delta->date = getkeyval(Kdate, NUM, false);
hshenter=true; /*reset hshenter for revision numbers.*/
Delta->author = getkeyval(Kauthor, ID, false);
Delta->state = getkeyval(Kstate, ID, true);
getkey(K_branches);
LastBranch = &Delta->branches;
while ((num = getdnum())) {
NewBranch = ftalloc(struct branchhead);
NewBranch->hsh = num;
*LastBranch = NewBranch;
LastBranch = &NewBranch->nextbranch;
}
*LastBranch = 0;
getsemi(K_branches);
getkey(Knext);
Delta->next = num = getdnum();
getsemi(Knext);
Delta->lockedby = 0;
Delta->log.string = 0;
Delta->selector = true;
Delta->ig = getphrases(Kdesc);
TotalDeltas++;
return (true);
}
void
gettree()
/* Function: Reads in the delta tree with getdelta(), then
* updates the lockedby fields.
*/
{
struct rcslock const *currlock;
while (getdelta())
continue;
currlock=Locks;
while (currlock) {
currlock->delta->lockedby = currlock->login;
currlock = currlock->nextlock;
}
}
void
getdesc(prdesc)
int prdesc;
/* Function: read in descriptive text
* nexttok is not advanced afterwards.
* If prdesc is set, the text is printed to stdout.
*/
{
getkeystring(Kdesc);
if (prdesc)
printstring(); /*echo string*/
else readstring(); /*skip string*/
}
static char const *
getkeyval(keyword, token, optional)
char const *keyword;
enum tokens token;
int optional;
/* reads a pair of the form
* <keyword> <token> ;
* where token is one of <id> or <num>. optional indicates whether
* <token> is optional. A pointer to
* the actual character string of <id> or <num> is returned.
*/
{
register char const *val = 0;
getkey(keyword);
if (nexttok==token) {
val = NextString;
nextlex();
} else {
if (!optional)
fatserror("missing %s", keyword);
}
getsemi(keyword);
return(val);
}
void
unexpected_EOF()
{
rcsfaterror("unexpected EOF in diff output");
}
void
initdiffcmd(dc)
register struct diffcmd *dc;
/* Initialize *dc suitably for getdiffcmd(). */
{
dc->adprev = 0;
dc->dafter = 0;
}
static void
badDiffOutput(buf)
char const *buf;
{
rcsfaterror("bad diff output line: %s", buf);
}
static void
diffLineNumberTooLarge(buf)
char const *buf;
{
rcsfaterror("diff line number too large: %s", buf);
}
int
getdiffcmd(finfile, delimiter, foutfile, dc)
RILE *finfile;
FILE *foutfile;
int delimiter;
struct diffcmd *dc;
/* Get a editing command output by 'diff -n' from fin.
* The input is delimited by SDELIM if delimiter is set, EOF otherwise.
* Copy a clean version of the command to fout (if nonnull).
* Yield 0 for 'd', 1 for 'a', and -1 for EOF.
* Store the command's line number and length into dc->line1 and dc->nlines.
* Keep dc->adprev and dc->dafter up to date.
*/
{
register int c;
declarecache;
register FILE *fout;
register char *p;
register RILE *fin;
long line1, nlines, t;
char buf[BUFSIZ];
fin = finfile;
fout = foutfile;
setupcache(fin); cache(fin);
cachegeteof_(c, { if (delimiter) unexpected_EOF(); return -1; } )
if (delimiter) {
if (c==SDELIM) {
cacheget_(c)
if (c==SDELIM) {
buf[0] = c;
buf[1] = 0;
badDiffOutput(buf);
}
uncache(fin);
nextc = c;
if (fout)
aprintf(fout, "%c%c", SDELIM, c);
return -1;
}
}
p = buf;
do {
if (buf+BUFSIZ-2 <= p) {
rcsfaterror("diff output command line too long");
}
*p++ = c;
cachegeteof_(c, unexpected_EOF();)
} while (c != '\n');
uncache(fin);
if (delimiter)
++rcsline;
*p = '\0';
for (p = buf+1; (c = *p++) == ' '; )
continue;
line1 = 0;
while (isdigit(c)) {
if (
LONG_MAX/10 < line1 ||
(t = line1 * 10, (line1 = t + (c - '0')) < t)
)
diffLineNumberTooLarge(buf);
c = *p++;
}
while (c == ' ')
c = *p++;
nlines = 0;
while (isdigit(c)) {
if (
LONG_MAX/10 < nlines ||
(t = nlines * 10, (nlines = t + (c - '0')) < t)
)
diffLineNumberTooLarge(buf);
c = *p++;
}
if (c == '\r')
c = *p++;
if (c || !nlines) {
badDiffOutput(buf);
}
if (line1+nlines < line1)
diffLineNumberTooLarge(buf);
switch (buf[0]) {
case 'a':
if (line1 < dc->adprev) {
rcsfaterror("backward insertion in diff output: %s", buf);
}
dc->adprev = line1 + 1;
break;
case 'd':
if (line1 < dc->adprev || line1 < dc->dafter) {
rcsfaterror("backward deletion in diff output: %s", buf);
}
dc->adprev = line1;
dc->dafter = line1 + nlines;
break;
default:
badDiffOutput(buf);
}
if (fout) {
aprintf(fout, "%s\n", buf);
}
dc->line1 = line1;
dc->nlines = nlines;
return buf[0] == 'a';
}
#ifdef SYNTEST
/* Input an RCS file and print its internal data structures. */
char const cmdid[] = "syntest";
int
main(argc,argv)
int argc; char * argv[];
{
if (argc<2) {
aputs("No input file\n",stderr);
exitmain(EXIT_FAILURE);
}
if (!(finptr = Iopen(argv[1], FOPEN_R, (struct stat*)0))) {
faterror("can't open input file %s", argv[1]);
}
Lexinit();
getadmin();
fdlock = STDOUT_FILENO;
putadmin();
gettree();
getdesc(true);
nextlex();
if (!eoflex()) {
fatserror("expecting EOF");
}
exitmain(EXIT_SUCCESS);
}
void exiterr() { _exit(EXIT_FAILURE); }
#endif

View File

@ -0,0 +1,191 @@
/* Convert between RCS time format and Posix and/or C formats. */
/* Copyright 1992, 1993, 1994, 1995 Paul Eggert
Distributed under license by the Free Software Foundation, Inc.
This file is part of RCS.
RCS 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.
RCS 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 RCS; see the file COPYING.
If not, write to the Free Software Foundation,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
Report problems and direct all questions to:
rcs-bugs@cs.purdue.edu
*/
#include "rcsbase.h"
#include "partime.h"
#include "maketime.h"
libId(rcstimeId, "$FreeBSD$")
static long zone_offset; /* seconds east of UTC, or TM_LOCAL_ZONE */
static int use_zone_offset; /* if zero, use UTC without zone indication */
/*
* Convert Unix time to RCS format.
* For compatibility with older versions of RCS,
* dates from 1900 through 1999 are stored without the leading "19".
*/
void
time2date(unixtime,date)
time_t unixtime;
char date[datesize];
{
register struct tm const *tm = time2tm(unixtime, RCSversion<VERSION(5));
VOID sprintf(date,
# if has_printf_dot
"%.2d.%.2d.%.2d.%.2d.%.2d.%.2d",
# else
"%02d.%02d.%02d.%02d.%02d.%02d",
# endif
tm->tm_year + ((unsigned)tm->tm_year < 100 ? 0 : 1900),
tm->tm_mon+1, tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec
);
}
/* Like str2time, except die if an error was found. */
static time_t str2time_checked P((char const*,time_t,long));
static time_t
str2time_checked(source, default_time, default_zone)
char const *source;
time_t default_time;
long default_zone;
{
time_t t = str2time(source, default_time, default_zone);
if (t == -1)
faterror("unknown date/time: %s", source);
return t;
}
/*
* Parse a free-format date in SOURCE, convert it
* into RCS internal format, and store the result into TARGET.
*/
void
str2date(source, target)
char const *source;
char target[datesize];
{
time2date(
str2time_checked(source, now(),
use_zone_offset ? zone_offset
: RCSversion<VERSION(5) ? TM_LOCAL_ZONE
: 0
),
target
);
}
/* Convert an RCS internal format date to time_t. */
time_t
date2time(source)
char const source[datesize];
{
char s[datesize + zonelenmax];
return str2time_checked(date2str(source, s), (time_t)0, 0);
}
/* Set the time zone for date2str output. */
void
zone_set(s)
char const *s;
{
if ((use_zone_offset = *s)) {
long zone;
char const *zonetail = parzone(s, &zone);
if (!zonetail || *zonetail)
error("%s: not a known time zone", s);
else
zone_offset = zone;
}
}
/*
* Format a user-readable form of the RCS format DATE into the buffer DATEBUF.
* Yield DATEBUF.
*/
char const *
date2str(date, datebuf)
char const date[datesize];
char datebuf[datesize + zonelenmax];
{
register char const *p = date;
while (*p++ != '.')
continue;
if (!use_zone_offset)
VOID sprintf(datebuf,
"19%.*s/%.2s/%.2s %.2s:%.2s:%s"
+ (date[2]=='.' && VERSION(5)<=RCSversion ? 0 : 2),
(int)(p-date-1), date,
p, p+3, p+6, p+9, p+12
);
else {
struct tm t;
struct tm const *z;
int non_hour;
long zone;
char c;
t.tm_year = atoi(date) - (date[2]=='.' ? 0 : 1900);
t.tm_mon = atoi(p) - 1;
t.tm_mday = atoi(p+3);
t.tm_hour = atoi(p+6);
t.tm_min = atoi(p+9);
t.tm_sec = atoi(p+12);
t.tm_wday = -1;
zone = zone_offset;
if (zone == TM_LOCAL_ZONE) {
time_t u = tm2time(&t, 0), d;
z = localtime(&u);
d = difftm(z, &t);
zone = (time_t)-1 < 0 || d < -d ? d : -(long)-d;
} else {
adjzone(&t, zone);
z = &t;
}
c = '+';
if (zone < 0) {
zone = -zone;
c = '-';
}
VOID sprintf(datebuf,
# if has_printf_dot
"%.2d-%.2d-%.2d %.2d:%.2d:%.2d%c%.2d",
# else
"%02d-%02d-%02d %02d:%02d:%02d%c%02d",
# endif
z->tm_year + 1900,
z->tm_mon + 1, z->tm_mday, z->tm_hour, z->tm_min, z->tm_sec,
c, (int) (zone / (60*60))
);
if ((non_hour = zone % (60*60))) {
# if has_printf_dot
static char const fmt[] = ":%.2d";
# else
static char const fmt[] = ":%02d";
# endif
VOID sprintf(datebuf + strlen(datebuf), fmt, non_hour / 60);
if ((non_hour %= 60))
VOID sprintf(datebuf + strlen(datebuf), fmt, non_hour);
}
}
return datebuf;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,2 @@
#include "rcsbase.h"
char const RCS_version_string[] = "5.7";

View File

@ -0,0 +1,8 @@
PROG= merge
SRCS= merge.c
CFLAGS+= -I${.CURDIR}/../lib
LDADD= ${LIBRCS}
DPADD= ${LIBRCS}
.include "../../Makefile.inc"
.include <bsd.prog.mk>

View File

@ -0,0 +1,137 @@
.de Id
.ds Rv \\$3
.ds Dt \\$4
..
.Id $FreeBSD$
.ds r \&\s-1RCS\s0
.TH MERGE 1 \*(Dt GNU
.SH NAME
merge \- three-way file merge
.SH SYNOPSIS
.B merge
[
.I "options"
]
.I "file1 file2 file3"
.SH DESCRIPTION
.B merge
incorporates all changes that lead from
.I file2
to
.I file3
into
.IR file1 .
The result ordinarily goes into
.IR file1 .
.B merge
is useful for combining separate changes to an original. Suppose
.I file2
is the original, and both
.I file1
and
.I file3
are modifications of
.IR file2 .
Then
.B merge
combines both changes.
.PP
A conflict occurs if both
.I file1
and
.I file3
have changes in a common segment of lines.
If a conflict is found,
.B merge
normally outputs a warning and brackets the conflict with
.B <<<<<<<
and
.B >>>>>>>
lines.
A typical conflict will look like this:
.LP
.RS
.nf
.BI <<<<<<< " file A"
.I "lines in file A"
.B "======="
.I "lines in file B"
.BI >>>>>>> " file B"
.RE
.fi
.LP
If there are conflicts, the user should edit the result and delete one of the
alternatives.
.SH OPTIONS
.TP
.B \-A
Output conflicts using the
.B \-A
style of
.BR diff3 (1),
if supported by
.BR diff3 .
This merges all changes leading from
.I file2
to
.I file3
into
.IR file1 ,
and generates the most verbose output.
.TP
\f3\-E\fP, \f3\-e\fP
These options specify conflict styles that generate less information
than
.BR \-A .
See
.BR diff3 (1)
for details.
The default is
.BR \-E .
With
.BR \-e ,
.B merge
does not warn about conflicts.
.TP
.BI \-L " label"
This option may be given up to three times, and specifies labels
to be used in place of the corresponding file names in conflict reports.
That is,
.B "merge\ \-L\ x\ \-L\ y\ \-L\ z\ a\ b\ c"
generates output that looks like it came from files
.BR x ,
.B y
and
.B z
instead of from files
.BR a ,
.B b
and
.BR c .
.TP
.BI \-p
Send results to standard output instead of overwriting
.IR file1 .
.TP
.BI \-q
Quiet; do not warn about conflicts.
.TP
.BI \-V
Print \*r's version number.
.SH DIAGNOSTICS
Exit status is 0 for no conflicts, 1 for some conflicts, 2 for trouble.
.SH IDENTIFICATION
Author: Walter F. Tichy.
.br
Manual Page Revision: \*(Rv; Release Date: \*(Dt.
.br
Copyright \(co 1982, 1988, 1989 Walter F. Tichy.
.br
Copyright \(co 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert.
.SH SEE ALSO
diff3(1), diff(1), rcsmerge(1), co(1).
.SH BUGS
It normally does not make sense to merge binary files as if they were text, but
.B merge
tries to do it anyway.
.br

View File

@ -0,0 +1,113 @@
/* merge - three-way file merge */
/* Copyright 1991, 1992, 1993, 1994, 1995 Paul Eggert
Distributed under license by the Free Software Foundation, Inc.
This file is part of RCS.
RCS 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.
RCS 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 RCS; see the file COPYING.
If not, write to the Free Software Foundation,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
Report problems and direct all questions to:
rcs-bugs@cs.purdue.edu
*/
#include "rcsbase.h"
static void badoption P((char const*));
static char const usage[] =
"\nmerge: usage: merge [-AeEpqxX3] [-L lab [-L lab [-L lab]]] file1 file2 file3";
static void
badoption(a)
char const *a;
{
error("unknown option: %s%s", a, usage);
}
mainProg(mergeId, "merge", "$FreeBSD$")
{
register char const *a;
char const *arg[3], *label[3], *edarg = 0;
int labels, tostdout;
labels = 0;
tostdout = false;
for (; (a = *++argv) && *a++ == '-'; --argc) {
switch (*a++) {
case 'A': case 'E': case 'e':
if (edarg && edarg[1] != (*argv)[1])
error("%s and %s are incompatible",
edarg, *argv
);
edarg = *argv;
break;
case 'p': tostdout = true; break;
case 'q': quietflag = true; break;
case 'L':
if (3 <= labels)
faterror("too many -L options");
if (!(label[labels++] = *++argv))
faterror("-L needs following argument");
--argc;
break;
case 'V':
printf("RCS version %s\n", RCS_version_string);
exitmain(0);
default:
badoption(a - 2);
continue;
}
if (*a)
badoption(a - 2);
}
if (argc != 4)
faterror("%s arguments%s",
argc<4 ? "not enough" : "too many", usage
);
/* This copy keeps us `const'-clean. */
arg[0] = argv[0];
arg[1] = argv[1];
arg[2] = argv[2];
for (; labels < 3; labels++)
label[labels] = arg[labels];
if (nerror)
exiterr();
exitmain(merge(tostdout, edarg, label, arg));
}
#if RCS_lint
# define exiterr mergeExit
#endif
void
exiterr()
{
tempunlink();
_exit(DIFF_TROUBLE);
}

View File

@ -0,0 +1,10 @@
# $FreeBSD$
PROG= rcs
MAN= rcs.1 rcsintro.1 rcsfile.5
CFLAGS+= -I${.CURDIR}/../lib
LDADD= ${LIBRCS}
DPADD= ${LIBRCS}
.include "../../Makefile.inc"
.include <bsd.prog.mk>

454
gnu/usr.bin/rcs/rcs/rcs.1 Normal file
View File

@ -0,0 +1,454 @@
.de Id
.ds Rv \\$3
.ds Dt \\$4
..
.Id $FreeBSD$
.ds r \&\s-1RCS\s0
.if n .ds - \%--
.if t .ds - \(em
.if !\n(.g \{\
. if !\w|\*(lq| \{\
. ds lq ``
. if \w'\(lq' .ds lq "\(lq
. \}
. if !\w|\*(rq| \{\
. ds rq ''
. if \w'\(rq' .ds rq "\(rq
. \}
.\}
.TH RCS 1 \*(Dt GNU
.SH NAME
rcs \- change RCS file attributes
.SH SYNOPSIS
.B rcs
.IR "options file " .\|.\|.
.SH DESCRIPTION
.B rcs
creates new \*r files or changes attributes of existing ones.
An \*r file contains multiple revisions of text,
an access list, a change log,
descriptive text,
and some control attributes.
For
.B rcs
to work, the caller's login name must be on the access list,
except if the access list is empty, the caller is the owner of the file
or the superuser, or
the
.B \-i
option is present.
.PP
Pathnames matching an \*r suffix denote \*r files;
all others denote working files.
Names are paired as explained in
.BR ci (1).
Revision numbers use the syntax described in
.BR ci (1).
.SH OPTIONS
.TP
.B \-i
Create and initialize a new \*r file, but do not deposit any revision.
If the \*r file has no path prefix, try to place it
first into the subdirectory
.BR ./RCS ,
and then into the current directory.
If the \*r file
already exists, print an error message.
.TP
.BI \-a "logins"
Append the login names appearing in the comma-separated list
.I logins
to the access list of the \*r file.
.TP
.BI \-A "oldfile"
Append the access list of
.I oldfile
to the access list of the \*r file.
.TP
.BR \-e [\f2logins\fP]
Erase the login names appearing in the comma-separated list
.I logins
from the access list of the \*r file.
If
.I logins
is omitted, erase the entire access list.
.TP
.BR \-b [\f2rev\fP]
Set the default branch to
.IR rev .
If
.I rev
is omitted, the default
branch is reset to the (dynamically) highest branch on the trunk.
.TP
.BI \-c string
Set the comment leader to
.IR string .
An initial
.BR ci ,
or an
.B "rcs\ \-i"
without
.BR \-c ,
guesses the comment leader from the suffix of the working filename.
.RS
.PP
This option is obsolescent, since \*r normally uses the preceding
.B $\&Log$
line's prefix when inserting log lines during checkout (see
.BR co (1)).
However, older versions of \*r use the comment leader instead of the
.B $\&Log$
line's prefix, so
if you plan to access a file with both old and new versions of \*r,
make sure its comment leader matches its
.B $\&Log$
line prefix.
.RE
.TP
.BI \-k subst
Set the default keyword substitution to
.IR subst .
The effect of keyword substitution is described in
.BR co (1).
Giving an explicit
.B \-k
option to
.BR co ,
.BR rcsdiff ,
and
.B rcsmerge
overrides this default.
Beware
.BR "rcs\ \-kv",
because
.B \-kv
is incompatible with
.BR "co\ \-l".
Use
.B "rcs\ \-kkv"
to restore the normal default keyword substitution.
.TP
.BR \-l [\f2rev\fP]
Lock the revision with number
.IR rev .
If a branch is given, lock the latest revision on that branch.
If
.I rev
is omitted, lock the latest revision on the default branch.
Locking prevents overlapping changes.
If someone else already holds the lock, the lock is broken as with
.B "rcs\ \-u"
(see below).
.TP
.BR \-u [\f2rev\fP]
Unlock the revision with number
.IR rev .
If a branch is given, unlock the latest revision on that branch.
If
.I rev
is omitted, remove the latest lock held by the caller.
Normally, only the locker of a revision can unlock it.
Somebody else unlocking a revision breaks the lock.
This causes a mail message to be sent to the original locker.
The message contains a commentary solicited from the breaker.
The commentary is terminated by end-of-file or by a line containing
.BR \&. "\ by"
itself.
.TP
.B \-L
Set locking to
.IR strict .
Strict locking means that the owner
of an \*r file is not exempt from locking for checkin.
This option should be used for files that are shared.
.TP
.B \-U
Set locking to non-strict. Non-strict locking means that the owner of
a file need not lock a revision for checkin.
This option should
.I not
be used for files that are shared.
Whether default locking is strict is determined by your system administrator,
but it is normally strict.
.TP
\f3\-m\fP\f2rev\fP\f3:\fP\f2msg\fP
Replace revision
.IR rev 's
log message with
.IR msg .
.TP
.B \-M
Do not send mail when breaking somebody else's lock.
This option is not meant for casual use;
it is meant for programs that warn users by other means, and invoke
.B "rcs\ \-u"
only as a low-level lock-breaking operation.
.TP
\f3\-n\fP\f2name\fP[\f3:\fP[\f2rev\fP]]
Associate the symbolic name
.I name
with the branch or
revision
.IR rev .
Delete the symbolic name if both
.B :
and
.I rev
are omitted; otherwise, print an error message if
.I name
is already associated with
another number.
If
.I rev
is symbolic, it is expanded before association.
A
.I rev
consisting of a branch number followed by a
.B .\&
stands for the current latest revision in the branch.
A
.B :
with an empty
.I rev
stands for the current latest revision on the default branch,
normally the trunk.
For example,
.BI "rcs\ \-n" name ":\ RCS/*"
associates
.I name
with the current latest revision of all the named \*r files;
this contrasts with
.BI "rcs\ \-n" name ":$\ RCS/*"
which associates
.I name
with the revision numbers extracted from keyword strings
in the corresponding working files.
.TP
\f3\-N\fP\f2name\fP[\f3:\fP[\f2rev\fP]]
Act like
.BR \-n ,
except override any previous assignment of
.IR name .
.TP
.BI \-o range
deletes (\*(lqoutdates\*(rq) the revisions given by
.IR range .
A range consisting of a single revision number means that revision.
A range consisting of a branch number means the latest revision on that
branch.
A range of the form
.IB rev1 : rev2
means
revisions
.I rev1
to
.I rev2
on the same branch,
.BI : rev
means from the beginning of the branch containing
.I rev
up to and including
.IR rev ,
and
.IB rev :
means
from revision
.I rev
to the end of the branch containing
.IR rev .
None of the outdated revisions can have branches or locks.
.TP
.B \-q
Run quietly; do not print diagnostics.
.TP
.B \-I
Run interactively, even if the standard input is not a terminal.
.TP
.B \-s\f2state\fP\f1[\fP:\f2rev\fP\f1]\fP
Set the state attribute of the revision
.I rev
to
.IR state .
If
.I rev
is a branch number, assume the latest revision on that branch.
If
.I rev
is omitted, assume the latest revision on the default branch.
Any identifier is acceptable for
.IR state .
A useful set of states
is
.B Exp
(for experimental),
.B Stab
(for stable), and
.B Rel
(for
released).
By default,
.BR ci (1)
sets the state of a revision to
.BR Exp .
.TP
.BR \-t [\f2file\fP]
Write descriptive text from the contents of the named
.I file
into the \*r file, deleting the existing text.
The
.IR file
pathname cannot begin with
.BR \- .
If
.I file
is omitted, obtain the text from standard input,
terminated by end-of-file or by a line containing
.BR \&. "\ by"
itself.
Prompt for the text if interaction is possible; see
.BR \-I .
With
.BR \-i ,
descriptive text is obtained
even if
.B \-t
is not given.
.TP
.BI \-t\- string
Write descriptive text from the
.I string
into the \*r file, deleting the existing text.
.TP
.B \-T
Preserve the modification time on the \*r file
unless a revision is removed.
This option can suppress extensive recompilation caused by a
.BR make (1)
dependency of some copy of the working file on the \*r file.
Use this option with care; it can suppress recompilation even when it is needed,
i.e. when a change to the \*r file
would mean a change to keyword strings in the working file.
.TP
.BI \-V
Print \*r's version number.
.TP
.BI \-V n
Emulate \*r version
.IR n .
See
.BR co (1)
for details.
.TP
.BI \-x "suffixes"
Use
.I suffixes
to characterize \*r files.
See
.BR ci (1)
for details.
.TP
.BI \-z zone
Use
.I zone
as the default time zone.
This option has no effect;
it is present for compatibility with other \*r commands.
.PP
At least one explicit option must be given,
to ensure compatibility with future planned extensions
to the
.B rcs
command.
.SH COMPATIBILITY
The
.BI \-b rev
option generates an \*r file that cannot be parsed by \*r version 3 or earlier.
.PP
The
.BI \-k subst
options (except
.BR \-kkv )
generate an \*r file that cannot be parsed by \*r version 4 or earlier.
.PP
Use
.BI "rcs \-V" n
to make an \*r file acceptable to \*r version
.I n
by discarding information that would confuse version
.IR n .
.PP
\*r version 5.5 and earlier does not support the
.B \-x
option, and requires a
.B ,v
suffix on an \*r pathname.
.SH FILES
.B rcs
accesses files much as
.BR ci (1)
does,
except that it uses the effective user for all accesses,
it does not write the working file or its directory,
and it does not even read the working file unless a revision number of
.B $
is specified.
.SH ENVIRONMENT
.TP
.B \s-1RCSINIT\s0
options prepended to the argument list, separated by spaces.
See
.BR ci (1)
for details.
.SH DIAGNOSTICS
The \*r pathname and the revisions outdated are written to
the diagnostic output.
The exit status is zero if and only if all operations were successful.
.SH IDENTIFICATION
Author: Walter F. Tichy.
.br
Manual Page Revision: \*(Rv; Release Date: \*(Dt.
.br
Copyright \(co 1982, 1988, 1989 Walter F. Tichy.
.br
Copyright \(co 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert.
.SH "SEE ALSO"
rcsintro(1), co(1), ci(1), ident(1), rcsclean(1), rcsdiff(1),
rcsmerge(1), rlog(1), rcsfile(5)
.br
Walter F. Tichy,
\*r\*-A System for Version Control,
.I "Software\*-Practice & Experience"
.BR 15 ,
7 (July 1985), 637-654.
.SH BUGS
A catastrophe (e.g. a system crash) can cause \*r to leave behind
a semaphore file that causes later invocations of \*r to claim
that the \*r file is in use.
To fix this, remove the semaphore file.
A semaphore file's name typically begins with
.B ,
or ends with
.BR _ .
.PP
The separator for revision ranges in the
.B \-o
option used to be
.B \-
instead of
.BR : ,
but this leads to confusion when symbolic names contain
.BR \- .
For backwards compatibility
.B "rcs \-o"
still supports the old
.B \-
separator, but it warns about this obsolete use.
.PP
Symbolic names need not refer to existing revisions or branches.
For example, the
.B \-o
option does not remove symbolic names for the outdated revisions; you must use
.B \-n
to remove the names.
.br

1629
gnu/usr.bin/rcs/rcs/rcs.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,425 @@
.lf 1 ./rcsfile.5in
.\" Set p to 1 if your formatter can handle pic output.
.if t .nr p 1
.de Id
.ds Rv \\$3
.ds Dt \\$4
..
.Id $FreeBSD$
.ds r \s-1RCS\s0
.if n .ds - \%--
.if t .ds - \(em
.TH RCSFILE 5 \*(Dt GNU
.SH NAME
rcsfile \- format of RCS file
.SH DESCRIPTION
An \*r file's
contents are described by the grammar
below.
.PP
The text is free format: space, backspace, tab, newline, vertical
tab, form feed, and carriage return (collectively,
.IR "white space")
have no significance except in strings.
However, white space cannot appear within an id, num, or sym,
and an \*r file must end with a newline.
.PP
Strings are enclosed by
.BR @ .
If a string contains a
.BR @ ,
it must be doubled;
otherwise, strings can contain arbitrary binary data.
.PP
The meta syntax uses the following conventions: `|' (bar) separates
alternatives; `{' and `}' enclose optional phrases; `{' and `}*' enclose
phrases that can be repeated zero or more times;
`{' and '}+' enclose phrases that must appear at least once and can be
repeated;
Terminal symbols are in
.BR boldface ;
nonterminal symbols are in
.IR italics .
.LP
.nr w \w'\f3deltatext\fP '
.nr y \w'\f3newphrase\fP '
.if \nw<\ny .nr w \ny
.nr x \w'\f3branches\fP'
.nr y \w'{ \f3comment\fP'
.if \nx<\ny .nr x \ny
.nr y \w'\f3{ branch\fP'
.if \nx<\ny .nr x \ny
.ta \nwu +\w'::= 'u +\nxu+\w' 'u
.fc #
.nf
\f2rcstext\fP ::= \f2admin\fP {\f2delta\fP}* \f2desc\fP {\f2deltatext\fP}*
.LP
\f2admin\fP ::= \f3head\fP {\f2num\fP}\f3;\fP
{ \f3branch\fP {\f2num\fP}\f3;\fP }
\f3access\fP {\f2id\fP}*\f3;\fP
\f3symbols\fP {\f2sym\fP \f3:\fP \f2num\fP}*\f3;\fP
\f3locks\fP {\f2id\fP \f3:\fP \f2num\fP}*\f3;\fP {\f3strict ;\fP}
{ \f3comment\fP {\f2string\fP}\f3;\fP }
{ \f3expand\fP {\f2string\fP}\f3;\fP }
{ \f2newphrase\fP }*
.LP
\f2delta\fP ::= \f2num\fP
\f3date\fP \f2num\fP\f3;\fP
\f3author\fP \f2id\fP\f3;\fP
\f3state\fP {\f2id\fP}\f3;\fP
\f3branches\fP {\f2num\fP}*\f3;\fP
\f3next\fP {\f2num\fP}\f3;\fP
{ \f2newphrase\fP }*
.LP
\f2desc\fP ::= \f3desc\fP \f2string\fP
.LP
\f2deltatext\fP ::= \f2num\fP
\f3log\fP \f2string\fP
{ \f2newphrase\fP }*
\f3text\fP \f2string\fP
.LP
\f2num\fP ::= {\f2digit\fP | \f3.\fP}+
.LP
\f2digit\fP ::= \f30\fP | \f31\fP | \f32\fP | \f33\fP | \f34\fP | \f35\fP | \f36\fP | \f37\fP | \f38\fP | \f39\fP
.LP
\f2id\fP ::= {\f2num\fP} \f2idchar\fP {\f2idchar\fP | \f2num\fP}*
.LP
\f2sym\fP ::= {\f2digit\fP}* \f2idchar\fP {\f2idchar\fP | \f2digit\fP}*
.LP
\f2idchar\fP ::= any visible graphic character except \f2special\fP
.LP
\f2special\fP ::= \f3$\fP | \f3,\fP | \f3.\fP | \f3:\fP | \f3;\fP | \f3@\fP
.LP
\f2string\fP ::= \f3@\fP{any character, with \f3@\fP doubled}*\f3@\fP
.LP
\f2newphrase\fP ::= \f2id\fP \f2word\fP* \f3;\fP
.LP
\f2word\fP ::= \f2id\fP | \f2num\fP | \f2string\fP | \f3:\fP
.fi
.PP
Identifiers are case sensitive. Keywords are in lower case only.
The sets of keywords and identifiers can overlap.
In most environments \*r uses the \s-1ISO\s0 8859/1 encoding:
visible graphic characters are codes 041\-176 and 240\-377,
and white space characters are codes 010\-015 and 040.
.PP
Dates, which appear after the
.B date
keyword, are of the form
\f2Y\fP\f3.\fP\f2mm\fP\f3.\fP\f2dd\fP\f3.\fP\f2hh\fP\f3.\fP\f2mm\fP\f3.\fP\f2ss\fP,
where
.I Y
is the year,
.I mm
the month (01\-12),
.I dd
the day (01\-31),
.I hh
the hour (00\-23),
.I mm
the minute (00\-59),
and
.I ss
the second (00\-60).
.I Y
contains just the last two digits of the year
for years from 1900 through 1999,
and all the digits of years thereafter.
Dates use the Gregorian calendar; times use UTC.
.PP
The
.I newphrase
productions in the grammar are reserved for future extensions
to the format of \*r files.
No
.I newphrase
will begin with any keyword already in use.
.PP
The
.I delta
nodes form a tree. All nodes whose numbers
consist of a single pair
(e.g., 2.3, 2.1, 1.3, etc.)
are on the trunk, and are linked through the
.B next
field in order of decreasing numbers.
The
.B head
field in the
.I admin
node points to the head of that sequence (i.e., contains
the highest pair).
The
.B branch
node in the admin node indicates the default
branch (or revision) for most \*r operations.
If empty, the default
branch is the highest branch on the trunk.
.PP
All
.I delta
nodes whose numbers consist of
.RI 2 n
fields
.RI ( n \(>=2)
(e.g., 3.1.1.1, 2.1.2.2, etc.)
are linked as follows.
All nodes whose first
.RI 2 n \-1
number fields are identical are linked through the
.B next
field in order of increasing numbers.
For each such sequence,
the
.I delta
node whose number is identical to the first
.RI 2 n \-2
number fields of the deltas on that sequence is called the branchpoint.
The
.B branches
field of a node contains a list of the
numbers of the first nodes of all sequences for which it is a branchpoint.
This list is ordered in increasing numbers.
.LP
The following diagram shows an example of an \*r file's organization.
.if !\np \{\
.nf
.vs 12
.ne 36
.cs 1 20
.eo
Head
|
|
v / \
--------- / \
/ \ / \ | | / \ / \
/ \ / \ | 2.1 | / \ / \
/ \ / \ | | / \ / \
/1.2.1.3\ /1.3.1.1\ | | /1.2.2.2\ /1.2.2.1.1.1\
--------- --------- --------- --------- -------------
^ ^ | ^ ^
| | | | |
| | v | |
/ \ | --------- / \ |
/ \ | \ 1.3 / / \ |
/ \ ---------\ / / \-----------
/1.2.1.1\ \ / /1.2.2.1\
--------- \ / ---------
^ | ^
| | |
| v |
| --------- |
| \ 1.2 / |
----------------------\ /---------
\ /
\ /
|
|
v
---------
\ 1.1 /
\ /
\ /
\ /
.ec
.cs 1
.vs
.fi
.\}
.if \np \{\
.lf 232
.PS 4.250i 3.812i
.\" -2.0625 -4.25 1.75 0
.\" 0.000i 4.250i 3.812i 0.000i
.nr 00 \n(.u
.nf
.nr 0x 1
\h'3.812i'
.sp -1
.lf 242
\h'2.062i-(\w'Head'u/2u)'\v'0.125i-(0v/2u)+0v+0.22m'Head
.sp -1
\h'2.062i'\v'0.250i'\D'l0.000i 0.500i'
.sp -1
\h'2.087i'\v'0.650i'\D'l-0.025i 0.100i'
.sp -1
\h'2.062i'\v'0.750i'\D'l-0.025i -0.100i'
.sp -1
\h'1.688i'\v'1.250i'\D'l0.750i 0.000i'
.sp -1
\h'2.438i'\v'1.250i'\D'l0.000i -0.500i'
.sp -1
\h'2.438i'\v'0.750i'\D'l-0.750i 0.000i'
.sp -1
\h'1.688i'\v'0.750i'\D'l0.000i 0.500i'
.sp -1
.lf 244
\h'2.062i-(\w'2.1'u/2u)'\v'1.000i-(0v/2u)+0v+0.22m'2.1
.sp -1
\h'2.062i'\v'1.250i'\D'l0.000i 0.500i'
.sp -1
\h'2.087i'\v'1.650i'\D'l-0.025i 0.100i'
.sp -1
\h'2.062i'\v'1.750i'\D'l-0.025i -0.100i'
.sp -1
.lf 246
\h'2.062i-(\w'1.3'u/2u)'\v'2.000i-(1v/2u)+0v+0.22m'1.3
.sp -1
\h'2.062i'\v'2.250i'\D'l-0.375i -0.500i'
.sp -1
\h'1.688i'\v'1.750i'\D'l0.750i 0.000i'
.sp -1
\h'2.438i'\v'1.750i'\D'l-0.375i 0.500i'
.sp -1
\h'1.875i'\v'2.000i'\D'~-0.500i 0.000i 0.000i -0.500i'
.sp -1
\h'1.350i'\v'1.600i'\D'l0.025i -0.100i'
.sp -1
\h'1.375i'\v'1.500i'\D'l0.025i 0.100i'
.sp -1
.lf 249
\h'1.375i-(\w'1.3.1.1'u/2u)'\v'1.250i-(1v/2u)+1v+0.22m'1.3.1.1
.sp -1
\h'1.375i'\v'1.000i'\D'l-0.375i 0.500i'
.sp -1
\h'1.000i'\v'1.500i'\D'l0.750i 0.000i'
.sp -1
\h'1.750i'\v'1.500i'\D'l-0.375i -0.500i'
.sp -1
\h'2.062i'\v'2.250i'\D'l0.000i 0.500i'
.sp -1
\h'2.087i'\v'2.650i'\D'l-0.025i 0.100i'
.sp -1
\h'2.062i'\v'2.750i'\D'l-0.025i -0.100i'
.sp -1
.lf 252
\h'2.062i-(\w'1.2'u/2u)'\v'3.000i-(1v/2u)+0v+0.22m'1.2
.sp -1
\h'2.062i'\v'3.250i'\D'l-0.375i -0.500i'
.sp -1
\h'1.688i'\v'2.750i'\D'l0.750i 0.000i'
.sp -1
\h'2.438i'\v'2.750i'\D'l-0.375i 0.500i'
.sp -1
\h'1.875i'\v'3.000i'\D'~-0.500i 0.000i -0.500i 0.000i -0.500i 0.000i 0.000i -0.500i'
.sp -1
\h'0.350i'\v'2.600i'\D'l0.025i -0.100i'
.sp -1
\h'0.375i'\v'2.500i'\D'l0.025i 0.100i'
.sp -1
.lf 255
\h'0.375i-(\w'1.2.1.1'u/2u)'\v'2.250i-(1v/2u)+1v+0.22m'1.2.1.1
.sp -1
\h'0.375i'\v'2.000i'\D'l-0.375i 0.500i'
.sp -1
\h'0.000i'\v'2.500i'\D'l0.750i 0.000i'
.sp -1
\h'0.750i'\v'2.500i'\D'l-0.375i -0.500i'
.sp -1
\h'0.375i'\v'2.000i'\D'l0.000i -0.500i'
.sp -1
\h'0.350i'\v'1.600i'\D'l0.025i -0.100i'
.sp -1
\h'0.375i'\v'1.500i'\D'l0.025i 0.100i'
.sp -1
.lf 257
\h'0.375i-(\w'1.2.1.3'u/2u)'\v'1.250i-(1v/2u)+1v+0.22m'1.2.1.3
.sp -1
\h'0.375i'\v'1.000i'\D'l-0.375i 0.500i'
.sp -1
\h'0.000i'\v'1.500i'\D'l0.750i 0.000i'
.sp -1
\h'0.750i'\v'1.500i'\D'l-0.375i -0.500i'
.sp -1
\h'2.250i'\v'3.000i'\D'~0.500i 0.000i 0.000i -0.500i'
.sp -1
\h'2.725i'\v'2.600i'\D'l0.025i -0.100i'
.sp -1
\h'2.750i'\v'2.500i'\D'l0.025i 0.100i'
.sp -1
.lf 261
\h'2.750i-(\w'1.2.2.1'u/2u)'\v'2.250i-(1v/2u)+1v+0.22m'1.2.2.1
.sp -1
\h'2.750i'\v'2.000i'\D'l-0.375i 0.500i'
.sp -1
\h'2.375i'\v'2.500i'\D'l0.750i 0.000i'
.sp -1
\h'3.125i'\v'2.500i'\D'l-0.375i -0.500i'
.sp -1
\h'2.938i'\v'2.250i'\D'~0.500i 0.000i 0.000i -0.500i 0.000i -0.500i'
.sp -1
\h'3.413i'\v'1.350i'\D'l0.025i -0.100i'
.sp -1
\h'3.438i'\v'1.250i'\D'l0.025i 0.100i'
.sp -1
.lf 264
\h'3.438i-(\w'\s-21.2.2.1.1.1\s0'u/2u)'\v'1.000i-(1v/2u)+1v+0.22m'\s-21.2.2.1.1.1\s0
.sp -1
\h'3.438i'\v'0.750i'\D'l-0.375i 0.500i'
.sp -1
\h'3.062i'\v'1.250i'\D'l0.750i 0.000i'
.sp -1
\h'3.812i'\v'1.250i'\D'l-0.375i -0.500i'
.sp -1
\h'2.750i'\v'2.000i'\D'l0.000i -0.500i'
.sp -1
\h'2.725i'\v'1.600i'\D'l0.025i -0.100i'
.sp -1
\h'2.750i'\v'1.500i'\D'l0.025i 0.100i'
.sp -1
.lf 267
\h'2.750i-(\w'1.2.2.2'u/2u)'\v'1.250i-(1v/2u)+1v+0.22m'1.2.2.2
.sp -1
\h'2.750i'\v'1.000i'\D'l-0.375i 0.500i'
.sp -1
\h'2.375i'\v'1.500i'\D'l0.750i 0.000i'
.sp -1
\h'3.125i'\v'1.500i'\D'l-0.375i -0.500i'
.sp -1
\h'2.062i'\v'3.250i'\D'l0.000i 0.500i'
.sp -1
\h'2.087i'\v'3.650i'\D'l-0.025i 0.100i'
.sp -1
\h'2.062i'\v'3.750i'\D'l-0.025i -0.100i'
.sp -1
.lf 270
\h'2.062i-(\w'1.1'u/2u)'\v'4.000i-(1v/2u)+0v+0.22m'1.1
.sp -1
\h'2.062i'\v'4.250i'\D'l-0.375i -0.500i'
.sp -1
\h'1.688i'\v'3.750i'\D'l0.750i 0.000i'
.sp -1
\h'2.438i'\v'3.750i'\D'l-0.375i 0.500i'
.sp -1
.sp 4.250i+1
.if \n(00 .fi
.br
.nr 0x 0
.lf 271
.PE
.lf 272
.\}
.SH IDENTIFICATION
.de VL
\\$2
..
Author: Walter F. Tichy,
Purdue University, West Lafayette, IN, 47907.
.br
Manual Page Revision: \*(Rv; Release Date: \*(Dt.
.br
Copyright \(co 1982, 1988, 1989 Walter F. Tichy.
.br
Copyright \(co 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert.
.SH SEE ALSO
rcsintro(1), ci(1), co(1), ident(1), rcs(1), rcsclean(1), rcsdiff(1),
rcsmerge(1), rlog(1)
.br
Walter F. Tichy,
\*r\*-A System for Version Control,
.I "Software\*-Practice & Experience"
.BR 15 ,
7 (July 1985), 637-654.

View File

@ -0,0 +1,302 @@
.de Id
.ds Rv \\$3
.ds Dt \\$4
..
.Id $FreeBSD$
.ds r \&\s-1RCS\s0
.if n .ds - \%--
.if t .ds - \(em
.if !\n(.g \{\
. if !\w|\*(lq| \{\
. ds lq ``
. if \w'\(lq' .ds lq "\(lq
. \}
. if !\w|\*(rq| \{\
. ds rq ''
. if \w'\(rq' .ds rq "\(rq
. \}
.\}
.am SS
.LP
..
.TH RCSINTRO 1 \*(Dt GNU
.SH NAME
rcsintro \- introduction to RCS commands
.SH DESCRIPTION
The Revision Control System (\*r) manages multiple revisions of files.
\*r automates the storing, retrieval, logging, identification, and merging
of revisions. \*r is useful for text that is revised frequently, for example
programs, documentation, graphics, papers, and form letters.
.PP
The basic user interface is extremely simple. The novice only needs
to learn two commands:
.BR ci (1)
and
.BR co (1).
.BR ci ,
short for \*(lqcheck in\*(rq, deposits the contents of a
file into an archival file called an \*r file. An \*r file
contains all revisions of a particular file.
.BR co ,
short for \*(lqcheck out\*(rq, retrieves revisions from an \*r file.
.SS "Functions of \*r"
.IP \(bu
Store and retrieve multiple revisions of text. \*r saves all old
revisions in a space efficient way.
Changes no longer destroy the original, because the
previous revisions remain accessible. Revisions can be retrieved according to
ranges of revision numbers, symbolic names, dates, authors, and
states.
.IP \(bu
Maintain a complete history of changes.
\*r logs all changes automatically.
Besides the text of each revision, \*r stores the author, the date and time of
check-in, and a log message summarizing the change.
The logging makes it easy to find out
what happened to a module, without having to compare
source listings or having to track down colleagues.
.IP \(bu
Resolve access conflicts. When two or more programmers wish to
modify the same revision, \*r alerts the programmers and prevents one
modification from corrupting the other.
.IP \(bu
Maintain a tree of revisions. \*r can maintain separate lines of development
for each module. It stores a tree structure that represents the
ancestral relationships among revisions.
.IP \(bu
Merge revisions and resolve conflicts.
Two separate lines of development of a module can be coalesced by merging.
If the revisions to be merged affect the same sections of code, \*r alerts the
user about the overlapping changes.
.IP \(bu
Control releases and configurations.
Revisions can be assigned symbolic names
and marked as released, stable, experimental, etc.
With these facilities, configurations of modules can be
described simply and directly.
.IP \(bu
Automatically identify each revision with name, revision number,
creation time, author, etc.
The identification is like a stamp that can be embedded at an appropriate place
in the text of a revision.
The identification makes it simple to determine which
revisions of which modules make up a given configuration.
.IP \(bu
Minimize secondary storage. \*r needs little extra space for
the revisions (only the differences). If intermediate revisions are
deleted, the corresponding deltas are compressed accordingly.
.SS "Getting Started with \*r"
Suppose you have a file
.B f.c
that you wish to put under control of \*r.
If you have not already done so, make an \*r directory with the command
.IP
.B "mkdir RCS"
.LP
Then invoke the check-in command
.IP
.B "ci f.c"
.LP
This command creates an \*r file in the
.B RCS
directory,
stores
.B f.c
into it as revision 1.1, and
deletes
.BR f.c .
It also asks you for a description. The description
should be a synopsis of the contents of the file. All later check-in
commands will ask you for a log entry, which should summarize the
changes that you made.
.PP
Files in the \*r directory are called \*r files;
the others are called working files.
To get back the working file
.B f.c
in the previous example, use the check-out
command
.IP
.B "co f.c"
.LP
This command extracts the latest revision from the \*r file
and writes
it into
.BR f.c .
If you want to edit
.BR f.c ,
you must lock it as you check it out with the command
.IP
.B "co \-l f.c"
.LP
You can now edit
.BR f.c .
.PP
Suppose after some editing you want to know what changes that you have made.
The command
.IP
.B "rcsdiff f.c"
.LP
tells you the difference between the most recently checked-in version
and the working file.
You can check the file back in by invoking
.IP
.B "ci f.c"
.LP
This increments the revision number properly.
.PP
If
.B ci
complains with the message
.IP
.BI "ci error: no lock set by " "your name"
.LP
then you have tried to check in a file even though you did not
lock it when you checked it out.
Of course, it is too late now to do the check-out with locking, because
another check-out would
overwrite your modifications. Instead, invoke
.IP
.B "rcs \-l f.c"
.LP
This command will lock the latest revision for you, unless somebody
else got ahead of you already. In this case, you'll have to negotiate with
that person.
.PP
Locking assures that you, and only you, can check in the next update, and
avoids nasty problems if several people work on the same file.
Even if a revision is locked, it can still be checked out for
reading, compiling, etc. All that locking
prevents is a
.I "check-in"
by anybody but the locker.
.PP
If your \*r file is private, i.e., if you are the only person who is going
to deposit revisions into it, strict locking is not needed and you
can turn it off.
If strict locking is turned off,
the owner of the \*r file need not have a lock for check-in; all others
still do. Turning strict locking off and on is done with the commands
.IP
.BR "rcs \-U f.c" " and " "rcs \-L f.c"
.LP
If you don't want to clutter your working directory with \*r files, create
a subdirectory called
.B RCS
in your working directory, and move all your \*r
files there. \*r commands will look first into that directory to find
needed files. All the commands discussed above will still work, without any
modification.
(Actually, pairs of \*r and working files can be specified in three ways:
(a) both are given, (b) only the working file is given, (c) only the
\*r file is given. Both \*r and working files may have arbitrary path prefixes;
\*r commands pair them up intelligently.)
.PP
To avoid the deletion of the working file during check-in (in case you want to
continue editing or compiling), invoke
.IP
.BR "ci \-l f.c" " or " "ci \-u f.c"
.LP
These commands check in
.B f.c
as usual, but perform an implicit
check-out. The first form also locks the checked in revision, the second one
doesn't. Thus, these options save you one check-out operation.
The first form is useful if you want to continue editing,
the second one if you just want to read the file.
Both update the identification markers in your working file (see below).
.PP
You can give
.B ci
the number you want assigned to a checked in
revision. Assume all your revisions were numbered 1.1, 1.2, 1.3, etc.,
and you would like to start release 2.
The command
.IP
.BR "ci \-r2 f.c" " or " "ci \-r2.1 f.c"
.LP
assigns the number 2.1 to the new revision.
From then on,
.B ci
will number the subsequent revisions
with 2.2, 2.3, etc. The corresponding
.B co
commands
.IP
.BR "co \-r2 f.c" " and " "co \-r2.1 f.c"
.PP
retrieve the latest revision numbered
.RI 2. x
and the revision 2.1,
respectively.
.B co
without a revision number selects
the latest revision on the
.IR trunk ,
i.e. the highest
revision with a number consisting of two fields. Numbers with more than two
fields are needed for branches.
For example, to start a branch at revision 1.3, invoke
.IP
.B "ci \-r1.3.1 f.c"
.LP
This command starts a branch numbered 1 at revision 1.3, and assigns
the number 1.3.1.1 to the new revision. For more information about
branches, see
.BR rcsfile (5).
.SS "Automatic Identification"
\*r can put special strings for identification into your source and object
code. To obtain such identification, place the marker
.IP
.B "$\&Id$"
.LP
into your text, for instance inside a comment.
\*r will replace this marker with a string of the form
.IP
.BI $\&Id: " filename revision date time author state " $
.LP
With such a marker on the first page of each module, you can
always see with which revision you are working.
\*r keeps the markers up to date automatically.
To propagate the markers into your object code, simply put
them into literal character strings. In C, this is done as follows:
.IP
.ft 3
static char rcsid[] = \&"$\&Id$\&";
.ft
.LP
The command
.B ident
extracts such markers from any file, even object code
and dumps.
Thus,
.B ident
lets you find out
which revisions of which modules were used in a given program.
.PP
You may also find it useful to put the marker
.B $\&Log$
into your text, inside a comment. This marker accumulates
the log messages that are requested during check-in.
Thus, you can maintain the complete history of your file directly inside it.
There are several additional identification markers; see
.BR co (1)
for
details.
.SH IDENTIFICATION
Author: Walter F. Tichy.
.br
Manual Page Revision: \*(Rv; Release Date: \*(Dt.
.br
Copyright \(co 1982, 1988, 1989 Walter F. Tichy.
.br
Copyright \(co 1990, 1991, 1992, 1993 Paul Eggert.
.SH "SEE ALSO"
ci(1), co(1), ident(1), rcs(1), rcsdiff(1), rcsintro(1), rcsmerge(1), rlog(1)
.br
Walter F. Tichy,
\*r\*-A System for Version Control,
.I "Software\*-Practice & Experience"
.BR 15 ,
7 (July 1985), 637-654.
.br

View File

@ -0,0 +1,8 @@
PROG= rcsclean
SRCS= rcsclean.c
CFLAGS+= -I${.CURDIR}/../lib
LDADD= ${LIBRCS}
DPADD= ${LIBRCS}
.include "../../Makefile.inc"
.include <bsd.prog.mk>

View File

@ -0,0 +1,203 @@
.de Id
.ds Rv \\$3
.ds Dt \\$4
..
.Id $FreeBSD$
.ds r \&\s-1RCS\s0
.if n .ds - \%--
.if t .ds - \(em
.TH RCSCLEAN 1 \*(Dt GNU
.SH NAME
rcsclean \- clean up working files
.SH SYNOPSIS
.B rcsclean
.RI [ options "] [ " file " .\|.\|. ]"
.SH DESCRIPTION
.B rcsclean
removes files that are not being worked on.
.B "rcsclean \-u"
also unlocks and removes files that are being worked on
but have not changed.
.PP
For each
.I file
given,
.B rcsclean
compares the working file and a revision in the corresponding
\*r file. If it finds a difference, it does nothing.
Otherwise, it first unlocks the revision if the
.B \-u
option is given,
and then removes the working file
unless the working file is writable and the revision is locked.
It logs its actions by outputting the corresponding
.B "rcs \-u"
and
.B "rm \-f"
commands on the standard output.
.PP
Files are paired as explained in
.BR ci (1).
If no
.I file
is given, all working files in the current directory are cleaned.
Pathnames matching an \*r suffix denote \*r files;
all others denote working files.
.PP
The number of the revision to which the working file is compared
may be attached to any of the options
.BR \-n ,
.BR \-q ,
.BR \-r ,
or
.BR \-u .
If no revision number is specified, then if the
.B \-u
option is given and the caller has one revision locked,
.B rcsclean
uses that revision; otherwise
.B rcsclean
uses the latest revision on the default branch, normally the root.
.PP
.B rcsclean
is useful for
.B clean
targets in makefiles.
See also
.BR rcsdiff (1),
which prints out the differences,
and
.BR ci (1),
which
normally reverts to the previous revision
if a file was not changed.
.SH OPTIONS
.TP
.BI \-k subst
Use
.I subst
style keyword substitution when retrieving the revision for comparison.
See
.BR co (1)
for details.
.TP
.BR \-n [\f2rev\fP]
Do not actually remove any files or unlock any revisions.
Using this option will tell you what
.B rcsclean
would do without actually doing it.
.TP
.BR \-q [\f2rev\fP]
Do not log the actions taken on standard output.
.TP
.BR \-r [\f2rev\fP]
This option has no effect other than specifying the revision for comparison.
.TP
.B \-T
Preserve the modification time on the \*r file
even if the \*r file changes because a lock is removed.
This option can suppress extensive recompilation caused by a
.BR make (1)
dependency of some other copy of the working file on the \*r file.
Use this option with care; it can suppress recompilation even when it is needed,
i.e. when the lock removal
would mean a change to keyword strings in the other working file.
.TP
.BR \-u [\f2rev\fP]
Unlock the revision if it is locked and no difference is found.
.TP
.BI \-V
Print \*r's version number.
.TP
.BI \-V n
Emulate \*r version
.IR n .
See
.BR co (1)
for details.
.TP
.BI \-x "suffixes"
Use
.I suffixes
to characterize \*r files.
See
.BR ci (1)
for details.
.TP
.BI \-z zone
Use
.I zone
as the time zone for keyword substitution;
see
.BR co (1)
for details.
.SH EXAMPLES
.LP
.RS
.ft 3
rcsclean *.c *.h
.ft
.RE
.LP
removes all working files ending in
.B .c
or
.B .h
that were not changed
since their checkout.
.LP
.RS
.ft 3
rcsclean
.ft
.RE
.LP
removes all working files in the current directory
that were not changed since their checkout.
.SH FILES
.B rcsclean
accesses files much as
.BR ci (1)
does.
.SH ENVIRONMENT
.TP
.B \s-1RCSINIT\s0
options prepended to the argument list, separated by spaces.
A backslash escapes spaces within an option.
The
.B \s-1RCSINIT\s0
options are prepended to the argument lists of most \*r commands.
Useful
.B \s-1RCSINIT\s0
options include
.BR \-q ,
.BR \-V ,
.BR \-x ,
and
.BR \-z .
.SH DIAGNOSTICS
The exit status is zero if and only if all operations were successful.
Missing working files and \*r files are silently ignored.
.SH IDENTIFICATION
Author: Walter F. Tichy.
.br
Manual Page Revision: \*(Rv; Release Date: \*(Dt.
.br
Copyright \(co 1982, 1988, 1989 Walter F. Tichy.
.br
Copyright \(co 1990, 1991, 1992, 1993 Paul Eggert.
.SH "SEE ALSO"
ci(1), co(1), ident(1), rcs(1), rcsdiff(1), rcsintro(1), rcsmerge(1), rlog(1),
rcsfile(5)
.br
Walter F. Tichy,
\*r\*-A System for Version Control,
.I "Software\*-Practice & Experience"
.BR 15 ,
7 (July 1985), 637-654.
.SH BUGS
At least one
.I file
must be given in older Unix versions that
do not provide the needed directory scanning operations.
.br

View File

@ -0,0 +1,333 @@
/* Clean up working files. */
/* Copyright 1991, 1992, 1993, 1994, 1995 Paul Eggert
Distributed under license by the Free Software Foundation, Inc.
This file is part of RCS.
RCS 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.
RCS 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 RCS; see the file COPYING.
If not, write to the Free Software Foundation,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
Report problems and direct all questions to:
rcs-bugs@cs.purdue.edu
*/
#include "rcsbase.h"
#if has_dirent
static int get_directory P((char const*,char***));
#endif
static int unlock P((struct hshentry *));
static void cleanup P((void));
static RILE *workptr;
static int exitstatus;
mainProg(rcscleanId, "rcsclean", "$FreeBSD$")
{
static char const usage[] =
"\nrcsclean: usage: rcsclean -ksubst -{nqru}[rev] -T -Vn -xsuff -zzone file ...";
static struct buf revision;
char *a, **newargv;
char const *rev, *p;
int dounlock, expmode, perform, unlocked, unlockflag, waslocked;
int Ttimeflag;
struct hshentries *deltas;
struct hshentry *delta;
struct stat workstat;
setrid();
expmode = -1;
rev = 0;
suffixes = X_DEFAULT;
perform = true;
unlockflag = false;
Ttimeflag = false;
argc = getRCSINIT(argc, argv, &newargv);
argv = newargv;
for (;;) {
if (--argc < 1) {
# if has_dirent
argc = get_directory(".", &newargv);
argv = newargv;
break;
# else
faterror("no pathnames specified");
# endif
}
a = *++argv;
if (!*a || *a++ != '-')
break;
switch (*a++) {
case 'k':
if (0 <= expmode)
redefined('k');
if ((expmode = str2expmode(a)) < 0)
goto unknown;
break;
case 'n':
perform = false;
goto handle_revision;
case 'q':
quietflag = true;
/* fall into */
case 'r':
handle_revision:
if (*a) {
if (rev)
warn("redefinition of revision number");
rev = a;
}
break;
case 'T':
if (*a)
goto unknown;
Ttimeflag = true;
break;
case 'u':
unlockflag = true;
goto handle_revision;
case 'V':
setRCSversion(*argv);
break;
case 'x':
suffixes = a;
break;
case 'z':
zone_set(a);
break;
default:
unknown:
error("unknown option: %s%s", *argv, usage);
}
}
dounlock = perform & unlockflag;
if (nerror)
cleanup();
else
for (; 0 < argc; cleanup(), ++argv, --argc) {
ffree();
if (!(
0 < pairnames(
argc, argv,
dounlock ? rcswriteopen : rcsreadopen,
true, true
) &&
(workptr = Iopen(workname, FOPEN_R_WORK, &workstat))
))
continue;
if (same_file(RCSstat, workstat, 0)) {
rcserror("RCS file is the same as working file %s.",
workname
);
continue;
}
gettree();
p = 0;
if (rev) {
if (!fexpandsym(rev, &revision, workptr))
continue;
p = revision.string;
} else if (Head)
switch (unlockflag ? findlock(false,&delta) : 0) {
default:
continue;
case 0:
p = Dbranch ? Dbranch : "";
break;
case 1:
p = delta->num;
break;
}
delta = 0;
deltas = 0; /* Keep lint happy. */
if (p && !(delta = genrevs(p,(char*)0,(char*)0,(char*)0,&deltas)))
continue;
waslocked = delta && delta->lockedby;
locker_expansion = unlock(delta);
unlocked = locker_expansion & unlockflag;
if (unlocked<waslocked && workstat.st_mode&(S_IWUSR|S_IWGRP|S_IWOTH))
continue;
if (unlocked && !checkaccesslist())
continue;
if (dorewrite(dounlock, unlocked) != 0)
continue;
if (0 <= expmode)
Expand = expmode;
else if (
waslocked &&
Expand == KEYVAL_EXPAND &&
WORKMODE(RCSstat.st_mode,true) == workstat.st_mode
)
Expand = KEYVALLOCK_EXPAND;
getdesc(false);
if (
!delta ? workstat.st_size!=0 :
0 < rcsfcmp(
workptr, &workstat,
buildrevision(deltas, delta, (FILE*)0, false),
delta
)
)
continue;
if (quietflag < unlocked)
aprintf(stdout, "rcs -u%s %s\n", delta->num, RCSname);
if (perform & unlocked) {
if_advise_access(deltas->first != delta, finptr, MADV_SEQUENTIAL);
if (donerewrite(true,
Ttimeflag ? RCSstat.st_mtime : (time_t)-1
) != 0)
continue;
}
if (!quietflag)
aprintf(stdout, "rm -f %s\n", workname);
Izclose(&workptr);
if (perform && un_link(workname) != 0)
eerror(workname);
}
tempunlink();
if (!quietflag)
Ofclose(stdout);
exitmain(exitstatus);
}
static void
cleanup()
{
if (nerror) exitstatus = EXIT_FAILURE;
Izclose(&finptr);
Izclose(&workptr);
Ozclose(&fcopy);
ORCSclose();
dirtempunlink();
}
#if RCS_lint
# define exiterr rcscleanExit
#endif
void
exiterr()
{
ORCSerror();
dirtempunlink();
tempunlink();
_exit(EXIT_FAILURE);
}
static int
unlock(delta)
struct hshentry *delta;
{
register struct rcslock **al, *l;
if (delta && delta->lockedby && strcmp(getcaller(),delta->lockedby)==0)
for (al = &Locks; (l = *al); al = &l->nextlock)
if (l->delta == delta) {
*al = l->nextlock;
delta->lockedby = 0;
return true;
}
return false;
}
#if has_dirent
static int
get_directory(dirname, aargv)
char const *dirname;
char ***aargv;
/*
* Put a vector of all DIRNAME's directory entries names into *AARGV.
* Ignore names of RCS files.
* Yield the number of entries found. Terminate the vector with 0.
* Allocate the storage for the vector and entry names.
* Do not sort the names. Do not include '.' and '..'.
*/
{
int i, entries = 0, entries_max = 64;
size_t chars = 0, chars_max = 1024;
size_t *offset = tnalloc(size_t, entries_max);
char *a = tnalloc(char, chars_max), **p;
DIR *d;
struct dirent *e;
if (!(d = opendir(dirname)))
efaterror(dirname);
while ((errno = 0, e = readdir(d))) {
char const *en = e->d_name;
size_t s = strlen(en) + 1;
if (en[0]=='.' && (!en[1] || (en[1]=='.' && !en[2])))
continue;
if (rcssuffix(en))
continue;
while (chars_max < s + chars)
a = trealloc(char, a, chars_max<<=1);
if (entries == entries_max)
offset = trealloc(size_t, offset, entries_max<<=1);
offset[entries++] = chars;
VOID strcpy(a+chars, en);
chars += s;
}
# if void_closedir
# define close_directory(d) (closedir(d), 0)
# else
# define close_directory(d) closedir(d)
# endif
if (errno || close_directory(d) != 0)
efaterror(dirname);
if (chars)
a = trealloc(char, a, chars);
else
tfree(a);
*aargv = p = tnalloc(char*, entries+1);
for (i=0; i<entries; i++)
*p++ = a + offset[i];
*p = 0;
tfree(offset);
return entries;
}
#endif

View File

@ -0,0 +1,8 @@
PROG= rcsdiff
SRCS= rcsdiff.c
CFLAGS+= -I${.CURDIR}/../lib
LDADD= ${LIBRCS}
DPADD= ${LIBRCS}
.include "../../Makefile.inc"
.include <bsd.prog.mk>

View File

@ -0,0 +1,158 @@
.de Id
.ds Rv \\$3
.ds Dt \\$4
..
.Id $FreeBSD$
.ds r \&\s-1RCS\s0
.if n .ds - \%--
.if t .ds - \(em
.TH RCSDIFF 1 \*(Dt GNU
.SH NAME
rcsdiff \- compare RCS revisions
.SH SYNOPSIS
.B rcsdiff
[
.BI \-k subst
] [
.B \-q
] [
.BI \-r rev1
[
.BI \-r rev2
] ] [
.B \-T
] [
.RI "\f3\-V\fP[" n ]
] [
.BI \-x suffixes
] [
.BI \-z zone
] [
.I "diff options"
]
.I "file .\|.\|."
.SH DESCRIPTION
.B rcsdiff
runs
.BR diff (1)
to compare two revisions of each \*r file given.
.PP
Pathnames matching an \*r suffix denote \*r files;
all others denote working files.
Names are paired as explained in
.BR ci (1).
.PP
The option
.B \-q
suppresses diagnostic output.
Zero, one, or two revisions may be specified with
.BR \-r .
The option
.BI \-k subst
affects keyword substitution when extracting
revisions, as described in
.BR co (1);
for example,
.B "\-kk\ \-r1.1\ \-r1.2"
ignores differences in keyword values when comparing revisions
.B 1.1
and
.BR 1.2 .
To avoid excess output from locker name substitution,
.B \-kkvl
is assumed if (1) at most one revision option is given,
(2) no
.B \-k
option is given, (3)
.B \-kkv
is the default keyword substitution, and
(4) the working file's mode would be produced by
.BR "co\ \-l".
See
.BR co (1)
for details
about
.BR \-T ,
.BR \-V ,
.B \-x
and
.BR \-z .
Otherwise, all options of
.BR diff (1)
that apply to regular files are accepted, with the same meaning as for
.BR diff .
.PP
If both
.I rev1
and
.I rev2
are omitted,
.B rcsdiff
compares the latest revision on the
default branch (by default the trunk)
with the contents of the corresponding working file. This is useful
for determining what you changed since the last checkin.
.PP
If
.I rev1
is given, but
.I rev2
is omitted,
.B rcsdiff
compares revision
.I rev1
of the \*r file with
the contents of the corresponding working file.
.PP
If both
.I rev1
and
.I rev2
are given,
.B rcsdiff
compares revisions
.I rev1
and
.I rev2
of the \*r file.
.PP
Both
.I rev1
and
.I rev2
may be given numerically or symbolically.
.SH EXAMPLE
The command
.LP
.B " rcsdiff f.c"
.LP
compares the latest revision on the default branch of the \*r file
to the contents of the working file
.BR f.c .
.SH ENVIRONMENT
.TP
.B \s-1RCSINIT\s0
options prepended to the argument list, separated by spaces.
See
.BR ci (1)
for details.
.SH DIAGNOSTICS
Exit status is 0 for no differences during any comparison,
1 for some differences, 2 for trouble.
.SH IDENTIFICATION
Author: Walter F. Tichy.
.br
Manual Page Revision: \*(Rv; Release Date: \*(Dt.
.br
Copyright \(co 1982, 1988, 1989 Walter F. Tichy.
.br
Copyright \(co 1990, 1991, 1992, 1993 Paul Eggert.
.SH "SEE ALSO"
ci(1), co(1), diff(1), ident(1), rcs(1), rcsintro(1), rcsmerge(1), rlog(1)
.br
Walter F. Tichy,
\*r\*-A System for Version Control,
.I "Software\*-Practice & Experience"
.BR 15 ,
7 (July 1985), 637-654.
.br

View File

@ -0,0 +1,480 @@
/* Compare RCS revisions. */
/* Copyright 1982, 1988, 1989 Walter Tichy
Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert
Distributed under license by the Free Software Foundation, Inc.
This file is part of RCS.
RCS 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.
RCS 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 RCS; see the file COPYING.
If not, write to the Free Software Foundation,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
Report problems and direct all questions to:
rcs-bugs@cs.purdue.edu
*/
/*
* Revision 5.19 1995/06/16 06:19:24 eggert
* Update FSF address.
*
* Revision 5.18 1995/06/01 16:23:43 eggert
* (main): Pass "--binary" if -kb and if --binary makes a difference.
* Don't treat + options specially.
*
* Revision 5.17 1994/03/17 14:05:48 eggert
* Specify subprocess input via file descriptor, not file name. Remove lint.
*
* Revision 5.16 1993/11/09 17:40:15 eggert
* -V now prints version on stdout and exits. Don't print usage twice.
*
* Revision 5.15 1993/11/03 17:42:27 eggert
* Add -z. Ignore -T. Pass -Vn to `co'. Add Name keyword.
* Put revision numbers in -c output. Improve quality of diagnostics.
*
* Revision 5.14 1992/07/28 16:12:44 eggert
* Add -V. Use co -M for better dates with traditional diff -c.
*
* Revision 5.13 1992/02/17 23:02:23 eggert
* Output more readable context diff headers.
* Suppress needless checkout and comparison of identical revisions.
*
* Revision 5.12 1992/01/24 18:44:19 eggert
* Add GNU diff 1.15.2's new options. lint -> RCS_lint
*
* Revision 5.11 1992/01/06 02:42:34 eggert
* Update usage string.
*
* Revision 5.10 1991/10/07 17:32:46 eggert
* Remove lint.
*
* Revision 5.9 1991/08/19 03:13:55 eggert
* Add RCSINIT, -r$. Tune.
*
* Revision 5.8 1991/04/21 11:58:21 eggert
* Add -x, RCSINIT, MS-DOS support.
*
* Revision 5.7 1990/12/13 06:54:07 eggert
* GNU diff 1.15 has -u.
*
* Revision 5.6 1990/11/01 05:03:39 eggert
* Remove unneeded setid check.
*
* Revision 5.5 1990/10/04 06:30:19 eggert
* Accumulate exit status across files.
*
* Revision 5.4 1990/09/27 01:31:43 eggert
* Yield 1, not EXIT_FAILURE, when diffs are found.
*
* Revision 5.3 1990/09/11 02:41:11 eggert
* Simplify -kkvl test.
*
* Revision 5.2 1990/09/04 17:07:19 eggert
* Diff's argv was too small by 1.
*
* Revision 5.1 1990/08/29 07:13:55 eggert
* Add -kkvl.
*
* Revision 5.0 1990/08/22 08:12:46 eggert
* Add -k, -V. Don't use access(). Add setuid support.
* Remove compile-time limits; use malloc instead.
* Don't pass arguments with leading '+' to diff; GNU DIFF treats them as options.
* Add GNU diff's flags. Make lock and temp files faster and safer.
* Ansify and Posixate.
*
* Revision 4.6 89/05/01 15:12:27 narten
* changed copyright header to reflect current distribution rules
*
* Revision 4.5 88/08/09 19:12:41 eggert
* Use execv(), not system(); yield exit status like diff(1)s; allow cc -R.
*
* Revision 4.4 87/12/18 11:37:46 narten
* changes Jay Lepreau made in the 4.3 BSD version, to add support for
* "-i", "-w", and "-t" flags and to permit flags to be bundled together,
* merged in.
*
* Revision 4.3 87/10/18 10:31:42 narten
* Updating version numbers. Changes relative to 1.1 actually
* relative to 4.1
*
* Revision 1.3 87/09/24 13:59:21 narten
* Sources now pass through lint (if you ignore printf/sprintf/fprintf
* warnings)
*
* Revision 1.2 87/03/27 14:22:15 jenkins
* Port to suns
*
* Revision 4.1 83/05/03 22:13:19 wft
* Added default branch, option -q, exit status like diff.
* Added fterror() to replace faterror().
*
* Revision 3.6 83/01/15 17:52:40 wft
* Expanded mainprogram to handle multiple RCS files.
*
* Revision 3.5 83/01/06 09:33:45 wft
* Fixed passing of -c (context) option to diff.
*
* Revision 3.4 82/12/24 15:28:38 wft
* Added call to catchsig().
*
* Revision 3.3 82/12/10 16:08:17 wft
* Corrected checking of return code from diff; improved error msgs.
*
* Revision 3.2 82/12/04 13:20:09 wft
* replaced getdelta() with gettree(). Changed diagnostics.
*
* Revision 3.1 82/11/28 19:25:04 wft
* Initial revision.
*
*/
#include "rcsbase.h"
#if DIFF_L
static char const *setup_label P((struct buf*,char const*,char const[datesize]));
#endif
static void cleanup P((void));
static int exitstatus;
static RILE *workptr;
static struct stat workstat;
mainProg(rcsdiffId, "rcsdiff", "$FreeBSD$")
{
static char const cmdusage[] =
"\nrcsdiff usage: rcsdiff -ksubst -q -rrev1 [-rrev2] -Vn -xsuff -zzone [diff options] file ...";
int revnums; /* counter for revision numbers given */
char const *rev1, *rev2; /* revision numbers from command line */
char const *xrev1, *xrev2; /* expanded revision numbers */
char const *expandarg, *lexpandarg, *suffixarg, *versionarg, *zonearg;
#if DIFF_L
static struct buf labelbuf[2];
int file_labels;
char const **diff_label1, **diff_label2;
char date2[datesize];
#endif
char const *cov[10 + !DIFF_L];
char const **diffv, **diffp, **diffpend; /* argv for subsidiary diff */
char const **pp, *p, *diffvstr;
struct buf commarg;
struct buf numericrev; /* expanded revision number */
struct hshentries *gendeltas; /* deltas to be generated */
struct hshentry * target;
char *a, *dcp, **newargv;
int no_diff_means_no_output;
register c;
exitstatus = DIFF_SUCCESS;
bufautobegin(&commarg);
bufautobegin(&numericrev);
revnums = 0;
rev1 = rev2 = xrev2 = 0;
#if DIFF_L
file_labels = 0;
#endif
expandarg = suffixarg = versionarg = zonearg = 0;
no_diff_means_no_output = true;
suffixes = X_DEFAULT;
/*
* Room for runv extra + args [+ --binary] [+ 2 labels]
* + 1 file + 1 trailing null.
*/
diffv = tnalloc(char const*, 1 + argc + !!OPEN_O_BINARY + 2*DIFF_L + 2);
diffp = diffv + 1;
*diffp++ = DIFF;
argc = getRCSINIT(argc, argv, &newargv);
argv = newargv;
while (a = *++argv, 0<--argc && *a++=='-') {
dcp = a;
while ((c = *a++)) switch (c) {
case 'r':
switch (++revnums) {
case 1: rev1=a; break;
case 2: rev2=a; break;
default: error("too many revision numbers");
}
goto option_handled;
case '-': case 'D':
no_diff_means_no_output = false;
/* fall into */
case 'C': case 'F': case 'I': case 'L': case 'W':
#if DIFF_L
if (c == 'L' && file_labels++ == 2)
faterror("too many -L options");
#endif
*dcp++ = c;
if (*a)
do *dcp++ = *a++;
while (*a);
else {
if (!--argc)
faterror("-%c needs following argument%s",
c, cmdusage
);
*diffp++ = *argv++;
}
break;
case 'y':
no_diff_means_no_output = false;
/* fall into */
case 'B': case 'H':
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
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':
*dcp++ = c;
break;
case 'q':
quietflag=true;
break;
case 'x':
suffixarg = *argv;
suffixes = *argv + 2;
goto option_handled;
case 'z':
zonearg = *argv;
zone_set(*argv + 2);
goto option_handled;
case 'T':
/* Ignore -T, so that RCSINIT can contain -T. */
if (*a)
goto unknown;
break;
case 'V':
versionarg = *argv;
setRCSversion(versionarg);
goto option_handled;
case 'k':
expandarg = *argv;
if (0 <= str2expmode(expandarg+2))
goto option_handled;
/* fall into */
default:
unknown:
error("unknown option: %s%s", *argv, cmdusage);
};
option_handled:
if (dcp != *argv+1) {
*dcp = 0;
*diffp++ = *argv;
}
} /* end of option processing */
for (pp = diffv+2, c = 0; pp<diffp; )
c += strlen(*pp++) + 1;
diffvstr = a = tnalloc(char, c + 1);
for (pp = diffv+2; pp<diffp; ) {
p = *pp++;
*a++ = ' ';
while ((*a = *p++))
a++;
}
*a = 0;
#if DIFF_L
diff_label1 = diff_label2 = 0;
if (file_labels < 2) {
if (!file_labels)
diff_label1 = diffp++;
diff_label2 = diffp++;
}
#endif
diffpend = diffp;
cov[1] = CO;
cov[2] = "-q";
# if !DIFF_L
cov[3] = "-M";
# endif
/* Now handle all pathnames. */
if (nerror)
cleanup();
else if (argc < 1)
faterror("no input file%s", cmdusage);
else
for (; 0 < argc; cleanup(), ++argv, --argc) {
ffree();
if (pairnames(argc, argv, rcsreadopen, true, false) <= 0)
continue;
diagnose("===================================================================\nRCS file: %s\n",RCSname);
if (!rev2) {
/* Make sure work file is readable, and get its status. */
if (!(workptr = Iopen(workname, FOPEN_R_WORK, &workstat))) {
eerror(workname);
continue;
}
}
gettree(); /* reads in the delta tree */
if (!Head) {
rcserror("no revisions present");
continue;
}
if (revnums==0 || !*rev1)
rev1 = Dbranch ? Dbranch : Head->num;
if (!fexpandsym(rev1, &numericrev, workptr)) continue;
if (!(target=genrevs(numericrev.string,(char *)0,(char *)0,(char *)0,&gendeltas))) continue;
xrev1=target->num;
#if DIFF_L
if (diff_label1)
*diff_label1 = setup_label(&labelbuf[0], target->num, target->date);
#endif
lexpandarg = expandarg;
if (revnums==2) {
if (!fexpandsym(
*rev2 ? rev2 : Dbranch ? Dbranch : Head->num,
&numericrev,
workptr
))
continue;
if (!(target=genrevs(numericrev.string,(char *)0,(char *)0,(char *)0,&gendeltas))) continue;
xrev2=target->num;
if (no_diff_means_no_output && xrev1 == xrev2)
continue;
} else if (
target->lockedby
&& !lexpandarg
&& Expand == KEYVAL_EXPAND
&& WORKMODE(RCSstat.st_mode,true) == workstat.st_mode
)
lexpandarg = "-kkvl";
Izclose(&workptr);
#if DIFF_L
if (diff_label2)
if (revnums == 2)
*diff_label2 = setup_label(&labelbuf[1], target->num, target->date);
else {
time2date(workstat.st_mtime, date2);
*diff_label2 = setup_label(&labelbuf[1], (char*)0, date2);
}
#endif
diagnose("retrieving revision %s\n", xrev1);
bufscpy(&commarg, "-p");
bufscat(&commarg, rev1); /* not xrev1, for $Name's sake */
pp = &cov[3 + !DIFF_L];
*pp++ = commarg.string;
if (lexpandarg) *pp++ = lexpandarg;
if (suffixarg) *pp++ = suffixarg;
if (versionarg) *pp++ = versionarg;
if (zonearg) *pp++ = zonearg;
*pp++ = RCSname;
*pp = 0;
diffp = diffpend;
# if OPEN_O_BINARY
if (Expand == BINARY_EXPAND)
*diffp++ = "--binary";
# endif
diffp[0] = maketemp(0);
if (runv(-1, diffp[0], cov)) {
rcserror("co failed");
continue;
}
if (!rev2) {
diffp[1] = workname;
if (*workname == '-') {
char *dp = ftnalloc(char, strlen(workname)+3);
diffp[1] = dp;
*dp++ = '.';
*dp++ = SLASH;
VOID strcpy(dp, workname);
}
} else {
diagnose("retrieving revision %s\n",xrev2);
bufscpy(&commarg, "-p");
bufscat(&commarg, rev2); /* not xrev2, for $Name's sake */
cov[3 + !DIFF_L] = commarg.string;
diffp[1] = maketemp(1);
if (runv(-1, diffp[1], cov)) {
rcserror("co failed");
continue;
}
}
if (!rev2)
diagnose("diff%s -r%s %s\n", diffvstr, xrev1, workname);
else
diagnose("diff%s -r%s -r%s\n", diffvstr, xrev1, xrev2);
diffp[2] = 0;
switch (runv(-1, (char*)0, diffv)) {
case DIFF_SUCCESS:
break;
case DIFF_FAILURE:
if (exitstatus == DIFF_SUCCESS)
exitstatus = DIFF_FAILURE;
break;
default:
workerror("diff failed");
}
}
tempunlink();
exitmain(exitstatus);
}
static void
cleanup()
{
if (nerror) exitstatus = DIFF_TROUBLE;
Izclose(&finptr);
Izclose(&workptr);
}
#if RCS_lint
# define exiterr rdiffExit
#endif
void
exiterr()
{
tempunlink();
_exit(DIFF_TROUBLE);
}
#if DIFF_L
static char const *
setup_label(b, num, date)
struct buf *b;
char const *num;
char const date[datesize];
{
char *p;
char datestr[datesize + zonelenmax];
VOID date2str(date, datestr);
bufalloc(b,
strlen(workname)
+ sizeof datestr + 4
+ (num ? strlen(num) : 0)
);
p = b->string;
if (num)
VOID sprintf(p, "-L%s\t%s\t%s", workname, datestr, num);
else
VOID sprintf(p, "-L%s\t%s", workname, datestr);
return p;
}
#endif

View File

@ -0,0 +1,7 @@
# $FreeBSD$
SCRIPTS= rcsfreeze.sh
MAN= rcsfreeze.1
.include "../../Makefile.inc"
.include <bsd.prog.mk>

View File

@ -0,0 +1,68 @@
.de Id
.ds Rv \\$3
.ds Dt \\$4
..
.Id $FreeBSD$
.ds r \s-1RCS\s0
.TH RCSFREEZE 1 \*(Dt GNU
.SH NAME
rcsfreeze \- freeze a configuration of sources checked in under RCS
.SH SYNOPSIS
.B rcsfreeze
.RI [ "name" ]
.SH DESCRIPTION
.B rcsfreeze
assigns a symbolic revision
number to a set of \*r files that form a valid configuration.
.PP
The idea is to run
.B rcsfreeze
each time a new version is checked
in. A unique symbolic name (\c
.BI C_ number,
where
.I number
is increased each time
.B rcsfreeze
is run) is then assigned to the most
recent revision of each \*r file of the main trunk.
.PP
An optional
.I name
argument to
.B rcsfreeze
gives a symbolic name to the configuration.
The unique identifier is still generated
and is listed in the log file but it will not appear as
part of the symbolic revision name in the actual \*r files.
.PP
A log message is requested from the user for future reference.
.PP
The shell script works only on all \*r files at one time.
All changed files must be checked in already.
Run
.IR rcsclean (1)
first and see whether any sources remain in the current directory.
.SH FILES
.TP
.B RCS/.rcsfreeze.ver
version number
.TP
.B RCS/.rcsfreeze.log
log messages, most recent first
.SH AUTHOR
Stephan v. Bechtolsheim
.SH "SEE ALSO"
co(1), rcs(1), rcsclean(1), rlog(1)
.SH BUGS
.B rcsfreeze
does not check whether any sources are checked out and modified.
.PP
Although both source file names and RCS file names are accepted,
they are not paired as usual with RCS commands.
.PP
Error checking is rudimentary.
.PP
.B rcsfreeze
is just an optional example shell script, and should not be taken too seriously.
See \s-1CVS\s0 for a more complete solution.

View File

@ -0,0 +1,99 @@
#! /bin/sh
# rcsfreeze - assign a symbolic revision number to a configuration of RCS files
# $FreeBSD$
# The idea is to run rcsfreeze each time a new version is checked
# in. A unique symbolic revision number (C_[number], where number
# is increased each time rcsfreeze is run) is then assigned to the most
# recent revision of each RCS file of the main trunk.
#
# If the command is invoked with an argument, then this
# argument is used as the symbolic name to freeze a configuration.
# The unique identifier is still generated
# and is listed in the log file but it will not appear as
# part of the symbolic revision name in the actual RCS file.
#
# A log message is requested from the user which is saved for future
# references.
#
# The shell script works only on all RCS files at one time.
# It is important that all changed files are checked in (there are
# no precautions against any error in this respect).
# file names:
# {RCS/}.rcsfreeze.ver version number
# {RCS/}.rscfreeze.log log messages, most recent first
PATH=/bin:/usr/bin:$PATH
export PATH
DATE=`LC_ALL=C date` || exit
# Check whether we have an RCS subdirectory, so we can have the right
# prefix for our paths.
if test -d RCS
then RCSDIR=RCS/ EXT=
else RCSDIR= EXT=,v
fi
# Version number stuff, log message file
VERSIONFILE=${RCSDIR}.rcsfreeze.ver
LOGFILE=${RCSDIR}.rcsfreeze.log
# Initialize, rcsfreeze never run before in the current directory
test -r $VERSIONFILE || { echo 0 >$VERSIONFILE && >>$LOGFILE; } || exit
# Get Version number, increase it, write back to file.
VERSIONNUMBER=`cat $VERSIONFILE` &&
VERSIONNUMBER=`expr $VERSIONNUMBER + 1` &&
echo $VERSIONNUMBER >$VERSIONFILE || exit
# Symbolic Revision Number
SYMREV=C_$VERSIONNUMBER
# Allow the user to give a meaningful symbolic name to the revision.
SYMREVNAME=${1-$SYMREV}
echo >&2 "rcsfreeze: symbolic revision number computed: \"${SYMREV}\"
rcsfreeze: symbolic revision number used: \"${SYMREVNAME}\"
rcsfreeze: the two differ only when rcsfreeze invoked with argument
rcsfreeze: give log message, summarizing changes (end with EOF or single '.')" \
|| exit
# Stamp the logfile. Because we order the logfile the most recent
# first we will have to save everything right now in a temporary file.
TMPLOG=/tmp/rcsfrz$$
trap 'rm -f $TMPLOG; exit 1' 1 2 13 15
# Now ask for a log message, continously add to the log file
(
echo "Version: $SYMREVNAME($SYMREV), Date: $DATE
-----------" || exit
while read MESS
do
case $MESS in
.) break
esac
echo " $MESS" || exit
done
echo "-----------
" &&
cat $LOGFILE
) >$TMPLOG &&
# combine old and new logfiles
cp $TMPLOG $LOGFILE &&
rm -f $TMPLOG &&
# Now the real work begins by assigning a symbolic revision number
# to each rcs file. Take the most recent version on the default branch.
# If there are any .*,v files, throw them in too.
# But ignore RCS/.* files that do not end in ,v.
DOTFILES=
for DOTFILE in ${RCSDIR}.*,v
do
if test -f "$DOTFILE"
then
DOTFILES="${RCSDIR}.*,v"
break
fi
done
exec rcs -q -n$SYMREVNAME: ${RCSDIR}*$EXT $DOTFILES

View File

@ -0,0 +1,8 @@
PROG= rcsmerge
SRCS= rcsmerge.c
CFLAGS+= -I${.CURDIR}/../lib
LDADD= ${LIBRCS}
DPADD= ${LIBRCS}
.include "../../Makefile.inc"
.include <bsd.prog.mk>

View File

@ -0,0 +1,189 @@
.de Id
.ds Rv \\$3
.ds Dt \\$4
..
.Id $FreeBSD$
.ds r \&\s-1RCS\s0
.if n .ds - \%--
.if t .ds - \(em
.TH RCSMERGE 1 \*(Dt GNU
.SH NAME
rcsmerge \- merge RCS revisions
.SH SYNOPSIS
.B rcsmerge
.RI [ options ] " file"
.SH DESCRIPTION
.B rcsmerge
incorporates the changes between two revisions
of an \*r file into the corresponding working file.
.PP
Pathnames matching an \*r suffix denote \*r files;
all others denote working files.
Names are paired as explained in
.BR ci (1).
.PP
At least one revision must be specified with one of the options
described below, usually
.BR \-r .
At most two revisions may be specified.
If only one revision is specified, the latest
revision on the default branch (normally the highest branch on the trunk)
is assumed for the second revision.
Revisions may be specified numerically or symbolically.
.PP
.B rcsmerge
prints a warning if there are overlaps, and delimits
the overlapping regions as explained in
.BR merge (1).
The command is useful for incorporating changes into a checked-out revision.
.SH OPTIONS
.TP
.B \-A
Output conflicts using the
.B \-A
style of
.BR diff3 (1),
if supported by
.BR diff3 .
This merges all changes leading from
.I file2
to
.I file3
into
.IR file1 ,
and generates the most verbose output.
.TP
\f3\-E\fP, \f3\-e\fP
These options specify conflict styles that generate less information
than
.BR \-A .
See
.BR diff3 (1)
for details.
The default is
.BR \-E .
With
.BR \-e ,
.B rcsmerge
does not warn about conflicts.
.TP
.BI \-k subst
Use
.I subst
style keyword substitution.
See
.BR co (1)
for details.
For example,
.B "\-kk\ \-r1.1\ \-r1.2"
ignores differences in keyword values when merging the changes from
.B 1.1
to
.BR 1.2 .
It normally does not make sense to merge binary files as if they were text, so
.B rcsmerge
refuses to merge files if
.B \-kb
expansion is used.
.TP
.BR \-p [\f2rev\fP]
Send the result to standard output instead of overwriting the working file.
.TP
.BR \-q [\f2rev\fP]
Run quietly; do not print diagnostics.
.TP
.BR \-r [\f2rev\fP]
Merge with respect to revision
.IR rev .
Here an empty
.I rev
stands for the latest revision on the default branch, normally the head.
.TP
.B \-T
This option has no effect;
it is present for compatibility with other \*r commands.
.TP
.BI \-V
Print \*r's version number.
.TP
.BI \-V n
Emulate \*r version
.IR n .
See
.BR co (1)
for details.
.TP
.BI \-x "suffixes"
Use
.I suffixes
to characterize \*r files.
See
.BR ci (1)
for details.
.TP
.BI \-z zone
Use
.I zone
as the time zone for keyword substitution.
See
.BR co (1)
for details.
.SH EXAMPLES
Suppose you have released revision 2.8 of
.BR f.c .
Assume
furthermore that after you complete an unreleased revision 3.4, you receive
updates to release 2.8 from someone else.
To combine the updates to 2.8 and your changes between 2.8 and 3.4,
put the updates to 2.8 into file f.c and execute
.LP
.B " rcsmerge \-p \-r2.8 \-r3.4 f.c >f.merged.c"
.PP
Then examine
.BR f.merged.c .
Alternatively, if you want to save the updates to 2.8 in the \*r file,
check them in as revision 2.8.1.1 and execute
.BR "co \-j":
.LP
.B " ci \-r2.8.1.1 f.c"
.br
.B " co \-r3.4 \-j2.8:2.8.1.1 f.c"
.PP
As another example, the following command undoes the changes
between revision 2.4 and 2.8 in your currently checked out revision
in
.BR f.c .
.LP
.B " rcsmerge \-r2.8 \-r2.4 f.c"
.PP
Note the order of the arguments, and that
.B f.c
will be
overwritten.
.SH ENVIRONMENT
.TP
.B \s-1RCSINIT\s0
options prepended to the argument list, separated by spaces.
See
.BR ci (1)
for details.
.SH DIAGNOSTICS
Exit status is 0 for no overlaps, 1 for some overlaps, 2 for trouble.
.SH IDENTIFICATION
Author: Walter F. Tichy.
.br
Manual Page Revision: \*(Rv; Release Date: \*(Dt.
.br
Copyright \(co 1982, 1988, 1989 Walter F. Tichy.
.br
Copyright \(co 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert.
.SH "SEE ALSO"
ci(1), co(1), ident(1), merge(1), rcs(1), rcsdiff(1), rcsintro(1), rlog(1),
rcsfile(5)
.br
Walter F. Tichy,
\*r\*-A System for Version Control,
.I "Software\*-Practice & Experience"
.BR 15 ,
7 (July 1985), 637-654.
.br

View File

@ -0,0 +1,286 @@
/* Merge RCS revisions. */
/* Copyright 1982, 1988, 1989 Walter Tichy
Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert
Distributed under license by the Free Software Foundation, Inc.
This file is part of RCS.
RCS 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.
RCS 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 RCS; see the file COPYING.
If not, write to the Free Software Foundation,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
Report problems and direct all questions to:
rcs-bugs@cs.purdue.edu
*/
/*
* Revision 5.15 1995/06/16 06:19:24 eggert
* Update FSF address.
*
* Revision 5.14 1995/06/01 16:23:43 eggert
* (main): Report an error if -kb, so don't worry about binary stdout.
* Punctuate messages properly. Rewrite to avoid `goto end'.
*
* Revision 5.13 1994/03/17 14:05:48 eggert
* Specify subprocess input via file descriptor, not file name. Remove lint.
*
* Revision 5.12 1993/11/09 17:40:15 eggert
* -V now prints version on stdout and exits. Don't print usage twice.
*
* Revision 5.11 1993/11/03 17:42:27 eggert
* Add -A, -E, -e, -z. Ignore -T. Allow up to three file labels.
* Pass -Vn to `co'. Pass unexpanded revision name to `co', so that Name works.
*
* Revision 5.10 1992/07/28 16:12:44 eggert
* Add -V.
*
* Revision 5.9 1992/01/24 18:44:19 eggert
* lint -> RCS_lint
*
* Revision 5.8 1992/01/06 02:42:34 eggert
* Update usage string.
*
* Revision 5.7 1991/11/20 17:58:09 eggert
* Don't Iopen(f, "r+"); it's not portable.
*
* Revision 5.6 1991/08/19 03:13:55 eggert
* Add -r$. Tune.
*
* Revision 5.5 1991/04/21 11:58:27 eggert
* Add -x, RCSINIT, MS-DOS support.
*
* Revision 5.4 1991/02/25 07:12:43 eggert
* Merging a revision to itself is no longer an error.
*
* Revision 5.3 1990/11/01 05:03:50 eggert
* Remove unneeded setid check.
*
* Revision 5.2 1990/09/04 08:02:28 eggert
* Check for I/O error when reading working file.
*
* Revision 5.1 1990/08/29 07:14:04 eggert
* Add -q. Pass -L options to merge.
*
* Revision 5.0 1990/08/22 08:13:41 eggert
* Propagate merge's exit status.
* Remove compile-time limits; use malloc instead.
* Make lock and temp files faster and safer. Ansify and Posixate. Add -V.
* Don't use access(). Tune.
*
* Revision 4.5 89/05/01 15:13:16 narten
* changed copyright header to reflect current distribution rules
*
* Revision 4.4 88/08/09 19:13:13 eggert
* Beware merging into a readonly file.
* Beware merging a revision to itself (no change).
* Use execv(), not system(); yield exit status like diff(1)'s.
*
* Revision 4.3 87/10/18 10:38:02 narten
* Updating version numbers. Changes relative to version 1.1
* actually relative to 4.1
*
* Revision 1.3 87/09/24 14:00:31 narten
* Sources now pass through lint (if you ignore printf/sprintf/fprintf
* warnings)
*
* Revision 1.2 87/03/27 14:22:36 jenkins
* Port to suns
*
* Revision 4.1 83/03/28 11:14:57 wft
* Added handling of default branch.
*
* Revision 3.3 82/12/24 15:29:00 wft
* Added call to catchsig().
*
* Revision 3.2 82/12/10 21:32:02 wft
* Replaced getdelta() with gettree(); improved error messages.
*
* Revision 3.1 82/11/28 19:27:44 wft
* Initial revision.
*
*/
#include "rcsbase.h"
static char const co[] = CO;
mainProg(rcsmergeId, "rcsmerge", "$FreeBSD$")
{
static char const cmdusage[] =
"\nrcsmerge usage: rcsmerge -rrev1 [-rrev2] -ksubst -{pq}[rev] -Vn -xsuff -zzone file";
static char const quietarg[] = "-q";
register int i;
char *a, **newargv;
char const *arg[3];
char const *rev[3], *xrev[3]; /*revision numbers*/
char const *edarg, *expandarg, *suffixarg, *versionarg, *zonearg;
int tostdout;
int status;
RILE *workptr;
struct buf commarg;
struct buf numericrev; /* holds expanded revision number */
struct hshentries *gendeltas; /* deltas to be generated */
struct hshentry * target;
bufautobegin(&commarg);
bufautobegin(&numericrev);
edarg = rev[1] = rev[2] = 0;
status = 0; /* Keep lint happy. */
tostdout = false;
expandarg = suffixarg = versionarg = zonearg = quietarg; /* no-op */
suffixes = X_DEFAULT;
argc = getRCSINIT(argc, argv, &newargv);
argv = newargv;
while (a = *++argv, 0<--argc && *a++=='-') {
switch (*a++) {
case 'p':
tostdout=true;
goto revno;
case 'q':
quietflag = true;
revno:
if (!*a)
break;
/* falls into -r */
case 'r':
if (!rev[1])
rev[1] = a;
else if (!rev[2])
rev[2] = a;
else
error("too many revision numbers");
break;
case 'A': case 'E': case 'e':
if (*a)
goto unknown;
edarg = *argv;
break;
case 'x':
suffixarg = *argv;
suffixes = a;
break;
case 'z':
zonearg = *argv;
zone_set(a);
break;
case 'T':
/* Ignore -T, so that RCSINIT can contain -T. */
if (*a)
goto unknown;
break;
case 'V':
versionarg = *argv;
setRCSversion(versionarg);
break;
case 'k':
expandarg = *argv;
if (0 <= str2expmode(expandarg+2))
break;
/* fall into */
default:
unknown:
error("unknown option: %s%s", *argv, cmdusage);
};
} /* end of option processing */
if (!rev[1]) faterror("no base revision number given");
/* Now handle all pathnames. */
if (!nerror) {
if (argc < 1)
faterror("no input file%s", cmdusage);
if (0 < pairnames(argc, argv, rcsreadopen, true, false)) {
if (argc>2 || (argc==2 && argv[1]))
warn("excess arguments ignored");
if (Expand == BINARY_EXPAND)
workerror("merging binary files");
diagnose("RCS file: %s\n", RCSname);
if (!(workptr = Iopen(workname, FOPEN_R_WORK, (struct stat*)0)))
efaterror(workname);
gettree(); /* reads in the delta tree */
if (!Head) rcsfaterror("no revisions present");
if (!*rev[1])
rev[1] = Dbranch ? Dbranch : Head->num;
if (fexpandsym(rev[1], &numericrev, workptr)
&& (target=genrevs(numericrev.string, (char *)0, (char *)0, (char*)0, &gendeltas))
) {
xrev[1] = target->num;
if (!rev[2] || !*rev[2])
rev[2] = Dbranch ? Dbranch : Head->num;
if (fexpandsym(rev[2], &numericrev, workptr)
&& (target=genrevs(numericrev.string, (char *)0, (char *)0, (char *)0, &gendeltas))
) {
xrev[2] = target->num;
if (strcmp(xrev[1],xrev[2]) == 0) {
if (tostdout) {
fastcopy(workptr, stdout);
Ofclose(stdout);
}
} else {
Izclose(&workptr);
for (i=1; i<=2; i++) {
diagnose("retrieving revision %s\n", xrev[i]);
bufscpy(&commarg, "-p");
bufscat(&commarg, rev[i]); /* not xrev[i], for $Name's sake */
if (run(
-1,
/* Do not collide with merger.c maketemp(). */
arg[i] = maketemp(i+2),
co, quietarg, commarg.string,
expandarg, suffixarg, versionarg, zonearg,
RCSname, (char*)0
))
rcsfaterror("co failed");
}
diagnose("Merging differences between %s and %s into %s%s\n",
xrev[1], xrev[2], workname,
tostdout?"; result to stdout":"");
arg[0] = xrev[0] = workname;
status = merge(tostdout, edarg, xrev, arg);
}
}
}
Izclose(&workptr);
}
}
tempunlink();
exitmain(nerror ? DIFF_TROUBLE : status);
}
#if RCS_lint
# define exiterr rmergeExit
#endif
void
exiterr()
{
tempunlink();
_exit(DIFF_TROUBLE);
}

454
gnu/usr.bin/rcs/rcstest Executable file
View File

@ -0,0 +1,454 @@
#! /bin/sh
# Test RCS's functions.
# The RCS commands are searched for in the PATH as usual;
# to test the working directory's commands, prepend . to your PATH.
# Test RCS by creating files RCS/a.* and RCS/a.c.
# If all goes well, output nothing, and remove the temporary files.
# Otherwise, send a message to standard output.
# Exit status is 0 if OK, 1 if an RCS bug is found, and 2 if scaffolding fails.
# With the -v option, output more debugging info.
# If diff outputs `No differences encountered' when comparing identical files,
# then rcstest may also output these noise lines; ignore them.
# The current directory and ./RCS must be readable, writable, and searchable.
# $FreeBSD$
# Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert
# Distributed under license by the Free Software Foundation, Inc.
#
# This file is part of RCS.
#
# RCS 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.
#
# RCS 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 RCS; see the file COPYING.
# If not, write to the Free Software Foundation,
# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# Report problems and direct all questions to:
#
# rcs-bugs@cs.purdue.edu
# The Makefile overrides the following defaults.
: ${ALL_CFLAGS=-Dhas_conf_h}
: ${CC=cc}
: ${DIFF=diff}
# : ${LDFLAGS=} ${LIBS=} tickles old shell bug
CL="$CC $ALL_CFLAGS $LDFLAGS -o a.out"
L=$LIBS
RCSINIT=-x
export RCSINIT
SLASH=/
RCSfile=RCS${SLASH}a.c
RCS_alt=RCS${SLASH}a.d
lockfile=RCS${SLASH}a._
case $1 in
-v) q=; set -x;;
'') q=-q;;
*) echo >&2 "$0: usage: $0 [-v]"; exit 2
esac
if test -d RCS
then rmdir=:
else rmdir=rmdir; mkdir RCS || exit
fi
rm -f a.* $RCSfile $RCS_alt $lockfile &&
echo 1.1 >a.11 &&
echo 1.1.1.1 >a.3x1 &&
echo 1.2 >a.12 || { echo "#initialization failed"; exit 2; }
case "`$DIFF -c a.11 a.3x1`" in
*!\ 1.1.1.1)
diff="$DIFF -c";;
*)
echo "#warning: $DIFF -c does not work, so diagnostics may be cryptic"
diff=$DIFF
esac
rcs -i -L -ta.11 $q a.c &&
test -r $RCSfile || {
echo "#rcs -i -L failed; perhaps RCS is not properly installed."
exit 1
}
rlog a.c >/dev/null || { echo "#rlog failed on empty RCS file"; exit 1; }
rm -f $RCSfile || exit 2
cp a.11 a.c &&
ci -ta.11 -mm $q a.c &&
test -r $RCSfile &&
rcs -L $q a.c || { echo "#ci+rcs -L failed"; exit 1; }
test ! -f a.c || { echo "#ci did not remove working file"; exit 1; }
for l in '' '-l'
do
co $l $q a.c &&
test -f a.c || { echo '#co' $l did not create working file; exit 1; }
$diff a.11 a.c || { echo '#ci' followed by co $l is not a no-op; exit 1; }
done
cp a.12 a.c &&
ci -mm $q a.c &&
co $q a.c &&
$diff a.12 a.c || { echo "#ci+co failed"; exit 1; }
rm -f a.c &&
co -r1.1 $q a.c &&
$diff a.11 a.c || { echo "#can't retrieve first revision"; exit 1; }
rm -f a.c &&
cp a.3x1 a.c &&
ci -r1.1.1 -mm $q a.c &&
co -r1.1.1.1 $q a.c &&
$diff a.3x1 a.c || { echo "#branches failed"; exit 1; }
rm -f a.c &&
co -l $q a.c &&
ci -f -mm $q a.c &&
co -r1.3 $q a.c &&
$diff a.12 a.c || { echo "#(co -l; ci -f) failed"; exit 1; }
rm -f a.c &&
co -l $q a.c &&
echo 1.4 >a.c &&
ci -l -mm $q a.c &&
echo error >a.c &&
ci -mm $q a.c || { echo "#ci -l failed"; exit 1; }
rm -f a.c &&
co -l $q a.c &&
echo 1.5 >a.c &&
ci -u -mm $q a.c &&
test -r a.c || { echo "#ci -u didn't create a working file"; exit 1; }
rm -f a.c &&
echo error >a.c || exit 2
ci -mm $q a.c 2>/dev/null && { echo "#ci -u didn't unlock the file"; exit 1; }
rm -f a.c &&
rcs -l $q a.c &&
co -u $q a.c || { echo "#rcs -l + co -u failed"; exit 1; }
rm -f a.c &&
echo error >a.c || exit 2
ci -mm $q a.c 2>/dev/null && { echo "#co -u didn't unlock the file"; exit 1; }
rm -f a.c &&
cp a.11 a.c &&
co -f $q a.c || { echo "#co -f failed"; exit 1; }
$diff a.11 a.c >/dev/null && { echo "#co -f had no effect"; exit 1; }
co -p1.1 $q a.c >a.t &&
$diff a.11 a.t || { echo "#co -p failed"; exit 1; }
for n in n N
do
rm -f a.c &&
co -l $q a.c &&
echo $n >a.$n &&
cp a.$n a.c &&
ci -${n}n -mm $q a.c &&
co -rn $q a.c &&
$diff a.$n a.c || { echo "#ci -$n failed"; exit 1; }
done
case $LOGNAME in
?*) me=$LOGNAME;;
*)
case $USER in
?*) me=$USER;;
*)
me=`who am i` || exit 2
me=`echo "$me" | sed -e 's/ .*//' -e 's/.*!//'`
case $me in
'') echo >&2 "$0: cannot deduce user name"; exit 2
esac
esac
esac
# Get the date of the previous revision in UTC.
date=`rlog -r a.c | sed -n '/^date: /{ s///; s/;.*//; p; q; }'` || exit
case $date in
[0-9][0-9][0-9]*[0-9]/[0-1][0-9]/[0-3][0-9]\ [0-2][0-9]:[0-5][0-9]:[0-6][0-9]);;
*) echo >&2 "$0: $date: bad rlog date output"; exit 1
esac
PWD=`pwd` && export PWD &&
rm -f a.c &&
co -l $q a.c &&
sed 's/@/$/g' >a.kv <<EOF
@Author: w @
@Date: $date @
@Header: $PWD$SLASH$RCSfile 2.1 $date w s @
@Id: a.c 2.1 $date w s @
@Locker: @
* @Log: a.c @
* Revision 2.1 $date w
* m
*
@Name: Oz @
@RCSfile: a.c @
@Revision: 2.1 @
@Source: $PWD$SLASH$RCSfile @
@State: s @
EOF
test $? = 0 &&
sed 's/:.*\$/$/' a.kv >a.k &&
sed -e 's/w s [$]/w s '"$me"' $/' -e 's/[$]Locker: /&'"$me/" a.kv >a.kvl &&
sed s/Oz//g a.kv >a.e &&
sed s/Oz/N/g a.kv >a.N &&
sed -e '/\$/!d' -e 's/\$$/: old $/' a.k >a.o &&
sed -e 's/\$[^ ]*: //' -e 's/ \$//' a.kv >a.v &&
cp a.o a.c &&
ci -d"$date" -nOz -ss -ww -u2.1 -mm $q a.c &&
$diff a.kv a.c || { echo "#keyword expansion failed"; exit 1; }
co -pOz -ko $q a.c >a.oo &&
$diff a.o a.oo || { echo "#co -p -ko failed"; exit 1; }
cp a.kv a.o && cp a.o a.b || exit 2
rcs -oOz $q a.c &&
rcs -l $q a.c &&
ci -k -u $q a.c &&
$diff a.kv a.c || { echo "#ci -k failed"; exit 1; }
sed -n 's/^[^$]*\$/$/p' a.kv >a.i &&
ident a.c >a.i1 &&
sed -e 1d -e 's/^[ ]*//' a.i1 >a.i2 &&
$diff a.i a.i2 || { echo "#ident failed"; exit 1; }
rcs -i $q a.c 2>/dev/null && { echo "#rcs -i permitted existing file"; exit 1; }
rm -f a.c &&
co -l $q a.c &&
echo 2.2 >a.c &&
ci -mm $q a.c &&
echo 1.1.1.2 >a.c &&
rcs -l1.1.1 $q a.c &&
ci -r1.1.1.2 -mm $q a.c &&
rcs -b1.1.1 $q a.c &&
test " `co -p $q a.c`" = ' 1.1.1.2' || { echo "#rcs -b1.1.1 failed"; exit 1; }
rcs -b $q a.c &&
test " `co -p $q a.c`" = ' 2.2' || { echo "#rcs -b failed"; exit 1; }
echo 2.3 >a.c || exit 2
rcs -U $q a.c || { echo "#rcs -U failed"; exit 1; }
ci -mm $q a.c || { echo "#rcs -U didn't unset strict locking"; exit 1; }
rcs -L $q a.c || { echo "#rcs -L failed"; exit 1; }
echo error >a.c || exit 2
ci -mm $q a.c 2>/dev/null && { echo "#ci retest failed"; exit 1; }
rm -f a.c &&
log0=`rlog -h a.c` &&
co -l $q a.c &&
ci -mm $q a.c &&
log1=`rlog -h a.c` &&
test " $log0" = " $log1" || { echo "#unchanged ci didn't revert"; exit 1; }
rm -f a.c &&
rcs -nN:1.1 $q a.c &&
co -rN $q a.c &&
$diff a.11 a.c || { echo "#rcs -n failed"; exit 1; }
rm -f a.c &&
rcs -NN:2.1 $q a.c &&
co -rN $q a.c &&
$diff a.N a.c || { echo "#rcs -N failed"; exit 1; }
rm -f a.c &&
co -l $q a.c &&
echo ':::$''Log$' >a.c &&
ci -u -mm $q a.c &&
test " `sed '$!d' a.c`" = ' :::' || { echo "#comment leader failed"; exit 1; }
rm -f a.c &&
rcs -o2.2: $q a.c &&
co $q a.c &&
$diff a.e a.c || { echo "#rcs -o failed"; exit 1; }
rcsdiff -r1.1 -rOz $q a.c >a.0
case $? in
1) ;;
*) echo "#rcsdiff bad status"; exit 1
esac
$DIFF a.11 a.kv >a.1
$diff a.0 a.1 || { echo "#rcsdiff failed"; exit 1; }
rcs -l2.1 $q a.c || { echo "#rcs -l2.1 failed"; exit 1; }
for i in b k kv kvl o v
do
rm -f a.c &&
cp a.$i a.c &&
rcsdiff -k$i -rOz $q a.c || { echo "#rcsdiff -k$i failed"; exit 1; }
done
co -p1.1 -ko $q a.c >a.t &&
$diff a.11 a.t || { echo "#co -p1.1 -ko failed"; exit 1; }
rcs -u2.1 $q a.c || { echo "#rcs -u2.1 failed"; exit 1; }
rm -f a.c &&
rcsclean $q a.c &&
rcsclean -u $q a.c || { echo "#rcsclean botched a nonexistent file"; exit 1; }
rm -f a.c &&
co $q a.c &&
rcsclean -n $q a.c &&
rcsclean -n -u $q a.c &&
test -f a.c || { echo "#rcsclean -n removed a file"; exit 1; }
rm -f a.c &&
co $q a.c &&
rcsclean $q a.c &&
test ! -f a.c || { echo "#rcsclean missed an unlocked file"; exit 1; }
rm -f a.c &&
co -l $q a.c &&
rcsclean $q a.c &&
test -f a.c || { echo "#rcsclean removed a locked file"; exit 1; }
rcsclean -u $q a.c &&
test ! -f a.c || {
echo "#rcsclean -u missed an unchanged locked file"; exit 1;
}
rm -f a.c &&
co -l $q a.c &&
echo change >>a.c &&
rcsclean $q a.c &&
rcsclean $q -u a.c &&
test -f a.c || { echo "#rcsclean removed a changed file"; exit 1; }
rm -f a.c &&
co -l $q a.c &&
cat >a.c <<'EOF'
2.2
a
b
c
d
EOF
test $? = 0 &&
ci -l -mm $q a.c &&
co -p2.2 $q a.c | sed -e s/2.2/2.3/ -e s/b/b1/ >a.c &&
ci -l -mm $q a.c &&
co -p2.2 $q a.c | sed -e s/2.2/new/ -e s/d/d1/ >a.c || exit 2
cat >a.0 <<'EOF'
2.3
a
b1
c
d1
EOF
cat >a.1 <<'EOF'
<<<<<<< a.c
new
=======
2.3
>>>>>>> 2.3
a
b1
c
d1
EOF
rcsmerge -E -r2.2 -r2.3 $q a.c
case $? in
0)
if $diff a.0 a.c >/dev/null
then echo "#warning: diff3 -E does not work, " \
"so merge and rcsmerge ignore overlaps and suppress overlap lines."
else
$diff a.1 a.c || { echo "#rcsmerge failed (status 0)"; exit 1; }
echo "#warning: The diff3 lib program exit status ignores overlaps," \
"so rcsmerge does not warn about overlap lines that it generates."
fi
;;
1)
$diff a.1 a.c || { echo "#rcsmerge failed (status 1)"; exit 1; }
;;
*)
echo "#rcsmerge bad status"; exit 1
esac
# Avoid `tr' if possible; it's not portable, and it can't handle null bytes.
# Our substitute exclusive-ORs with '\n';
# this ensures null bytes on output, which is even better than `tr',
# since some diffs think a file is binary only if it contains null bytes.
cat >a.c <<'EOF'
#include <stdio.h>
int main() {
int c;
while ((c=getchar()) != EOF)
putchar(c ^ '\n');
return 0;
}
EOF
tr=tr
if (rm -f a.exe a.out && $CL a.c $L >&2) >/dev/null 2>&1
then
if test -s a.out
then tr=./a.out
elif test -s a.exe
then tr=./a.exe
fi
fi
{
co -p $q a.c | $tr '\012' '\200' >a.24 &&
cp a.24 a.c &&
ciOut=`(ci -l -mm $q a.c 2>&1)` &&
case $ciOut in
?*) echo >&2 "$ciOut"
esac &&
co -p $q a.c | $tr '\200' '\012' >a.c &&
rcsdiff -r2.3 $q a.c >/dev/null &&
echo 2.5 >a.c &&
ci -l -mm $q a.c &&
cp a.24 a.c &&
rcsdiff -r2.4 $q a.c >/dev/null
} || echo "#warning: Traditional diff is used, so RCS is limited to text files."
rcs -u -o2.4: $q a.c || { echo "#rcs -u -o failed"; exit 1; }
rcs -i -Aa.c -t- $q a.d || { echo "#rcs -i -A failed"; exit 1; }
rlog -r2.1 a.c >a.t &&
grep '^checked in with -k' a.t >/dev/null &&
sed '/^checked in with -k/d' a.t >a.u &&
$diff - a.u <<EOF
RCS file: $RCSfile
Working file: a.c
head: 2.3
branch:
locks: strict
access list:
symbolic names:
N: 2.1
Oz: 2.1
n: 1.8
keyword substitution: kv
total revisions: 13; selected revisions: 1
description:
1.1
----------------------------
revision 2.1
date: $date; author: w; state: s; lines: +14 -1
=============================================================================
EOF
test $? = 0 || { echo "#rlog failed"; exit 1; }
test ! -f $lockfile || { echo "#lock file not removed"; exit 1; }
rm -f a.* $RCSfile $RCS_alt
$rmdir RCS

View File

@ -0,0 +1,8 @@
PROG= rlog
SRCS= rlog.c
CFLAGS+= -I${.CURDIR}/../lib
LDADD= ${LIBRCS}
DPADD= ${LIBRCS}
.include "../../Makefile.inc"
.include <bsd.prog.mk>

318
gnu/usr.bin/rcs/rlog/rlog.1 Normal file
View File

@ -0,0 +1,318 @@
.de Id
.ds Rv \\$3
.ds Dt \\$4
..
.Id $FreeBSD$
.ds i \&\s-1ISO\s0
.ds r \&\s-1RCS\s0
.ds u \&\s-1UTC\s0
.if n .ds - \%--
.if t .ds - \(em
.TH RLOG 1 \*(Dt GNU
.SH NAME
rlog \- print log messages and other information about RCS files
.SH SYNOPSIS
.B rlog
.RI [ " options " ] " file " .\|.\|.
.SH DESCRIPTION
.B rlog
prints information about \*r files.
.PP
Pathnames matching an \*r suffix denote \*r files;
all others denote working files.
Names are paired as explained in
.BR ci (1).
.PP
.B rlog
prints the following information for each
\*r file: \*r pathname, working pathname, head (i.e., the number
of the latest revision on the trunk), default branch, access list, locks,
symbolic names, suffix, total number of revisions,
number of revisions selected for printing, and
descriptive text. This is followed by entries for the selected revisions in
reverse chronological order for each branch. For each revision,
.B rlog
prints revision number, author, date/time, state, number of
lines added/deleted (with respect to the previous revision),
locker of the revision (if any), and log message.
All times are displayed in Coordinated Universal Time (\*u) by default;
this can be overridden with
.BR \-z .
Without options,
.B rlog
prints complete information.
The options below restrict this output.
.nr n \w'\f3\-V\fP\f2n\fP'+2n-1/1n
.ds n \nn
.if \n(.g .if r an-tag-sep .ds n \w'\f3\-V\fP\f2n\fP'u+\n[an-tag-sep]u
.TP \*n
.B \-L
Ignore \*r files that have no locks set.
This is convenient in combination with
.BR \-h ,
.BR \-l ,
and
.BR \-R .
.TP
.B \-R
Print only the name of the \*r file.
This is convenient for translating a
working pathname into an \*r pathname.
.TP
.BI \-v "[string]"
Print only the working pathname and tip-revision.
The optional string is prepended to the outputline.
.TP
.B \-h
Print only the \*r pathname, working pathname, head,
default branch, access list, locks,
symbolic names, and suffix.
.TP
.B \-t
Print the same as
.BR \-h ,
plus the descriptive text.
.TP
.B \-N
Do not print the symbolic names.
.TP
.B \-b
Print information about the revisions on the default branch, normally
the highest branch on the trunk.
.TP
.BI \-d "dates"
Print information about revisions with a checkin date/time in the ranges given by
the semicolon-separated list of
.IR dates .
A range of the form
.IB d1 < d2
or
.IB d2 > d1
selects the revisions that were deposited between
.I d1
and
.I d2
exclusive.
A range of the form
.BI < d
or
.IB d >
selects
all revisions earlier than
.IR d .
A range of the form
.IB d <
or
.BI > d
selects
all revisions dated later than
.IR d .
If
.B <
or
.B >
is followed by
.B =
then the ranges are inclusive, not exclusive.
A range of the form
.I d
selects the single, latest revision dated
.I d
or earlier.
The date/time strings
.IR d ,
.IR d1 ,
and
.I d2
are in the free format explained in
.BR co (1).
Quoting is normally necessary, especially for
.B <
and
.BR > .
Note that the separator is
a semicolon.
.TP
.BR \-l [\f2lockers\fP]
Print information about locked revisions only.
In addition, if the comma-separated list
.I lockers
of login names is given,
ignore all locks other than those held by the
.IR lockers .
For example,
.B "rlog\ \-L\ \-R\ \-lwft\ RCS/*"
prints the name of \*r files locked by the user
.BR wft .
.TP
.BR \-r [\f2revisions\fP]
prints information about revisions given in the comma-separated list
.I revisions
of revisions and ranges.
A range
.IB rev1 : rev2
means revisions
.I rev1
to
.I rev2
on the same branch,
.BI : rev
means revisions from the beginning of the branch up to and including
.IR rev ,
and
.IB rev :
means revisions starting with
.I rev
to the end of the branch containing
.IR rev .
An argument that is a branch means all
revisions on that branch.
A range of branches means all revisions
on the branches in that range.
A branch followed by a
.B .\&
means the latest revision in that branch.
A bare
.B \-r
with no
.I revisions
means the latest revision on the default branch, normally the trunk.
.TP
.BI \-s states
prints information about revisions whose state attributes match one of the
states given in the comma-separated list
.IR states .
.TP
.BR \-w [\f2logins\fP]
prints information about revisions checked in by users with
login names appearing in the comma-separated list
.IR logins .
If
.I logins
is omitted, the user's login is assumed.
.TP
.B \-T
This option has no effect;
it is present for compatibility with other \*r commands.
.TP
.BI \-V
Print \*r's version number.
.TP
.BI \-V n
Emulate \*r version
.I n
when generating logs.
See
.BR co (1)
for more.
.TP
.BI \-x "suffixes"
Use
.I suffixes
to characterize \*r files.
See
.BR ci (1)
for details.
.PP
.B rlog
prints the intersection of the revisions selected with
the options
.BR \-d ,
.BR \-l ,
.BR \-s ,
and
.BR \-w ,
intersected
with the union of the revisions selected by
.B \-b
and
.BR \-r .
.TP
.BI \-z zone
specifies the date output format,
and specifies the default time zone for
.I date
in the
.BI \-d dates
option.
The
.I zone
should be empty, a numeric \*u offset, or the special string
.B LT
for local time.
The default is an empty
.IR zone ,
which uses the traditional \*r format of \*u without any time zone indication
and with slashes separating the parts of the date;
otherwise, times are output in \*i 8601 format with time zone indication.
For example, if local time is January 11, 1990, 8pm Pacific Standard Time,
eight hours west of \*u,
then the time is output as follows:
.RS
.LP
.RS
.nf
.ta \w'\f3\-z+05:30\fP 'u +\w'\f31990-01-11 09:30:00+05:30\fP 'u
.ne 4
\f2option\fP \f2time output\fP
\f3\-z\fP \f31990/01/12 04:00:00\fP \f2(default)\fP
\f3\-zLT\fP \f31990-01-11 20:00:00\-08\fP
\f3\-z+05:30\fP \f31990-01-12 09:30:00+05:30\fP
.ta 4n +4n +4n +4n
.fi
.RE
.SH EXAMPLES
.LP
.nf
.B " rlog \-L \-R RCS/*"
.B " rlog \-L \-h RCS/*"
.B " rlog \-L \-l RCS/*"
.B " rlog RCS/*"
.fi
.LP
The first command prints the names of all \*r files in the subdirectory
.B RCS
that have locks. The second command prints the headers of those files,
and the third prints the headers plus the log messages of the locked revisions.
The last command prints complete information.
.SH ENVIRONMENT
.TP
.B \s-1RCSINIT\s0
options prepended to the argument list, separated by spaces.
See
.BR ci (1)
for details.
.SH DIAGNOSTICS
The exit status is zero if and only if all operations were successful.
.SH IDENTIFICATION
Author: Walter F. Tichy.
.br
Manual Page Revision: \*(Rv; Release Date: \*(Dt.
.br
Copyright \(co 1982, 1988, 1989 Walter F. Tichy.
.br
Copyright \(co 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert.
.SH "SEE ALSO"
ci(1), co(1), ident(1), rcs(1), rcsdiff(1), rcsintro(1), rcsmerge(1),
rcsfile(5)
.br
Walter F. Tichy,
\*r\*-A System for Version Control,
.I "Software\*-Practice & Experience"
.BR 15 ,
7 (July 1985), 637-654.
.SH BUGS
The separator for revision ranges in the
.B \-r
option used to be
.B \-
instead of
.BR : ,
but this leads to confusion when symbolic names contain
.BR \- .
For backwards compatibility
.B "rlog \-r"
still supports the old
.B \-
separator, but it warns about this obsolete use.
.br

1290
gnu/usr.bin/rcs/rlog/rlog.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,6 @@
# $FreeBSD$
SUBDIR= rcs rcs_func
.include <bsd.subdir.mk>

View File

@ -0,0 +1,5 @@
# $FreeBSD$
VOLUME= psd/13.rcs
MACROS= -ms
SRCDIR= ${.CURDIR}/../../../../../gnu/usr.bin/rcs/doc

View File

@ -0,0 +1,7 @@
# $FreeBSD$
SRCS= rcs.ms
USE_PIC=
USE_TBL=
.include <bsd.doc.mk>

View File

@ -0,0 +1,6 @@
# $FreeBSD$
DOC= rcs_func
SRCS= rcs_func.ms
.include <bsd.doc.mk>

View File

@ -20,6 +20,7 @@ SUBDIR= title \
05.sysman \
06.Clang \
12.make \
13.rcs \
15.yacc \
16.lex \
17.m4 \

View File

@ -909,6 +909,11 @@ This includes
.Xr rlogin 1 ,
.Xr rsh 1 ,
etc.
.It Va WITHOUT_RCS
.\" from FreeBSD: head/tools/build/options/WITHOUT_RCS 156932 2006-03-21 07:50:50Z ru
Set to not build
.Xr rcs 1
and related utilities.
.It Va WITHOUT_RESCUE
.\" from FreeBSD: head/tools/build/options/WITHOUT_RESCUE 156932 2006-03-21 07:50:50Z ru
Set to not build

View File

@ -334,6 +334,7 @@ __DEFAULT_YES_OPTIONS = \
PROFILE \
QUOTAS \
RCMDS \
RCS \
RESCUE \
ROUTED \
SENDMAIL \

View File

@ -3859,6 +3859,31 @@ OLD_FILES+=usr/share/man/man8/rshd.8.gz
OLD_FILES+=usr/share/man/man8/rwhod.8.gz
.endif
.if ${MK_RCS} == no
OLD_FILES+=usr/bin/ci
OLD_FILES+=usr/bin/co
OLD_FILES+=usr/bin/ident
OLD_FILES+=usr/bin/merge
OLD_FILES+=usr/bin/rcs
OLD_FILES+=usr/bin/rcsclean
OLD_FILES+=usr/bin/rcsdiff
OLD_FILES+=usr/bin/rcsfreeze
OLD_FILES+=usr/bin/rcsmerge
OLD_FILES+=usr/bin/rlog
OLD_FILES+=usr/share/man/man1/ci.1.gz
OLD_FILES+=usr/share/man/man1/co.1.gz
OLD_FILES+=usr/share/man/man1/ident.1.gz
OLD_FILES+=usr/share/man/man1/merge.1.gz
OLD_FILES+=usr/share/man/man1/rcs.1.gz
OLD_FILES+=usr/share/man/man1/rcsclean.1.gz
OLD_FILES+=usr/share/man/man1/rcsdiff.1.gz
OLD_FILES+=usr/share/man/man1/rcsfreeze.1.gz
OLD_FILES+=usr/share/man/man1/rcsintro.1.gz
OLD_FILES+=usr/share/man/man1/rcsmerge.1.gz
OLD_FILES+=usr/share/man/man1/rlog.1.gz
OLD_FILES+=usr/share/man/man5/rcsfile.5.gz
.endif
#.if ${MK_RESCUE} == no
# to be filled in or replaced with a special target
#.endif

View File

@ -0,0 +1,4 @@
.\" $FreeBSD$
Set to not build
.Xr rcs 1
and related utilities.