diff options
author | peter <peter@FreeBSD.org> | 1995-10-28 21:07:39 +0000 |
---|---|---|
committer | peter <peter@FreeBSD.org> | 1995-10-28 21:07:39 +0000 |
commit | f88f3a686799dfede79eb3c8d8cfec9b1e7ae343 (patch) | |
tree | f3e1577c5b11357c106fe7ee15105d608cb96192 /gnu/usr.bin/rcs | |
parent | 27378b5694b4941bb63a62097f7ee523da0a35bb (diff) | |
download | FreeBSD-src-f88f3a686799dfede79eb3c8d8cfec9b1e7ae343.zip FreeBSD-src-f88f3a686799dfede79eb3c8d8cfec9b1e7ae343.tar.gz |
Import rcs-5.7, required for full support of cvs-1.6.
This is going to be pretty messy.... Although the vendor import was correct,
both the vendor and release tags are the same "gnu"... :-/
Getting cvs to choose the correct one might be rather interesting...
Diffstat (limited to 'gnu/usr.bin/rcs')
59 files changed, 8498 insertions, 4721 deletions
diff --git a/gnu/usr.bin/rcs/CREDITS b/gnu/usr.bin/rcs/CREDITS new file mode 100644 index 0000000..6eca1b9 --- /dev/null +++ b/gnu/usr.bin/rcs/CREDITS @@ -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. + +$Id: CREDITS,v 1.1 1993/11/03 17:50:39 eggert Exp $ diff --git a/gnu/usr.bin/rcs/Makefile b/gnu/usr.bin/rcs/Makefile index 2181815..4a9fd08 100644 --- a/gnu/usr.bin/rcs/Makefile +++ b/gnu/usr.bin/rcs/Makefile @@ -1,3 +1,3 @@ -SUBDIR= lib ci co ident merge rcs rcsdiff rcsmerge rlog rcsfreeze +SUBDIR= lib ci co ident merge rcs rcsclean rcsdiff rcsmerge rlog rcsfreeze .include <bsd.subdir.mk> diff --git a/gnu/usr.bin/rcs/Makefile.inc b/gnu/usr.bin/rcs/Makefile.inc index b9eca7d..cd593ed 100644 --- a/gnu/usr.bin/rcs/Makefile.inc +++ b/gnu/usr.bin/rcs/Makefile.inc @@ -1,3 +1,7 @@ -# @(#)Makefile.inc 5.1 (Berkeley) 5/11/90 +# Location of librcs -BINDIR?= /usr/bin +.if exists(${.CURDIR}/../lib/obj) +LIBRCS= ${.CURDIR}/../lib/obj/librcs.a +.else +LIBRCS= ${.CURDIR}/../lib/librcs.a +.endif diff --git a/gnu/usr.bin/rcs/NEWS b/gnu/usr.bin/rcs/NEWS new file mode 100644 index 0000000..62b7c2b --- /dev/null +++ b/gnu/usr.bin/rcs/NEWS @@ -0,0 +1,548 @@ +Recent changes to RCS (and possible future changes) + + $Id: NEWS,v 1.5 1995/06/16 06:19:24 eggert Exp $ + + 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. diff --git a/gnu/usr.bin/rcs/REFS b/gnu/usr.bin/rcs/REFS new file mode 100644 index 0000000..eaf96a5 --- /dev/null +++ b/gnu/usr.bin/rcs/REFS @@ -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. + + $Id: REFS,v 1.1 1995/06/16 06:19:24 eggert Exp $ + + +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. diff --git a/gnu/usr.bin/rcs/ci/Makefile b/gnu/usr.bin/rcs/ci/Makefile index 9b64e08..2fbb74f 100644 --- a/gnu/usr.bin/rcs/ci/Makefile +++ b/gnu/usr.bin/rcs/ci/Makefile @@ -1,7 +1,8 @@ -PROG= ci - -SRCS= ci.c -LDADD= -L${.CURDIR}/../lib/obj -lrcs +PROG= ci +SRCS= ci.c CFLAGS+= -I${.CURDIR}/../lib +LDADD= ${LIBRCS} +DPADD= ${LIBRCS} +.include "../../Makefile.inc" .include <bsd.prog.mk> diff --git a/gnu/usr.bin/rcs/ci/ci.1 b/gnu/usr.bin/rcs/ci/ci.1 index 5736dc9..d096096 100644 --- a/gnu/usr.bin/rcs/ci/ci.1 +++ b/gnu/usr.bin/rcs/ci/ci.1 @@ -2,8 +2,10 @@ .ds Rv \\$3 .ds Dt \\$4 .. -.Id $Id: ci.1,v 5.9 1991/10/07 17:32:46 eggert Exp $ +.Id $Id: ci.1,v 5.17 1995/06/16 06:19:24 eggert Exp $ +.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 @@ -41,7 +43,7 @@ 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 may be broken with the +A lock held by someone else can be broken with the .B rcs command. .PP @@ -105,7 +107,9 @@ 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 , @@ -114,7 +118,15 @@ of the deposited revision can be given by any of the options or .BR \-u . .I rev -may be symbolic, numeric, or mixed. +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 @@ -124,6 +136,15 @@ 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 @@ -172,25 +193,28 @@ Exception: On the trunk, revisions can be appended to the end, but not inserted. .SH OPTIONS .TP -.BR \-r [\f2rev\fP] -checks in a revision, releases the corresponding lock, and -removes the working file. This is the default. -.RS -.PP -The +.BI \-r rev +Check in revision +.IR rev . +.TP +.BR \-r +The bare .B \-r -option has an unusual meaning in +option (without any revision) has an unusual meaning in .BR ci . -In other \*r commands, +With other \*r commands, a bare .B \-r -merely specifies a revision number, -but in -.B ci -it also releases a lock and removes the working file. -See +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 -for a tricky example. -.RE +options established by shell aliases or scripts. .TP .BR \-l [\f2rev\fP] works like @@ -213,6 +237,7 @@ immediately after checkin. .PP The .BR \-l , +bare .BR \-r , and .B \-u @@ -221,7 +246,7 @@ For example, .B "ci\ \-u\ \-r" is equivalent to .B "ci\ \-r" -because +because bare .B \-r overrides .BR \-u . @@ -244,7 +269,7 @@ 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 may be overridden +The extracted keyword values and the default log message can be overridden with the options .BR \-d , .BR \-m , @@ -259,6 +284,14 @@ 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 @@ -296,6 +329,18 @@ Use this option with care; it can confuse 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 @@ -326,7 +371,7 @@ into the \*r file, deleting the existing text. The .I file -may not begin with +cannot begin with .BR \- . .TP .BI \-t\- string @@ -356,6 +401,41 @@ For backward compatibility with older versions of \*r, a bare 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 @@ -364,6 +444,9 @@ 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 . @@ -375,9 +458,9 @@ for details. 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/ file +.BI RCS/ path or -.IB path /RCS/ file. +.IB path1 /RCS/ path2. The .B \-x option can specify a list of suffixes @@ -398,10 +481,49 @@ The default for .IR suffixes is installation-dependent; normally it is .B ,v/ -for hosts like Unix that permit commas in file names, +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 may be specified in three ways +Pairs of \*r files and working files can be specified in three ways (see also the example section). .PP @@ -423,9 +545,9 @@ If .I X is empty, .IB path1 / -must be +must start with .B RCS/ -or must end in +or must contain .BR /RCS/ . .PP 2) Only the \*r file is given. Then the working file is created in the current @@ -516,7 +638,7 @@ preserves its read and execute permissions. .B ci always turns off all write permissions of \*r files. .SH FILES -Several temporary files may be created in the directory containing +Temporary files are created in the directory containing the working file, and also in the temporary directory (see .B \s-1TMPDIR\s0 under @@ -576,21 +698,23 @@ 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 '+1n-1/1n -.IP \(bu \nn +.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() +.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() +.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 user is +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 @@ -599,7 +723,7 @@ Choose a user to serve as \*r administrator for the set of users. Only .I A -will be able to invoke the +can invoke the .B rcs command on the users' \*r files. .I A @@ -608,9 +732,9 @@ should not be or any other user with special powers. Mutually suspicious sets of users should use different administrators. .IP \(bu \nn -Choose a path name +Choose a pathname .I B -that will be a directory of files to be executed by the users. +to be a directory of files to be executed by the users. .IP \(bu \nn Have .I A @@ -733,8 +857,9 @@ Useful options include .BR \-q , .BR \-V , +.BR \-x , and -.BR \-x . +.BR \-z . .TP .B \s-1TMPDIR\s0 Name of the temporary directory. @@ -755,14 +880,16 @@ The exit status is zero if and only if all operations were successful. .SH IDENTIFICATION Author: Walter F. Tichy. .br -Revision Number: \*(Rv; Release Date: \*(Dt. +Manual Page Revision: \*(Rv; Release Date: \*(Dt. .br -Copyright \(co 1982, 1988, 1989 by Walter F. Tichy. +Copyright \(co 1982, 1988, 1989 Walter F. Tichy. .br -Copyright \(co 1990, 1991 by Paul Eggert. +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), rcsfile(5) +co(1), +emacs(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, diff --git a/gnu/usr.bin/rcs/ci/ci.c b/gnu/usr.bin/rcs/ci/ci.c index 566747e..c9671d1 100644 --- a/gnu/usr.bin/rcs/ci/ci.c +++ b/gnu/usr.bin/rcs/ci/ci.c @@ -1,5 +1,7 @@ -/* Copyright (C) 1982, 1988, 1989 Walter Tichy - Copyright 1990, 1991 by Paul Eggert +/* Check in revisions of RCS files 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. @@ -15,8 +17,9 @@ 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, 675 Mass Ave, Cambridge, MA 02139, USA. +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: @@ -25,16 +28,50 @@ Report problems and direct all questions to: */ /* - * RCS checkin operation - */ -/******************************************************************* - * check revisions into RCS files - ******************************************************************* - */ - - - -/* $Log: ci.c,v $ + * $Log: ci.c,v $ + * Revision 5.30 1995/06/16 06:19:24 eggert + * Update FSF address. + * + * Revision 5.29 1995/06/01 16:23:43 eggert + * (main): Add -kb. + * Use `cmpdate', not `cmpnum', to compare dates. + * This is for MKS RCS's incompatible 20th-century date format. + * Don't worry about errno after ftruncate fails. + * Fix input file rewinding bug when large_memory && !maps_memory + * and checking in a branch tip. + * + * (fixwork): Fall back on chmod if fchmod fails, since it might be ENOSYS. + * + * Revision 5.28 1994/03/20 04:52:58 eggert + * Do not generate a corrupted RCS file if the user modifies the working file + * while `ci' is running. + * Do not remove the lock when `ci -l' reverts. + * Move buffer-flushes out of critical sections, since they aren't critical. + * Use ORCSerror to clean up after a fatal error. + * Specify subprocess input via file descriptor, not file name. + * + * Revision 5.27 1993/11/09 17:40:15 eggert + * -V now prints version on stdout and exits. Don't print usage twice. + * + * Revision 5.26 1993/11/03 17:42:27 eggert + * Add -z. Don't subtract from RCS file timestamp even if -T. + * Scan for and use Name keyword if -k. + * Don't discard ignored phrases. Improve quality of diagnostics. + * + * Revision 5.25 1992/07/28 16:12:44 eggert + * Add -i, -j, -V. Check that working and RCS files are distinct. + * + * Revision 5.24 1992/02/17 23:02:06 eggert + * `-rREV' now just specifies a revision REV; only bare `-r' reverts to default. + * Add -T. + * + * Revision 5.23 1992/01/27 16:42:51 eggert + * Always unlock branchpoint if caller has a lock. + * Add support for bad_chmod_close, bad_creat0. lint -> RCS_lint + * + * Revision 5.22 1992/01/06 02:42:34 eggert + * Invoke utime() before chmod() to keep some buggy systems happy. + * * Revision 5.21 1991/11/20 17:58:07 eggert * Don't read the delta tree from a nonexistent RCS file. * @@ -214,16 +251,16 @@ struct Symrev { }; static char const *getcurdate P((void)); -static int addbranch P((struct hshentry*,struct buf*)); +static int addbranch P((struct hshentry*,struct buf*,int)); static int addelta P((void)); static int addsyms P((char const*)); -static int fixwork P((mode_t,char const*)); +static int fixwork P((mode_t,time_t)); static int removelock P((struct hshentry*)); -static int xpandfile P((RILE*,char const*,struct hshentry const*,char const**)); +static int xpandfile P((RILE*,struct hshentry const*,char const**,int)); static struct cbuf getlogmsg P((void)); static void cleanup P((void)); static void incnum P((char const*,struct buf*)); -static void addassoclst P((int, char *)); +static void addassoclst P((int,char const*)); static FILE *exfile; static RILE *workptr; /* working file pointer */ @@ -236,37 +273,41 @@ static struct hshentries *gendeltas; /* deltas to be generated */ static struct hshentry *targetdelta; /* old delta to be generated */ static struct hshentry newdelta; /* new delta to be inserted */ static struct stat workstat; -static struct Symrev *assoclst, *lastassoc; +static struct Symrev *assoclst, **nextassoc; -mainProg(ciId, "ci", "$Id: ci.c,v 5.21 1991/11/20 17:58:07 eggert Exp $") +mainProg(ciId, "ci", "$Id: ci.c,v 5.30 1995/06/16 06:19:24 eggert Exp $") { static char const cmdusage[] = - "\nci usage: ci -{fklqru}[rev] -mmsg -{nN}name -sstate -t[textfile] -Vn file ..."; + "\nci usage: ci -{fIklMqru}[rev] -d[date] -mmsg -{nN}name -sstate -ttext -T -Vn -wwho -xsuff -zzone file ..."; static char const default_state[] = DEFAULTSTATE; char altdate[datesize]; char olddate[datesize]; - char newdatebuf[datesize], targetdatebuf[datesize]; + char newdatebuf[datesize + zonelenmax]; + char targetdatebuf[datesize + zonelenmax]; char *a, **newargv, *textfile; char const *author, *krev, *rev, *state; - char const *diffilename, *expfilename; - char const *workdiffname, *newworkfilename; - char const *mtime; - int lockflag, lockthis, mtimeflag, removedlock; + char const *diffname, *expname; + char const *newworkname; + int initflag, mustread; + int lockflag, lockthis, mtimeflag, removedlock, Ttimeflag; int r; - int changedRCS, changework, newhead; + int changedRCS, changework, dolog, newhead; int usestatdate; /* Use mod time of file for -d. */ mode_t newworkmode; /* mode for working file */ + time_t mtime, wtime; struct hshentry *workdelta; - + setrid(); - author = rev = state = textfile = nil; - lockflag = false; + author = rev = state = textfile = 0; + initflag = lockflag = mustread = false; mtimeflag = false; + Ttimeflag = false; altdate[0]= '\0'; /* empty alternate date for -d */ usestatdate=false; suffixes = X_DEFAULT; + nextassoc = &assoclst; argc = getRCSINIT(argc, argv, &newargv); argv = newargv; @@ -274,7 +315,13 @@ mainProg(ciId, "ci", "$Id: ci.c,v 5.21 1991/11/20 17:58:07 eggert Exp $") switch (*a++) { case 'r': + if (*a) + goto revno; keepworkingfile = lockflag = false; + break; + + case 'l': + keepworkingfile = lockflag = true; revno: if (*a) { if (rev) warn("redefinition of revision number"); @@ -282,14 +329,18 @@ mainProg(ciId, "ci", "$Id: ci.c,v 5.21 1991/11/20 17:58:07 eggert Exp $") } break; - case 'l': - keepworkingfile=lockflag=true; - goto revno; - case 'u': keepworkingfile=true; lockflag=false; goto revno; + case 'i': + initflag = true; + goto revno; + + case 'j': + mustread = true; + goto revno; + case 'I': interactiveflag = true; goto revno; @@ -310,7 +361,7 @@ mainProg(ciId, "ci", "$Id: ci.c,v 5.21 1991/11/20 17:58:07 eggert Exp $") if (msg.size) redefined('m'); msg = cleanlogmsg(a, strlen(a)); if (!msg.size) - warn("missing message for -m option"); + error("missing message for -m option"); break; case 'n': @@ -318,7 +369,7 @@ mainProg(ciId, "ci", "$Id: ci.c,v 5.21 1991/11/20 17:58:07 eggert Exp $") error("missing symbolic name after -n"); break; } - checksid(a); + checkssym(a); addassoclst(false, a); break; @@ -327,7 +378,7 @@ mainProg(ciId, "ci", "$Id: ci.c,v 5.21 1991/11/20 17:58:07 eggert Exp $") error("missing symbolic name after -N"); break; } - checksid(a); + checkssym(a); addassoclst(true, a); break; @@ -337,7 +388,7 @@ mainProg(ciId, "ci", "$Id: ci.c,v 5.21 1991/11/20 17:58:07 eggert Exp $") checksid(a); state = a; } else - warn("missing state for -s option"); + error("missing state for -s option"); break; case 't': @@ -350,7 +401,7 @@ mainProg(ciId, "ci", "$Id: ci.c,v 5.21 1991/11/20 17:58:07 eggert Exp $") case 'd': if (altdate[0] || usestatdate) redefined('d'); - altdate[0] = 0; + altdate[0] = '\0'; if (!(usestatdate = !*a)) str2date(a, altdate); break; @@ -365,7 +416,7 @@ mainProg(ciId, "ci", "$Id: ci.c,v 5.21 1991/11/20 17:58:07 eggert Exp $") checksid(a); author = a; } else - warn("missing author for -w option"); + error("missing author for -w option"); break; case 'x': @@ -376,26 +427,34 @@ mainProg(ciId, "ci", "$Id: ci.c,v 5.21 1991/11/20 17:58:07 eggert Exp $") setRCSversion(*argv); break; + case 'z': + zone_set(a); + break; - + case 'T': + if (!*a) { + Ttimeflag = true; + break; + } + /* fall into */ default: - faterror("unknown option: %s%s", *argv, cmdusage); + error("unknown option: %s%s", *argv, cmdusage); }; } /* end processing of options */ - if (argc<1) faterror("no input file%s", cmdusage); - - /* now handle all filenames */ - do { - targetdelta=nil; + /* Handle all pathnames. */ + if (nerror) cleanup(); + else if (argc < 1) faterror("no input file%s", cmdusage); + else for (; 0 < argc; cleanup(), ++argv, --argc) { + targetdelta = 0; ffree(); - switch (pairfilenames(argc, argv, rcswriteopen, false, false)) { + switch (pairnames(argc, argv, rcswriteopen, mustread, false)) { case -1: /* New RCS file */ # if has_setuid && has_getuid if (euid() != ruid()) { - error("setuid initial checkin prohibited; use `rcs -i -a' first"); + workerror("setuid initial checkin prohibited; use `rcs -i -a' first"); continue; } # endif @@ -406,38 +465,52 @@ mainProg(ciId, "ci", "$Id: ci.c,v 5.21 1991/11/20 17:58:07 eggert Exp $") continue; case 1: /* Normal checkin with prev . RCS file */ + if (initflag) { + rcserror("already exists"); + continue; + } rcsinitflag = !Head; } - /* now RCSfilename contains the name of the RCS file, and - * workfilename contains the name of the working file. + /* + * RCSname contains the name of the RCS file, and + * workname contains the name of the working file. * If the RCS file exists, finptr contains the file descriptor for the - * RCS file. The admin node is initialized. - * RCSstat is set. + * RCS file, and RCSstat is set. The admin node is initialized. */ - diagnose("%s <-- %s\n", RCSfilename,workfilename); + diagnose("%s <-- %s\n", RCSname, workname); - if (!(workptr = Iopen(workfilename, FOPEN_R_WORK, &workstat))) { - eerror(workfilename); + if (!(workptr = Iopen(workname, FOPEN_R_WORK, &workstat))) { + eerror(workname); continue; } - if (finptr && !checkaccesslist()) continue; /* give up */ + + if (finptr) { + if (same_file(RCSstat, workstat, 0)) { + rcserror("RCS file is the same as working file %s.", + workname + ); + continue; + } + if (!checkaccesslist()) + continue; + } krev = rev; if (keepflag) { /* get keyword values from working file */ if (!getoldkeys(workptr)) continue; if (!rev && !*(krev = prevrev.string)) { - error("can't find a revision number in %s",workfilename); + workerror("can't find a revision number"); continue; } if (!*prevdate.string && *altdate=='\0' && usestatdate==false) - warn("can't find a date in %s", workfilename); + workwarn("can't find a date"); if (!*prevauthor.string && !author) - warn("can't find an author in %s", workfilename); + workwarn("can't find an author"); if (!*prevstate.string && !state) - warn("can't find a state in %s", workfilename); + workwarn("can't find a state"); } /* end processing keepflag */ /* Read the delta tree. */ @@ -453,17 +526,20 @@ mainProg(ciId, "ci", "$Id: ci.c,v 5.21 1991/11/20 17:58:07 eggert Exp $") continue; newdelta.num = newdelnum.string; - newdelta.branches=nil; - newdelta.lockedby=nil; /*might be changed by addlock() */ + newdelta.branches = 0; + newdelta.lockedby = 0; /* This might be changed by addlock(). */ newdelta.selector = true; + newdelta.name = 0; + clear_buf(&newdelta.ig); + clear_buf(&newdelta.igtext); /* set author */ - if (author!=nil) + if (author) newdelta.author=author; /* set author given by -w */ else if (keepflag && *prevauthor.string) newdelta.author=prevauthor.string; /* preserve old author if possible*/ else newdelta.author=getcaller();/* otherwise use caller's id */ newdelta.state = default_state; - if (state!=nil) + if (state) newdelta.state=state; /* set state given by -s */ else if (keepflag && *prevstate.string) newdelta.state=prevstate.string; /* preserve old state if possible */ @@ -479,9 +555,9 @@ mainProg(ciId, "ci", "$Id: ci.c,v 5.21 1991/11/20 17:58:07 eggert Exp $") } else newdelta.date = getcurdate(); /* use current date */ /* now check validity of date -- needed because of -d and -k */ - if (targetdelta!=nil && - cmpnum(newdelta.date,targetdelta->date) < 0) { - error("Date %s precedes %s in existing revision %s.", + if (targetdelta && + cmpdate(newdelta.date,targetdelta->date) < 0) { + rcserror("Date %s precedes %s in revision %s.", date2str(newdelta.date, newdatebuf), date2str(targetdelta->date, targetdatebuf), targetdelta->num @@ -490,48 +566,46 @@ mainProg(ciId, "ci", "$Id: ci.c,v 5.21 1991/11/20 17:58:07 eggert Exp $") } - if (lockflag && addlock(&newdelta) < 0) continue; + if (lockflag && addlock(&newdelta, true) < 0) continue; + + if (keepflag && *prevname.string) + if (addsymbol(newdelta.num, prevname.string, false) < 0) + continue; if (!addsyms(newdelta.num)) continue; - putadmin(frewrite); + putadmin(); puttree(Head,frewrite); putdesc(false,textfile); - changework = Expand != OLD_EXPAND; + changework = Expand < MIN_UNCHANGED_EXPAND; + dolog = true; lockthis = lockflag; workdelta = &newdelta; /* build rest of file */ if (rcsinitflag) { - diagnose("initial revision: %s\n", newdelnum.string); + diagnose("initial revision: %s\n", newdelta.num); /* get logmessage */ newdelta.log=getlogmsg(); - if (!putdftext(newdelnum.string,newdelta.log,workptr,frewrite,false)) continue; + putdftext(&newdelta, workptr, frewrite, false); RCSstat.st_mode = workstat.st_mode; + RCSstat.st_nlink = 0; changedRCS = true; } else { - diffilename = maketemp(0); - workdiffname = workfilename; - if (workdiffname[0] == '+') { - /* Some diffs have options with leading '+'. */ - char *dp = ftnalloc(char, strlen(workfilename)+3); - workdiffname = dp; - *dp++ = '.'; - *dp++ = SLASH; - VOID strcpy(dp, workfilename); - } + diffname = maketemp(0); newhead = Head == &newdelta; if (!newhead) foutptr = frewrite; - expfilename = buildrevision( + expname = buildrevision( gendeltas, targetdelta, (FILE*)0, false ); if ( !forceciflag && + strcmp(newdelta.state, targetdelta->state) == 0 && (changework = rcsfcmp( - workptr, &workstat, expfilename, targetdelta + workptr, &workstat, expname, targetdelta )) <= 0 ) { diagnose("file is unchanged; reverting to previous revision %s\n", @@ -541,12 +615,8 @@ mainProg(ciId, "ci", "$Id: ci.c,v 5.21 1991/11/20 17:58:07 eggert Exp $") diagnose("previous revision was not locked; ignoring -l option\n"); lockthis = 0; } - if (!(changedRCS = - lockflag < removedlock - || assoclst - || newdelta.state != default_state - && strcmp(newdelta.state, targetdelta->state) != 0 - )) + dolog = false; + if (! (changedRCS = lockflag<removedlock || assoclst)) workdelta = targetdelta; else { /* @@ -555,20 +625,22 @@ mainProg(ciId, "ci", "$Id: ci.c,v 5.21 1991/11/20 17:58:07 eggert Exp $") */ long hwm = ftell(frewrite); int bad_truncate; - if (fseek(frewrite, 0L, SEEK_SET) != 0) - Oerror(); + Orewind(frewrite); + + /* + * Work around a common ftruncate() bug: + * NFS won't let you truncate a file that you + * currently lack permissions for, even if you + * had permissions when you opened it. + * Also, Posix 1003.1b-1993 sec 5.6.7.2 p 128 l 1022 + * says ftruncate might fail because it's not supported. + */ # if !has_ftruncate - bad_truncate = 1; -# else - /* - * Work around a common ftruncate() bug. - * We can't rely on has_truncate, because we might - * be using a filesystem exported to us via NFS. - */ - bad_truncate = ftruncate(fileno(frewrite),(off_t)0); - if (bad_truncate && errno != EACCES) - Oerror(); +# undef ftruncate +# define ftruncate(fd,length) (-1) # endif + bad_truncate = ftruncate(fileno(frewrite), (off_t)0); + Irewind(finptr); Lexinit(); getadmin(); @@ -581,11 +653,11 @@ mainProg(ciId, "ci", "$Id: ci.c,v 5.21 1991/11/20 17:58:07 eggert Exp $") workdelta->log = targetdelta->log; if (newdelta.state != default_state) workdelta->state = newdelta.state; - if (removedlock && removelock(workdelta)<0) + if (lockthis<removedlock && removelock(workdelta)<0) continue; if (!addsyms(workdelta->num)) continue; - if (!dorewrite(true, true)) + if (dorewrite(true, true) != 0) continue; fastcopy(finptr, frewrite); if (bad_truncate) @@ -595,48 +667,127 @@ mainProg(ciId, "ci", "$Id: ci.c,v 5.21 1991/11/20 17:58:07 eggert Exp $") afputc('\n', frewrite); } } else { + int wfd = Ifileno(workptr); + struct stat checkworkstat; + char const *diffv[6 + !!OPEN_O_BINARY], **diffp; +# if large_memory && !maps_memory + FILE *wfile = workptr->stream; + long wfile_off; +# endif +# if !has_fflush_input && !(large_memory && maps_memory) + off_t wfd_off; +# endif + diagnose("new revision: %s; previous revision: %s\n", - newdelnum.string, targetdelta->num + newdelta.num, targetdelta->num ); newdelta.log = getlogmsg(); - switch (run((char*)0, diffilename, - DIFF DIFF_FLAGS, - newhead ? workdiffname : expfilename, - newhead ? expfilename : workdiffname, - (char*)0 - )) { +# if !large_memory + Irewind(workptr); +# if has_fflush_input + if (fflush(workptr) != 0) + Ierror(); +# endif +# else +# if !maps_memory + if ( + (wfile_off = ftell(wfile)) == -1 + || fseek(wfile, 0L, SEEK_SET) != 0 +# if has_fflush_input + || fflush(wfile) != 0 +# endif + ) + Ierror(); +# endif +# endif +# if !has_fflush_input && !(large_memory && maps_memory) + wfd_off = lseek(wfd, (off_t)0, SEEK_CUR); + if (wfd_off == -1 + || (wfd_off != 0 + && lseek(wfd, (off_t)0, SEEK_SET) != 0)) + Ierror(); +# endif + diffp = diffv; + *++diffp = DIFF; + *++diffp = DIFFFLAGS; +# if OPEN_O_BINARY + if (Expand == BINARY_EXPAND) + *++diffp = "--binary"; +# endif + *++diffp = newhead ? "-" : expname; + *++diffp = newhead ? expname : "-"; + *++diffp = 0; + switch (runv(wfd, diffname, diffv)) { case DIFF_FAILURE: case DIFF_SUCCESS: break; - default: faterror("diff failed"); + default: rcsfaterror("diff failed"); } +# if !has_fflush_input && !(large_memory && maps_memory) + if (lseek(wfd, wfd_off, SEEK_CUR) == -1) + Ierror(); +# endif +# if large_memory && !maps_memory + if (fseek(wfile, wfile_off, SEEK_SET) != 0) + Ierror(); +# endif if (newhead) { Irewind(workptr); - if (!putdftext(newdelnum.string,newdelta.log,workptr,frewrite,false)) continue; - if (!putdtext(targetdelta->num,targetdelta->log,diffilename,frewrite,true)) continue; + putdftext(&newdelta, workptr, frewrite, false); + if (!putdtext(targetdelta,diffname,frewrite,true)) continue; } else - if (!putdtext(newdelnum.string,newdelta.log,diffilename,frewrite,true)) continue; + if (!putdtext(&newdelta,diffname,frewrite,true)) continue; + + /* + * Check whether the working file changed during checkin, + * to avoid producing an inconsistent RCS file. + */ + if ( + fstat(wfd, &checkworkstat) != 0 + || workstat.st_mtime != checkworkstat.st_mtime + || workstat.st_size != checkworkstat.st_size + ) { + workerror("file changed during checkin"); + continue; + } + changedRCS = true; } } - if (!donerewrite(changedRCS)) + + /* Deduce time_t of new revision if it is needed later. */ + wtime = (time_t)-1; + if (mtimeflag | Ttimeflag) + wtime = date2time(workdelta->date); + + if (donerewrite(changedRCS, + !Ttimeflag ? (time_t)-1 + : finptr && wtime < RCSstat.st_mtime ? RCSstat.st_mtime + : wtime + ) != 0) continue; if (!keepworkingfile) { Izclose(&workptr); - r = un_link(workfilename); /* Get rid of old file */ + r = un_link(workname); /* Get rid of old file */ } else { newworkmode = WORKMODE(RCSstat.st_mode, ! (Expand==VAL_EXPAND || lockthis < StrictLocks) ); - mtime = mtimeflag ? workdelta->date : (char const*)0; + mtime = mtimeflag ? wtime : (time_t)-1; /* Expand if it might change or if we can't fix mode, time. */ if (changework || (r=fixwork(newworkmode,mtime)) != 0) { Irewind(workptr); /* Expand keywords in file. */ locker_expansion = lockthis; + workdelta->name = + namedrev( + assoclst ? assoclst->ssymbol + : keepflag && *prevname.string ? prevname.string + : rev, + workdelta + ); switch (xpandfile( - workptr, workfilename, - workdelta, &newworkfilename + workptr, workdelta, &newworkname, dolog )) { default: continue; @@ -651,24 +802,24 @@ mainProg(ciId, "ci", "$Id: ci.c,v 5.21 1991/11/20 17:58:07 eggert Exp $") break; /* fall into */ case 1: - if (!(r = setfiledate(newworkfilename,mtime))) { - Izclose(&workptr); - ignoreints(); - r = chnamemod(&exfile, newworkfilename, workfilename, newworkmode); - keepdirtemp(newworkfilename); - restoreints(); - } + Izclose(&workptr); + aflush(exfile); + ignoreints(); + r = chnamemod(&exfile, newworkname, + workname, 1, newworkmode, mtime + ); + keepdirtemp(newworkname); + restoreints(); } } } if (r != 0) { - eerror(workfilename); + eerror(workname); continue; } diagnose("done\n"); - } while (cleanup(), - ++argv, --argc >=1); + } tempunlink(); exitmain(exitstatus); @@ -682,16 +833,17 @@ cleanup() Izclose(&workptr); Ozclose(&exfile); Ozclose(&fcopy); - Ozclose(&frewrite); + ORCSclose(); dirtempunlink(); } -#if lint +#if RCS_lint # define exiterr ciExit #endif - exiting void + void exiterr() { + ORCSerror(); dirtempunlink(); tempunlink(); _exit(EXIT_FAILURE); @@ -710,26 +862,28 @@ addelta() */ { register char *tp; - register unsigned i; + register int i; int removedlock; - unsigned newdnumlength; /* actual length of new rev. num. */ + int newdnumlength; /* actual length of new rev. num. */ newdnumlength = countnumflds(newdelnum.string); if (rcsinitflag) { /* this covers non-existing RCS file and a file initialized with rcs -i */ - if ((newdnumlength==0)&&(Dbranch!=nil)) { + if (newdnumlength==0 && Dbranch) { bufscpy(&newdelnum, Dbranch); newdnumlength = countnumflds(Dbranch); } if (newdnumlength==0) bufscpy(&newdelnum, "1.1"); else if (newdnumlength==1) bufscat(&newdelnum, ".1"); else if (newdnumlength>2) { - error("Branch point doesn't exist for %s.",newdelnum.string); + rcserror("Branch point doesn't exist for revision %s.", + newdelnum.string + ); return -1; } /* newdnumlength == 2 is OK; */ Head = &newdelta; - newdelta.next=nil; + newdelta.next = 0; return 0; } if (newdnumlength==0) { @@ -752,11 +906,11 @@ addelta() } else if (!targetdelta->next && countnumflds(targetdelta->num)>2) { /* new tip revision on side branch */ targetdelta->next= &newdelta; - newdelta.next = nil; + newdelta.next = 0; } else { /* middle revision; start a new branch */ bufscpy(&newdelnum, ""); - return addbranch(targetdelta,&newdelnum); + return addbranch(targetdelta, &newdelnum, 1); } incnum(targetdelta->num, &newdelnum); return 1; /* successful use of existing lock */ @@ -765,7 +919,7 @@ addelta() /* no existing lock; try Dbranch */ /* update newdelnum */ if (StrictLocks || !myself(RCSstat.st_uid)) { - error("no lock set by %s",getcaller()); + rcserror("no lock set by %s", getcaller()); return -1; } if (Dbranch) { @@ -787,8 +941,9 @@ addelta() bufscat(&newdelnum, ".1"); } if (cmpnum(newdelnum.string,Head->num) <= 0) { - error("deltanumber %s too low; must be higher than %s", - newdelnum.string, Head->num); + rcserror("revision %s too low; must be higher than %s", + newdelnum.string, Head->num + ); return -1; } targetdelta = Head; @@ -803,44 +958,46 @@ addelta() /* put new revision on side branch */ /*first, get branch point */ tp = newdelnum.string; - for (i = newdnumlength - (newdnumlength&1 ^ 1); (--i); ) + for (i = newdnumlength - ((newdnumlength&1) ^ 1); --i; ) while (*tp++ != '.') - ; + continue; *--tp = 0; /* Kill final dot to get old delta temporarily. */ - if (!(targetdelta=genrevs(newdelnum.string,(char*)nil,(char*)nil,(char*)nil,&gendeltas))) + if (!(targetdelta=genrevs(newdelnum.string,(char*)0,(char*)0,(char*)0,&gendeltas))) return -1; if (cmpnum(targetdelta->num, newdelnum.string) != 0) { - error("can't find branchpoint %s", newdelnum.string); + rcserror("can't find branch point %s", newdelnum.string); return -1; } *tp = '.'; /* Restore final dot. */ - return addbranch(targetdelta,&newdelnum); + return addbranch(targetdelta, &newdelnum, 0); } } static int -addbranch(branchpoint,num) +addbranch(branchpoint, num, removedlock) struct hshentry *branchpoint; struct buf *num; + int removedlock; /* adds a new branch and branch delta at branchpoint. * If num is the null string, appends the new branch, incrementing * the highest branch number (initially 1), and setting the level number to 1. * the new delta and branchhead are in globals newdelta and newbranch, resp. * the new number is placed into num. * Return -1 on error, 1 if a lock is removed, 0 otherwise. + * If REMOVEDLOCK is 1, a lock was already removed. */ { struct branchhead *bhead, **btrail; struct buf branchnum; - int removedlock, result; - unsigned field, numlength; + int result; + int field, numlength; static struct branchhead newbranch; /* new branch to be inserted */ numlength = countnumflds(num->string); - if (branchpoint->branches==nil) { + if (!branchpoint->branches) { /* start first branch */ branchpoint->branches = &newbranch; if (numlength==0) { @@ -848,7 +1005,7 @@ addbranch(branchpoint,num) bufscat(num, ".1.1"); } else if (numlength&1) bufscat(num, ".1"); - newbranch.nextbranch=nil; + newbranch.nextbranch = 0; } else if (numlength==0) { /* append new branch to the end */ @@ -860,10 +1017,10 @@ addbranch(branchpoint,num) incnum(branchnum.string, num); bufautoend(&branchnum); bufscat(num, ".1"); - newbranch.nextbranch=nil; + newbranch.nextbranch = 0; } else { /* place the branch properly */ - field = numlength - (numlength&1 ^ 1); + field = numlength - ((numlength&1) ^ 1); /* field of branch number */ btrail = &branchpoint->branches; while (0 < (result=cmpnumfld(num->string,(*btrail)->hsh->num,field))) { @@ -882,17 +1039,22 @@ addbranch(branchpoint,num) /* branch exists; append to end */ bufautobegin(&branchnum); getbranchno(num->string, &branchnum); - targetdelta=genrevs(branchnum.string,(char*)nil, - (char*)nil,(char*)nil,&gendeltas); + targetdelta = genrevs( + branchnum.string, (char*)0, (char*)0, (char*)0, + &gendeltas + ); bufautoend(&branchnum); if (!targetdelta) return -1; if (cmpnum(num->string,targetdelta->num) <= 0) { - error("deltanumber %s too low; must be higher than %s", - num->string,targetdelta->num); + rcserror("revision %s too low; must be higher than %s", + num->string, targetdelta->num + ); return -1; } - if (0 <= (removedlock = removelock(targetdelta))) { + if (!removedlock + && 0 <= (removedlock = removelock(targetdelta)) + ) { if (numlength&1) incnum(targetdelta->num,num); targetdelta->next = &newdelta; @@ -903,8 +1065,11 @@ addbranch(branchpoint,num) } } newbranch.hsh = &newdelta; - newdelta.next=nil; - return 0; + newdelta.next = 0; + if (branchpoint->lockedby) + if (strcmp(branchpoint->lockedby, getcaller()) == 0) + return removelock(branchpoint); /* This returns 1. */ + return removedlock; } static int @@ -914,7 +1079,7 @@ addsyms(num) register struct Symrev *p; for (p = assoclst; p; p = p->nextsym) - if (!addsymbol(num, p->ssymbol, p->override)) + if (addsymbol(num, p->ssymbol, p->override) < 0) return false; return true; } @@ -966,7 +1131,7 @@ struct hshentry * delta; * return 0; return 1 if a lock is actually removed. */ { - register struct lock *next, **trail; + register struct rcslock *next, **trail; char const *num; num=delta->num; @@ -978,12 +1143,12 @@ struct hshentry * delta; delta->lockedby = 0; return 1; } else { - error("revision %s locked by %s",num,next->login); + rcserror("revision %s locked by %s", num, next->login); return -1; } if (!StrictLocks && myself(RCSstat.st_uid)) return 0; - error("no lock set by %s for revision %s", getcaller(), num); + rcserror("no lock set by %s for revision %s", getcaller(), num); return -1; } @@ -994,76 +1159,70 @@ getcurdate() /* Return a pointer to the current date. */ { static char buffer[datesize]; /* date buffer */ - time_t t; - if (!buffer[0]) { - t = time((time_t *)0); - if (t == -1) - faterror("time not available"); - time2date(t, buffer); - } + if (!buffer[0]) + time2date(now(), buffer); return buffer; } static int #if has_prototypes -fixwork(mode_t newworkmode, char const *mtime) +fixwork(mode_t newworkmode, time_t mtime) /* The `#if has_prototypes' is needed because mode_t might promote to int. */ #else fixwork(newworkmode, mtime) mode_t newworkmode; - char const *mtime; + time_t mtime; #endif { - int r; return 1 < workstat.st_nlink - || newworkmode&S_IWUSR && !myself(workstat.st_uid) + || (newworkmode&S_IWUSR && !myself(workstat.st_uid)) + || setmtime(workname, mtime) != 0 ? -1 - : - workstat.st_mode != newworkmode - && - (r = -# if has_fchmod - fchmod(Ifileno(workptr), newworkmode) -# else - chmod(workfilename, newworkmode) -# endif - ) != 0 - ? r - : - setfiledate(workfilename, mtime); + : workstat.st_mode == newworkmode ? 0 +#if has_fchmod + : fchmod(Ifileno(workptr), newworkmode) == 0 ? 0 +#endif +#if bad_chmod_close + : -1 +#else + : chmod(workname, newworkmode) +#endif + ; } static int -xpandfile(unexfile, dir, delta, exfilename) +xpandfile(unexfile, delta, exname, dolog) RILE *unexfile; - char const *dir; struct hshentry const *delta; - char const **exfilename; + char const **exname; + int dolog; /* * Read unexfile and copy it to a - * file in dir, performing keyword substitution with data from delta. + * file, performing keyword substitution with data from delta. * Return -1 if unsuccessful, 1 if expansion occurred, 0 otherwise. * If successful, stores the stream descriptor into *EXFILEP - * and its name into *EXFILENAME. + * and its name into *EXNAME. */ { - char const *targetfname; + char const *targetname; int e, r; - targetfname = makedirtemp(dir, 1); - if (!(exfile = fopen(targetfname, FOPEN_W_WORK))) { - eerror(targetfname); - error("can't expand working file"); + targetname = makedirtemp(1); + if (!(exfile = fopenSafer(targetname, FOPEN_W_WORK))) { + eerror(targetname); + workerror("can't build working file"); return -1; } r = 0; - if (Expand == OLD_EXPAND) + if (MIN_UNEXPAND <= Expand) fastcopy(unexfile,exfile); else { for (;;) { - e = expandline(unexfile,exfile,delta,false,(FILE*)nil); + e = expandline( + unexfile, exfile, delta, false, (FILE*)0, dolog + ); if (e < 0) break; r |= e; @@ -1071,8 +1230,7 @@ xpandfile(unexfile, dir, delta, exfilename) break; } } - *exfilename = targetfname; - aflush(exfile); + *exname = targetname; return r & 1; } @@ -1113,7 +1271,7 @@ getlogmsg() /* generate std. log message */ caller = getcaller(); i = sizeof(ciklog)+strlen(caller)+3; - bufalloc(&logbuf, i+datesize); + bufalloc(&logbuf, i + datesize + zonelenmax); tp = logbuf.string; VOID sprintf(tp, "%s%s at ", ciklog, caller); VOID date2str(getcurdate(), tp+i); @@ -1147,19 +1305,15 @@ getlogmsg() static void addassoclst(flag, sp) -int flag; -char * sp; + int flag; + char const *sp; { struct Symrev *pt; pt = talloc(struct Symrev); pt->ssymbol = sp; pt->override = flag; - pt->nextsym = nil; - if (lastassoc) - lastassoc->nextsym = pt; - else - assoclst = pt; - lastassoc = pt; - return; + pt->nextsym = 0; + *nextassoc = pt; + nextassoc = &pt->nextsym; } diff --git a/gnu/usr.bin/rcs/co/Makefile b/gnu/usr.bin/rcs/co/Makefile index e9de8da..0c73865 100644 --- a/gnu/usr.bin/rcs/co/Makefile +++ b/gnu/usr.bin/rcs/co/Makefile @@ -1,7 +1,8 @@ -PROG= co - -SRCS= co.c -LDADD= -L${.CURDIR}/../lib/obj -lrcs +PROG= co +SRCS= co.c CFLAGS+= -I${.CURDIR}/../lib +LDADD= ${LIBRCS} +DPADD= ${LIBRCS} +.include "../../Makefile.inc" .include <bsd.prog.mk> diff --git a/gnu/usr.bin/rcs/co/co.1 b/gnu/usr.bin/rcs/co/co.1 index d9ce65e..8d0a6e7 100644 --- a/gnu/usr.bin/rcs/co/co.1 +++ b/gnu/usr.bin/rcs/co/co.1 @@ -2,9 +2,10 @@ .ds Rv \\$3 .ds Dt \\$4 .. -.Id $Id: co.1,v 5.7 1991/08/19 03:13:55 eggert Exp $ -.ds g \&\s-1UTC\s0 +.Id $Id: co.1,v 5.13 1995/06/01 16:23:43 eggert Exp $ +.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 @@ -23,12 +24,12 @@ all others denote working files. Names are paired as explained in .BR ci (1). .PP -Revisions of an \*r file may be checked out locked or unlocked. Locking a +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 may be broken with +(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 @@ -51,7 +52,7 @@ on the default branch (normally the trunk, see the .B \-b option of .BR rcs (1)). -A revision or branch number may be attached +A revision or branch number can be attached to any of the options .BR \-f , .BR \-I , @@ -73,7 +74,7 @@ retrieve from a single branch, the .I selected branch, which is either specified by one of -.BR \-f, +.BR \-f , \&.\|.\|., .BR \-u , or the default branch. @@ -88,7 +89,7 @@ always performs keyword substitution (see below). .TP .BR \-r [\f2rev\fP] retrieves the latest revision whose number is less than or equal to -.I rev. +.IR rev . If .I rev indicates a branch rather than a revision, @@ -108,7 +109,16 @@ is .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. The numeric equivalent of a symbolic field +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 @@ -166,7 +176,7 @@ Like except that a locker's name is always inserted if the given revision is currently locked. .TP -.BR \-kk +.B \-kk Generate only keyword names in keyword strings; omit their values. See .SM "KEYWORD SUBSTITUTION" @@ -176,11 +186,17 @@ For example, for the keyword, generate the string .B $\&Revision$ instead of -.BR "$\&Revision: \*(Rv $". +.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 -.BR \-ko +.B \-ko Generate the old keyword string, present in the working file just before it was checked in. For example, for the @@ -190,18 +206,33 @@ keyword, generate the string instead of .B "$\&Revision: \*(Rv $" if that is how the string appeared when the file was checked in. -This can be useful for binary file formats +This can be useful for file formats that cannot tolerate any changes to substrings that happen to take the form of keyword strings. .TP -.BR \-kv +.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 $". +.BR "$\&Revision: \*(Rv $" . This can help generate files in programming languages where it is hard to strip keyword delimiters like .B "$\&Revision:\ $" @@ -233,8 +264,8 @@ even if the standard input is not a terminal. .BI \-d date retrieves the latest revision on the selected branch whose checkin date/time is less than or equal to -.I date. -The date and time may be given in free format. +.IR date . +The date and time can be given in free format. The time zone .B LT stands for local time; @@ -243,38 +274,43 @@ 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 (\*g): +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 9 +.ne 10 \f38:00 pm lt\fP -\f34:00 AM, Jan. 12, 1990\fP note: default is \*g -\f31990/01/12 04:00:00\fP \*r date format +\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 -\f3Fri-JST, 1990, 1pm Jan 12\fP -\f312-January-1990, 04:00-WET\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 may be defaulted. -The default time zone is \*g. +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, the date +For example, without +.BR \-z , +the date .B "20, 10:30" defaults to -10:30:00 \*g of the 20th of the \*g time zone's current month and year. +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 @@ -286,12 +322,22 @@ Use this option with care; it can confuse .TP .BI \-s state retrieves the latest revision on the selected branch whose state is set to -.I state. +.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 -.I login. +.IR login . If the argument .I login is @@ -299,7 +345,7 @@ omitted, the caller's login is assumed. .TP .BI \-j joinlist generates a new revision which is the join of the revisions on -.I joinlist. +.IR joinlist . This option is largely obsoleted by .BR rcsmerge (1) but is retained for backwards compatibility. @@ -319,7 +365,7 @@ For the initial such pair, .I rev1 denotes the revision selected by the above options -.BR \-f, +.BR \-f , \&.\|.\|., .BR \-w . For all other pairs, @@ -335,13 +381,13 @@ joins revisions and .I rev3 with respect to -.I rev2. +.IR rev2 . This means that all changes that transform .I rev2 into .I rev1 are applied to a copy of -.I rev3. +.IR rev3 . This is particularly useful if .I rev1 and @@ -372,7 +418,7 @@ reports overlaps as described in .PP For the initial pair, .I rev2 -may be omitted. The default is the common +can be omitted. The default is the common ancestor. If any of the arguments indicate branches, the latest revisions on those branches are assumed. @@ -381,22 +427,28 @@ The options and .B \-u lock or unlock -.I rev1. +.IR rev1 . .RE .TP +.BI \-V +Print \*r's version number. +.TP .BI \-V n Emulate \*r version .I n, where .I n -may be +can be .BR 3 , .BR 4 , or .BR 5 . -This may be useful when interchanging \*r files with others who are +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 @@ -404,15 +456,14 @@ if none of the first few lines of output contain the string 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 will lose its default branch. -An \*r revision generated while emulating version 4 or earlier will have -a timestamp that is off by up to 13 hours. -A revision extracted while emulating version 4 or earlier will contain -dates of the form +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 -instead of -.IB yyyy / mm / dd -and may also contain different white space in the substitution for +and can also contain different white space and line prefixes +in the substitution for .BR $\&Log$ . .TP .BI \-x "suffixes" @@ -422,6 +473,46 @@ 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 $ @@ -436,7 +527,7 @@ where and .I value are pairs listed below. -Keywords may be embedded in literal strings +Keywords can be embedded in literal strings or comments to identify a revision. .PP Initially, the user enters strings of the form @@ -459,12 +550,18 @@ Keywords and their corresponding values: The login name of the user who checked in the revision. .TP .B $\&Date$ -The date and time (\*g) the revision was checked in. +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 (\*g), the author, the state, +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 @@ -477,7 +574,10 @@ The login name of the user who locked the revision (empty if not locked). .B $\&Log$ The log message supplied during checkin, preceded by a header containing the \*r filename, the revision number, the author, and the date -(\*g). +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. @@ -485,6 +585,58 @@ 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. @@ -502,6 +654,22 @@ 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 @@ -523,7 +691,10 @@ is given, the working file is deleted without asking. .B co accesses files much as .BR ci (1) -does, except that it does not need to read the working file. +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 @@ -539,14 +710,14 @@ The exit status is zero if and only if all operations were successful. .SH IDENTIFICATION Author: Walter F. Tichy. .br -Revision Number: \*(Rv; Release Date: \*(Dt. +Manual Page Revision: \*(Rv; Release Date: \*(Dt. .br -Copyright \(co 1982, 1988, 1989 by Walter F. Tichy. +Copyright \(co 1982, 1988, 1989 Walter F. Tichy. .br -Copyright \(co 1990, 1991 by Paul Eggert. +Copyright \(co 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert. .SH "SEE ALSO" -ci(1), ctime(3), date(1), ident(1), make(1), -rcs(1), rcsdiff(1), rcsintro(1), rcsmerge(1), rlog(1), +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, @@ -562,8 +733,4 @@ by writing them differently. In nroff and troff, this is done by embedding the null-character .B \e& into the keyword. -.SH BUGS -The -.B \-d -option sometimes gets confused, and accepts no date before 1970. .br diff --git a/gnu/usr.bin/rcs/co/co.c b/gnu/usr.bin/rcs/co/co.c index 9435574..2312b30 100644 --- a/gnu/usr.bin/rcs/co/co.c +++ b/gnu/usr.bin/rcs/co/co.c @@ -1,5 +1,7 @@ -/* Copyright (C) 1982, 1988, 1989 Walter Tichy - Copyright 1990, 1991 by Paul Eggert +/* 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. @@ -15,8 +17,9 @@ 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, 675 Mass Ave, Cambridge, MA 02139, USA. +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: @@ -25,17 +28,40 @@ Report problems and direct all questions to: */ /* - * RCS checkout operation - */ -/***************************************************************************** - * check out revisions from RCS files - ***************************************************************************** - */ - - -/* $Log: co.c,v $ + * $Log: co.c,v $ + * 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 - * ci -u src/RCS/co.c,v src/co.c <<\. * -k affects just working file, not RCS file. * * Revision 5.8 1991/08/19 03:13:55 eggert @@ -143,17 +169,19 @@ Report problems and direct all questions to: #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((void)); +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, *join, *suffixarg, *versionarg; -static char const *joinlist[joinlength]; /* revisions to be joined */ +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; @@ -164,25 +192,31 @@ static struct hshentries *gendeltas; /* deltas to be generated */ static struct hshentry *targetdelta; /* final delta to be generated */ static struct stat workstat; -mainProg(coId, "co", "$Id: co.c,v 5.9 1991/10/07 17:32:46 eggert Exp $") +mainProg(coId, "co", "$Id: co.c,v 5.18 1995/06/16 06:19:24 eggert Exp $") { static char const cmdusage[] = - "\nco usage: co -{flpqru}[rev] -ddate -jjoinlist -sstate -w[login] -Vn file ..."; + "\nco usage: co -{fIlMpqru}[rev] -ddate -jjoins -ksubst -sstate -T -w[who] -Vn -xsuff -zzone file ..."; - char *a, **newargv; + char *a, *joinflag, **newargv; char const *author, *date, *rev, *state; - char const *joinfilename, *newdate, *neworkfilename; + 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 = nil; + 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; @@ -203,14 +237,14 @@ mainProg(coId, "co", "$Id: co.c,v 5.9 1991/10/07 17:32:46 eggert Exp $") case 'l': if (lockflag < 0) { - warn("-l overrides -u."); + warn("-u overridden by -l."); } lockflag = 1; goto revno; case 'u': if (0 < lockflag) { - warn("-l overrides -u."); + warn("-l overridden by -u."); } lockflag = -1; goto revno; @@ -236,8 +270,8 @@ mainProg(coId, "co", "$Id: co.c,v 5.9 1991/10/07 17:32:46 eggert Exp $") case 'j': if (*a) { - if (join) redefined('j'); - join = a; + if (joinflag) redefined('j'); + joinflag = a; } break; @@ -252,6 +286,12 @@ mainProg(coId, "co", "$Id: co.c,v 5.9 1991/10/07 17:32:46 eggert Exp $") } break; + case 'T': + if (*a) + goto unknown; + Ttimeflag = true; + break; + case 'w': if (author) redefined('w'); if (*a) @@ -270,6 +310,11 @@ mainProg(coId, "co", "$Id: co.c,v 5.9 1991/10/07 17:32:46 eggert Exp $") setRCSversion(versionarg); break; + case 'z': + zonearg = *argv; + zone_set(a); + break; + case 'k': /* set keyword expand mode */ expandarg = *argv; if (0 <= expmode) redefined('k'); @@ -277,69 +322,81 @@ mainProg(coId, "co", "$Id: co.c,v 5.9 1991/10/07 17:32:46 eggert Exp $") break; /* fall into */ default: - faterror("unknown option: %s%s", *argv, cmdusage); + unknown: + error("unknown option: %s%s", *argv, cmdusage); }; } /* end of option processing */ - if (argc<1) faterror("no input file%s", cmdusage); - if (tostdout) -# if text_equals_binary_stdio || text_work_stdio - workstdout = stdout; -# else - if (!(workstdout = fdopen(STDOUT_FILENO, FOPEN_W_WORK))) - efaterror("stdout"); -# endif - - /* now handle all filenames */ - do { + /* 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 (pairfilenames(argc, argv, lockflag?rcswriteopen:rcsreadopen, true, false) <= 0) + if (pairnames(argc, argv, lockflag?rcswriteopen:rcsreadopen, true, false) <= 0) continue; - /* now RCSfilename contains the name of the RCS file, and finptr - * points at it. workfilename contains the name of the working file. + /* + * 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", RCSfilename,tostdout?"stdout":workfilename); + diagnose("%s --> %s\n", RCSname, tostdout?"standard output":workname); workstatstat = -1; if (tostdout) { - neworkfilename = 0; - neworkptr = workstdout; +# 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(workfilename, &workstat); - neworkfilename = makedirtemp(workfilename, 1); - if (!(neworkptr = fopen(neworkfilename, FOPEN_W_WORK))) { + 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) - error("%s: parent directory isn't writable", - workfilename - ); + workerror("permission denied on parent directory"); else - eerror(neworkfilename); + eerror(neworkname); continue; } } gettree(); /* reads in the delta tree */ - if (Head==nil) { + 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; - /* Can't reserve a delta, so don't call addlock */ } else { - if (rev!=nil) { + int locks = lockflag ? findlock(false, &targetdelta) : 0; + if (rev) { /* expand symbolic revision number */ if (!expandsym(rev, &numericrev)) continue; - } else - switch (lockflag<0 ? findlock(false,&targetdelta) : 0) { + } else { + switch (locks) { default: continue; case 0: @@ -349,6 +406,7 @@ mainProg(coId, "co", "$Id: co.c,v 5.9 1991/10/07 17:32:46 eggert Exp $") bufscpy(&numericrev, targetdelta->num); break; } + } /* get numbers of deltas to be generated */ if (!(targetdelta=genrevs(numericrev.string,date,author,state,&gendeltas))) continue; @@ -359,23 +417,24 @@ mainProg(coId, "co", "$Id: co.c,v 5.9 1991/10/07 17:32:46 eggert Exp $") : lockflag == 0 ? 0 : - addlock(targetdelta); + addlock(targetdelta, true); if ( - changelock < 0 || - changelock && !checkaccesslist() || - !dorewrite(lockflag, changelock) + changelock < 0 + || (changelock && !checkaccesslist()) + || dorewrite(lockflag, changelock) != 0 ) continue; if (0 <= expmode) Expand = expmode; if (0 < lockflag && Expand == VAL_EXPAND) { - error("cannot combine -kv and -l"); + rcserror("cannot combine -kv and -l"); continue; } - if (join && !preparejoin()) continue; + if (joinflag && !preparejoin(joinflag)) + continue; diagnose("revision %s%s\n",targetdelta->num, 0<lockflag ? " (locked)" : @@ -389,10 +448,11 @@ mainProg(coId, "co", "$Id: co.c,v 5.9 1991/10/07 17:32:46 eggert Exp $") getdesc(false); /* don't echo*/ locker_expansion = 0 < lockflag; - joinfilename = buildrevision( + targetdelta->name = namedrev(rev, targetdelta); + joinname = buildrevision( gendeltas, targetdelta, - join&&tostdout ? (FILE*)0 : neworkptr, - Expand!=OLD_EXPAND + joinflag&&tostdout ? (FILE*)0 : neworkptr, + Expand < MIN_UNEXPAND ); # if !large_memory if (fcopy == neworkptr) @@ -402,46 +462,48 @@ mainProg(coId, "co", "$Id: co.c,v 5.9 1991/10/07 17:32:46 eggert Exp $") finptr, MADV_SEQUENTIAL ); - if (!donerewrite(changelock)) + 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 (join) { + if (joinflag) { newdate = 0; - if (!joinfilename) { + if (!joinname) { aflush(neworkptr); - joinfilename = neworkfilename; + joinname = neworkname; } - if (!buildjoin(joinfilename)) + if (Expand == BINARY_EXPAND) + workerror("merging binary files"); + if (!buildjoin(joinname)) continue; } } if (!tostdout) { - r = 0; - if (mtimeflag && newdate) { - if (!join) - aflush(neworkptr); - r = setfiledate(neworkfilename, newdate); - } - if (r == 0) { - ignoreints(); - r = chnamemod(&neworkptr, neworkfilename, workfilename, - WORKMODE(RCSstat.st_mode, - !(Expand==VAL_EXPAND || lockflag<=0&&StrictLocks) - ) - ); - keepdirtemp(neworkfilename); - restoreints(); - } + 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(workfilename); - error("see %s", neworkfilename); + eerror(workname); + error("see %s", neworkname); continue; } diagnose("done\n"); } - } while (cleanup(), - ++argv, --argc >=1); + } tempunlink(); Ofclose(workstdout); @@ -454,7 +516,7 @@ cleanup() { if (nerror) exitstatus = EXIT_FAILURE; Izclose(&finptr); - Ozclose(&frewrite); + ORCSclose(); # if !large_memory if (fcopy!=workstdout) Ozclose(&fcopy); # endif @@ -462,12 +524,13 @@ cleanup() dirtempunlink(); } -#if lint +#if RCS_lint # define exiterr coExit #endif - exiting void + void exiterr() { + ORCSerror(); dirtempunlink(); tempunlink(); _exit(EXIT_FAILURE); @@ -480,7 +543,8 @@ exiterr() static int rmworkfile() -/* Function: prepares to remove workfilename, if it exists, and if +/* + * 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); @@ -491,12 +555,12 @@ rmworkfile() 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): ", - workfilename, + workname, myself(workstat.st_uid) ? "" : ", and you do not own it" )) { error(!quietflag && ttystdin() ? "checkout aborted" - : "writable %s exists; checkout aborted", workfilename); + : "writable %s exists; checkout aborted", workname); return false; } } @@ -513,31 +577,33 @@ rmlock(delta) * 0 if there is no lock on delta, * and 1 if a lock was found and removed. */ -{ register struct lock * next, * trail; +{ register struct rcslock * next, * trail; char const *num; - struct lock dummy; + struct rcslock dummy; int whomatch, nummatch; num=delta->num; dummy.nextlock=next=Locks; trail = &dummy; - while (next!=nil) { + 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)) { - error("revision %s locked by %s; use co -r or rcs -u",num,next->login); + rcserror("revision %s locked by %s; use co -r or rcs -u", + num, next->login + ); return -1; } trail=next; next=next->nextlock; } - if (next!=nil) { + if (next) { /*found one; delete it */ trail->nextlock=next->nextlock; Locks=dummy.nextlock; - next->delta->lockedby=nil; /* reset locked-by */ + next->delta->lockedby = 0; return 1; /*success*/ } else return 0; /*no lock on delta*/ } @@ -550,15 +616,15 @@ rmlock(delta) *****************************************************************/ - static char const * + static char * addjoin(joinrev) char *joinrev; /* Add joinrev's number to joinlist, yielding address of char past joinrev, - * or nil if no such revision exists. + * or 0 if no such revision exists. */ { register char *j; - register struct hshentry const *d; + register struct hshentry *d; char terminator; struct buf numrev; struct hshentries *joindeltas; @@ -580,32 +646,32 @@ addjoin(joinrev) bufautobegin(&numrev); d = 0; if (expandsym(joinrev, &numrev)) - d = genrevs(numrev.string,(char*)nil,(char*)nil,(char*)nil,&joindeltas); + d = genrevs(numrev.string,(char*)0,(char*)0,(char*)0,&joindeltas); bufautoend(&numrev); *j = terminator; if (d) { joinlist[++lastjoin] = d->num; return j; } - return nil; + return 0; } static int -preparejoin() -/* Function: Parses a join list pointed to by join and places pointers to the +preparejoin(j) + register char *j; +/* Parse join list J and place pointers to the * revision numbers into joinlist. */ { - register char const *j; - - j=join; lastjoin= -1; for (;;) { while ((*j==' ')||(*j=='\t')||(*j==',')) j++; if (*j=='\0') break; if (lastjoin>=joinlength-2) { - error("too many joins"); - return(false); + 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++; @@ -615,8 +681,7 @@ preparejoin() if (*j!='\0') { if (!(j = addjoin(j))) return false; } else { - error("join pair incomplete"); - return false; + rcsfaterror("join pair incomplete"); } } else { if (lastjoin==0) { /* first pair */ @@ -627,15 +692,13 @@ preparejoin() if (!(joinlist[0] = getancestor(targetdelta->num,joinlist[1]))) return false; } else { - error("join pair incomplete"); - return false; + rcsfaterror("join pair incomplete"); } } } - if (lastjoin<1) { - error("empty join"); - return false; - } else return true; + if (lastjoin < 1) + rcsfaterror("empty join"); + return true; } @@ -643,13 +706,13 @@ preparejoin() static char const * getancestor(r1, r2) char const *r1, *r2; -/* Yield the common ancestor of r1 and r2 if successful, nil otherwise. +/* 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; - unsigned l1, l2, l3; + int l1, l2, l3; char const *r; l1 = countnumflds(r1); @@ -662,16 +725,16 @@ getancestor(r1, r2) /* 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 ? (unsigned)2 : l1); - VOID partialno(&t2, r2, l2>2 ? (unsigned)2 : l2); + 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); } - error("common ancestor of %s and %s undefined", r1, r2); - return nil; + rcserror("common ancestor of %s and %s undefined", r1, r2); + return 0; } @@ -688,7 +751,7 @@ buildjoin(initialfile) struct buf subs; char const *rev2, *rev3; int i; - char const *cov[10], *mergev[12]; + char const *cov[10], *mergev[11]; char const **p; bufautobegin(&commarg); @@ -696,22 +759,19 @@ buildjoin(initialfile) rev2 = maketemp(0); rev3 = maketemp(3); /* buildrevision() may use 1 and 2 */ - cov[0] = nil; - /* cov[1] setup below */ - cov[2] = CO; - /* cov[3] setup below */ - p = &cov[4]; + 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++ = RCSfilename; - *p = nil; + *p++ = RCSname; + *p = 0; - mergev[0] = nil; - mergev[1] = nil; - mergev[2] = MERGE; - mergev[3] = mergev[5] = "-L"; + mergev[1] = MERGE; + mergev[2] = mergev[4] = "-L"; /* rest of mergev setup below */ i=0; @@ -728,28 +788,26 @@ buildjoin(initialfile) diagnose("revision %s\n",joinlist[i]); bufscpy(&commarg, "-p"); bufscat(&commarg, joinlist[i]); - cov[1] = rev2; - cov[3] = commarg.string; - if (runv(cov)) + 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[1] = rev3; - cov[3] = commarg.string; - if (runv(cov)) + cov[2] = commarg.string; + if (runv(-1, rev3, cov)) goto badmerge; diagnose("merging...\n"); - mergev[4] = subs.string; - mergev[6] = joinlist[i+1]; - p = &mergev[7]; + 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 = nil; - switch (runv(mergev)) { + *p = 0; + switch (runv(-1, (char*)0, mergev)) { case DIFF_FAILURE: case DIFF_SUCCESS: break; default: diff --git a/gnu/usr.bin/rcs/doc/rcs.ms b/gnu/usr.bin/rcs/doc/rcs.ms index 7b3f807..1c90810 100644 --- a/gnu/usr.bin/rcs/doc/rcs.ms +++ b/gnu/usr.bin/rcs/doc/rcs.ms @@ -3,15 +3,6 @@ .\" .\" \*s stands for $, and avoids problems when this file is checked in. .ds s $ -.\" PS and PE center pic diagrams. (The corresponding ms-macros may not.) -.de PS -.nr pE (\\n(.lu-\\$2u)/2u -.in +\\n(pEu -.ne \\$1u -.. -.de PE -.in -\\n(pEu -.. .de D( .DS .nr VS 12p @@ -27,7 +18,7 @@ .de Id .ND \\$4 .. -.Id $Id: rcs.ms,v 5.2 1991/01/03 10:57:28 eggert Exp $ +.Id $Id: rcs.ms,v 5.4 1995/06/01 16:23:43 eggert Exp $ .RP .TL RCS\*-A System for Version Control @@ -292,7 +283,7 @@ has a similar function. It accumulates the log messages that are requested during check-in. Thus, one can maintain the complete history of a revision directly inside it, by enclosing it in a comment. -Figure 1 is a partial reproduction of a log contained in revision 4.1 of +Figure 1 is an edited version of a log contained in revision 4.1 of the file \fIci.c\fR. The log appears at the beginning of the file, and makes it easy to determine what the recent modifications were. .sp @@ -301,23 +292,25 @@ and makes it easy to determine what the recent modifications were. .ne 18 .nf .in +0.5i -/* \*sLog: ci.c,v \*s - * Revision 4.1 1983/05/10 17:03:06 wft - * Added option \-d and \-w, and updated assignment of date, etc. to new delta. - * Added handling of default branches. - * - * Revision 3.9 1983/02/15 15:25:44 wft - * Added call to fastcopy() to copy remainder of RCS file. - * - * Revision 3.8 1983/01/14 15:34:05 wft - * Added ignoring of interrupts while new RCS file is renamed; - * avoids deletion of RCS files by interrupts. - * - * Revision 3.7 1982/12/10 16:09:20 wft - * Corrected checking of return code from diff. - * An RCS file now inherits its mode during the first ci from the working file, - * except that write permission is removed. - */ +/* +.in +\w'/'u +* \*sLog: ci.c,v \*s +* Revision 4.1 1983/05/10 17:03:06 wft +* Added option \-d and \-w, and updated assignment of date, etc. to new delta. +* Added handling of default branches. +* +* Revision 3.9 1983/02/15 15:25:44 wft +* Added call to fastcopy() to copy remainder of RCS file. +* +* Revision 3.8 1983/01/14 15:34:05 wft +* Added ignoring of interrupts while new RCS file is renamed; +* avoids deletion of RCS files by interrupts. +* +* Revision 3.7 1982/12/10 16:09:20 wft +* Corrected checking of return code from diff. +* An RCS file now inherits its mode during the first ci from the working file, +* except that write permission is removed. +*/ .in 0 .ce 1 Figure 1. Log entries produced by the marker \*sLog\*s. @@ -421,7 +414,7 @@ between 1.3 and 1.3.1.1 into a revision at level 2. The operation \fIrcsmerge\fR automates this process (see the Appendix). .ne 7 -.PS 4i +.PS 4i .ps -2 box "1.1" arrow @@ -460,7 +453,7 @@ that the customer has received revision 1.3, added his local modifications as revision 1.3.1.1, then received revision 2.4, and merged 2.4 and 1.3.1.1, resulting in 2.4.1.1. .ne 7 -.PS 4i +.PS 4i .ps -2 R13: box "1.3" line invis @@ -561,7 +554,7 @@ branch revision is reached. Figure 5 illustrates a tree with one side branch. Triangles pointing to the left and right represent reverse and forward deltas, respectively. .ne 8 -.PS 4i +.PS 4i .ps -2 define BD X [line invis $1 right .5; line up .3 then left .5 down .3 then right .5 down .3 then up .3] X @@ -570,21 +563,21 @@ define FD X [line invis $1 right .5; line left .5 down .3 then up .6 then right .5 down .3;] X right -D11: BD(" 1.1") +D11: BD(" 1.1") arrow right from D11.e -D12: BD(" 1.2") - arrow right from D12.e -D13: BD(" 1.3") - arrow right from D13.e -D21: BD(" 2.1") - arrow right from D21.e -D22: box "2.2" +D12: BD(" 1.2") + arrow right from D12.e +D13: BD(" 1.3") + arrow right from D13.e +D21: BD(" 2.1") + arrow right from D21.e +D22: box "2.2" line invis down from D21.s -F1: FD("1.3.1.1 ") +F1: FD("1.3.1.1 ") arrow from D13.se to F1.w arrow from F1.e right right -F2: FD("1.3.1.2 ") +F2: FD("1.3.1.2 ") .ps +2 .PE .ce 1 @@ -1291,9 +1284,10 @@ locks, unlocks, breaks locks, toggles the strict-locking feature, sets state attributes and symbolic revision numbers, changes the description, and deletes revisions. A revision can only be deleted if it is not the fork of a side branch. +.br +.ne 10 .IP "\fIrcsclean\fP \fB\- clean working directory\fP" .sp 0 -.ne 10 \fIRcsclean\fR removes working files that were checked out but never changed.* .FS * The \fIrcsclean\fP and \fIrcsfreeze\fP commands diff --git a/gnu/usr.bin/rcs/ident/Makefile b/gnu/usr.bin/rcs/ident/Makefile index 1a618e5..f28f8d3 100644 --- a/gnu/usr.bin/rcs/ident/Makefile +++ b/gnu/usr.bin/rcs/ident/Makefile @@ -1,7 +1,8 @@ -PROG= ident - -SRCS= ident.c -LDADD= -L${.CURDIR}/../lib/obj -lrcs +PROG= ident +SRCS= ident.c CFLAGS+= -I${.CURDIR}/../lib +LDADD= ${LIBRCS} +DPADD= ${LIBRCS} +.include "../../Makefile.inc" .include <bsd.prog.mk> diff --git a/gnu/usr.bin/rcs/ident/ident.1 b/gnu/usr.bin/rcs/ident/ident.1 index 37c8eda..33c10eb 100644 --- a/gnu/usr.bin/rcs/ident/ident.1 +++ b/gnu/usr.bin/rcs/ident/ident.1 @@ -3,25 +3,28 @@ .ds Dt \\$4 .ds iD \\$3 \\$4 \\$5 \\$6 \\$7 .. -.Id $Id: ident.1,v 5.0 1990/08/22 09:09:36 eggert Exp $ -.ds r \s-1RCS\s0 +.Id $Id: ident.1,v 5.4 1993/11/09 17:40:15 eggert Exp $ +.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 files +ident \- identify RCS keyword strings in files .SH SYNOPSIS .B ident [ .B \-q ] [ +.B \-V +] [ .I file \&.\|.\|. ] .SH DESCRIPTION .B ident -searches for all occurrences of the pattern -.BI $ keyword : .\|.\|. $ -in the named files or, if no file name appears, the standard input. +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), @@ -30,6 +33,11 @@ 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. @@ -37,7 +45,15 @@ For example, if the C program in .B f.c contains .IP -\f3char rcsid[] = \&"$\&Id: f.c,v \*(iD $\&";\fP +.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 @@ -57,14 +73,104 @@ 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 -Revision Number: \*(Rv; Release Date: \*(Dt. +Manual Page Revision: \*(Rv; Release Date: \*(Dt. .br -Copyright \(co 1982, 1988, 1989 by Walter F. Tichy. +Copyright \(co 1982, 1988, 1989 Walter F. Tichy. .br -Copyright \(co 1990 by Paul Eggert. +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) diff --git a/gnu/usr.bin/rcs/ident/ident.c b/gnu/usr.bin/rcs/ident/ident.c index a2cc018..612544c 100644 --- a/gnu/usr.bin/rcs/ident/ident.c +++ b/gnu/usr.bin/rcs/ident/ident.c @@ -1,5 +1,7 @@ -/* Copyright (C) 1982, 1988, 1989 Walter Tichy - Copyright 1990, 1991 by Paul Eggert +/* 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. @@ -15,8 +17,9 @@ 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, 675 Mass Ave, Cambridge, MA 02139, USA. +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: @@ -25,10 +28,26 @@ Report problems and direct all questions to: */ /* - * RCS identification operation - */ - -/* $Log: ident.c,v $ + * $Log: ident.c,v $ + * 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. @@ -81,85 +100,123 @@ Report problems and direct all questions to: #include "rcsbase.h" static int match P((FILE*)); -static void scanfile P((FILE*,char const*,int)); +static int scanfile P((FILE*,char const*,int)); +static void reportError P((char const*)); -mainProg(identId, "ident", "$Id: ident.c,v 5.3 1991/09/10 22:15:46 eggert Exp $") +mainProg(identId, "ident", "$Id: ident.c,v 5.9 1995/06/16 06:19:24 eggert Exp $") /* Ident searches the named files for all occurrences - * of the pattern $keyword:...$, where the keywords are - * Author, Date, Header, Id, Log, RCSfile, Revision, Source, and State. + * of the pattern $@: text $ where @ is a keyword. */ { FILE *fp; - int quiet; + int quiet = 0; int status = EXIT_SUCCESS; + char const *a; - if ((quiet = argc > 1 && strcmp("-q",argv[1])==0)) { - argc--; argv++; - } + while ((a = *++argv) && *a=='-') + while (*++a) + switch (*a) { + case 'q': + quiet = 1; + break; - if (argc<2) - scanfile(stdin, (char*)0, quiet); + 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)); - while ( --argc > 0 ) { - if (!(fp = fopen(*++argv, FOPEN_R))) { - VOID fprintf(stderr, "%s error: can't open %s\n", cmdid, *argv); - status = EXIT_FAILURE; - } else { - scanfile(fp, *argv, quiet); - if (argc>1) VOID putchar('\n'); - } - } if (ferror(stdout) || fclose(stdout)!=0) { - VOID fprintf(stderr, "%s error: write error\n", cmdid); + reportError("standard output"); status = EXIT_FAILURE; } exitmain(status); } -#if lint - exiting void identExit() { _exit(EXIT_FAILURE); } +#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 false if there's a read error. + * Return -1 if there's a write error; exit immediately on a read error. */ { register int c; - if (name) + if (name) { VOID printf("%s:\n", name); - else - name = "input"; + if (ferror(stdout)) + return -1; + } else + name = "standard input"; c = 0; - for (;;) { - if (c < 0) { - if (feof(file)) - break; - if (ferror(file)) - goto read_error; - } + 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); - if (fclose(file) == 0) - return; - - read_error: - VOID fprintf(stderr, "%s error: %s: read error\n", cmdid, name); - exit(EXIT_FAILURE); + return 0; } @@ -174,7 +231,7 @@ match(fp) /* group substring between two KDELIM's; then do pattern match */ tp = line; while ((c = getc(fp)) != VDELIM) { - if (c < 0) + if (c == EOF && feof(fp) | ferror(fp)) return c; switch (ctab[c]) { case LETTER: case Letter: @@ -193,7 +250,7 @@ match(fp) /* group substring between two KDELIM's; then do pattern match */ return c ? c : '\n'; *tp++ = c; while( (c = getc(fp)) != KDELIM ) { - if (c < 0 && feof(fp) | ferror(fp)) + if (c == EOF && feof(fp) | ferror(fp)) return c; switch (ctab[c]) { default: @@ -209,6 +266,6 @@ match(fp) /* group substring between two KDELIM's; then do pattern match */ return c; *tp++ = c; /*append trailing KDELIM*/ *tp = '\0'; - VOID fprintf(stdout, " %c%s\n", KDELIM, line); + VOID printf(" %c%s\n", KDELIM, line); return 0; } diff --git a/gnu/usr.bin/rcs/lib/Makefile b/gnu/usr.bin/rcs/lib/Makefile index b198e9e..0170d4a 100644 --- a/gnu/usr.bin/rcs/lib/Makefile +++ b/gnu/usr.bin/rcs/lib/Makefile @@ -1,5 +1,14 @@ -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 rcsutil.c merger.c +# 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 + +NOPROFILE=noprofile + +install: .include <bsd.lib.mk> diff --git a/gnu/usr.bin/rcs/lib/conf.h b/gnu/usr.bin/rcs/lib/conf.h index d29e511..ceae147 100644 --- a/gnu/usr.bin/rcs/lib/conf.h +++ b/gnu/usr.bin/rcs/lib/conf.h @@ -1,6 +1,6 @@ /* RCS compile-time configuration */ - /* $Id: conf.sh,v 5.14 1991/11/20 18:21:10 eggert Exp $ */ + /* $Id: conf.sh,v 5.25 1995/06/16 06:19:24 eggert Exp $ */ /* * This file is generated automatically. @@ -10,7 +10,8 @@ */ #define exitmain(n) return n /* how to exit from main() */ -/* #define _POSIX_SOURCE */ /* Define this if Posix + strict Standard C. */ +/* #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> @@ -22,48 +23,28 @@ #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 the following symbols to be 1 or 0. */ -#define has_sys_dir_h 1 /* Does #include <sys/dir.h> work? */ +/* 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 */ -/* #undef NAME_MAX */ /* Uncomment this if NAME_MAX is broken. */ - -#if !defined(NAME_MAX) && !defined(_POSIX_NAME_MAX) -# if has_sys_dir_h -# include <sys/dir.h> -# endif -# ifndef NAME_MAX -# ifndef MAXNAMLEN -# define MAXNAMLEN 14 -# endif -# define NAME_MAX MAXNAMLEN -# endif -#endif -#if !defined(PATH_MAX) && !defined(_POSIX_PATH_MAX) -# if has_sys_param_h -# include <sys/param.h> -# define included_sys_param_h 1 -# endif -# ifndef PATH_MAX -# ifndef MAXPATHLEN -# define MAXPATHLEN 1024 -# endif -# define PATH_MAX (MAXPATHLEN-1) -# endif -#endif #if has_readlink && !defined(MAXSYMLINKS) -# if has_sys_param_h && !included_sys_param_h +# if has_sys_param_h # include <sys/param.h> # endif # ifndef MAXSYMLINKS @@ -71,25 +52,55 @@ # endif #endif -/* Comment out the keyword definitions below if the keywords work. */ -/* #define const */ -/* #define volatile */ - /* 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 int sig_atomic_t; */ /* typedef unsigned size_t; */ /* typedef int ssize_t; */ /* typedef long time_t; */ /* typedef int uid_t; */ -/* Define the following symbols to be 1 or 0. */ +/* 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 0 /* Does <varargs.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 @@ -113,105 +124,114 @@ typedef int sig_atomic_t; #else # define vararg_start(ap,p) va_start(ap) #endif - -#define text_equals_binary_stdio 1 /* Does stdio treat text like binary? */ -#define text_work_stdio 0 /* Text i/o for working file, binary for RCS file? */ -#if text_equals_binary_stdio - /* Text and binary i/o behave the same, or binary i/o does not work. */ -# define FOPEN_R "r" -# define FOPEN_W "w" -# define FOPEN_WPLUS "w+" -#else - /* Text and binary i/o behave differently. */ - /* This is incompatible with Posix and Unix. */ -# define FOPEN_R "rb" -# define FOPEN_W "wb" -# define FOPEN_WPLUS "w+b" -#endif -#if text_work_stdio -# define FOPEN_R_WORK "r" -# define FOPEN_W_WORK "w" -# define FOPEN_WPLUS_WORK "w+" +#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 FOPEN_R_WORK FOPEN_R -# define FOPEN_W_WORK FOPEN_W -# define FOPEN_WPLUS_WORK FOPEN_WPLUS +# define exiting #endif - -/* Define or comment out the following symbols as needed. */ -#define bad_fopen_wplus 0 /* Does fopen(f,FOPEN_WPLUS) fail to truncate f? */ -#define getlogin_is_secure 0 /* Is getlogin() secure? Usually it's not. */ #define has_dirent 1 /* Do opendir(), readdir(), closedir() work? */ -#define has_fchmod 0 /* Does fchmod() work? */ -#define has_fputs 0 /* Does fputs() 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_link 1 /* Does link() 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 has_mmap 0 /* Does mmap() work on regular files? */ +#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 0 /* Does seteuid() work? See README. */ +#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 0 /* Does struct sigaction have sa_sigaction? */ #define has_signal 1 /* Does signal() work? */ -#define signal_args P((int)) /* arguments of signal handlers */ #define signal_type void /* type returned by signal handlers */ #define sig_zaps_handler 0 /* Must a signal handler reinvoke signal()? */ -#define has_sigaction 1 /* Does struct sigaction work? */ /* #define has_sigblock ? */ /* Does sigblock() work? */ /* #define sigmask(s) (1 << ((s)-1)) */ /* Yield mask for signal number. */ -#define has_sys_siglist 0 /* Does sys_siglist[] work? */ -typedef ssize_t fread_type; /* type returned by fread() and fwrite() */ +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_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 0 /* Does vfork() work? */ +#define has_vfork 1 /* Does vfork() work? */ #define has_fork 1 /* Does fork() work? */ #define has_spawn 0 /* Does spawn*() work? */ -#define has_wait 1 /* Does wait() work? */ -#define has_waitpid 0 /* Does waitpid() 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 0 /* Can main memory hold entire RCS files? */ -/* #undef ULONG_MAX */ /* Uncomment this if ULONG_MAX is broken (e.g. < 0). */ -/* struct utimbuf { time_t actime, modtime; }; */ /* Uncomment this if needed. */ -#define CO "/usr/bin/co" /* name of 'co' program */ +#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/local/bin/co" /* name of 'co' program */ #define COMPAT2 0 /* Are version 2 files supported? */ -#define DATEFORM "%.2d.%.2d.%.2d.%.2d.%.2d.%.2d" /* e.g. 01.01.01.01.01.01 */ #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 DIFF_FLAGS , "-an" /* Make diff output suitable for RCS. */ -#define DIFF_L 1 /* Does diff -L work? */ +#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 MERGE "/usr/local/bin/merge" /* name of 'merge' program */ #define TMPDIR "/tmp" /* default directory for temporary files */ -#define SLASH '/' /* principal pathname separator */ -#define SLASHes '/' /* `case SLASHes:' labels all pathname separators */ -#define isSLASH(c) ((c) == SLASH) /* Is arg a pathname separator? */ +#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 ALL_ABSOLUTE 1 /* Are all subprograms absolute pathnames? */ -#define SENDMAIL "/usr/bin/mail" /* how to send mail */ +#define SENDMAIL "/usr/sbin/sendmail" /* how to send mail */ #define TZ_must_be_set 0 /* Must TZ be set for gmtime() to work? */ @@ -219,40 +239,21 @@ typedef void *malloc_type; /* type returned by malloc() */ /* Adjust the following declarations as needed. */ -#if __GNUC__ && !__STRICT_ANSI__ -# define exiting volatile /* GCC extension: function cannot return */ -#else -# define exiting -#endif +/* 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. */ -#if has_ftruncate - int ftruncate P((int,off_t)); -#endif -/* <sys/mman.h> */ -#if has_madvise - int madvise P((caddr_t,size_t,int)); -#endif -#if has_mmap - caddr_t mmap P((caddr_t,size_t,int,int,int,off_t)); - int munmap P((caddr_t,size_t)); +/* 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) */ -/* These definitions are for the benefit of non-Posix hosts, and */ -/* Posix hosts that have Standard C compilers but traditional include files. */ -/* Unfortunately, mixed-up hosts are all too common. */ /* <fcntl.h> */ -#ifdef F_DUPFD - int fcntl P((int,int,...)); -#else - int dup2 P((int,int)); -#endif -#ifndef O_BINARY /* some non-Posix hosts need O_BINARY */ -# define O_BINARY 0 /* no effect on Posix */ -#endif #ifdef O_CREAT # define open_can_creat 1 #else @@ -262,43 +263,12 @@ typedef void *malloc_type; /* type returned by malloc() */ # define O_RDWR 2 # define O_CREAT 01000 # define O_TRUNC 02000 - int creat P((char const*,mode_t)); #endif #ifndef O_EXCL -# define O_EXCL 0 -#endif - -/* <pwd.h> */ -#if has_getpwuid - struct passwd *getpwuid P((uid_t)); +#define O_EXCL 0 #endif -/* <signal.h> */ -#if has_sigaction - int sigaction P((int,struct sigaction const*,struct sigaction*)); - int sigaddset P((sigset_t*,int)); - int sigemptyset P((sigset_t*)); -#else -#if has_sigblock - /* BSD */ - int sigblock P((int)); - int sigmask P((int)); - int sigsetmask P((int)); -#endif -#endif - -/* <stdio.h> */ -FILE *fdopen P((int,char const*)); -int fileno P((FILE*)); - /* <sys/stat.h> */ -int chmod P((char const*,mode_t)); -int fstat P((int,struct stat*)); -int stat P((char const*,struct stat*)); -mode_t umask P((mode_t)); -#if has_fchmod - int fchmod P((int,mode_t)); -#endif #ifndef S_IRUSR # ifdef S_IREAD # define S_IRUSR S_IREAD @@ -326,166 +296,100 @@ mode_t umask P((mode_t)); # endif #endif #ifndef S_ISREG -# define S_ISREG(n) (((n) & S_IFMT) == S_IFREG) +#define S_ISREG(n) (((n) & S_IFMT) == S_IFREG) #endif /* <sys/wait.h> */ -#if has_wait - pid_t wait P((int*)); -#endif #ifndef WEXITSTATUS -# define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8) -# undef WIFEXITED /* Avoid 4.3BSD incompatibility with Posix. */ +#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) & 255)) +#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)); -int close P((int)); -int isatty P((int)); -int link P((char const*,char const*)); -int open P((char const*,int,...)); -int unlink P((char const*)); -int _filbuf P((FILE*)); /* keeps lint quiet in traditional C */ -int _flsbuf P((int,FILE*)); /* keeps lint quiet in traditional C */ -long pathconf P((char const*,int)); -ssize_t write P((int,void const*,size_t)); #ifndef STDIN_FILENO # define STDIN_FILENO 0 # define STDOUT_FILENO 1 # define STDERR_FILENO 2 #endif -#if has_fork -# if !has_vfork -# undef vfork -# define vfork fork -# endif - pid_t vfork P((void)); /* vfork is nonstandard but faster */ +#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_getuid - uid_t getuid P((void)); -#endif -#if has_readlink -/* ssize_t readlink P((char const*,char*,size_t)); *//* BSD; not standard yet */ -#endif -#if has_setuid -# if !has_seteuid -# undef seteuid -# define seteuid setuid -# endif - int seteuid P((uid_t)); - uid_t geteuid P((void)); +#if has_setuid && !has_seteuid +# undef seteuid +# define seteuid setuid #endif #if has_spawn - int spawnv P((int,char const*,char*const*)); # if ALL_ABSOLUTE # define spawn_RCS spawnv # else # define spawn_RCS spawnvp - int spawnvp P((int,char const*,char*const*)); # endif #else - int execv P((char const*,char*const*)); # if ALL_ABSOLUTE # define exec_RCS execv # else # define exec_RCS execvp - int execvp P((char const*,char*const*)); # endif #endif /* utime.h */ -int utime P((char const*,struct utimbuf const*)); +#if !has_utimbuf + struct utimbuf { time_t actime, modtime; }; +#endif /* Standard C library */ -/* These definitions are for the benefit of hosts that have */ -/* traditional C include files, possibly with Standard C compilers. */ -/* Unfortunately, mixed-up hosts are all too common. */ - -/* <errno.h> */ -extern int errno; - -/* <limits.h> */ -#ifndef ULONG_MAX - /* This does not work in #ifs, but it's good enough for us. */ -# define ULONG_MAX ((unsigned long)-1) -#endif - -/* <signal.h> */ -#if has_signal - signal_type (*signal P((int,signal_type(*)signal_args)))signal_args; -#endif /* <stdio.h> */ -FILE *fopen P((char const*,char const*)); -fread_type fread P((void*,freadarg_type,freadarg_type,FILE*)); -fread_type fwrite P((void const*,freadarg_type,freadarg_type,FILE*)); -int fclose P((FILE*)); -int feof P((FILE*)); -int ferror P((FILE*)); -int fflush P((FILE*)); -int fprintf P((FILE*,char const*,...)); -int fputs P((char const*,FILE*)); -int fseek P((FILE*,long,int)); -int printf P((char const*,...)); -int rename P((char const*,char const*)); -int sprintf P((char*,char const*,...)); -/* long ftell P((FILE*)); */ -void clearerr P((FILE*)); -void perror P((char const*)); #ifndef L_tmpnam -# define L_tmpnam 32 /* power of 2 > sizeof("/usr/tmp/xxxxxxxxxxxxxxx") */ +#define L_tmpnam 32 /* power of 2 > sizeof("/usr/tmp/xxxxxxxxxxxxxxx") */ #endif #ifndef SEEK_SET -# define SEEK_SET 0 +#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 -#if has_vfprintf - int vfprintf P((FILE*,char const*,va_list)); -#else -#if has__doprintf - void _doprintf P((FILE*,char const*,va_list)); /* Minix */ -#else - void _doprnt P((char const*,va_list,FILE*)); /* BSD */ -#endif -#endif /* <stdlib.h> */ char *getenv P((char const*)); -exiting void _exit P((int)); -exiting void exit P((int)); +void _exit P((int)) exiting; +void exit P((int)) exiting; malloc_type malloc P((size_t)); malloc_type realloc P((malloc_type,size_t)); -void free P((malloc_type)); #ifndef EXIT_FAILURE -# define EXIT_FAILURE 1 +#define EXIT_FAILURE 1 #endif #ifndef EXIT_SUCCESS -# define EXIT_SUCCESS 0 -#endif -#if !has_fork && !has_spawn - int system P((char const*)); +#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)); -int memcmp P((void const*,void const*,size_t)); -int strcmp P((char const*,char const*)); -size_t strlen P((char const*)); void *memcpy P((void*,void const*,size_t)); #if has_memmove void *memmove P((void*,void const*,size_t)); diff --git a/gnu/usr.bin/rcs/lib/maketime.c b/gnu/usr.bin/rcs/lib/maketime.c index c95c9f0..bf14bb9 100644 --- a/gnu/usr.bin/rcs/lib/maketime.c +++ b/gnu/usr.bin/rcs/lib/maketime.c @@ -1,344 +1,344 @@ -# -/* - * MAKETIME derive 32-bit time value from TM structure. - * - * Usage: - * int zone; Minutes west of GMT, or - * 48*60 for localtime - * time_t t; - * struct tm *tp; Pointer to TM structure from <time.h> - * t = maketime(tp,zone); - * - * Returns: - * -1 if failure; parameter out of range or nonsensical. - * else time-value. - * Notes: - * This code is quasi-public; it may be used freely in like software. - * It is not to be sold, nor used in licensed software without - * permission of the author. - * For everyone's benefit, please report bugs and improvements! - * Copyright 1981 by Ken Harrenstien, SRI International. - * (ARPANET: KLH @ SRI) - */ -/* $Log: maketime.c,v $ - * Revision 5.3 1991/08/19 03:13:55 eggert - * Add setfiledate, str2time, TZ_must_be_set. - * - * Revision 5.2 1990/11/01 05:03:30 eggert - * Remove lint. - * - * Revision 5.1 1990/10/04 06:30:13 eggert - * Calculate the GMT offset of 'xxx LT' as of xxx, not as of now. - * Don't assume time_t is 32 bits. Fix bugs near epoch and near end of time. - * - * Revision 5.0 1990/08/22 08:12:38 eggert - * Switch to GMT and fix the bugs exposed thereby. - * Permit dates past 1999/12/31. Ansify and Posixate. - * - * Revision 1.8 88/11/08 13:54:53 narten - * allow negative timezones (-24h <= x <= 24h) - * - * Revision 1.7 88/08/28 14:47:52 eggert - * Allow cc -R. Remove unportable "#endif XXX"s. - * - * Revision 1.6 87/12/18 17:05:58 narten - * include rcsparam.h - * - * Revision 1.5 87/12/18 11:35:51 narten - * maketime.c: fixed USG code - you have tgo call "tzset" in order to have - * "timezone" set. ("localtime" calls it, but it's probably better not to - * count on "localtime" having been called.) - * - * Revision 1.4 87/10/18 10:26:57 narten - * Updating version numbers. Changes relative to 1.0 are actually - * relative to 1.2 - * - * Revision 1.3 87/09/24 13:58:45 narten - * Sources now pass through lint (if you ignore printf/sprintf/fprintf - * warnings) - * - * Revision 1.2 87/03/27 14:21:48 jenkins - * Port to suns - * - * Revision 1.2 83/12/05 10:12:56 wft - * added cond. compilation for USG Unix; long timezone; - * - * Revision 1.1 82/05/06 11:38:00 wft - * Initial revision - * - */ - - -#include "rcsbase.h" - -libId(maketId, "$Id: maketime.c,v 5.3 1991/08/19 03:13:55 eggert Exp $") - -static struct tm const *time2tm P((time_t)); - -#define given(v) (0 <= (v)) /* Negative values are unspecified. */ - -static int const daytb[] = { - /* # days in year thus far, indexed by month (0-12!!) */ - 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 -}; +/* Convert struct partime into time_t. */ - static time_t -maketime(atm,zone) - struct tm const *atm; - int zone; -{ - register struct tm const *tp; - register int i; - int year, yday, mon, day, hour, min, sec, leap, localzone; - int attempts; - time_t t, tres; - - attempts = 2; - localzone = zone==48*60; - tres = -1; - year = mon = day = 0; /* Keep lint happy. */ - - do { - - if (localzone || !given(atm->tm_year)) { - if (tres == -1) - if ((tres = time((time_t*)0)) == -1) - return -1; - tp = time2tm(tres); - /* Get breakdowns of default time, adjusting to zone. */ - year = tp->tm_year; /* Use to set up defaults */ - yday = tp->tm_yday; - mon = tp->tm_mon; - day = tp->tm_mday; - hour = tp->tm_hour; - min = tp->tm_min; - if (localzone) { - tp = localtime(&tres); - zone = - min - tp->tm_min + 60*( - hour - tp->tm_hour + 24*( - /* If years differ, it's by one day. */ - year - tp->tm_year - ? year - tp->tm_year - : yday - tp->tm_yday)); - } - /* Adjust the default day, month and year according to zone. */ - if ((min -= zone) < 0) { - if (hour-(59-min)/60 < 0 && --day <= 0) { - if (--mon < 0) { - --year; - mon = 11; - } - day = daytb[mon+1] - daytb[mon] + (mon==1&&!(year&3)); - } - } else - if ( - 24 <= hour+min/60 && - daytb[mon+1] - daytb[mon] + (mon==1&&!(year&3)) < ++day - ) { - if (11 < ++mon) { - ++year; - mon = 0; - } - day = 1; - } - } - if (zone < -24*60 || 24*60 < zone) - return -1; +/* Copyright 1992, 1993, 1994, 1995 Paul Eggert + Distributed under license by the Free Software Foundation, Inc. +This file is part of RCS. -#ifdef DEBUG -printf("first YMD: %d %d %d\n",year,mon,day); -#endif - tp = atm; - - /* First must find date, using specified year, month, day. - * If one of these is unspecified, it defaults either to the - * current date (if no more global spec was given) or to the - * zero-value for that spec (i.e. a more global spec was seen). - * Reject times that do not fit in time_t, - * without assuming that time_t is 32 bits or is signed. - */ - if (given(tp->tm_year)) - { - year = tp->tm_year; - mon = 0; /* Since year was given, default */ - day = 1; /* for remaining specs is zero */ - } - if (year < 69) /* 1969/12/31 OK in some timezones. */ - return -1; /* ERR: year out of range */ - leap = !(year&3) && (year%100 || !((year+300)%400)); - year -= 70; /* UNIX time starts at 1970 */ +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. - /* - * Find day of year. - */ - { - if (given(tp->tm_mon)) - { mon = tp->tm_mon; /* Month was specified */ - day = 1; /* so set remaining default */ - } - if (11 < (unsigned)mon) - return -1; /* ERR: bad month */ - if (given(tp->tm_mday)) day = tp->tm_mday; - if(day < 1 - || (((daytb[mon+1]-daytb[mon]) < day) - && (day!=29 || mon!=1 || !leap) )) - return -1; /* ERR: bad day */ - yday = daytb[mon] /* Add # of days in months so far */ - + ((leap /* Leap year, and past Feb? If */ - && mon>1)? 1:0) /* so, add leap day for this year */ - + day-1; /* And finally add # days this mon */ +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. - } - if (leap+365 <= (unsigned)yday) - return -1; /* ERR: bad YDAY */ - - if (year < 0) { - if (yday != 364) - return -1; /* ERR: too early */ - t = -1; - } else { - tres = year*365; /* Get # days of years so far */ - if (tres/365 != year) - return -1; /* ERR: overflow */ - t = tres - + ((year+1)>>2) /* plus # of leap days since 1970 */ - + yday; /* and finally add # days this year */ - if (t+4 < tres) - return -1; /* ERR: overflow */ - } - tres = t; +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. - if (given(i = tp->tm_wday)) /* Check WDAY if present */ - if (i != (tres+4)%7) /* 1970/01/01 was Thu = 4 */ - return -1; /* ERR: bad WDAY */ +Report problems and direct all questions to: -#ifdef DEBUG -printf("YMD: %d %d %d, T=%ld\n",year,mon,day,tres); -#endif - /* - * Now determine time. If not given, default to zeros - * (since time is always the least global spec) - */ - tres *= 86400L; /* Get # seconds (24*60*60) */ - if (tres/86400L != t) - return -1; /* ERR: overflow */ - hour = min = sec = 0; - if (given(tp->tm_hour)) hour = tp->tm_hour; - if (given(tp->tm_min )) min = tp->tm_min; - if (given(tp->tm_sec )) sec = tp->tm_sec; - if (60 <= (unsigned)min || 60 < (unsigned)sec) - return -1; /* ERR: MS out of range */ - if (24 <= (unsigned)hour) - if(hour != 24 || (min+sec) !=0) /* Allow 24:00 */ - return -1; /* ERR: H out of range */ - - t = tres; - tres += sec + 60L*(zone + min + 60*hour); - -#ifdef DEBUG -printf("HMS: %d %d %d T=%ld\n",hour,min,sec,tres); + 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 - if (!localzone) /* check for overflow */ - return (year<0 ? (tres<0||86400L<=tres) : tres<t) ? -1 : tres; - - /* Check results; LT may have had a different GMT offset back then. */ - tp = localtime(&tres); - if (given(atm->tm_sec) && atm->tm_sec != tp->tm_sec) - return -1; /* If seconds don't match, we're in trouble. */ - if (!( - given(atm->tm_min) && atm->tm_min != tp->tm_min || - given(atm->tm_hour) && atm->tm_hour != tp->tm_hour || - given(atm->tm_mday) && atm->tm_mday != tp->tm_mday || - given(atm->tm_mon) && atm->tm_mon != tp->tm_mon || - given(atm->tm_year) && atm->tm_year != tp->tm_year - )) - return tres; /* Everything matches. */ - - } while (--attempts); - - return -1; +#include "partime.h" +#include "maketime.h" + +char const maketId[] + = "$Id: maketime.c,v 5.11 1995/06/16 06:19:24 eggert Exp $"; + +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 Unix time to struct tm format. -* Use Coordinated Universal Time (UTC) if version 5 or newer; -* use local time otherwise. +* Convert UNIXTIME to struct tm form. +* Use gmtime if available and if !LOCALZONE, localtime otherwise. */ - static struct tm const * -time2tm(unixtime) + struct tm * +time2tm(unixtime, localzone) time_t unixtime; + int localzone; { - struct tm const *tm; + struct tm *tm; # if TZ_must_be_set static char const *TZ; if (!TZ && !(TZ = getenv("TZ"))) - faterror("TZ is not set"); + faterror("The TZ environment variable is not set; please set it to your timezone"); # endif - if (!(tm = (RCSversion<VERSION(5) ? localtime : gmtime)(&unixtime))) - faterror("UTC is not available; perhaps TZ is not set?"); + 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); +} + /* -* Convert Unix time to RCS format. -* For compatibility with older versions of RCS, -* dates before AD 2000 are stored without the leading "19". +* 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 -time2date(unixtime,date) - time_t unixtime; - char date[datesize]; +adjzone(t, seconds) + register struct tm *t; + long seconds; { - register struct tm const *tm = time2tm(unixtime); - VOID sprintf(date, DATEFORM, - tm->tm_year + (tm->tm_year<100 ? 0 : 1900), - tm->tm_mon+1, tm->tm_mday, - tm->tm_hour, tm->tm_min, tm->tm_sec - ); + /* + * 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 -str2time(source) - char const *source; -/* Parse a free-format date in SOURCE, yielding a Unix format time. */ +maketime(pt, default_time) + struct partime const *pt; + time_t default_time; { - int zone; - time_t unixtime; - struct tm parseddate; + 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 (!partime(source, &parseddate, &zone)) - faterror("can't parse date/time: %s", source); - if ((unixtime = maketime(&parseddate, zone)) == -1) - faterror("bad date/time: %s", source); - return unixtime; + 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; } - void -str2date(source, target) +/* Parse a free-format date in SOURCE, yielding a Unix format time. */ + time_t +str2time(source, default_time, default_zone) char const *source; - char target[datesize]; -/* Parse a free-format date in SOURCE, convert it - * into RCS internal format, and store the result into TARGET. - */ + time_t default_time; + long default_zone; { - time2date(str2time(source), target); + 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 -setfiledate(file, date) - char const *file, date[datesize]; -/* Set the access and modification time of FILE to DATE. */ +main(argc, argv) int argc; char **argv; { - static struct utimbuf times; /* static so unused fields are zero */ - char datebuf[datesize]; - - if (!date) - return 0; - times.actime = times.modtime = str2time(date2str(date, datebuf)); - return utime(file, ×); + time_t default_time = time((time_t *)0); + long default_zone = argv[1] ? atol(argv[1]) : 0; + char buf[1000]; + while (gets(buf)) { + time_t t = str2time(buf, default_time, default_zone); + printf("%s", asctime(gmtime(&t))); + } + return 0; } +#endif diff --git a/gnu/usr.bin/rcs/lib/maketime.h b/gnu/usr.bin/rcs/lib/maketime.h new file mode 100644 index 0000000..fbe1256 --- /dev/null +++ b/gnu/usr.bin/rcs/lib/maketime.h @@ -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)); diff --git a/gnu/usr.bin/rcs/lib/merger.c b/gnu/usr.bin/rcs/lib/merger.c index 7162ffa..003e0a6 100644 --- a/gnu/usr.bin/rcs/lib/merger.c +++ b/gnu/usr.bin/rcs/lib/merger.c @@ -1,6 +1,6 @@ -/* merger - three-way file merge internals */ +/* three-way file merge internals */ -/* Copyright 1991 by Paul Eggert +/* Copyright 1991, 1992, 1993, 1994, 1995 Paul Eggert Distributed under license by the Free Software Foundation, Inc. This file is part of RCS. @@ -16,8 +16,9 @@ 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, 675 Mass Ave, Cambridge, MA 02139, USA. +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: @@ -27,38 +28,40 @@ Report problems and direct all questions to: #include "rcsbase.h" -libId(mergerId, "$Id: merger.c,v 1.3 1991/08/20 23:05:00 eggert Exp $") +libId(mergerId, "$Id: merger.c,v 1.7 1995/06/16 06:19:24 eggert Exp $") + 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.. + * Set *B to the address of any storage that was allocated. */ { char *t; - switch (*s) { - case '-': case '+': - *b = t = testalloc(strlen(s) + 3); - VOID sprintf(t, ".%c%s", SLASH, s); - return t; - default: - *b = 0; - return s; + 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, label, argv) +merge(tostdout, edarg, label, argv) int tostdout; - char const *const label[2]; + char const *edarg; + char const *const label[3]; char const *const argv[3]; /* - * Do `merge [-p] -L l0 -L l1 a0 a1 a2', + * Do `merge [-p] EDARG -L l0 -L l1 -L l2 a0 a1 a2', * where TOSTDOUT specifies whether -p is present, - * LABEL gives l0 and l1, and ARGV gives a0, a1, and a2. + * 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. */ { @@ -74,30 +77,35 @@ merge(tostdout, label, argv) 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( - (char*)0, t, - DIFF3, "-am", "-L", label[0], "-L", label[1], + -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: - if (!quietflag) - warn("overlaps during merge"); + warn("conflicts during merge"); break; default: exiterr(); } if (t) { - if (!(f = fopen(argv[0], FOPEN_W))) + if (!(f = fopenSafer(argv[0], "w"))) efaterror(argv[0]); - if (!(rt = Iopen(t, FOPEN_R, (struct stat*)0))) + if (!(rt = Iopen(t, "r", (struct stat*)0))) efaterror(t); fastcopy(rt, f); Ifclose(rt); @@ -106,29 +114,30 @@ merge(tostdout, label, argv) #else for (i=0; i<2; i++) switch (run( - (char*)0, d[i]=maketemp(i), + -1, d[i]=maketemp(i), DIFF, a[i], a[2], (char*)0 )) { case DIFF_FAILURE: case DIFF_SUCCESS: break; - default: exiterr(); + default: faterror("diff failed"); } t = maketemp(2); s = run( - (char*)0, t, - DIFF3, "-E", d[0], d[1], a[0], a[1], a[2], - label[0], label[1], (char*)0 + -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; - if (!quietflag) - warn("overlaps or other problems during merge"); + warn("overlaps or other problems during merge"); } - if (!(f = fopen(t, "a"))) + if (!(f = fopenSafer(t, "a+"))) efaterror(t); aputs(tostdout ? "1,$p\n" : "w\n", f); - Ofclose(f); - if (run(t, (char*)0, ED, "-", a[0], (char*)0)) + Orewind(f); + aflush(f); + if (run(fileno(f), (char*)0, ED, "-", a[0], (char*)0)) exiterr(); + Ofclose(f); #endif tempunlink(); diff --git a/gnu/usr.bin/rcs/lib/partime.c b/gnu/usr.bin/rcs/lib/partime.c index 4751fc5..4246566 100644 --- a/gnu/usr.bin/rcs/lib/partime.c +++ b/gnu/usr.bin/rcs/lib/partime.c @@ -1,639 +1,701 @@ -/* - * PARTIME parse date/time string into a TM structure - * - * Returns: - * 0 if parsing failed - * else time values in specified TM structure and zone (unspecified values - * set to TMNULL) - * Notes: - * This code is quasi-public; it may be used freely in like software. - * It is not to be sold, nor used in licensed software without - * permission of the author. - * For everyone's benefit, please report bugs and improvements! - * Copyright 1980 by Ken Harrenstien, SRI International. - * (ARPANET: KLH @ SRI) - */ - -/* Hacknotes: - * If parsing changed so that no backup needed, could perhaps modify - * to use a FILE input stream. Need terminator, though. - * Perhaps should return 0 on success, else a non-zero error val? - */ - -/* $Log: partime.c,v $ - * Revision 5.6 1991/08/19 03:13:55 eggert - * Update timezones. - * - * Revision 5.5 1991/04/21 11:58:18 eggert - * Don't put , just before } in initializer. - * - * Revision 5.4 1990/10/04 06:30:15 eggert - * Remove date vs time heuristics that fail between 2000 and 2400. - * Check for overflow when lexing an integer. - * Parse 'Jan 10 LT' as 'Jan 10, LT', not 'Jan, 10 LT'. - * - * Revision 5.3 1990/09/24 18:56:31 eggert - * Update timezones. - * - * Revision 5.2 1990/09/04 08:02:16 eggert - * Don't parse two-digit years, because it won't work after 1999/12/31. - * Don't permit 'Aug Aug'. - * - * Revision 5.1 1990/08/29 07:13:49 eggert - * Be able to parse our own date format. Don't assume year<10000. - * - * Revision 5.0 1990/08/22 08:12:40 eggert - * Switch to GMT and fix the bugs exposed thereby. Update timezones. - * Ansify and Posixate. Fix peekahead and int-size bugs. - * - * Revision 1.4 89/05/01 14:48:46 narten - * fixed #ifdef DEBUG construct - * - * Revision 1.3 88/08/28 14:53:40 eggert - * Remove unportable "#endif XXX"s. - * - * Revision 1.2 87/03/27 14:21:53 jenkins - * Port to suns - * - * Revision 1.1 82/05/06 11:38:26 wft - * Initial revision - * - */ - -#include "rcsbase.h" - -libId(partId, "$Id: partime.c,v 5.6 1991/08/19 03:13:55 eggert Exp $") - -#define given(v) (0 <= (v)) -#define TMNULL (-1) /* Items not given are given this value */ -#define TZ_OFFSET (24*60) /* TMNULL < zone_offset - TZ_OFFSET */ - -struct tmwent { - char const *went; - short wval; - char wflgs; - char wtype; -}; - /* wflgs */ -#define TWTIME 02 /* Word is a time value (absence implies date) */ -#define TWDST 04 /* Word is a DST-type timezone */ - /* wtype */ -#define TM_MON 1 /* month name */ -#define TM_WDAY 2 /* weekday name */ -#define TM_ZON 3 /* time zone name */ -#define TM_LT 4 /* local time */ -#define TM_DST 5 /* daylight savings time */ -#define TM_12 6 /* AM, PM, NOON, or MIDNIGHT */ - /* wval (for wtype==TM_12) */ -#define T12_AM 1 -#define T12_PM 2 -#define T12_NOON 12 -#define T12_MIDNIGHT 0 - -static struct tmwent const tmwords [] = { - {"january", 0, 0, TM_MON}, - {"february", 1, 0, TM_MON}, - {"march", 2, 0, TM_MON}, - {"april", 3, 0, TM_MON}, - {"may", 4, 0, TM_MON}, - {"june", 5, 0, TM_MON}, - {"july", 6, 0, TM_MON}, - {"august", 7, 0, TM_MON}, - {"september", 8, 0, TM_MON}, - {"october", 9, 0, TM_MON}, - {"november", 10, 0, TM_MON}, - {"december", 11, 0, TM_MON}, - - {"sunday", 0, 0, TM_WDAY}, - {"monday", 1, 0, TM_WDAY}, - {"tuesday", 2, 0, TM_WDAY}, - {"wednesday", 3, 0, TM_WDAY}, - {"thursday", 4, 0, TM_WDAY}, - {"friday", 5, 0, TM_WDAY}, - {"saturday", 6, 0, TM_WDAY}, - - {"gmt", 0*60, TWTIME, TM_ZON}, /* Greenwich */ - {"utc", 0*60, TWTIME, TM_ZON}, - {"ut", 0*60, TWTIME, TM_ZON}, - {"cut", 0*60, TWTIME, TM_ZON}, - - {"nzst", -12*60, TWTIME, TM_ZON}, /* New Zealand */ - {"jst", -9*60, TWTIME, TM_ZON}, /* Japan */ - {"kst", -9*60, TWTIME, TM_ZON}, /* Korea */ - {"ist", -5*60-30, TWTIME, TM_ZON},/* India */ - {"eet", -2*60, TWTIME, TM_ZON}, /* Eastern Europe */ - {"cet", -1*60, TWTIME, TM_ZON}, /* Central Europe */ - {"met", -1*60, TWTIME, TM_ZON}, /* Middle Europe */ - {"wet", 0*60, TWTIME, TM_ZON}, /* Western Europe */ - {"nst", 3*60+30, TWTIME, TM_ZON},/* Newfoundland */ - {"ast", 4*60, TWTIME, TM_ZON}, /* Atlantic */ - {"est", 5*60, TWTIME, TM_ZON}, /* Eastern */ - {"cst", 6*60, TWTIME, TM_ZON}, /* Central */ - {"mst", 7*60, TWTIME, TM_ZON}, /* Mountain */ - {"pst", 8*60, TWTIME, TM_ZON}, /* Pacific */ - {"akst", 9*60, TWTIME, TM_ZON}, /* Alaska */ - {"hast", 10*60, TWTIME, TM_ZON}, /* Hawaii-Aleutian */ - {"hst", 10*60, TWTIME, TM_ZON}, /* Hawaii */ - {"sst", 11*60, TWTIME, TM_ZON}, /* Samoa */ - - {"nzdt", -12*60, TWTIME+TWDST, TM_ZON}, /* New Zealand */ - {"kdt", -9*60, TWTIME+TWDST, TM_ZON}, /* Korea */ - {"bst", 0*60, TWTIME+TWDST, TM_ZON}, /* Britain */ - {"ndt", 3*60+30, TWTIME+TWDST, TM_ZON}, /* Newfoundland */ - {"adt", 4*60, TWTIME+TWDST, TM_ZON}, /* Atlantic */ - {"edt", 5*60, TWTIME+TWDST, TM_ZON}, /* Eastern */ - {"cdt", 6*60, TWTIME+TWDST, TM_ZON}, /* Central */ - {"mdt", 7*60, TWTIME+TWDST, TM_ZON}, /* Mountain */ - {"pdt", 8*60, TWTIME+TWDST, TM_ZON}, /* Pacific */ - {"akdt", 9*60, TWTIME+TWDST, TM_ZON}, /* Alaska */ - {"hadt", 10*60, TWTIME+TWDST, TM_ZON}, /* Hawaii-Aleutian */ +/* Parse a string, yielding a struct partime that describes it. */ -#if 0 - /* - * The following names are duplicates or are not well attested. - * A standard is needed. - */ - {"east", -10*60, TWTIME, TM_ZON}, /* Eastern Australia */ - {"cast", -9*60-30, TWTIME, TM_ZON},/* Central Australia */ - {"cst", -8*60, TWTIME, TM_ZON}, /* China */ - {"hkt", -8*60, TWTIME, TM_ZON}, /* Hong Kong */ - {"sst", -8*60, TWTIME, TM_ZON}, /* Singapore */ - {"wast", -8*60, TWTIME, TM_ZON}, /* Western Australia */ - {"?", -6*60-30, TWTIME, TM_ZON},/* Burma */ - {"?", -4*60-30, TWTIME, TM_ZON},/* Afghanistan */ - {"it", -3*60-30, TWTIME, TM_ZON},/* Iran */ - {"ist", -2*60, TWTIME, TM_ZON}, /* Israel */ - {"mez", -1*60, TWTIME, TM_ZON}, /* Mittel-Europaeische Zeit */ - {"ast", 1*60, TWTIME, TM_ZON}, /* Azores */ - {"fst", 2*60, TWTIME, TM_ZON}, /* Fernando de Noronha */ - {"bst", 3*60, TWTIME, TM_ZON}, /* Brazil */ - {"wst", 4*60, TWTIME, TM_ZON}, /* Western Brazil */ - {"ast", 5*60, TWTIME, TM_ZON}, /* Acre Brazil */ - {"?", 9*60+30, TWTIME, TM_ZON},/* Marquesas */ - {"?", 12*60, TWTIME, TM_ZON}, /* Kwajalein */ - - {"eadt", -10*60, TWTIME+TWDST, TM_ZON}, /* Eastern Australia */ - {"cadt", -9*60-30, TWTIME+TWDST, TM_ZON}, /* Central Australia */ - {"cdt", -8*60, TWTIME+TWDST, TM_ZON}, /* China */ - {"wadt", -8*60, TWTIME+TWDST, TM_ZON}, /* Western Australia */ - {"idt", -2*60, TWTIME+TWDST, TM_ZON}, /* Israel */ - {"eest", -2*60, TWTIME+TWDST, TM_ZON}, /* Eastern Europe */ - {"cest", -1*60, TWTIME+TWDST, TM_ZON}, /* Central Europe */ - {"mest", -1*60, TWTIME+TWDST, TM_ZON}, /* Middle Europe */ - {"mesz", -1*60, TWTIME+TWDST, TM_ZON}, /* Mittel-Europaeische Sommerzeit */ - {"west", 0*60, TWTIME+TWDST, TM_ZON}, /* Western Europe */ - {"adt", 1*60, TWTIME+TWDST, TM_ZON}, /* Azores */ - {"fdt", 2*60, TWTIME+TWDST, TM_ZON}, /* Fernando de Noronha */ - {"edt", 3*60, TWTIME+TWDST, TM_ZON}, /* Eastern Brazil */ - {"wdt", 4*60, TWTIME+TWDST, TM_ZON}, /* Western Brazil */ - {"adt", 5*60, TWTIME+TWDST, TM_ZON}, /* Acre Brazil */ +/* 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 - {"lt", 0, TWTIME, TM_LT}, /* local time */ - {"dst", 1*60, TWTIME, TM_DST}, /* daylight savings time */ - {"ddst", 2*60, TWTIME, TM_DST}, /* double dst */ +#include <ctype.h> +#undef isdigit +#define isdigit(c) (((unsigned)(c)-'0') <= 9) /* faster than stock */ + +#include "partime.h" + +char const partimeId[] + = "$Id: partime.c,v 5.13 1995/06/16 06:19:24 eggert Exp $"; - {"am", T12_AM, TWTIME, TM_12}, - {"pm", T12_PM, TWTIME, TM_12}, - {"noon", T12_NOON, TWTIME, TM_12}, - {"midnight", T12_MIDNIGHT, TWTIME, TM_12}, - {0, 0, 0, 0} /* Zero entry to terminate searches */ +/* Lookup tables for names of months, weekdays, time zones. */ + +#define NAME_LENGTH_MAXIMUM 4 + +struct name_val { + char name[NAME_LENGTH_MAXIMUM]; + int val; }; -struct token { - char const *tcp;/* pointer to string */ - int tcnt; /* # chars */ - char tbrk; /* "break" char */ - char tbrkl; /* last break char */ - char tflg; /* 0 = alpha, 1 = numeric */ - union { /* Resulting value; */ - int tnum;/* either a #, or */ - struct tmwent const *ttmw;/* a ptr to a tmwent. */ - } tval; + +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 tmwent const*ptmatchstr P((char const*,int,struct tmwent const*)); -static int pt12hack P((struct tm *,int)); -static int ptitoken P((struct token *)); -static int ptstash P((int *,int)); -static int pttoken P((struct token *)); +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 -goodzone(t, offset, am) - register struct token const *t; - int offset; - int *am; +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. */ { - register int m; - if ( - t->tflg && - t->tcnt == 4+offset && - (m = t->tval.tnum) <= 2400 && - isdigit(t->tcp[offset]) && - (m%=100) < 60 - ) { - m += t->tval.tnum/100 * 60; - if (t->tcp[offset-1]=='+') - m = -m; - *am = m; - return 1; + 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; } - int -partime(astr, atm, zone) -char const *astr; -register struct tm *atm; -int *zone; + 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. +*/ { - register int i; - struct token btoken, atoken; - int zone_offset; /* minutes west of GMT, plus TZ_OFFSET */ - register char const *cp; - register char ch; - int ord, midnoon; - int *atmfield, dst, m; - int got1 = 0; - - atm->tm_sec = TMNULL; - atm->tm_min = TMNULL; - atm->tm_hour = TMNULL; - atm->tm_mday = TMNULL; - atm->tm_mon = TMNULL; - atm->tm_year = TMNULL; - atm->tm_wday = TMNULL; - atm->tm_yday = TMNULL; - midnoon = TMNULL; /* and our own temp stuff */ - zone_offset = TMNULL; - dst = TMNULL; - btoken.tcnt = btoken.tbrk = 0; - btoken.tcp = astr; - - for (;; got1=1) { - if (!ptitoken(&btoken)) /* Get a token */ - { if(btoken.tval.tnum) return(0); /* Read error? */ - if (given(midnoon)) /* EOF, wrap up */ - if (!pt12hack(atm, midnoon)) - return 0; - if (!given(atm->tm_min)) - atm->tm_min = 0; - *zone = - (given(zone_offset) ? zone_offset-TZ_OFFSET : 0) - - (given(dst) ? dst : 0); - return got1; - } - if(btoken.tflg == 0) /* Alpha? */ - { i = btoken.tval.ttmw->wval; - switch (btoken.tval.ttmw->wtype) { - default: + int n = 0; + char const *lim = s + digits; + while (s < lim) { + unsigned d = *s++ - '0'; + if (9 < d) return 0; - case TM_MON: - atmfield = &atm->tm_mon; - break; - case TM_WDAY: - atmfield = &atm->tm_wday; - break; - case TM_DST: - atmfield = &dst; + 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; - case TM_LT: - if (ptstash(&dst, 0)) + + default: + minutesEastOfUTC = lookup (s, zone_names); + if (minutesEastOfUTC == -1) return 0; - i = 48*60; /* local time magic number -- see maketime() */ - /* fall into */ - case TM_ZON: - i += TZ_OFFSET; - if (btoken.tval.ttmw->wflgs & TWDST) - if (ptstash(&dst, 60)) - return 0; - /* Peek ahead for offset immediately afterwards. */ + + /* 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 ( - (btoken.tbrk=='-' || btoken.tbrk=='+') && - (atoken=btoken, ++atoken.tcnt, ptitoken(&atoken)) && - goodzone(&atoken, 0, &m) + (s[0]=='D' || s[0]=='d') && + (s[1]=='S' || s[1]=='s') && + (s[2]=='T' || s[2]=='t') ) { - i += m; - btoken = atoken; + s += 3; + trailing_dst: + *zone = z + 60*60; + return (char *) s; + } + + switch (*s) { + case '-': case '+': break; + default: return (char *) s; } - atmfield = &zone_offset; - break; - case TM_12: - atmfield = &midnoon; - } - if (ptstash(atmfield, i)) - return(0); /* ERR: val already set */ - continue; - } - - /* Token is number. Lots of hairy heuristics. */ - if (!isdigit(*btoken.tcp)) { - if (!goodzone(&btoken, 1, &m)) - return 0; - zone_offset = TZ_OFFSET + m; - continue; } + sign = *s++; - i = btoken.tval.tnum; /* Value now known to be valid; get it. */ - if (btoken.tcnt == 3) /* 3 digits = HMM */ - { -hhmm4: if (ptstash(&atm->tm_min, i%100)) - return(0); /* ERR: min conflict */ - i /= 100; -hh2: if (ptstash(&atm->tm_hour, i)) - return(0); /* ERR: hour conflict */ - continue; - } - - if (4 < btoken.tcnt) - goto year4; /* far in the future */ - if(btoken.tcnt == 4) /* 4 digits = YEAR or HHMM */ - { if (given(atm->tm_year)) goto hhmm4; /* Already got yr? */ - if (given(atm->tm_hour)) goto year4; /* Already got hr? */ - if(btoken.tbrk == ':') /* HHMM:SS ? */ - if ( ptstash(&atm->tm_hour, i/100) - || ptstash(&atm->tm_min, i%100)) - return(0); /* ERR: hr/min clash */ - else goto coltm2; /* Go handle SS */ - if(btoken.tbrk != ',' && btoken.tbrk != '/' - && (atoken=btoken, ptitoken(&atoken)) /* Peek */ - && ( atoken.tflg - ? !isdigit(*atoken.tcp) - : atoken.tval.ttmw->wflgs & TWTIME)) /* HHMM-ZON */ - goto hhmm4; - goto year4; /* Give up, assume year. */ - } - - /* From this point on, assume tcnt == 1 or 2 */ - /* 2 digits = MM, DD, or HH (MM and SS caught at coltime) */ - if(btoken.tbrk == ':') /* HH:MM[:SS] */ - goto coltime; /* must be part of time. */ - if (31 < i) + if (!(s = parse_ranged (s, 2, 0, 23, &hh))) return 0; - - /* Check for numerical-format date */ - for (cp = "/-."; ch = *cp++;) - { ord = (ch == '.' ? 0 : 1); /* n/m = D/M or M/D */ - if(btoken.tbrk == ch) /* "NN-" */ - { if(btoken.tbrkl != ch) - { - atoken = btoken; - atoken.tcnt++; - if (ptitoken(&atoken) - && atoken.tflg == 0 - && atoken.tval.ttmw->wtype == TM_MON) - goto dd2; - if(ord)goto mm2; else goto dd2; /* "NN-" */ - } /* "-NN-" */ - if (!given(atm->tm_mday) - && given(atm->tm_year)) /* If "YYYY-NN-" */ - goto mm2; /* then always MM */ - if(ord)goto dd2; else goto mm2; - } - if(btoken.tbrkl == ch /* "-NN" */ - && given(ord ? atm->tm_mon : atm->tm_mday)) - if (!given(ord ? atm->tm_mday : atm->tm_mon)) /* MM/DD */ - if(ord)goto dd2; else goto mm2; - } - - /* Now reduced to choice between HH and DD */ - if (given(atm->tm_hour)) goto dd2; /* Have hour? Assume day. */ - if (given(atm->tm_mday)) goto hh2; /* Have day? Assume hour. */ - if (given(atm->tm_mon)) goto dd2; /* Have month? Assume day. */ - if(i > 24) goto dd2; /* Impossible HH means DD */ - atoken = btoken; - if (!ptitoken(&atoken)) /* Read ahead! */ - if(atoken.tval.tnum) return(0); /* ERR: bad token */ - else goto dd2; /* EOF, assume day. */ - if ( atoken.tflg - ? !isdigit(*atoken.tcp) - : atoken.tval.ttmw->wflgs & TWTIME) - /* If next token is a time spec, assume hour */ - goto hh2; /* e.g. "3 PM", "11-EDT" */ - -dd2: if (ptstash(&atm->tm_mday, i)) /* Store day (1 based) */ - return(0); - continue; - -mm2: if (ptstash(&atm->tm_mon, i-1)) /* Store month (make zero based) */ - return(0); - continue; - -year4: if ((i-=1900) < 0 || ptstash(&atm->tm_year, i)) /* Store year-1900 */ - return(0); /* ERR: year conflict */ - continue; - - /* Hack HH:MM[[:]SS] */ -coltime: - if (ptstash(&atm->tm_hour, i)) return 0; - if (!ptitoken(&btoken)) - return(!btoken.tval.tnum); - if(!btoken.tflg) return(0); /* ERR: HH:<alpha> */ - if(btoken.tcnt == 4) /* MMSS */ - if (ptstash(&atm->tm_min, btoken.tval.tnum/100) - || ptstash(&atm->tm_sec, btoken.tval.tnum%100)) - return(0); - else continue; - if(btoken.tcnt != 2 - || ptstash(&atm->tm_min, btoken.tval.tnum)) - return(0); /* ERR: MM bad */ - if (btoken.tbrk != ':') continue; /* Seconds follow? */ -coltm2: if (!ptitoken(&btoken)) - return(!btoken.tval.tnum); - if(!btoken.tflg || btoken.tcnt != 2 /* Verify SS */ - || ptstash(&atm->tm_sec, btoken.tval.tnum)) - return(0); /* ERR: SS bad */ - } -} - -/* Store date/time value, return 0 if successful. - * Fail if entry is already set. - */ - static int -ptstash(adr,val) -int *adr; -int val; -{ register int *a; - if (given(*(a=adr))) - return 1; - *a = val; - return(0); -} - -/* This subroutine is invoked for AM, PM, NOON and MIDNIGHT when wrapping up - * just prior to returning from partime. - */ - static int -pt12hack(tm, aval) -register struct tm *tm; -register int aval; -{ register int h = tm->tm_hour; - switch (aval) { - case T12_AM: - case T12_PM: - if (h > 12) - return 0; - if (h == 12) - tm->tm_hour = 0; - if (aval == T12_PM) - tm->tm_hour += 12; - break; - default: - if (0 < tm->tm_min || 0 < tm->tm_sec) - return 0; - if (!given(h) || h==12) - tm->tm_hour = aval; - else if (aval==T12_MIDNIGHT && (h==0 || h==24)) + 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; + } } - return 1; + 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; } -/* Get a token and identify it to some degree. - * Returns 0 on failure; token.tval will be 0 for normal EOF, otherwise - * hit error of some sort - */ - - static int -ptitoken(tkp) -register struct token *tkp; + 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. +*/ { - register char const *cp; - register int i, j, k; - - if (!pttoken(tkp)) -#ifdef DEBUG - { - VOID printf("EOF\n"); - return(0); - } -#else - return(0); -#endif - cp = tkp->tcp; + switch (c) { + case '$': /* The next character must be a non-digit. */ + if (isdigit ((unsigned char) *s)) + return 0; + break; -#ifdef DEBUG - VOID printf("Token: \"%.*s\" ", tkp->tcnt, cp); -#endif + case '-': case '/': case ':': + /* These characters stand for themselves. */ + if (*s++ != c) + return 0; + break; - if (tkp->tflg) { - i = tkp->tcnt; - if (*cp == '+' || *cp == '-') { - cp++; - i--; - } - while (0 <= --i) { - j = tkp->tval.tnum*10; - k = j + (*cp++ - '0'); - if (j/10 != tkp->tval.tnum || k < j) { - /* arithmetic overflow */ - tkp->tval.tnum = 1; + 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; } - tkp->tval.tnum = k; - } - } else if (!(tkp->tval.ttmw = ptmatchstr(cp, tkp->tcnt, tmwords))) - { -#ifdef DEBUG - VOID printf("Not found!\n"); -#endif - tkp->tval.tnum = 1; - return 0; - } + break; -#ifdef DEBUG - if(tkp->tflg) - VOID printf("Val: %d.\n",tkp->tval.tnum); - else VOID printf("Found: \"%s\", val: %d, type %d\n", - tkp->tval.ttmw->went,tkp->tval.ttmw->wval,tkp->tval.ttmw->wtype); -#endif + 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; - return(1); + 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; } -/* Read token from input string into token structure */ static int -pttoken(tkp) -register struct token *tkp; +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. +*/ { - register char const *cp; - register int c; - char const *astr; - - tkp->tcp = astr = cp = tkp->tcp + tkp->tcnt; - tkp->tbrkl = tkp->tbrk; /* Set "last break" */ - tkp->tcnt = tkp->tbrk = tkp->tflg = 0; - tkp->tval.tnum = 0; - - while(c = *cp++) - { switch(c) - { case ' ': case '\t': /* Flush all whitespace */ - case '\r': case '\n': - case '\v': case '\f': - if (!tkp->tcnt) { /* If no token yet */ - tkp->tcp = cp; /* ignore the brk */ - continue; /* and go on. */ - } - /* fall into */ - case '(': case ')': /* Perhaps any non-alphanum */ - case '-': case ',': /* shd qualify as break? */ - case '+': - case '/': case ':': case '.': /* Break chars */ - if(tkp->tcnt == 0) /* If no token yet */ - { tkp->tcp = cp; /* ignore the brk */ - tkp->tbrkl = c; - continue; /* and go on. */ - } - tkp->tbrk = c; - return(tkp->tcnt); - } - if (!tkp->tcnt++) { /* If first char of token, */ - if (isdigit(c)) { - tkp->tflg = 1; - if (astr<cp-2 && (cp[-2]=='-'||cp[-2]=='+')) { - /* timezone is break+sign+digit */ - tkp->tcp--; - tkp->tcnt++; - } - } - } else if ((isdigit(c)!=0) != tkp->tflg) { /* else check type */ - tkp->tbrk = c; - return --tkp->tcnt; /* Wrong type, back up */ - } - } - return(tkp->tcnt); /* When hit EOF */ +# 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; } - - static struct tmwent const * -ptmatchstr(astr,cnt,astruc) - char const *astr; - int cnt; - struct tmwent const *astruc; + 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. +*/ { - register char const *cp, *mp; - register int c; - struct tmwent const *lastptr; - int i; - - lastptr = 0; - for(;mp = astruc->went; astruc += 1) - { cp = astr; - for(i = cnt; i > 0; i--) - { - switch (*cp++ - (c = *mp++)) - { case 0: continue; /* Exact match */ - case 'A'-'a': - if (ctab[c] == Letter) - continue; - } - break; - } - if(i==0) - if (!*mp) return astruc; /* Exact match */ - else if(lastptr) return(0); /* Ambiguous */ - else lastptr = astruc; /* 1st ambig */ - } - return lastptr; + 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; } diff --git a/gnu/usr.bin/rcs/lib/partime.h b/gnu/usr.bin/rcs/lib/partime.h new file mode 100644 index 0000000..5d3983f --- /dev/null +++ b/gnu/usr.bin/rcs/lib/partime.h @@ -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 *)); diff --git a/gnu/usr.bin/rcs/lib/rcsbase.h b/gnu/usr.bin/rcs/lib/rcsbase.h index c0904bb..147f7d7 100644 --- a/gnu/usr.bin/rcs/lib/rcsbase.h +++ b/gnu/usr.bin/rcs/lib/rcsbase.h @@ -1,11 +1,9 @@ +/* RCS common definitions and data structures */ -/* - * RCS common definitions and data structures - */ -#define RCSBASE "$Id: rcsbase.h,v 5.11 1991/10/07 17:32:46 eggert Exp $" +#define RCSBASE "$Id: rcsbase.h,v 5.20 1995/06/16 06:19:24 eggert Exp $" -/* Copyright (C) 1982, 1988, 1989 Walter Tichy - Copyright 1990, 1991 by Paul Eggert +/* 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. @@ -21,8 +19,9 @@ 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, 675 Mass Ave, Cambridge, MA 02139, USA. +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: @@ -30,19 +29,55 @@ Report problems and direct all questions to: */ - - -/***************************************************************************** - * INSTRUCTIONS: - * ============= - * See the Makefile for how to define C preprocessor symbols. - * If you need to change the comment leaders, update the table comtable[] - * in rcsfnms.c. (This can wait until you know what a comment leader is.) - ***************************************************************************** - */ - - -/* $Log: rcsbase.h,v $ +/* + * $Log: rcsbase.h,v $ + * 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. * @@ -156,10 +191,10 @@ Report problems and direct all questions to: #define EXIT_TROUBLE DIFF_TROUBLE -#ifdef PATH_MAX -# define SIZEABLE_PATH PATH_MAX /* size of a large path; not a hard limit */ -#else +#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 */ @@ -188,8 +223,7 @@ Report problems and direct all questions to: /* used in production environments. */ #define yearlength 16 /* (good through AD 9,999,999,999,999,999) */ -#define datesize (yearlength+16) /* size of output of DATEFORM */ -#define joinlength 20 /* number of joined revisions permitted */ +#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 */ @@ -199,7 +233,6 @@ Report problems and direct all questions to: #define true 1 #define false 0 -#define nil 0 /* @@ -208,68 +241,79 @@ Report problems and direct all questions to: * 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 - * Ifileno, Irewind, Iseek, Itell - analogs to stdio routines + * 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 { + typedef struct RILE { Iptr_type ptr, lim; - unsigned char *base; /* for lint, not Iptr_type even if has_mmap */ -# if has_mmap -# define Ifileno(f) ((f)->fd) - int fd; + unsigned char *base; /* not Iptr_type for lint's sake */ + unsigned char *readlim; + int fd; +# if maps_memory + void (*deallocate) P((struct RILE *)); # else -# define Ifileno(f) fileno((f)->stream) FILE *stream; - unsigned char *readlim; # endif } RILE; -# if has_mmap +# 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++ +# 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++ +# 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 Itell(f) ((f)->ptr) -# define Iseek(f,p) ((f)->ptr = (p)) -# define Irewind(f) Iseek(f, (f)->base) -# define cachetell() 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))<0){testIerror(f);if(feof(f))s}else -# define cachegeteof(c,s) Igeteof(ptr,c,s) -# define Iget(f,c) if (((c)=getc(f))<0) testIeof(f); else -# define cacheget(c) Iget(ptr,c) +# 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)<0) testOerror(o); else +#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 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)&~(S_IWUSR|S_IWGRP|S_IWOTH) | ((writable)?S_IWUSR:0)) +#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 */ @@ -286,7 +330,7 @@ enum tokens { * there should be no overlap among SDELIM, KDELIM, and VDELIM */ -#define isdigit(c) ((unsigned)((c)-'0') <= 9) /* faster than ctab[c]==DIGIT */ +#define isdigit(c) (((unsigned)(c)-'0') <= 9) /* faster than ctab[c]==DIGIT */ @@ -313,13 +357,15 @@ struct hshentry { 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 of revision */ + 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 */ - unsigned long insertlns;/* lines inserted (computed by rlog) */ - unsigned long deletelns;/* lines deleted (computed by rlog) */ + long insertlns;/* lines inserted (computed by rlog) */ + long deletelns;/* lines deleted (computed by rlog) */ char selector; /* true if selected, false if deleted */ }; @@ -342,10 +388,10 @@ struct access { }; /* list element for locks */ -struct lock { +struct rcslock { char const * login; struct hshentry * delta; - struct lock * nextlock; + struct rcslock * nextlock; }; /* list element for symbolic names */ @@ -358,12 +404,12 @@ struct assoc { #define mainArgs (argc,argv) int argc; char **argv; -#if lint +#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(name,cmd,rcsid) char const copyright[] = "Copyright 1982,1988,1989 by Walter F. Tichy\nPurdue CS\nCopyright 1990,1991 by Paul Eggert", rcsbaseId[] = RCSBASE, cmdid[] = cmd; libId(name,rcsid) int main mainArgs +# 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 /* @@ -376,6 +422,7 @@ struct assoc { #define IDH "Id" #define LOCKER "Locker" #define LOG "Log" +#define NAME "Name" #define RCSFILE "RCSfile" #define REVISION "Revision" #define SOURCE "Source" @@ -383,7 +430,7 @@ struct assoc { #define keylength 8 /* max length of any of the above keywords */ enum markers { Nomatch, Author, Date, Header, Id, - Locker, Log, RCSfile, Revision, Source, State }; + Locker, Log, Name, RCSfile, Revision, Source, State }; /* This must be in the same order as rcskeys.c's Keyword[] array. */ #define DELNUMFORM "\n\n%s\n%s\n" @@ -393,39 +440,31 @@ enum markers { Nomatch, Author, Date, Header, Id, /* main program */ extern char const cmdid[]; -exiting void exiterr P((void)); - -/* maketime */ -int setfiledate P((char const*,char const[datesize])); -void str2date P((char const*,char[datesize])); -void time2date P((time_t,char[datesize])); +void exiterr P((void)) exiting; /* merge */ -int merge P((int,char const*const[2],char const*const[3])); - -/* partime */ -int partime P((char const*,struct tm*,int*)); +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 *resultfile; +extern char const *resultname; extern char const ciklog[ciklogsize]; extern int locker_expansion; -extern struct buf dirtfname[]; -#define newRCSfilename (dirtfname[0].string) RILE *rcswriteopen P((struct buf*,struct stat*,int)); -char const *makedirtemp P((char const*,int)); +char const *makedirtemp P((int)); char const *getcaller P((void)); -int addlock P((struct hshentry*)); +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*,mode_t)); -int donerewrite P((int)); +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 expandline P((RILE*,FILE*,struct hshentry const*,int,FILE*,int)); int findlock P((int,struct hshentry**)); -void aflush P((FILE*)); +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)); @@ -450,20 +489,21 @@ void xpandstring P((struct hshentry const*)); int rcsfcmp P((RILE*,struct stat const*,char const*,struct hshentry const*)); /* rcsfnms */ -#define bufautobegin(b) ((void) ((b)->string = 0, (b)->size = 0)) +#define bufautobegin(b) clear_buf(b) +#define clear_buf(b) (VOID ((b)->string = 0, (b)->size = 0)) extern FILE *workstdout; -extern char *workfilename; -extern char const *RCSfilename; +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 *basename P((char const*)); +char const *basefilename P((char const*)); char const *getfullRCSname P((void)); char const *maketemp P((int)); char const *rcssuffix P((char const*)); -int pairfilenames P((int,char**,RILE*(*)P((struct buf*,struct stat*,int)),int,int)); -size_t dirlen 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*)); @@ -477,15 +517,17 @@ 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*,...)); +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, prevrev, prevstate; +extern struct buf prevauthor, prevdate, prevname, prevrev, prevstate; int getoldkeys P((RILE*)); /* rcskeys */ @@ -502,16 +544,19 @@ extern int hshenter; extern int nerror; extern int nextc; extern int quietflag; -extern unsigned long rcsline; +extern long rcsline; char const *getid P((void)); -exiting void efaterror P((char const*)); -exiting void enfaterror P((int,char const*)); -exiting void faterror P((char const*,...)); -exiting void fatserror P((char const*,...)); -exiting void Ieof P((void)); -exiting void Ierror P((void)); -exiting void Oerror 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)); @@ -522,16 +567,19 @@ 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*,...)); +void aprintf P((FILE*,char const*,...)) printf_string(2,3); void aputs P((char const*,FILE*)); void checksid P((char*)); -void diagnose P((char const*,...)); +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*,...)); +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*)); @@ -540,10 +588,14 @@ 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*,...)); +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) @@ -551,7 +603,7 @@ void warnignore P((void)); # define advise_access(f,advice) # define if_advise_access(p,f,advice) #endif -#if has_mmap && large_memory +#if large_memory && maps_memory RILE *I_open P((char const*,struct stat*)); # define Iopen(f,m,s) I_open(f,s) #else @@ -563,18 +615,20 @@ void warnignore P((void)); #endif /* rcsmap */ -extern const enum tokens ctab[]; +extern enum tokens const ctab[]; /* rcsrev */ -char *partialno P((struct buf*,char const*,unsigned)); +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*,unsigned)); -int compartial P((char const*,char const*,unsigned)); +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**)); -unsigned countnumflds P((char const*)); +int countnumflds P((char const*)); void getbranchno P((char const*,struct buf*)); /* rcssyn */ @@ -584,8 +638,12 @@ void getbranchno P((char const*,struct buf*)); #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 { - unsigned long + long line1, /* number of first line */ nlines, /* number of lines affected */ adprev, /* previous 'a' line1+1 or 'd' line1 */ @@ -595,45 +653,55 @@ extern char const * Dbranch; extern struct access * AccessList; extern struct assoc * Symbols; extern struct cbuf Comment; -extern struct lock * Locks; +extern struct cbuf Ignored; +extern struct rcslock *Locks; extern struct hshentry * Head; extern int Expand; extern int StrictLocks; -extern unsigned TotalDeltas; +extern int TotalDeltas; extern char const *const expand_names[]; -extern char const Kdesc[]; -extern char const Klog[]; -extern char const Ktext[]; +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 putdftext P((char const*,struct cbuf,RILE*,FILE*,int)); -int putdtext P((char const*,struct cbuf,char const*,FILE*,int)); int str2expmode P((char const*)); void getadmin P((void)); void getdesc P((int)); void gettree P((void)); -void ignorephrase P((void)); +void ignorephrases P((char const*)); void initdiffcmd P((struct diffcmd*)); -void putadmin P((FILE*)); +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 *date2str P((char const[datesize],char[datesize])); char const *getusername P((int)); +int fdSafer P((int)); int getRCSINIT P((int,char**,char***)); -int run P((char const*,char const*,...)); -int runv P((char const**)); +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 lint +#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) @@ -645,6 +713,7 @@ malloc_type testrealloc P((malloc_type,size_t)); # 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)); @@ -659,6 +728,14 @@ void setRCSversion P((char const*)); # 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()) @@ -675,3 +752,6 @@ void setRCSversion P((char const*)); # define seteid() # define setrid() #endif + +/* version */ +extern char const RCS_version_string[]; diff --git a/gnu/usr.bin/rcs/lib/rcsedit.c b/gnu/usr.bin/rcs/lib/rcsedit.c index fab4f62..6000a68 100644 --- a/gnu/usr.bin/rcs/lib/rcsedit.c +++ b/gnu/usr.bin/rcs/lib/rcsedit.c @@ -1,15 +1,14 @@ -/* - * RCS stream editor - */ -/********************************************************************************** +/* RCS stream editor */ + +/****************************************************************************** * edits the input file according to a * script from stdin, generated by diff -n * performs keyword expansion - ********************************************************************************** + ****************************************************************************** */ -/* Copyright (C) 1982, 1988, 1989 Walter Tichy - Copyright 1990, 1991 by Paul Eggert +/* 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. @@ -25,8 +24,9 @@ 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, 675 Mass Ave, Cambridge, MA 02139, USA. +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: @@ -34,8 +34,56 @@ Report problems and direct all questions to: */ - -/* $Log: rcsedit.c,v $ +/* + * $Log: rcsedit.c,v $ + * Revision 5.19 1995/06/16 06:19:24 eggert + * Update FSF address. + * + * Revision 5.18 1995/06/01 16:23:43 eggert + * (dirtpname): No longer external. + * (do_link): Simplify logic. + * (finisheditline, finishedit): Replace Iseek/Itell with what they stand for. + * (fopen_update_truncate): Replace `#if' with `if'. + * (keyreplace, makedirtemp): dirlen(x) -> basefilename(x)-x. + * + * (edit_string): Fix bug: if !large_memory, a bogus trailing `@' was output + * at the end of incomplete lines. + * + * (keyreplace): Do not assume that seeking backwards + * at the start of a file will fail; on some systems it succeeds. + * Convert C- and Pascal-style comment starts to ` *' in comment leader. + * + * (rcswriteopen): Use fdSafer to get safer file descriptor. + * Open RCS file with FOPEN_RB. + * + * (chnamemod): Work around bad_NFS_rename bug; don't ignore un_link result. + * Fall back on chmod if fchmod fails, since it might be ENOSYS. + * + * (aflush): Move to rcslex.c. + * + * Revision 5.17 1994/03/20 04:52:58 eggert + * Normally calculate the $Log prefix from context, not from RCS file. + * Move setmtime here from rcsutil.c. Add ORCSerror. Remove lint. + * + * Revision 5.16 1993/11/03 17:42:27 eggert + * Add -z. Add Name keyword. If bad_unlink, ignore errno when unlink fails. + * Escape white space, $, and \ in keyword string file names. + * Don't output 2 spaces between date and time after Log. + * + * Revision 5.15 1992/07/28 16:12:44 eggert + * Some hosts have readlink but not ELOOP. Avoid `unsigned'. + * Preserve dates more systematically. Statement macro names now end in _. + * + * Revision 5.14 1992/02/17 23:02:24 eggert + * Add -T support. + * + * Revision 5.13 1992/01/24 18:44:19 eggert + * Add support for bad_chmod_close, bad_creat0. + * + * Revision 5.12 1992/01/06 02:42:34 eggert + * Add setmode parameter to chnamemod. addsymbol now reports changes. + * while (E) ; -> while (E) continue; + * * Revision 5.11 1991/11/03 01:11:44 eggert * Move the warning about link breaking to where they're actually being broken. * @@ -154,27 +202,36 @@ Report problems and direct all questions to: #include "rcsbase.h" -libId(editId, "$Id: rcsedit.c,v 5.11 1991/11/03 01:11:44 eggert Exp $") - -static void keyreplace P((enum markers,struct hshentry const*,FILE*)); +libId(editId, "$Id: rcsedit.c,v 5.19 1995/06/16 06:19:24 eggert Exp $") +static void editEndsPrematurely P((void)) exiting; +static void editLineNumberOverflow P((void)) exiting; +static void escape_string P((FILE*,char const*)); +static void keyreplace P((enum markers,struct hshentry const*,int,RILE*,FILE*,int)); FILE *fcopy; /* result file descriptor */ -char const *resultfile; /* result file name */ +char const *resultname; /* result pathname */ int locker_expansion; /* should the locker name be appended to Id val? */ #if !large_memory static RILE *fedit; /* edit file descriptor */ - static char const *editfile; /* edit pathname */ + static char const *editname; /* edit pathname */ #endif -static unsigned long editline; /* edit line counter; #lines before cursor */ +static long editline; /* edit line counter; #lines before cursor */ static long linecorr; /* #adds - #deletes in each edit run. */ /*used to correct editline in case file is not rewound after */ /* applying one delta */ -#define DIRTEMPNAMES 2 +/* indexes into dirtpname */ +#define lockdirtp_index 0 +#define newRCSdirtp_index bad_creat0 +#define newworkdirtp_index (newRCSdirtp_index+1) +#define DIRTEMPNAMES (newworkdirtp_index + 1) + enum maker {notmade, real, effective}; -struct buf dirtfname[DIRTEMPNAMES]; /* unlink these when done */ -static enum maker volatile dirtfmaker[DIRTEMPNAMES]; /* if these are set */ +static struct buf dirtpname[DIRTEMPNAMES]; /* unlink these when done */ +static enum maker volatile dirtpmaker[DIRTEMPNAMES]; /* if these are set */ +#define lockname (dirtpname[lockdirtp_index].string) +#define newRCSname (dirtpname[newRCSdirtp_index].string) #if has_NFS || bad_unlink @@ -187,17 +244,19 @@ un_link(s) */ { # if bad_unlink - int e; if (unlink(s) == 0) return 0; - e = errno; -# if has_NFS - if (e == ENOENT) - return 0; -# endif - if (chmod(s, S_IWUSR) != 0) { - errno = e; - return -1; + else { + int e = errno; + /* + * Forge ahead even if errno == ENOENT; some completely + * brain-damaged hosts (e.g. PCTCP 2.2) yield ENOENT + * even for existing unwritable files. + */ + if (chmod(s, S_IWUSR) != 0) { + errno = e; + return -1; + } } # endif # if has_NFS @@ -212,38 +271,37 @@ un_link(s) # if !has_NFS # define do_link(s,t) link(s,t) # else + static int do_link P((char const*,char const*)); static int do_link(s, t) char const *s, *t; /* Link S to T, ignoring bogus EEXIST problems due to NFS failures. */ { - struct stat sb, tb; - - if (link(s,t) == 0) - return 0; - if (errno != EEXIST) - return -1; - if ( - stat(s, &sb) == 0 && - stat(t, &tb) == 0 && - sb.st_ino == tb.st_ino && - sb.st_dev == tb.st_dev - ) - return 0; - errno = EEXIST; - return -1; + int r = link(s, t); + + if (r != 0 && errno == EEXIST) { + struct stat sb, tb; + if ( + stat(s, &sb) == 0 && + stat(t, &tb) == 0 && + same_file(sb, tb, 0) + ) + r = 0; + errno = EEXIST; + } + return r; } # endif #endif - static exiting void + static void editEndsPrematurely() { fatserror("edit script ends prematurely"); } - static exiting void + static void editLineNumberOverflow() { fatserror("edit script refers to line past end of file"); @@ -255,11 +313,12 @@ editLineNumberOverflow() #if has_memmove # define movelines(s1, s2, n) VOID memmove(s1, s2, (n)*sizeof(Iptr_type)) #else + static void movelines P((Iptr_type*,Iptr_type const*,long)); static void movelines(s1, s2, n) register Iptr_type *s1; register Iptr_type const *s2; - register unsigned long n; + register long n; { if (s1 < s2) do { @@ -275,22 +334,26 @@ movelines(s1, s2, n) } #endif +static void deletelines P((long,long)); +static void finisheditline P((RILE*,FILE*,Iptr_type,struct hshentry const*)); +static void insertline P((long,Iptr_type)); +static void snapshotline P((FILE*,Iptr_type)); + /* * `line' contains pointers to the lines in the currently `edited' file. * It is a 0-origin array that represents linelim-gapsize lines. - * line[0..gap-1] and line[gap+gapsize..linelim-1] contain pointers to lines. - * line[gap..gap+gapsize-1] contains garbage. + * line[0 .. gap-1] and line[gap+gapsize .. linelim-1] hold pointers to lines. + * line[gap .. gap+gapsize-1] contains garbage. * * Any @s in lines are duplicated. * Lines are terminated by \n, or (for a last partial line only) by single @. */ static Iptr_type *line; -static unsigned long gap, gapsize, linelim; - +static size_t gap, gapsize, linelim; static void insertline(n, l) - unsigned long n; + long n; Iptr_type l; /* Before line N, insert line L. N is 0-origin. */ { @@ -316,10 +379,10 @@ insertline(n, l) static void deletelines(n, nlines) - unsigned long n, nlines; + long n, nlines; /* Delete lines N through N+NLINES-1. N is 0-origin. */ { - unsigned long l = n + nlines; + long l = n + nlines; if (linelim-gapsize < l || l < n) editLineNumberOverflow(); if (l < gap) @@ -340,7 +403,7 @@ snapshotline(f, l) do { if ((c = *l++) == SDELIM && *l++ != SDELIM) return; - aputc(c, f); + aputc_(c, f) } while (c != '\n'); } @@ -363,8 +426,8 @@ finisheditline(fin, fout, l, delta) Iptr_type l; struct hshentry const *delta; { - Iseek(fin, l); - if (expandline(fin, fout, delta, true, (FILE*)0) < 0) + fin->ptr = l; + if (expandline(fin, fout, delta, true, (FILE*)0, true) < 0) faterror("finisheditline internal error"); } @@ -386,28 +449,27 @@ finishedit(delta, outfile, done) else { register Iptr_type *p, *lim, *l = line; register RILE *fin = finptr; - Iptr_type here = Itell(fin); + Iptr_type here = fin->ptr; for (p=l, lim=l+gap; p<lim; ) finisheditline(fin, outfile, *p++, delta); for (p+=gapsize, lim=l+linelim; p<lim; ) finisheditline(fin, outfile, *p++, delta); - Iseek(fin, here); + fin->ptr = here; } } } -/* Open a temporary FILENAME for output, truncating any previous contents. */ -# define fopen_update_truncate(filename) fopen(filename, FOPEN_W_WORK) +/* Open a temporary NAME for output, truncating any previous contents. */ +# define fopen_update_truncate(name) fopenSafer(name, FOPEN_W_WORK) #else /* !large_memory */ + static FILE * fopen_update_truncate P((char const*)); static FILE * -fopen_update_truncate(filename) - char const *filename; +fopen_update_truncate(name) + char const *name; { -# if bad_fopen_wplus - if (un_link(filename) != 0) - efaterror(filename); -# endif - return fopen(filename, FOPEN_WPLUS_WORK); + if (bad_fopen_wplus && un_link(name) != 0) + efaterror(name); + return fopenSafer(name, FOPEN_WPLUS_WORK); } #endif @@ -417,31 +479,31 @@ openfcopy(f) FILE *f; { if (!(fcopy = f)) { - if (!resultfile) - resultfile = maketemp(2); - if (!(fcopy = fopen_update_truncate(resultfile))) - efaterror(resultfile); + if (!resultname) + resultname = maketemp(2); + if (!(fcopy = fopen_update_truncate(resultname))) + efaterror(resultname); } } #if !large_memory + static void swapeditfiles P((FILE*)); static void swapeditfiles(outfile) FILE *outfile; -/* Function: swaps resultfile and editfile, assigns fedit=fcopy, +/* Function: swaps resultname and editname, assigns fedit=fcopy, * and rewinds fedit for reading. Set fcopy to outfile if nonnull; - * otherwise, set fcopy to be resultfile opened for reading and writing. + * otherwise, set fcopy to be resultname opened for reading and writing. */ { char const *tmpptr; editline = 0; linecorr = 0; - if (fseek(fcopy, 0L, SEEK_SET) != 0) - Oerror(); + Orewind(fcopy); fedit = fcopy; - tmpptr=editfile; editfile=resultfile; resultfile=tmpptr; + tmpptr=editname; editname=resultname; resultname=tmpptr; openfcopy(outfile); } @@ -450,7 +512,7 @@ snapshotedit(f) FILE *f; /* Copy the current state of the edits to F. */ { - finishedit((struct hshentry *)nil, (FILE*)0, false); + finishedit((struct hshentry *)0, (FILE*)0, false); fastcopy(fedit, f); Irewind(fedit); } @@ -461,7 +523,7 @@ finishedit(delta, outfile, done) FILE *outfile; int done; /* copy the rest of the edit file and close it (if it exists). - * if delta!=nil, perform keyword substitution at the same time. + * if delta, perform keyword substitution at the same time. * If DONE is set, we are finishing the last pass. */ { @@ -471,8 +533,8 @@ finishedit(delta, outfile, done) fe = fedit; if (fe) { fc = fcopy; - if (delta!=nil) { - while (1 < expandline(fe,fc,delta,false,(FILE*)0)) + if (delta) { + while (1 < expandline(fe,fc,delta,false,(FILE*)0,true)) ; } else { fastcopy(fe,fc); @@ -489,13 +551,14 @@ finishedit(delta, outfile, done) #if large_memory # define copylines(upto,delta) (editline = (upto)) #else + static void copylines P((long,struct hshentry const*)); static void -copylines(upto,delta) - register unsigned long upto; +copylines(upto, delta) + register long upto; struct hshentry const *delta; /* * Copy input lines editline+1..upto from fedit to fcopy. - * If delta != nil, keyword expansion is done simultaneously. + * If delta, keyword expansion is done simultaneously. * editline is updated. Rewinds a file only if necessary. */ { @@ -506,7 +569,7 @@ copylines(upto,delta) if (upto < editline) { /* swap files */ - finishedit((struct hshentry *)nil, (FILE*)0, false); + finishedit((struct hshentry *)0, (FILE*)0, false); /* assumes edit only during last pass, from the beginning*/ } fe = fedit; @@ -514,15 +577,15 @@ copylines(upto,delta) if (editline < upto) if (delta) do { - if (expandline(fe,fc,delta,false,(FILE*)0) <= 1) - editLineNumberOverflow(); + if (expandline(fe,fc,delta,false,(FILE*)0,true) <= 1) + editLineNumberOverflow(); } while (++editline < upto); else { setupcache(fe); cache(fe); do { do { - cachegeteof(c, editLineNumberOverflow();); - aputc(c, fc); + cachegeteof_(c, editLineNumberOverflow();) + aputc_(c, fc) } while (c != '\n'); } while (++editline < upto); uncache(fe); @@ -541,8 +604,8 @@ xpandstring(delta) * If foutptr is nonnull, the string is also copied unchanged to foutptr. */ { - while (1 < expandline(finptr,fcopy,delta,true,foutptr)) - ; + while (1 < expandline(finptr,fcopy,delta,true,foutptr,true)) + continue; } @@ -566,7 +629,7 @@ copystring() fcop = fcopy; amidline = false; for (;;) { - GETC(frew,c); + GETC_(frew,c) switch (c) { case '\n': ++editline; @@ -574,7 +637,7 @@ copystring() amidline = false; break; case SDELIM: - GETC(frew,c); + GETC_(frew,c) if (c != SDELIM) { /* end of string */ nextc = c; @@ -587,7 +650,7 @@ copystring() amidline = true; break; } - aputc(c,fcop); + aputc_(c,fcop) } } @@ -597,18 +660,18 @@ enterstring() /* Like copystring, except the string is put into the edit data structure. */ { #if !large_memory - editfile = 0; + editname = 0; fedit = 0; editline = linecorr = 0; - resultfile = maketemp(1); - if (!(fcopy = fopen_update_truncate(resultfile))) - efaterror(resultfile); + resultname = maketemp(1); + if (!(fcopy = fopen_update_truncate(resultname))) + efaterror(resultname); copystring(); #else register int c; declarecache; register FILE *frew; - register unsigned long e, oe; + register long e, oe; register int amidline, oamidline; register Iptr_type optr; register RILE *fin; @@ -622,8 +685,8 @@ enterstring() frew = foutptr; amidline = false; for (;;) { - optr = cachetell(); - GETC(frew,c); + optr = cacheptr(); + GETC_(frew,c) oamidline = amidline; oe = e; switch (c) { @@ -633,7 +696,7 @@ enterstring() amidline = false; break; case SDELIM: - GETC(frew,c); + GETC_(frew,c) if (c != SDELIM) { /* end of string */ nextc = c; @@ -667,13 +730,13 @@ edit_string() * Read an edit script from finptr and applies it to the edit file. #if !large_memory * The result is written to fcopy. - * If delta!=nil, keyword expansion is performed simultaneously. + * If delta, keyword expansion is performed simultaneously. * If running out of lines in fedit, fedit and fcopy are swapped. - * editfile is the name of the file that goes with fedit. + * editname is the name of the file that goes with fedit. #endif * If foutptr is set, the edit script is also copied verbatim to foutptr. * Assumes that all these files are open. - * resultfile is the name of the file that goes with fcopy. + * resultname is the name of the file that goes with fcopy. * Assumes the next input character from finptr is the first character of * the edit script. Resets nextc on exit. */ @@ -684,13 +747,13 @@ edit_string() register FILE *frew; # if !large_memory register FILE *f; - unsigned long line_lim = ULONG_MAX; + long line_lim = LONG_MAX; register RILE *fe; # endif - register unsigned long i; + register long i; register RILE *fin; # if large_memory - register unsigned long j; + register long j; # endif struct diffcmd dc; @@ -718,12 +781,13 @@ edit_string() do { /*skip next line*/ do { - Igeteof(fe, c, { if (i!=1) editLineNumberOverflow(); line_lim = dc.dafter; break; } ); + Igeteof_(fe, c, { if (i!=1) editLineNumberOverflow(); line_lim = dc.dafter; break; } ) } while (c != '\n'); } while (--i); # endif } else { - copylines(dc.line1, delta); /*copy only; no delete*/ + /* Copy lines without deleting any. */ + copylines(dc.line1, delta); i = dc.nlines; # if large_memory j = editline+linecorr; @@ -733,7 +797,7 @@ edit_string() f = fcopy; if (delta) do { - switch (expandline(fin,f,delta,true,frew)) { + switch (expandline(fin,f,delta,true,frew,true)){ case 0: case 1: if (i==1) return; @@ -748,17 +812,12 @@ edit_string() cache(fin); do { # if large_memory - insertline(j++, cachetell()); + insertline(j++, cacheptr()); # endif for (;;) { - GETC(frew, c); -# if !large_memory - aputc(c, f); -# endif - if (c == '\n') - break; + GETC_(frew, c) if (c==SDELIM) { - GETC(frew, c); + GETC_(frew, c) if (c!=SDELIM) { if (--i) editEndsPrematurely(); @@ -767,6 +826,11 @@ edit_string() return; } } +# if !large_memory + aputc_(c, f) +# endif + if (c == '\n') + break; } ++rcsline; } while (--i); @@ -782,17 +846,18 @@ edit_string() int -expandline(infile, outfile, delta, delimstuffed, frewfile) +expandline(infile, outfile, delta, delimstuffed, frewfile, dolog) RILE *infile; FILE *outfile, *frewfile; struct hshentry const *delta; - int delimstuffed; + int delimstuffed, dolog; /* * Read a line from INFILE and write it to OUTFILE. + * Do keyword expansion with data from DELTA. * If DELIMSTUFFED is true, double SDELIM is replaced with single SDELIM. - * Keyword expansion is performed with data from delta. * If FREWFILE is set, copy the line unchanged to FREWFILE. * DELIMSTUFFED must be true if FREWFILE is set. + * Append revision history to log only if DOLOG is set. * Yields -1 if no data is copied, 0 if an incomplete line is copied, * 2 if a complete line is copied; adds 1 to yield if expansion occurred. */ @@ -815,15 +880,15 @@ expandline(infile, outfile, delta, delimstuffed, frewfile) r = -1; for (;;) { - if (ds) { - GETC(frew, c); - } else - cachegeteof(c, goto uncache_exit;); + if (ds) + GETC_(frew, c) + else + cachegeteof_(c, goto uncache_exit;) for (;;) { switch (c) { case SDELIM: if (ds) { - GETC(frew, c); + GETC_(frew, c) if (c != SDELIM) { /* end of string */ nextc=c; @@ -832,13 +897,13 @@ expandline(infile, outfile, delta, delimstuffed, frewfile) } /* fall into */ default: - aputc(c,out); + aputc_(c,out) r = 0; break; case '\n': rcsline += ds; - aputc(c,out); + aputc_(c,out) r = 2; goto uncache_exit; @@ -849,11 +914,11 @@ expandline(infile, outfile, delta, delimstuffed, frewfile) tp = keyval.string; *tp++ = KDELIM; for (;;) { - if (ds) { - GETC(frew, c); - } else - cachegeteof(c, goto keystring_eof;); - if (tp < keyval.string+keylength+1) + if (ds) + GETC_(frew, c) + else + cachegeteof_(c, goto keystring_eof;) + if (tp <= &keyval.string[keylength]) switch (ctab[c]) { case LETTER: case Letter: *tp++ = c; @@ -876,17 +941,17 @@ expandline(infile, outfile, delta, delimstuffed, frewfile) /* try to find closing KDELIM, and replace value */ tlim = keyval.string + keyval.size; for (;;) { - if (ds) { - GETC(frew, c); - } else - cachegeteof(c, goto keystring_eof;); + if (ds) + GETC_(frew, c) + else + cachegeteof_(c, goto keystring_eof;) if (c=='\n' || c==KDELIM) break; *tp++ =c; if (tlim <= tp) tp = bufenlarge(&keyval, &tlim); if (c==SDELIM && ds) { /*skip next SDELIM */ - GETC(frew, c); + GETC_(frew, c) if (c != SDELIM) { /* end of string before closing KDELIM or newline */ nextc = c; @@ -902,7 +967,9 @@ expandline(infile, outfile, delta, delimstuffed, frewfile) } } /* now put out the new keyword value */ - keyreplace(matchresult,delta,out); + uncache(infile); + keyreplace(matchresult, delta, ds, infile, out, dolog); + cache(infile); e = 1; break; } @@ -919,113 +986,228 @@ expandline(infile, outfile, delta, delimstuffed, frewfile) } + static void +escape_string(out, s) + register FILE *out; + register char const *s; +/* Output to OUT the string S, escaping chars that would break `ci -k'. */ +{ + register char c; + for (;;) + switch ((c = *s++)) { + case 0: return; + case '\t': aputs("\\t", out); break; + case '\n': aputs("\\n", out); break; + case ' ': aputs("\\040", out); break; + case KDELIM: aputs("\\044", out); break; + case '\\': if (VERSION(5)<=RCSversion) {aputs("\\\\", out); break;} + /* fall into */ + default: aputc_(c, out) break; + } +} + char const ciklog[ciklogsize] = "checked in with -k by "; static void -keyreplace(marker,delta,out) +keyreplace(marker, delta, delimstuffed, infile, out, dolog) enum markers marker; register struct hshentry const *delta; + int delimstuffed; + RILE *infile; register FILE *out; + int dolog; /* function: outputs the keyword value(s) corresponding to marker. * Attributes are derived from delta. */ { register char const *sp, *cp, *date; - register char c; + register int c; register size_t cs, cw, ls; char const *sp1; - char datebuf[datesize]; + char datebuf[datesize + zonelenmax]; int RCSv; + int exp; sp = Keyword[(int)marker]; - - if (Expand == KEY_EXPAND) { - aprintf(out, "%c%s%c", KDELIM, sp, KDELIM); - return; - } - - date= delta->date; + exp = Expand; + date = delta->date; RCSv = RCSversion; - if (Expand == KEYVAL_EXPAND || Expand == KEYVALLOCK_EXPAND) - aprintf(out, "%c%s%c%c", KDELIM, sp, VDELIM, + if (exp != VAL_EXPAND) + aprintf(out, "%c%s", KDELIM, sp); + if (exp != KEY_EXPAND) { + + if (exp != VAL_EXPAND) + aprintf(out, "%c%c", VDELIM, marker==Log && RCSv<VERSION(5) ? '\t' : ' ' ); - switch (marker) { - case Author: + switch (marker) { + case Author: aputs(delta->author, out); break; - case Date: + case Date: aputs(date2str(date,datebuf), out); break; - case Id: - case Header: - aprintf(out, "%s %s %s %s %s", - marker==Id || RCSv<VERSION(4) - ? basename(RCSfilename) - : getfullRCSname(), + case Id: + case Header: + escape_string(out, + marker==Id || RCSv<VERSION(4) + ? basefilename(RCSname) + : getfullRCSname() + ); + aprintf(out, " %s %s %s %s", delta->num, date2str(date, datebuf), delta->author, RCSv==VERSION(3) && delta->lockedby ? "Locked" : delta->state ); - if (delta->lockedby!=nil) + if (delta->lockedby) if (VERSION(5) <= RCSv) { - if (locker_expansion || Expand==KEYVALLOCK_EXPAND) + if (locker_expansion || exp==KEYVALLOCK_EXPAND) aprintf(out, " %s", delta->lockedby); } else if (RCSv == VERSION(4)) aprintf(out, " Locker: %s", delta->lockedby); break; - case Locker: + case Locker: if (delta->lockedby) if ( locker_expansion - || Expand == KEYVALLOCK_EXPAND + || exp == KEYVALLOCK_EXPAND || RCSv <= VERSION(4) ) aputs(delta->lockedby, out); break; - case Log: - case RCSfile: - aputs(basename(RCSfilename), out); + case Log: + case RCSfile: + escape_string(out, basefilename(RCSname)); break; - case Revision: + case Name: + if (delta->name) + aputs(delta->name, out); + break; + case Revision: aputs(delta->num, out); break; - case Source: - aputs(getfullRCSname(), out); + case Source: + escape_string(out, getfullRCSname()); break; - case State: + case State: aputs(delta->state, out); break; - default: + default: break; - } - if (Expand == KEYVAL_EXPAND || Expand == KEYVALLOCK_EXPAND) { + } + if (exp != VAL_EXPAND) afputc(' ', out); - afputc(KDELIM, out); } - if (marker == Log) { + if (exp != VAL_EXPAND) + afputc(KDELIM, out); + + if (marker == Log && dolog) { + struct buf leader; + sp = delta->log.string; ls = delta->log.size; if (sizeof(ciklog)-1<=ls && !memcmp(sp,ciklog,sizeof(ciklog)-1)) return; + bufautobegin(&leader); + if (RCSversion < VERSION(5)) { + cp = Comment.string; + cs = Comment.size; + } else { + int kdelim_found = 0; + Ioffset_type chars_read = Itell(infile); + declarecache; + setupcache(infile); cache(infile); + + c = 0; /* Pacify `gcc -Wall'. */ + + /* + * Back up to the start of the current input line, + * setting CS to the number of characters before `$Log'. + */ + cs = 0; + for (;;) { + if (!--chars_read) + goto done_backing_up; + cacheunget_(infile, c) + if (c == '\n') + break; + if (c == SDELIM && delimstuffed) { + if (!--chars_read) + break; + cacheunget_(infile, c) + if (c != SDELIM) { + cacheget_(c) + break; + } + } + cs += kdelim_found; + kdelim_found |= c==KDELIM; + } + cacheget_(c) + done_backing_up:; + + /* Copy characters before `$Log' into LEADER. */ + bufalloc(&leader, cs); + cp = leader.string; + for (cw = 0; cw < cs; cw++) { + leader.string[cw] = c; + if (c == SDELIM && delimstuffed) + cacheget_(c) + cacheget_(c) + } + + /* Convert traditional C or Pascal leader to ` *'. */ + for (cw = 0; cw < cs; cw++) + if (ctab[(unsigned char) cp[cw]] != SPACE) + break; + if ( + cw+1 < cs + && cp[cw+1] == '*' + && (cp[cw] == '/' || cp[cw] == '(') + ) { + size_t i = cw+1; + for (;;) + if (++i == cs) { + warn( + "`%c* $Log' is obsolescent; use ` * $Log'.", + cp[cw] + ); + leader.string[cw] = ' '; + break; + } else if (ctab[(unsigned char) cp[i]] != SPACE) + break; + } + + /* Skip `$Log ... $' string. */ + do { + cacheget_(c) + } while (c != KDELIM); + uncache(infile); + } afputc('\n', out); - cp = Comment.string; - cw = cs = Comment.size; awrite(cp, cs, out); - /* oddity: 2 spaces between date and time, not 1 as usual */ - sp1 = strchr(date2str(date,datebuf), ' '); - aprintf(out, "Revision %s %.*s %s %s", - delta->num, (int)(sp1-datebuf), datebuf, sp1, delta->author - ); + sp1 = date2str(date, datebuf); + if (VERSION(5) <= RCSv) { + aprintf(out, "Revision %s %s %s", + delta->num, sp1, delta->author + ); + } else { + /* oddity: 2 spaces between date and time, not 1 as usual */ + sp1 = strchr(sp1, ' '); + aprintf(out, "Revision %s %.*s %s %s", + delta->num, (int)(sp1-datebuf), datebuf, sp1, + delta->author + ); + } /* Do not include state: it may change and is not updated. */ - /* Comment is the comment leader. */ + cw = cs; if (VERSION(5) <= RCSv) for (; cw && (cp[cw-1]==' ' || cp[cw-1]=='\t'); --cw) - ; + continue; for (;;) { afputc('\n', out); awrite(cp, cw, out); @@ -1044,10 +1226,12 @@ keyreplace(marker,delta,out) } while (c != '\n'); } } + bufautoend(&leader); } } #if has_readlink + static int resolve_symlink P((struct buf*)); static int resolve_symlink(L) struct buf *L; @@ -1063,7 +1247,7 @@ resolve_symlink(L) size_t s; ssize_t r; struct buf bigbuf; - unsigned linkcount = MAXSYMLINKS + 1; + int linkcount = MAXSYMLINKS; b = a; s = sizeof(a); @@ -1073,21 +1257,29 @@ resolve_symlink(L) bufalloc(&bigbuf, s<<1); b = bigbuf.string; s = bigbuf.size; - } else if (!--linkcount) { + } else if (!linkcount--) { +# ifndef ELOOP + /* + * Some pedantic Posix 1003.1-1990 hosts have readlink + * but not ELOOP. Approximate ELOOP with EMLINK. + */ +# define ELOOP EMLINK +# endif errno = ELOOP; return -1; } else { /* Splice symbolic link into L. */ b[r] = '\0'; - L->string[ROOTPATH(b) ? (size_t)0 : dirlen(L->string)] = '\0'; + L->string[ + ROOTPATH(b) ? 0 : basefilename(L->string) - L->string + ] = '\0'; bufscat(L, b); } e = errno; bufautoend(&bigbuf); errno = e; switch (e) { - case ENXIO: - case EINVAL: return 1; + case readlink_isreg_errno: return 1; case ENOENT: return 0; default: return -1; } @@ -1100,24 +1292,23 @@ rcswriteopen(RCSbuf, status, mustread) struct stat *status; int mustread; /* - * Create the lock file corresponding to RCSNAME. - * Then try to open RCSNAME for reading and yield its FILE* descriptor. + * Create the lock file corresponding to RCSBUF. + * Then try to open RCSBUF for reading and yield its RILE* descriptor. * Put its status into *STATUS too. * MUSTREAD is true if the file must already exist, too. * If all goes well, discard any previously acquired locks, - * and set frewrite to the FILE* descriptor of the lock file, - * which will eventually turn into the new RCS file. + * and set fdlock to the file descriptor of the RCS lockfile. */ { register char *tp; - register char const *sp, *RCSname, *x; + register char const *sp, *RCSpath, *x; RILE *f; size_t l; - int e, exists, fdesc, previouslock, r; + int e, exists, fdesc, fdescSafer, r, waslocked; struct buf *dirt; struct stat statbuf; - previouslock = frewrite != 0; + waslocked = 0 <= fdlock; exists = # if has_readlink resolve_symlink(RCSbuf); @@ -1125,7 +1316,7 @@ rcswriteopen(RCSbuf, status, mustread) stat(RCSbuf->string, &statbuf) == 0 ? 1 : errno==ENOENT ? 0 : -1; # endif - if (exists < (mustread|previouslock)) + if (exists < (mustread|waslocked)) /* * There's an unusual problem with the RCS file; * or the RCS file doesn't exist, @@ -1133,26 +1324,26 @@ rcswriteopen(RCSbuf, status, mustread) */ return 0; - RCSname = RCSbuf->string; - sp = basename(RCSname); - l = sp - RCSname; - dirt = &dirtfname[previouslock]; - bufscpy(dirt, RCSname); + RCSpath = RCSbuf->string; + sp = basefilename(RCSpath); + l = sp - RCSpath; + dirt = &dirtpname[waslocked]; + bufscpy(dirt, RCSpath); tp = dirt->string + l; - x = rcssuffix(RCSname); + x = rcssuffix(RCSpath); # if has_readlink if (!x) { - error("symbolic link to non RCS filename `%s'", RCSname); + error("symbolic link to non RCS file `%s'", RCSpath); errno = EINVAL; return 0; } # endif if (*sp == *x) { - error("RCS filename `%s' incompatible with suffix `%s'", sp, x); + error("RCS pathname `%s' incompatible with suffix `%s'", sp, x); errno = EINVAL; return 0; } - /* Create a lock file whose name is a function of the RCS filename. */ + /* Create a lock filename that is a function of the RCS filename. */ if (*x) { /* * The suffix is nonempty. @@ -1172,38 +1363,41 @@ rcswriteopen(RCSbuf, status, mustread) * with last char replaced by '_'. */ while ((*tp++ = *sp++)) - ; + continue; tp -= 2; if (*tp == '_') { - error("RCS filename `%s' ends with `%c'", RCSname, *tp); + error("RCS pathname `%s' ends with `%c'", RCSpath, *tp); errno = EINVAL; return 0; } *tp = '_'; } - sp = tp = dirt->string; + sp = dirt->string; f = 0; /* * good news: - * open(f, O_CREAT|O_EXCL|O_TRUNC|O_WRONLY, READONLY) is atomic - * according to Posix 1003.1-1990. + * open(f, O_CREAT|O_EXCL|O_TRUNC|..., OPEN_CREAT_READONLY) + * is atomic according to Posix 1003.1-1990. * bad news: * NFS ignores O_EXCL and doesn't comply with Posix 1003.1-1990. * good news: - * (O_TRUNC,READONLY) normally guarantees atomicity even with NFS. + * (O_TRUNC,OPEN_CREAT_READONLY) normally guarantees atomicity + * even with NFS. * bad news: - * If you're root, (O_TRUNC,READONLY) doesn't guarantee atomicity. + * If you're root, (O_TRUNC,OPEN_CREAT_READONLY) doesn't + * guarantee atomicity. * good news: * Root-over-the-wire NFS access is rare for security reasons. * This bug has never been reported in practice with RCS. * So we don't worry about this bug. * * An even rarer NFS bug can occur when clients retry requests. - * Suppose client A renames the lock file ",f," to "f,v" - * at about the same time that client B creates ",f,", + * This can happen in the usual case of NFS over UDP. + * Suppose client A releases a lock by renaming ",f," to "f,v" at + * about the same time that client B obtains a lock by creating ",f,", * and suppose A's first rename request is delayed, so A reissues it. * The sequence of events might be: * A sends rename(",f,", "f,v") @@ -1216,20 +1410,20 @@ rcswriteopen(RCSbuf, status, mustread) * This not only wrongly deletes B's lock, it removes the RCS file! * Most NFS implementations have idempotency caches that usually prevent * this scenario, but such caches are finite and can be overrun. - * This problem afflicts programs that use the traditional + * This problem afflicts not only RCS, which uses open() and rename() + * to get and release locks; it also afflicts the traditional * Unix method of using link() and unlink() to get and release locks, - * as well as RCS's method of using open() and rename(). - * There is no easy workaround for either link-unlink or open-rename. + * and the less traditional method of using mkdir() and rmdir(). + * There is no easy workaround. * Any new method based on lockf() seemingly would be incompatible with * the old methods; besides, lockf() is notoriously buggy under NFS. * Since this problem afflicts scads of Unix programs, but is so rare * that nobody seems to be worried about it, we won't worry either. */ -# define READONLY (S_IRUSR|S_IRGRP|S_IROTH) # if !open_can_creat -# define create(f) creat(f, READONLY) +# define create(f) creat(f, OPEN_CREAT_READONLY) # else -# define create(f) open(f, O_BINARY|O_CREAT|O_EXCL|O_TRUNC|O_WRONLY, READONLY) +# define create(f) open(f, OPEN_O_BINARY|OPEN_O_LOCK|OPEN_O_WRONLY|O_CREAT|O_EXCL|O_TRUNC, OPEN_CREAT_READONLY) # endif catchints(); @@ -1241,34 +1435,35 @@ rcswriteopen(RCSbuf, status, mustread) */ seteid(); fdesc = create(sp); + fdescSafer = fdSafer(fdesc); /* Do it now; setrid might use stderr. */ e = errno; setrid(); - if (fdesc < 0) { - if (e == EACCES && stat(tp,&statbuf) == 0) + if (0 <= fdesc) + dirtpmaker[0] = effective; + + if (fdescSafer < 0) { + if (e == EACCES && stat(sp,&statbuf) == 0) /* The RCS file is busy. */ e = EEXIST; } else { - dirtfmaker[0] = effective; e = ENOENT; if (exists) { - f = Iopen(RCSname, FOPEN_R, status); + f = Iopen(RCSpath, FOPEN_RB, status); e = errno; - if (f && previouslock) { + if (f && waslocked) { /* Discard the previous lock in favor of this one. */ - Ozclose(&frewrite); + ORCSclose(); seteid(); - if ((r = un_link(newRCSfilename)) != 0) - e = errno; + r = un_link(lockname); + e = errno; setrid(); if (r != 0) - enfaterror(e, newRCSfilename); - bufscpy(&dirtfname[0], tp); + enfaterror(e, lockname); + bufscpy(&dirtpname[lockdirtp_index], sp); } } - if (!(frewrite = fdopen(fdesc, FOPEN_W))) { - efaterror(newRCSfilename); - } + fdlock = fdescSafer; } restoreints(); @@ -1286,33 +1481,31 @@ keepdirtemp(name) { register int i; for (i=DIRTEMPNAMES; 0<=--i; ) - if (dirtfname[i].string == name) { - dirtfmaker[i] = notmade; + if (dirtpname[i].string == name) { + dirtpmaker[i] = notmade; return; } faterror("keepdirtemp"); } char const * -makedirtemp(name, n) - register char const *name; - int n; +makedirtemp(isworkfile) + int isworkfile; /* - * Have maketemp() do all the work if name is null. - * Otherwise, create a unique filename in name's dir using n and name - * and store it into the dirtfname[n]. - * Because of storage in tfnames, dirtempunlink() can unlink the file later. - * Return a pointer to the filename created. + * Create a unique pathname and store it into dirtpname. + * Because of storage in tpnames, dirtempunlink() can unlink the file later. + * Return a pointer to the pathname created. + * If ISWORKFILE is 1, put it into the working file's directory; + * if 0, put the unique file in RCSfile's directory. */ { register char *tp, *np; register size_t dl; register struct buf *bn; + register char const *name = isworkfile ? workname : RCSname; - if (!name) - return maketemp(n); - dl = dirlen(name); - bn = &dirtfname[n]; + dl = basefilename(name) - name; + bn = &dirtpname[newRCSdirtp_index + isworkfile]; bufalloc(bn, # if has_mktemp dl + 9 @@ -1324,19 +1517,19 @@ makedirtemp(name, n) np = tp = bn->string; tp += dl; *tp++ = '_'; - *tp++ = '0'+n; + *tp++ = '0'+isworkfile; catchints(); # if has_mktemp VOID strcpy(tp, "XXXXXX"); if (!mktemp(np) || !*np) - faterror("can't make temporary file name `%.*s%c_%cXXXXXX'", - (int)dl, name, SLASH, '0'+n + faterror("can't make temporary pathname `%.*s_%cXXXXXX'", + (int)dl, name, '0'+isworkfile ); # else /* * Posix 1003.1-1990 has no reliable way * to create a unique file in a named directory. - * We fudge here. If the working file name is abcde, + * We fudge here. If the filename is abcde, * the temp filename is _Ncde where N is a digit. */ name += dl; @@ -1344,7 +1537,7 @@ makedirtemp(name, n) if (*name) name++; VOID strcpy(tp, name); # endif - dirtfmaker[n] = real; + dirtpmaker[newRCSdirtp_index + isworkfile] = real; return np; } @@ -1356,84 +1549,127 @@ dirtempunlink() enum maker m; for (i = DIRTEMPNAMES; 0 <= --i; ) - if ((m = dirtfmaker[i]) != notmade) { + if ((m = dirtpmaker[i]) != notmade) { if (m == effective) seteid(); - VOID un_link(dirtfname[i].string); + VOID un_link(dirtpname[i].string); if (m == effective) setrid(); - dirtfmaker[i] = notmade; + dirtpmaker[i] = notmade; } } int #if has_prototypes -chnamemod(FILE **fromp, char const *from, char const *to, mode_t mode) +chnamemod( + FILE **fromp, char const *from, char const *to, + int set_mode, mode_t mode, time_t mtime +) /* The `#if has_prototypes' is needed because mode_t might promote to int. */ #else - chnamemod(fromp,from,to,mode) FILE **fromp; char const *from,*to; mode_t mode; + chnamemod(fromp, from, to, set_mode, mode, mtime) + FILE **fromp; char const *from,*to; + int set_mode; mode_t mode; time_t mtime; #endif /* - * Rename a file (with optional stream pointer *FROMP) from FROM to TO. + * Rename a file (with stream pointer *FROMP) from FROM to TO. * FROM already exists. - * Change its mode to MODE, before renaming if possible. - * If FROMP, close and clear *FROMP before renaming it. + * If 0 < SET_MODE, change the mode to MODE, before renaming if possible. + * If MTIME is not -1, change its mtime to MTIME before renaming. + * Close and clear *FROMP before renaming it. * Unlink TO if it already exists. * Return -1 on error (setting errno), 0 otherwise. */ { + mode_t mode_while_renaming = mode; + int fchmod_set_mode = 0; + +# if bad_a_rename || bad_NFS_rename + struct stat st; + if (bad_NFS_rename || (bad_a_rename && set_mode <= 0)) { + if (fstat(fileno(*fromp), &st) != 0) + return -1; + if (bad_a_rename && set_mode <= 0) + mode = st.st_mode; + } +# endif + # if bad_a_rename /* - * This host is brain damaged. A race condition is possible - * while the lock file is temporarily writable. - * There doesn't seem to be a workaround. - */ - mode_t mode_while_renaming = mode|S_IWUSR; -# else -# define mode_while_renaming mode + * There's a short window of inconsistency + * during which the lock file is writable. + */ + mode_while_renaming = mode|S_IWUSR; + if (mode != mode_while_renaming) + set_mode = 1; # endif - if (fromp) { -# if has_fchmod - if (fchmod(fileno(*fromp), mode_while_renaming) != 0) - return -1; -# endif - Ozclose(fromp); - } + # if has_fchmod - else + if (0<set_mode && fchmod(fileno(*fromp),mode_while_renaming) == 0) + fchmod_set_mode = set_mode; # endif - if (chmod(from, mode_while_renaming) != 0) + /* If bad_chmod_close, we must close before chmod. */ + Ozclose(fromp); + if (fchmod_set_mode<set_mode && chmod(from, mode_while_renaming) != 0) + return -1; + + if (setmtime(from, mtime) != 0) return -1; # if !has_rename || bad_b_rename - VOID un_link(to); /* - * We need not check the result; - * link() or rename() will catch it. - * No harm is done if TO does not exist. - * However, there's a short window of inconsistency - * during which TO does not exist. - */ + * There's a short window of inconsistency + * during which TO does not exist. + */ + if (un_link(to) != 0 && errno != ENOENT) + return -1; # endif - return -# if !has_rename - do_link(from,to) != 0 ? -1 : un_link(from) -# else - rename(from, to) != 0 -# if has_NFS - && errno != ENOENT -# endif - ? -1 -# if bad_a_rename - : mode != mode_while_renaming ? chmod(to, mode) -# endif - : 0 -# endif - ; +# if has_rename + if (rename(from,to) != 0 && !(has_NFS && errno==ENOENT)) + return -1; +# else + if (do_link(from,to) != 0 || un_link(from) != 0) + return -1; +# endif -# undef mode_while_renaming +# if bad_NFS_rename + { + /* + * Check whether the rename falsely reported success. + * A race condition can occur between the rename and the stat. + */ + struct stat tostat; + if (stat(to, &tostat) != 0) + return -1; + if (! same_file(st, tostat, 0)) { + errno = EIO; + return -1; + } + } +# endif + +# if bad_a_rename + if (0 < set_mode && chmod(to, mode) != 0) + return -1; +# endif + + return 0; +} + + int +setmtime(file, mtime) + char const *file; + time_t mtime; +/* Set FILE's last modified time to MTIME, but do nothing if MTIME is -1. */ +{ + static struct utimbuf amtime; /* static so unused fields are zero */ + if (mtime == -1) + return 0; + amtime.actime = now(); + amtime.modtime = mtime; + return utime(file, &amtime); } @@ -1449,13 +1685,13 @@ findlock(delete, target) * Return 0 for no locks, 1 for one, 2 for two or more. */ { - register struct lock *next, **trail, **found; + register struct rcslock *next, **trail, **found; found = 0; for (trail = &Locks; (next = *trail); trail = &next->nextlock) if (strcmp(getcaller(), next->login) == 0) { if (found) { - error("multiple revisions locked by %s; please specify one", getcaller()); + rcserror("multiple revisions locked by %s; please specify one", getcaller()); return 2; } found = trail; @@ -1465,36 +1701,37 @@ findlock(delete, target) next = *found; *target = next->delta; if (delete) { - next->delta->lockedby = nil; + next->delta->lockedby = 0; *found = next->nextlock; } return 1; } int -addlock(delta) +addlock(delta, verbose) struct hshentry * delta; + int verbose; /* * Add a lock held by caller to DELTA and yield 1 if successful. - * Print an error message and yield -1 if no lock is added because + * Print an error message if verbose and yield -1 if no lock is added because * DELTA is locked by somebody other than caller. * Return 0 if the caller already holds the lock. */ { - register struct lock *next; + register struct rcslock *next; - next=Locks; for (next = Locks; next; next = next->nextlock) if (cmpnum(delta->num, next->delta->num) == 0) if (strcmp(getcaller(), next->login) == 0) return 0; else { - error("revision %s already locked by %s", - delta->num, next->login - ); + if (verbose) + rcserror("Revision %s is already locked by %s.", + delta->num, next->login + ); return -1; } - next = ftalloc(struct lock); + next = ftalloc(struct rcslock); delta->lockedby = next->login = getcaller(); next->delta = delta; next->nextlock = Locks; @@ -1511,28 +1748,30 @@ addsymbol(num, name, rebind) * Associate with revision NUM the new symbolic NAME. * If NAME already exists and REBIND is set, associate NAME with NUM; * otherwise, print an error message and return false; - * Return true if successful. + * Return -1 if unsuccessful, 0 if no change, 1 if change. */ { register struct assoc *next; for (next = Symbols; next; next = next->nextassoc) if (strcmp(name, next->symbol) == 0) - if (rebind || strcmp(next->num,num) == 0) { + if (strcmp(next->num,num) == 0) + return 0; + else if (rebind) { next->num = num; - return true; + return 1; } else { - error("symbolic name %s already bound to %s", + rcserror("symbolic name %s already bound to %s", name, next->num ); - return false; + return -1; } next = ftalloc(struct assoc); next->symbol = name; next->num = num; next->nextassoc = Symbols; Symbols = next; - return true; + return 1; } @@ -1568,7 +1807,7 @@ checkaccesslist() return true; } while ((next = next->nextaccess)); - error("user %s not on the access list", getcaller()); + rcserror("user %s not on the access list", getcaller()); return false; } @@ -1581,45 +1820,65 @@ dorewrite(lockflag, changed) * Prepare to rewrite an RCS file if CHANGED is positive. * Stop rewriting if CHANGED is zero, because there won't be any changes. * Fail if CHANGED is negative. - * Return true on success. + * Return 0 on success, -1 on failure. */ { - int r, e; + int r = 0, e; if (lockflag) if (changed) { if (changed < 0) - return false; - putadmin(frewrite); + return -1; + putadmin(); puttree(Head, frewrite); aprintf(frewrite, "\n\n%s%c", Kdesc, nextc); foutptr = frewrite; } else { - Ozclose(&frewrite); +# if bad_creat0 + int nr = !!frewrite, ne = 0; +# endif + ORCSclose(); seteid(); ignoreints(); - r = un_link(newRCSfilename); +# if bad_creat0 + if (nr) { + nr = un_link(newRCSname); + ne = errno; + keepdirtemp(newRCSname); + } +# endif + r = un_link(lockname); e = errno; - keepdirtemp(newRCSfilename); + keepdirtemp(lockname); restoreints(); setrid(); - if (r != 0) { - enerror(e, RCSfilename); - return false; - } + if (r != 0) + enerror(e, lockname); +# if bad_creat0 + if (nr != 0) { + enerror(ne, newRCSname); + r = -1; + } +# endif } - return true; + return r; } int -donerewrite(changed) +donerewrite(changed, newRCStime) int changed; + time_t newRCStime; /* * Finish rewriting an RCS file if CHANGED is nonzero. - * Return true on success. + * Set its mode if CHANGED is positive. + * Set its modification time to NEWRCSTIME unless it is -1. + * Return 0 on success, -1 on failure. */ { - int r, e; + int r = 0, e = 0; +# if bad_creat0 + int lr, le; +# endif if (changed && !nerror) { if (finptr) { @@ -1627,30 +1886,64 @@ donerewrite(changed) Izclose(&finptr); } if (1 < RCSstat.st_nlink) - warn("breaking hard link to %s", RCSfilename); + rcswarn("breaking hard link"); + aflush(frewrite); seteid(); ignoreints(); - r = chnamemod(&frewrite, newRCSfilename, RCSfilename, - RCSstat.st_mode & ~(S_IWUSR|S_IWGRP|S_IWOTH) + r = chnamemod( + &frewrite, newRCSname, RCSname, changed, + RCSstat.st_mode & (mode_t)~(S_IWUSR|S_IWGRP|S_IWOTH), + newRCStime ); e = errno; - keepdirtemp(newRCSfilename); + keepdirtemp(newRCSname); +# if bad_creat0 + lr = un_link(lockname); + le = errno; + keepdirtemp(lockname); +# endif restoreints(); setrid(); if (r != 0) { - enerror(e, RCSfilename); - error("saved in %s", newRCSfilename); - dirtempunlink(); - return false; + enerror(e, RCSname); + error("saved in %s", newRCSname); } +# if bad_creat0 + if (lr != 0) { + enerror(le, lockname); + r = -1; + } +# endif } - return true; + return r; } void -aflush(f) - FILE *f; +ORCSclose() +{ + if (0 <= fdlock) { + if (close(fdlock) != 0) + efaterror(lockname); + fdlock = -1; + } + Ozclose(&frewrite); +} + + void +ORCSerror() +/* +* Like ORCSclose, except we are cleaning up after an interrupt or fatal error. +* Do not report errors, since this may loop. This is needed only because +* some brain-damaged hosts (e.g. OS/2) cannot unlink files that are open, and +* some nearly-Posix hosts (e.g. NFS) work better if the files are closed first. +* This isn't a completely reliable away to work around brain-damaged hosts, +* because of the gap between actual file opening and setting frewrite etc., +* but it's better than nothing. +*/ { - if (fflush(f) != 0) - Oerror(); + if (0 <= fdlock) + VOID close(fdlock); + if (frewrite) + /* Avoid fclose, since stdio may not be reentrant. */ + VOID close(fileno(frewrite)); } diff --git a/gnu/usr.bin/rcs/lib/rcsfcmp.c b/gnu/usr.bin/rcs/lib/rcsfcmp.c index 75a6bbc..9adfa17 100644 --- a/gnu/usr.bin/rcs/lib/rcsfcmp.c +++ b/gnu/usr.bin/rcs/lib/rcsfcmp.c @@ -1,14 +1,13 @@ -/* - * RCS file comparison - */ +/* Compare working files, ignoring RCS keyword strings. */ + /***************************************************************************** * rcsfcmp() * Testprogram: define FCMPTEST ***************************************************************************** */ -/* Copyright (C) 1982, 1988, 1989 Walter Tichy - Copyright 1990, 1991 by Paul Eggert +/* 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. @@ -24,8 +23,9 @@ 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, 675 Mass Ave, Cambridge, MA 02139, USA. +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: @@ -37,7 +37,25 @@ Report problems and direct all questions to: -/* $Log: rcsfcmp.c,v $ +/* + * $Log: rcsfcmp.c,v $ + * 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. * @@ -101,8 +119,9 @@ Report problems and direct all questions to: #include "rcsbase.h" -libId(fcmpId, "$Id: rcsfcmp.c,v 5.9 1991/10/07 17:32:46 eggert Exp $") +libId(fcmpId, "$Id: rcsfcmp.c,v 5.14 1995/06/16 06:19:24 eggert Exp $") + static int discardkeyval P((int,RILE*)); static int discardkeyval(c, f) register int c; @@ -114,24 +133,24 @@ discardkeyval(c, f) case '\n': return c; default: - Igeteof(f, c, return EOF;); + Igeteof_(f, c, return EOF;) break; } } int -rcsfcmp(xfp, xstatp, ufname, delta) +rcsfcmp(xfp, xstatp, uname, delta) register RILE *xfp; struct stat const *xstatp; - char const *ufname; + char const *uname; struct hshentry const *delta; -/* Compare the files xfp and ufname. Return zero - * if xfp has the same contents as ufname and neither has keywords, +/* 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 ufname, with the keywords expanded. + * 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, @@ -145,23 +164,24 @@ rcsfcmp(xfp, xstatp, ufname, delta) 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(ufname, FOPEN_R_WORK, &ustat))) { - efaterror(ufname); + if (!(ufp = Iopen(uname, FOPEN_R_WORK, &ustat))) { + efaterror(uname); } xeof = ueof = false; - if (Expand==OLD_EXPAND) { + if (MIN_UNEXPAND <= Expand) { if (!(result = xstatp->st_size!=ustat.st_size)) { -# if has_mmap && large_memory +# 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;); + Igeteof_(xfp, xc, xeof=true;) + Igeteof_(ufp, uc, ueof=true;) if (xeof | ueof) goto eof; if (xc != uc) @@ -172,21 +192,22 @@ rcsfcmp(xfp, xstatp, ufname, delta) } 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;); + 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;); + Igeteof_(xfp, xc, xeof=true;) + Igeteof_(ufp, uc, ueof=true;) if (xeof | ueof) goto eof; if (xc != uc) @@ -221,8 +242,8 @@ rcsfcmp(xfp, xstatp, ufname, delta) } switch (xc) { default: - Igeteof(xfp, xc, xeof=true;); - Igeteof(ufp, uc, ueof=true;); + Igeteof_(xfp, xc, xeof=true;) + Igeteof_(ufp, uc, ueof=true;) if (xeof | ueof) goto eof; continue; @@ -237,38 +258,47 @@ rcsfcmp(xfp, xstatp, ufname, delta) goto return1; if (xc==KDELIM) { /* Skip closing KDELIM. */ - Igeteof(xfp, xc, xeof=true;); - Igeteof(ufp, uc, ueof=true;); + 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 */ - unsigned lncnt; + 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. */ - lncnt = 3; - while (ls--) if (*sp++=='\n') lncnt++; + /* + * 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;); + 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....$) */ - for (ccnt=Comment.size; ccnt--; ) { - Igeteof(xfp, xc, goto returnresult;); - if(xc=='\n') break; + 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. Some editors strip - * trailing white space from a leader like " * ". + * whatever comes first, because the leader's + * trailing white space was probably stripped. */ - } + } while (ccnt-- && (xc!='\n' || --c1)); } } } else { @@ -284,6 +314,10 @@ rcsfcmp(xfp, xstatp, ufname, delta) } if (xc != uc) goto return1; + if (xc == '\n') + leaderlen = 0; + else + leaderlen++; } } diff --git a/gnu/usr.bin/rcs/lib/rcsfnms.c b/gnu/usr.bin/rcs/lib/rcsfnms.c index 02562f0..e3b4f71 100644 --- a/gnu/usr.bin/rcs/lib/rcsfnms.c +++ b/gnu/usr.bin/rcs/lib/rcsfnms.c @@ -1,15 +1,14 @@ -/* - * RCS file name handling - */ +/* RCS filename and pathname handling */ + /**************************************************************************** * creation and deletion of /tmp temporaries - * pairing of RCS file names and working file names. + * pairing of RCS pathnames and working pathnames. * Testprogram: define PAIRTEST **************************************************************************** */ -/* Copyright (C) 1982, 1988, 1989 Walter Tichy - Copyright 1990, 1991 by Paul Eggert +/* 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. @@ -25,8 +24,9 @@ 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, 675 Mass Ave, Cambridge, MA 02139, USA. +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: @@ -37,7 +37,45 @@ Report problems and direct all questions to: -/* $Log: rcsfnms.c,v $ +/* + * $Log: rcsfnms.c,v $ + * Revision 5.16 1995/06/16 06:19:24 eggert + * Update FSF address. + * + * Revision 5.15 1995/06/01 16:23:43 eggert + * (basefilename): Renamed from basename to avoid collisions. + * (dirlen): Remove (for similar reasons). + * (rcsreadopen): Open with FOPEN_RB. + * (SLASHSLASH_is_SLASH): Default is 0. + * (getcwd): Work around bad_wait_if_SIGCHLD_ignored bug. + * + * Revision 5.14 1994/03/17 14:05:48 eggert + * Strip trailing SLASHes from TMPDIR; some systems need this. Remove lint. + * + * Revision 5.13 1993/11/03 17:42:27 eggert + * Determine whether a file name is too long indirectly, + * by examining inode numbers, instead of trying to use operating system + * primitives like pathconf, which are not trustworthy in general. + * File names may now hold white space or $. + * Do not flatten ../X in pathnames; that may yield wrong answer for symlinks. + * Add getabsname hook. Improve quality of diagnostics. + * + * Revision 5.12 1992/07/28 16:12:44 eggert + * Add .sty. .pl now implies Perl, not Prolog. Fix fdlock initialization bug. + * Check that $PWD is really ".". Be consistent about pathnames vs filenames. + * + * Revision 5.11 1992/02/17 23:02:25 eggert + * `a/RCS/b/c' is now an RCS file with an empty extension, not just `a/b/RCS/c'. + * + * Revision 5.10 1992/01/24 18:44:19 eggert + * Fix bug: Expand and Ignored weren't reinitialized. + * Avoid `char const c=ch;' compiler bug. + * Add support for bad_creat0. + * + * Revision 5.9 1992/01/06 02:42:34 eggert + * Shorten long (>31 chars) name. + * while (E) ; -> while (E) continue; + * * Revision 5.8 1991/09/24 00:28:40 eggert * Don't export bindex(). * @@ -66,8 +104,8 @@ Report problems and direct all questions to: * * Revision 5.0 1990/08/22 08:12:50 eggert * Ignore signals when manipulating the semaphore file. - * Modernize list of file name extensions. - * Permit paths of arbitrary length. Beware file names beginning with "-". + * Modernize list of filename extensions. + * Permit paths of arbitrary length. Beware filenames beginning with "-". * Remove compile-time limits; use malloc instead. * Permit dates past 1999/12/31. Make lock and temp files faster and safer. * Ansify and Posixate. @@ -94,10 +132,10 @@ Report problems and direct all questions to: * Comment leader '% ' for '*.tex' files added. * * Revision 4.3 83/12/15 12:26:48 wft - * Added check for KDELIM in file names to pairfilenames(). + * Added check for KDELIM in filenames to pairfilenames(). * * Revision 4.2 83/12/02 22:47:45 wft - * Added csh, red, and sl file name suffixes. + * Added csh, red, and sl filename suffixes. * * Revision 4.1 83/05/11 16:23:39 wft * Added initialization of Dbranch to InitAdmin(). Canged pairfilenames(): @@ -106,7 +144,7 @@ Report problems and direct all questions to: * 3. added ignoring of directories. * * Revision 3.7 83/05/11 15:01:58 wft - * Added comtable[] which pairs file name suffixes with comment leaders; + * Added comtable[] which pairs filename suffixes with comment leaders; * updated InitAdmin() accordingly. * * Revision 3.6 83/04/05 14:47:36 wft @@ -121,7 +159,7 @@ Report problems and direct all questions to: * removed unused variable. * * Revision 3.3 82/11/28 20:31:37 wft - * Changed mktempfile() to store the generated file names. + * Changed mktempfile() to store the generated filenames. * Changed getfullRCSname() to store the file and pathname, and to * delete leading "../" and "./". * @@ -140,72 +178,87 @@ Report problems and direct all questions to: #include "rcsbase.h" -libId(fnmsId, "$Id: rcsfnms.c,v 5.8 1991/09/24 00:28:40 eggert Exp $") +libId(fnmsId, "$Id: rcsfnms.c,v 5.16 1995/06/16 06:19:24 eggert Exp $") + +static char const *bindex P((char const*,int)); +static int fin2open P((char const*, size_t, char const*, size_t, char const*, size_t, RILE*(*)P((struct buf*,struct stat*,int)), int)); +static int finopen P((RILE*(*)P((struct buf*,struct stat*,int)), int)); +static int suffix_matches P((char const*,char const*)); +static size_t dir_useful_len P((char const*)); +static size_t suffixlen P((char const*)); +static void InitAdmin P((void)); -char const *RCSfilename; -char *workfilename; +char const *RCSname; +char *workname; +int fdlock; FILE *workstdout; struct stat RCSstat; char const *suffixes; static char const rcsdir[] = "RCS"; -#define rcsdirlen (sizeof(rcsdir)-1) +#define rcslen (sizeof(rcsdir)-1) static struct buf RCSbuf, RCSb; static int RCSerrno; -/* Temp file names to be unlinked when done, if they are not nil. */ +/* Temp names to be unlinked when done, if they are not 0. */ #define TEMPNAMES 5 /* must be at least DIRTEMPNAMES (see rcsedit.c) */ -static char *volatile tfnames[TEMPNAMES]; +static char *volatile tpnames[TEMPNAMES]; struct compair { char const *suffix, *comlead; }; +/* +* This table is present only for backwards compatibility. +* Normally we ignore this table, and use the prefix of the `$Log' line instead. +*/ static struct compair const comtable[] = { -/* comtable pairs each filename suffix with a comment leader. The comment */ -/* leader is placed before each line generated by the $Log keyword. This */ -/* table is used to guess the proper comment leader from the working file's */ -/* suffix during initial ci (see InitAdmin()). Comment leaders are needed */ -/* for languages without multiline comments; for others they are optional. */ - "a", "-- ", /* Ada */ - "ada", "-- ", - "asm", ";; ", /* assembler (MS-DOS) */ - "bat", ":: ", /* batch (MS-DOS) */ - "c", " * ", /* C */ - "c++", "// ", /* C++ in all its infinite guises */ - "cc", "// ", - "cpp", "// ", - "cxx", "// ", - "cl", ";;; ", /* Common Lisp */ - "cmd", ":: ", /* command (OS/2) */ - "cmf", "c ", /* CM Fortran */ - "cs", " * ", /* C* */ - "el", "; ", /* Emacs Lisp */ - "f", "c ", /* Fortran */ - "for", "c ", - "h", " * ", /* C-header */ - "hpp", "// ", /* C++ header */ - "hxx", "// ", - "l", " * ", /* lex NOTE: conflict between lex and franzlisp */ - "lisp",";;; ", /* Lucid Lisp */ - "lsp", ";; ", /* Microsoft Lisp */ - "mac", ";; ", /* macro (DEC-10, MS-DOS, PDP-11, VMS, etc) */ - "me", ".\\\" ",/* me-macros t/nroff*/ - "ml", "; ", /* mocklisp */ - "mm", ".\\\" ",/* mm-macros t/nroff*/ - "ms", ".\\\" ",/* ms-macros t/nroff*/ - "p", " * ", /* Pascal */ - "pas", " * ", - "pl", "% ", /* Prolog */ - "tex", "% ", /* TeX */ - "y", " * ", /* yacc */ - nil, "# " /* default for unknown suffix; must always be last */ + { "a" , "-- " }, /* Ada */ + { "ada" , "-- " }, + { "adb" , "-- " }, + { "ads" , "-- " }, + { "asm" , ";; " }, /* assembler (MS-DOS) */ + { "bat" , ":: " }, /* batch (MS-DOS) */ + { "body", "-- " }, /* Ada */ + { "c" , " * " }, /* C */ + { "c++" , "// " }, /* C++ in all its infinite guises */ + { "cc" , "// " }, + { "cpp" , "// " }, + { "cxx" , "// " }, + { "cl" , ";;; "}, /* Common Lisp */ + { "cmd" , ":: " }, /* command (OS/2) */ + { "cmf" , "c " }, /* CM Fortran */ + { "cs" , " * " }, /* C* */ + { "el" , "; " }, /* Emacs Lisp */ + { "f" , "c " }, /* Fortran */ + { "for" , "c " }, + { "h" , " * " }, /* C-header */ + { "hpp" , "// " }, /* C++ header */ + { "hxx" , "// " }, + { "l" , " * " }, /* lex (NOTE: franzlisp disagrees) */ + { "lisp", ";;; "}, /* Lucid Lisp */ + { "lsp" , ";; " }, /* Microsoft Lisp */ + { "m" , "// " }, /* Objective C */ + { "mac" , ";; " }, /* macro (DEC-10, MS-DOS, PDP-11, VMS, etc) */ + { "me" , ".\\\" "}, /* troff -me */ + { "ml" , "; " }, /* mocklisp */ + { "mm" , ".\\\" "}, /* troff -mm */ + { "ms" , ".\\\" "}, /* troff -ms */ + { "p" , " * " }, /* Pascal */ + { "pas" , " * " }, + { "ps" , "% " }, /* PostScript */ + { "spec", "-- " }, /* Ada */ + { "sty" , "% " }, /* LaTeX style */ + { "tex" , "% " }, /* TeX */ + { "y" , " * " }, /* yacc */ + { 0 , "# " } /* default for unknown suffix; must be last */ }; #if has_mktemp + static char const *tmp P((void)); static char const * tmp() /* Yield the name of the tmp directory. */ @@ -224,14 +277,14 @@ tmp() char const * maketemp(n) int n; -/* Create a unique filename using n and the process id and store it - * into the nth slot in tfnames. - * Because of storage in tfnames, tempunlink() can unlink the file later. - * Returns a pointer to the filename created. +/* Create a unique pathname using n and the process id and store it + * into the nth slot in tpnames. + * Because of storage in tpnames, tempunlink() can unlink the file later. + * Return a pointer to the pathname created. */ { char *p; - char const *t = tfnames[n]; + char const *t = tpnames[n]; if (t) return t; @@ -240,25 +293,26 @@ maketemp(n) { # if has_mktemp char const *tp = tmp(); - p = testalloc(strlen(tp) + 10); - VOID sprintf(p, "%s%cT%cXXXXXX", tp, SLASH, '0'+n); + size_t tplen = dir_useful_len(tp); + p = testalloc(tplen + 10); + VOID sprintf(p, "%.*s%cT%cXXXXXX", (int)tplen, tp, SLASH, '0'+n); if (!mktemp(p) || !*p) - faterror("can't make temporary file name `%s%cT%cXXXXXX'", - tp, SLASH, '0'+n + faterror("can't make temporary pathname `%.*s%cT%cXXXXXX'", + (int)tplen, tp, SLASH, '0'+n ); # else - static char tfnamebuf[TEMPNAMES][L_tmpnam]; - p = tfnamebuf[n]; + static char tpnamebuf[TEMPNAMES][L_tmpnam]; + p = tpnamebuf[n]; if (!tmpnam(p) || !*p) # ifdef P_tmpdir - faterror("can't make temporary file name `%s...'",P_tmpdir); + faterror("can't make temporary pathname `%s...'",P_tmpdir); # else - faterror("can't make temporary file name"); + faterror("can't make temporary pathname"); # endif # endif } - tfnames[n] = p; + tpnames[n] = p; return p; } @@ -271,28 +325,28 @@ tempunlink() register char *p; for (i = TEMPNAMES; 0 <= --i; ) - if ((p = tfnames[i])) { + if ((p = tpnames[i])) { VOID unlink(p); /* * We would tfree(p) here, * but this might dump core if we're handing a signal. * We're about to exit anyway, so we won't bother. */ - tfnames[i] = 0; + tpnames[i] = 0; } } static char const * -bindex(sp,ch) +bindex(sp, c) register char const *sp; - int ch; + register int c; /* Function: Finds the last occurrence of character c in string sp * and returns a pointer to the character just beyond it. If the * character doesn't occur in the string, sp is returned. */ { - register char const c=ch, *r; + register char const *r; r = sp; while (*sp) { if (*sp++ == c) r=sp; @@ -333,64 +387,22 @@ InitAdmin() register char const *Suffix; register int i; - Head=nil; Dbranch=nil; AccessList=nil; Symbols=nil; Locks=nil; + Head=0; Dbranch=0; AccessList=0; Symbols=0; Locks=0; StrictLocks=STRICT_LOCKING; /* guess the comment leader from the suffix*/ - Suffix=bindex(workfilename, '.'); - if (Suffix==workfilename) Suffix= ""; /* empty suffix; will get default*/ + Suffix = bindex(workname, '.'); + if (Suffix==workname) Suffix= ""; /* empty suffix; will get default*/ for (i=0; !suffix_matches(Suffix,comtable[i].suffix); i++) - ; + continue; Comment.string = comtable[i].comlead; Comment.size = strlen(comtable[i].comlead); + Expand = KEYVAL_EXPAND; + clear_buf(&Ignored); Lexinit(); /* note: if !finptr, reads nothing; only initializes */ } -/* 'cpp' does not like this line. It seems to be the leading '_' in the */ -/* second occurence of '_POSIX_NO_TRUNC'. It evaluates correctly with */ -/* just the first term so lets just do that for now. */ -/*#if defined(_POSIX_NO_TRUNC) && _POSIX_NO_TRUNC!=-1*/ -#if defined(_POSIX_NO_TRUNC) -# define LONG_NAMES_MAY_BE_SILENTLY_TRUNCATED 0 -#else -# define LONG_NAMES_MAY_BE_SILENTLY_TRUNCATED 1 -#endif - -#if LONG_NAMES_MAY_BE_SILENTLY_TRUNCATED -#ifdef NAME_MAX -# define filenametoolong(path) (NAME_MAX < strlen(basename(path))) -#else - static int -filenametoolong(path) - char *path; -/* Yield true if the last file name in PATH is too long. */ -{ - static unsigned long dot_namemax; - - register size_t namelen; - register char *base; - register unsigned long namemax; - - base = path + dirlen(path); - namelen = strlen(base); - if (namelen <= _POSIX_NAME_MAX) /* fast check for shorties */ - return false; - if (base != path) { - *--base = 0; - namemax = pathconf(path, _PC_NAME_MAX); - *base = SLASH; - } else { - /* Cache the results for the working directory, for speed. */ - if (!dot_namemax) - dot_namemax = pathconf(".", _PC_NAME_MAX); - namemax = dot_namemax; - } - /* If pathconf() yielded -1, namemax is now ULONG_MAX. */ - return namemax<namelen; -} -#endif -#endif void bufalloc(b, size) @@ -422,7 +434,7 @@ bufrealloc(b, size) bufalloc(b, size); else { while ((b->size <<= 1) < size) - ; + continue; b->string = trealloc(char, b->string, b->size); } } @@ -495,7 +507,7 @@ bufscpy(b, s) char const * -basename(p) +basefilename(p) char const *p; /* Yield the address of the base filename of the pathname P. */ { @@ -507,19 +519,11 @@ basename(p) } } - size_t -dirlen(p) - char const *p; -/* Yield the length of P's directory, including its trailing SLASH. */ -{ - return basename(p) - p; -} - static size_t suffixlen(x) char const *x; -/* Yield the length of X, an RCS filename suffix. */ +/* Yield the length of X, an RCS pathname suffix. */ { register char const *p; @@ -538,10 +542,10 @@ suffixlen(x) char const * rcssuffix(name) char const *name; -/* Yield the suffix of NAME if it is an RCS filename, 0 otherwise. */ +/* Yield the suffix of NAME if it is an RCS pathname, 0 otherwise. */ { char const *x, *p, *nz; - size_t dl, nl, xl; + size_t nl, xl; nl = strlen(name); nz = name + nl; @@ -550,30 +554,29 @@ rcssuffix(name) if ((xl = suffixlen(x))) { if (xl <= nl && memcmp(p = nz-xl, x, xl) == 0) return p; - } else { - dl = dirlen(name); - if ( - rcsdirlen < dl && - !memcmp(p = name+(dl-=rcsdirlen+1), rcsdir, rcsdirlen) && - (!dl || isSLASH(*--p)) - ) - return nz; - } + } else + for (p = name; p < nz - rcslen; p++) + if ( + isSLASH(p[rcslen]) + && (p==name || isSLASH(p[-1])) + && memcmp(p, rcsdir, rcslen) == 0 + ) + return nz; x += xl; } while (*x++); return 0; } /*ARGSUSED*/ RILE * -rcsreadopen(RCSname, status, mustread) - struct buf *RCSname; +rcsreadopen(RCSpath, status, mustread) + struct buf *RCSpath; struct stat *status; int mustread; -/* Open RCSNAME for reading and yield its FILE* descriptor. +/* Open RCSPATH for reading and yield its FILE* descriptor. * If successful, set *STATUS to its status. - * Pass this routine to pairfilenames() for read-only access to the file. */ + * Pass this routine to pairnames() for read-only access to the file. */ { - return Iopen(RCSname->string, FOPEN_R, status); + return Iopen(RCSpath->string, FOPEN_RB, status); } static int @@ -594,7 +597,7 @@ finopen(rcsopen, mustread) * We prefer an old name to that of a nonexisting new RCS file, * unless we tried locking the old name and failed. */ - preferold = RCSbuf.string[0] && (mustread||frewrite); + preferold = RCSbuf.string[0] && (mustread||0<=fdlock); finptr = (*rcsopen)(&RCSb, &RCSstat, mustread); interesting = finptr || errno!=ENOENT; @@ -615,7 +618,7 @@ fin2open(d, dlen, base, baselen, x, xlen, rcsopen, mustread) /* * D is a directory name with length DLEN (including trailing slash). * BASE is a filename with length BASELEN. - * X is an RCS filename suffix with length XLEN. + * X is an RCS pathname suffix with length XLEN. * Use RCSOPEN to open an RCS file; MUSTREAD is set if the file must be read. * Yield true if successful. * Try dRCS/basex first; if that fails and x is nonempty, try dbasex. @@ -626,12 +629,12 @@ fin2open(d, dlen, base, baselen, x, xlen, rcsopen, mustread) { register char *p; - bufalloc(&RCSb, dlen + rcsdirlen + 1 + baselen + xlen + 1); + bufalloc(&RCSb, dlen + rcslen + 1 + baselen + xlen + 1); /* Try dRCS/basex. */ VOID memcpy(p = RCSb.string, d, dlen); - VOID memcpy(p += dlen, rcsdir, rcsdirlen); - p += rcsdirlen; + VOID memcpy(p += dlen, rcsdir, rcslen); + p += rcslen; *p++ = SLASH; VOID memcpy(p, base, baselen); VOID memcpy(p += baselen, x, xlen); @@ -651,16 +654,17 @@ fin2open(d, dlen, base, baselen, x, xlen, rcsopen, mustread) } int -pairfilenames(argc, argv, rcsopen, mustread, quiet) +pairnames(argc, argv, rcsopen, mustread, quiet) int argc; char **argv; RILE *(*rcsopen)P((struct buf*,struct stat*,int)); int mustread, quiet; -/* Function: Pairs the filenames pointed to by argv; argc indicates +/* + * Pair the pathnames pointed to by argv; argc indicates * how many there are. - * Places a pointer to the RCS filename into RCSfilename, - * and a pointer to the name of the working file into workfilename. - * If both the workfilename and the RCS filename are given, and workstdout + * Place a pointer to the RCS pathname into RCSname, + * and a pointer to the pathname of the working file into workname. + * If both are given, and workstdout * is set, a warning is printed. * * If the RCS file exists, places its status into RCSstat. @@ -677,80 +681,62 @@ pairfilenames(argc, argv, rcsopen, mustread, quiet) static struct buf tempbuf; register char *p, *arg, *RCS1; - char const *purefname, *pureRCSname, *x; + char const *base, *RCSbase, *x; int paired; size_t arglen, dlen, baselen, xlen; - if (!(arg = *argv)) return 0; /* already paired filename */ + fdlock = -1; + + if (!(arg = *argv)) return 0; /* already paired pathname */ if (*arg == '-') { - error("%s option is ignored after file names", arg); + error("%s option is ignored after pathnames", arg); return 0; } - purefname = basename(arg); - - /* Allocate buffer temporary to hold the default paired file name. */ - p = arg; - for (;;) { - switch (*p++) { - /* Beware characters that cause havoc with ci -k. */ - case KDELIM: - error("RCS file name `%s' contains %c", arg, KDELIM); - return 0; - case ' ': case '\n': case '\t': - error("RCS file name `%s' contains white space", arg); - return 0; - default: - continue; - case 0: - break; - } - break; - } - + base = basefilename(arg); paired = false; /* first check suffix to see whether it is an RCS file or not */ if ((x = rcssuffix(arg))) { - /* RCS file name given*/ + /* RCS pathname given */ RCS1 = arg; - pureRCSname = purefname; - baselen = x - purefname; + RCSbase = base; + baselen = x - base; if ( 1 < argc && - !rcssuffix(workfilename = p = argv[1]) && + !rcssuffix(workname = p = argv[1]) && baselen <= (arglen = strlen(p)) && - ((p+=arglen-baselen) == workfilename || isSLASH(p[-1])) && - memcmp(purefname, p, baselen) == 0 + ((p+=arglen-baselen) == workname || isSLASH(p[-1])) && + memcmp(base, p, baselen) == 0 ) { argv[1] = 0; paired = true; } else { - bufscpy(&tempbuf, purefname); - workfilename = p = tempbuf.string; + bufscpy(&tempbuf, base); + workname = p = tempbuf.string; p[baselen] = 0; } } else { /* working file given; now try to find RCS file */ - workfilename = arg; - baselen = p - purefname - 1; - /* derive RCS file name*/ + workname = arg; + baselen = strlen(base); + /* Derive RCS pathname. */ if ( 1 < argc && (x = rcssuffix(RCS1 = argv[1])) && baselen <= x - RCS1 && - ((pureRCSname=x-baselen)==RCS1 || isSLASH(pureRCSname[-1])) && - memcmp(purefname, pureRCSname, baselen) == 0 + ((RCSbase=x-baselen)==RCS1 || isSLASH(RCSbase[-1])) && + memcmp(base, RCSbase, baselen) == 0 ) { argv[1] = 0; paired = true; } else - pureRCSname = RCS1 = 0; + RCSbase = RCS1 = 0; } - /* now we have a (tentative) RCS filename in RCS1 and workfilename */ + /* Now we have a (tentative) RCS pathname in RCS1 and workname. */ /* Second, try to find the right RCS file */ - if (pureRCSname!=RCS1) { + if (RCSbase!=RCS1) { /* a path for RCSfile is given; single RCS file to look for */ bufscpy(&RCSbuf, RCS1); finptr = (*rcsopen)(&RCSbuf, &RCSstat, mustread); @@ -758,16 +744,16 @@ pairfilenames(argc, argv, rcsopen, mustread, quiet) } else { bufscpy(&RCSbuf, ""); if (RCS1) - /* RCS file name was given without path. */ - VOID fin2open(arg, (size_t)0, pureRCSname, baselen, + /* RCS filename was given without path. */ + VOID fin2open(arg, (size_t)0, RCSbase, baselen, x, strlen(x), rcsopen, mustread ); else { - /* No RCS file name was given. */ + /* No RCS pathname was given. */ /* Try each suffix in turn. */ - dlen = purefname-arg; + dlen = base-arg; x = suffixes; - while (! fin2open(arg, dlen, purefname, baselen, + while (! fin2open(arg, dlen, base, baselen, x, xlen=suffixlen(x), rcsopen, mustread )) { x += xlen; @@ -776,7 +762,7 @@ pairfilenames(argc, argv, rcsopen, mustread, quiet) } } } - RCSfilename = p = RCSbuf.string; + RCSname = p = RCSbuf.string; if (finptr) { if (!S_ISREG(RCSstat.st_mode)) { error("%s isn't a regular file -- ignored", p); @@ -784,7 +770,7 @@ pairfilenames(argc, argv, rcsopen, mustread, quiet) } Lexinit(); getadmin(); } else { - if (RCSerrno!=ENOENT || mustread || !frewrite) { + if (RCSerrno!=ENOENT || mustread || fdlock<0) { if (RCSerrno == EEXIST) error("RCS file %s is in use", p); else if (!quiet || RCSerrno!=ENOENT) @@ -793,25 +779,9 @@ pairfilenames(argc, argv, rcsopen, mustread, quiet) } InitAdmin(); }; -# if LONG_NAMES_MAY_BE_SILENTLY_TRUNCATED - if (filenametoolong(p)) { - error("RCS file name %s is too long", p); - return 0; - } -# ifndef NAME_MAX - /* - * Check workfilename too, even though it cannot be longer, - * because it may reside on a different filesystem. - */ - if (filenametoolong(workfilename)) { - error("working file name %s is too long", workfilename); - return 0; - } -# endif -# endif if (paired && workstdout) - warn("Option -p is set; ignoring output file %s",workfilename); + workwarn("Working file ignored due to -p option"); prevkeys = false; return finptr ? 1 : -1; @@ -820,83 +790,106 @@ pairfilenames(argc, argv, rcsopen, mustread, quiet) char const * getfullRCSname() -/* Function: returns a pointer to the full path name of the RCS file. - * Gets the working directory's name at most once. - * Removes leading "../" and "./". +/* + * Return a pointer to the full pathname of the RCS file. + * Remove leading `./'. */ { - static char const *wdptr; - static struct buf rcsbuf, wdbuf; - static size_t pathlength; - - register char const *realname; - register size_t parentdirlength; - register unsigned dotdotcounter; - register char *d; - register char const *wd; - - if (ROOTPATH(RCSfilename)) { - return(RCSfilename); - } else { + if (ROOTPATH(RCSname)) { + return RCSname; + } else { + static struct buf rcsbuf; +# if needs_getabsname + bufalloc(&rcsbuf, SIZEABLE_PATH + 1); + while (getabsname(RCSname, rcsbuf.string, rcsbuf.size) != 0) + if (errno == ERANGE) + bufalloc(&rcsbuf, rcsbuf.size<<1); + else + efaterror("getabsname"); +# else + static char const *wdptr; + static struct buf wdbuf; + static size_t wdlen; + + register char const *r; + register size_t dlen; + register char *d; + register char const *wd; + if (!(wd = wdptr)) { /* Get working directory for the first time. */ - if (!(d = cgetenv("PWD"))) { + char *PWD = cgetenv("PWD"); + struct stat PWDstat, dotstat; + if (! ( + (d = PWD) && + ROOTPATH(PWD) && + stat(PWD, &PWDstat) == 0 && + stat(".", &dotstat) == 0 && + same_file(PWDstat, dotstat, 1) + )) { bufalloc(&wdbuf, SIZEABLE_PATH + 1); -# if !has_getcwd && has_getwd - d = getwd(wdbuf.string); +# if has_getcwd || !has_getwd + while (!(d = getcwd(wdbuf.string, wdbuf.size))) + if (errno == ERANGE) + bufalloc(&wdbuf, wdbuf.size<<1); + else if ((d = PWD)) + break; + else + efaterror("getcwd"); # else - while ( - !(d = getcwd(wdbuf.string, wdbuf.size)) - && errno==ERANGE - ) - bufalloc(&wdbuf, wdbuf.size<<1); + d = getwd(wdbuf.string); + if (!d && !(d = PWD)) + efaterror("getwd"); # endif - if (!d) - efaterror("working directory"); } - parentdirlength = strlen(d); - while (parentdirlength && isSLASH(d[parentdirlength-1])) { - d[--parentdirlength] = 0; - /* Check needed because some getwd implementations */ - /* generate "/" for the root. */ - } + wdlen = dir_useful_len(d); + d[wdlen] = 0; wdptr = wd = d; - pathlength = parentdirlength; - } - /*the following must be redone since RCSfilename may change*/ - /* Find how many `../'s to remove from RCSfilename. */ - dotdotcounter =0; - realname = RCSfilename; - while (realname[0]=='.') { - if (isSLASH(realname[1])) { - /* drop leading ./ */ - realname += 2; - } else if (realname[1]=='.' && isSLASH(realname[2])) { - /* drop leading ../ and remember */ - dotdotcounter++; - realname += 3; - } else - break; } - /* Now remove dotdotcounter trailing directories from wd. */ - parentdirlength = pathlength; - while (dotdotcounter && parentdirlength) { - /* move pointer backwards over trailing directory */ - if (isSLASH(wd[--parentdirlength])) { - dotdotcounter--; - } - } - /* build full path name */ - bufalloc(&rcsbuf, parentdirlength+strlen(realname)+2); + /* + * Remove leading `./'s from RCSname. + * Do not try to handle `../', since removing it may yield + * the wrong answer in the presence of symbolic links. + */ + for (r = RCSname; r[0]=='.' && isSLASH(r[1]); r += 2) + /* `.////' is equivalent to `./'. */ + while (isSLASH(r[2])) + r++; + /* Build full pathname. */ + dlen = wdlen; + bufalloc(&rcsbuf, dlen + strlen(r) + 2); d = rcsbuf.string; - VOID memcpy(d, wd, parentdirlength); - d += parentdirlength; + VOID memcpy(d, wd, dlen); + d += dlen; *d++ = SLASH; - VOID strcpy(d, realname); - return rcsbuf.string; + VOID strcpy(d, r); +# endif + return rcsbuf.string; } } + static size_t +dir_useful_len(d) + char const *d; +/* +* D names a directory; yield the number of characters of D's useful part. +* To create a file in D, append a SLASH and a file name to D's useful part. +* Ignore trailing slashes if possible; not only are they ugly, +* but some non-Posix systems misbehave unless the slashes are omitted. +*/ +{ +# ifndef SLASHSLASH_is_SLASH +# define SLASHSLASH_is_SLASH 0 +# endif + size_t dlen = strlen(d); + if (!SLASHSLASH_is_SLASH && dlen==2 && isSLASH(d[0]) && isSLASH(d[1])) + --dlen; + else + while (dlen && isSLASH(d[dlen-1])) + --dlen; + return dlen; +} + #ifndef isSLASH int isSLASH(c) @@ -927,9 +920,6 @@ getcwd(path, size) register char *p, *lim; int closeerrno, closeerror, e, fd[2], readerror, toolong, wstatus; pid_t child; -# if !has_waitpid - pid_t w; -# endif if (!size) { errno = EINVAL; @@ -937,6 +927,12 @@ getcwd(path, size) } if (pipe(fd) != 0) return 0; +# if bad_wait_if_SIGCHLD_ignored +# ifndef SIGCHLD +# define SIGCHLD SIGCLD +# endif + VOID signal(SIGCHLD, SIG_DFL); +# endif if (!(child = vfork())) { if ( close(fd[0]) == 0 && @@ -988,12 +984,15 @@ getcwd(path, size) if (waitpid(child, &wstatus, 0) < 0) wstatus = 1; # else - do { - if ((w = wait(&wstatus)) < 0) { - wstatus = 1; - break; - } - } while (w != child); + { + pid_t w; + do { + if ((w = wait(&wstatus)) < 0) { + wstatus = 1; + break; + } + } while (w != child); + } # endif } if (!fp) { @@ -1026,7 +1025,7 @@ getcwd(path, size) #ifdef PAIRTEST -/* test program for pairfilenames() and getfullRCSname() */ +/* test program for pairnames() and getfullRCSname() */ char const cmdid[] = "pair"; @@ -1052,20 +1051,20 @@ int argc; char *argv[]; } do { - RCSfilename=workfilename=nil; - result = pairfilenames(argc,argv,rcsreadopen,!initflag,quietflag); + RCSname = workname = 0; + result = pairnames(argc,argv,rcsreadopen,!initflag,quietflag); if (result!=0) { - diagnose("RCS file: %s; working file: %s\nFull RCS file name: %s\n", - RCSfilename,workfilename,getfullRCSname() + diagnose("RCS pathname: %s; working pathname: %s\nFull RCS pathname: %s\n", + RCSname, workname, getfullRCSname() ); } switch (result) { case 0: continue; /* already paired file */ case 1: if (initflag) { - error("RCS file %s exists already",RCSfilename); + rcserror("already exists"); } else { - diagnose("RCS file %s exists\n",RCSfilename); + diagnose("RCS file %s exists\n", RCSname); } Ifclose(finptr); break; @@ -1078,7 +1077,7 @@ int argc; char *argv[]; } - exiting void + void exiterr() { dirtempunlink(); diff --git a/gnu/usr.bin/rcs/lib/rcsgen.c b/gnu/usr.bin/rcs/lib/rcsgen.c index 9a6072e..a87257e 100644 --- a/gnu/usr.bin/rcs/lib/rcsgen.c +++ b/gnu/usr.bin/rcs/lib/rcsgen.c @@ -1,9 +1,7 @@ -/* - * RCS revision generation - */ +/* Generate RCS revisions. */ -/* Copyright (C) 1982, 1988, 1989 Walter Tichy - Copyright 1990, 1991 by Paul Eggert +/* 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. @@ -19,8 +17,9 @@ 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, 675 Mass Ave, Cambridge, MA 02139, USA. +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: @@ -28,9 +27,29 @@ Report problems and direct all questions to: */ - - -/* $Log: rcsgen.c,v $ +/* + * $Log: rcsgen.c,v $ + * 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. * @@ -122,12 +141,14 @@ Report problems and direct all questions to: #include "rcsbase.h" -libId(genId, "$Id: rcsgen.c,v 5.10 1991/10/07 17:32:46 eggert Exp $") +libId(genId, "$Id: rcsgen.c,v 5.16 1995/06/16 06:19:24 eggert Exp $") 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)); @@ -145,11 +166,11 @@ buildrevision(deltas, target, outfile, expandflag) * otherwise written into a temporary file. * Temporary files are allocated by maketemp(). * if expandflag is set, keyword expansion is performed. - * Return nil if outfile is set, the name of the temporary file otherwise. + * 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 (resultfile and - * editfile). The last revision is then edited in, performing simultaneous + * 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. @@ -163,7 +184,7 @@ buildrevision(deltas, target, outfile, expandflag) return 0; else { Ozclose(&fcopy); - return(resultfile); + return resultname; } } else { /* several revisions to generate */ @@ -175,17 +196,17 @@ buildrevision(deltas, target, outfile, expandflag) } if (expandflag || outfile) { /* first, get to beginning of file*/ - finishedit((struct hshentry *)nil, outfile, false); + finishedit((struct hshentry*)0, outfile, false); } - scandeltatext(deltas->first, expandflag?edit_expand:edit, true); + scandeltatext(target, expandflag?edit_expand:edit, true); finishedit( - expandflag ? deltas->first : (struct hshentry*)nil, + expandflag ? target : (struct hshentry*)0, outfile, true ); if (outfile) return 0; Ozclose(&fcopy); - return resultfile; + return resultname; } } @@ -193,12 +214,13 @@ buildrevision(deltas, target, outfile, expandflag) static void scandeltatext(delta, func, needlog) - struct hshentry * delta; + 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. */ @@ -217,11 +239,11 @@ scandeltatext(delta, func, needlog) if (needlog && delta==nextdelta) { cb = savestring(&curlogbuf); delta->log = cleanlogmsg(curlogbuf.string, cb.size); + nextlex(); + delta->igtext = getphrases(Ktext); } else {readstring(); + ignorephrases(Ktext); } - nextlex(); - while (nexttok==ID && strcmp(NextString,Ktext)!=0) - ignorephrase(); getkeystring(Ktext); if (delta==nextdelta) @@ -233,7 +255,7 @@ scandeltatext(delta, func, needlog) case enter: enterstring(); break; case copy: copystring(); break; case expand: xpandstring(delta); break; - case edit: editstring((struct hshentry *)nil); break; + case edit: editstring((struct hshentry *)0); break; case edit_expand: editstring(delta); break; } } @@ -284,7 +306,7 @@ getcstdin() if (feof(in) && ttystdin()) clearerr(in); c = getc(in); - if (c < 0) { + if (c == EOF) { testIerror(in); if (feof(in) && ttystdin()) afputc('\n',stderr); @@ -327,9 +349,9 @@ putdesc(textflag, textfile) char *textfile; /* Function: puts the descriptive text into file frewrite. * if finptr && !textflag, the text is copied from the old description. - * Otherwise, if the textfile!=nil, the text is read from that - * file, or from stdin, if textfile==nil. - * A textfile with a leading '-' is treated as a string, not a file name. + * 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. */ @@ -369,13 +391,13 @@ putdesc(textflag, textfile) p = textfile + 1; s = strlen(p); } else { - if (!(txt = fopen(textfile, "r"))) + if (!(txt = fopenSafer(textfile, "r"))) efaterror(textfile); bufalloc(&desc, 1); p = desc.string; plim = p + desc.size; for (;;) { - if ((c=getc(txt)) < 0) { + if ((c=getc(txt)) == EOF) { testIerror(txt); if (feof(txt)) break; @@ -392,7 +414,7 @@ putdesc(textflag, textfile) desclean = cleanlogmsg(p, s); } putstring(frew, false, desclean, true); - aputc('\n', frew); + aputc_('\n', frew) } } @@ -406,13 +428,14 @@ getsstdin(option, name, note, buf) register size_t i; register int tty = ttystdin(); - if (tty) + if (tty) { aprintf(stderr, "enter %s, terminated with single '.' or end of file:\n%s>> ", name, note ); - else if (feof(stdin)) - faterror("can't reread redirected stdin for %s; use -%s<%s>", + eflush(); + } else if (feof(stdin)) + rcsfaterror("can't reread redirected stdin for %s; use -%s<%s>", name, option, name ); @@ -426,7 +449,234 @@ getsstdin(option, name, note, buf) /* Remove trailing '.'. */ --i; break; - } else if (tty) + } 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); +} diff --git a/gnu/usr.bin/rcs/lib/rcskeep.c b/gnu/usr.bin/rcs/lib/rcskeep.c index 1a0c78f..31f2f9b 100644 --- a/gnu/usr.bin/rcs/lib/rcskeep.c +++ b/gnu/usr.bin/rcs/lib/rcskeep.c @@ -1,14 +1,7 @@ -/* - * RCS keyword extraction - */ -/***************************************************************************** - * main routine: getoldkeys() - * Testprogram: define KEEPTEST - ***************************************************************************** - */ +/* Extract RCS keyword string values from working files. */ -/* Copyright (C) 1982, 1988, 1989 Walter Tichy - Copyright 1990, 1991 by Paul Eggert +/* 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. @@ -24,8 +17,9 @@ 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, 675 Mass Ave, Cambridge, MA 02139, USA. +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: @@ -33,9 +27,26 @@ Report problems and direct all questions to: */ - - -/* $Log: rcskeep.c,v $ +/* + * $Log: rcskeep.c,v $ + * 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. * @@ -86,33 +97,29 @@ Report problems and direct all questions to: * */ -/* -#define KEEPTEST -*/ -/* Testprogram; prints out the keyword values found. */ - #include "rcsbase.h" -libId(keepId, "$Id: rcskeep.c,v 5.4 1991/08/19 03:13:55 eggert Exp $") +libId(keepId, "$Id: rcskeep.c,v 5.10 1995/06/16 06:19:24 eggert Exp $") -static int checknum P((char const*,int)); -static int getval P((RILE*,struct buf*,int)); +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, prevrev, prevstate; +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 FNAME is nonnull, it is opened and closed instead of using FP. + * If fp is null, workname is opened and closed instead of using fp. * The results are placed into - * prevauthor, prevdate, prevrev, prevstate. + * 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 @@ -123,14 +130,15 @@ getoldkeys(fp) 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(workfilename, FOPEN_R_WORK, (struct stat*)0))) { - eerror(workfilename); + if (!(fp = Iopen(workname, FOPEN_R_WORK, (struct stat*)0))) { + eerror(workname); return false; } needs_closing = true; @@ -139,6 +147,7 @@ getoldkeys(fp) /* initialize to empty */ bufscpy(&prevauthor, ""); bufscpy(&prevdate, ""); + bufscpy(&prevname, ""); prevname_found = 0; bufscpy(&prevrev, ""); bufscpy(&prevstate, ""); @@ -149,7 +158,7 @@ getoldkeys(fp) /* try to get keyword */ tp = keyword; for (;;) { - Igeteof(fp, c, goto ok;); + Igeteof_(fp, c, goto ok;) switch (c) { default: if (keyword+keylength <= tp) @@ -165,7 +174,7 @@ getoldkeys(fp) } while (c==KDELIM); if (c!=VDELIM) continue; *tp = c; - Igeteof(fp, c, break;); + Igeteof_(fp, c, break;) switch (c) { case ' ': case '\t': break; default: continue; @@ -184,7 +193,7 @@ getoldkeys(fp) case Header: case Id: if (!( - getval(fp, (struct buf*)nil, false) && + getval(fp, (struct buf*)0, false) && keeprev(fp) && (c = keepdate(fp)) && keepid(c, fp, &prevauthor) && @@ -192,8 +201,8 @@ getoldkeys(fp) )) return false; /* Skip either ``who'' (new form) or ``Locker: who'' (old). */ - if (getval(fp, (struct buf*)nil, true) && - getval(fp, (struct buf*)nil, true)) + if (getval(fp, (struct buf*)0, true) && + getval(fp, (struct buf*)0, true)) c = 0; else if (nerror) return false; @@ -201,13 +210,24 @@ getoldkeys(fp) 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*)nil, false)) + 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; @@ -222,16 +242,18 @@ getoldkeys(fp) continue; } if (!c) - Igeteof(fp, c, c=0;); + Igeteof_(fp, c, c=0;) if (c != KDELIM) { - error("closing %c missing on keyword", KDELIM); + workerror("closing %c missing on keyword", KDELIM); return false; } - if (*prevauthor.string && *prevdate.string && *prevrev.string && *prevstate.string) { + if (prevname_found && + *prevauthor.string && *prevdate.string && + *prevrev.string && *prevstate.string + ) break; - } } - Igeteof(fp, c, break;); + Igeteof_(fp, c, break;) } ok: @@ -246,7 +268,7 @@ getoldkeys(fp) static int badly_terminated() { - error("badly terminated keyword value"); + workerror("badly terminated keyword value"); return false; } @@ -257,12 +279,12 @@ getval(fp, target, optional) 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 nil. + * 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();); + Igeteof_(fp, c, return badly_terminated();) return get0val(c, fp, target, optional); } @@ -305,8 +327,6 @@ get0val(c, fp, target, optional) VOID printf("getval: %s\n", target); # endif } - if (!got1) - error("too much white space in keyword value"); return got1; case KDELIM: @@ -317,7 +337,7 @@ get0val(c, fp, target, optional) case 0: return badly_terminated(); } - Igeteof(fp, c, return badly_terminated();); + Igeteof_(fp, c, return badly_terminated();) } } @@ -329,8 +349,7 @@ keepdate(fp) * Return 0 on error, lookahead character otherwise. */ { - struct buf prevday, prevtime, prevzone; - register char const *p; + struct buf prevday, prevtime; register int c; c = 0; @@ -338,24 +357,18 @@ keepdate(fp) if (getval(fp,&prevday,false)) { bufautobegin(&prevtime); if (getval(fp,&prevtime,false)) { - bufautobegin(&prevzone); - bufscpy(&prevzone, ""); - Igeteof(fp, c, c=0;); - if (c=='-' || c=='+') - if (!get0val(c,fp,&prevzone,false)) - c = 0; - else - Igeteof(fp, c, c=0;); + Igeteof_(fp, c, c=0;) if (c) { - p = prevday.string; - bufalloc(&prevdate, strlen(p) + strlen(prevtime.string) + strlen(prevzone.string) + 5); - VOID sprintf(prevdate.string, "%s%s %s %s", + 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(p[0]) && isdigit(p[1]) && p[2]=='/' ? "19" : "", - p, prevtime.string, prevzone.string + isdigit(d[0]) && isdigit(d[1]) && !isdigit(d[2]) + ? "19" : "", + d, t, + strchr(t,'-') || strchr(t,'+') ? "" : "+0000" ); } - bufautoend(&prevzone); } bufautoend(&prevtime); } @@ -371,11 +384,11 @@ keepid(c, fp, b) /* Get previous identifier from C+FP into B. */ { if (!c) - Igeteof(fp, c, return false;); + Igeteof_(fp, c, return false;) if (!get0val(c, fp, b, false)) return false; checksid(b->string); - return true; + return !nerror; } static int @@ -383,28 +396,45 @@ keeprev(fp) RILE *fp; /* Get previous revision from FP into prevrev. */ { - return getval(fp,&prevrev,false) && checknum(prevrev.string,-1); + return getval(fp,&prevrev,false) && checknum(prevrev.string); } static int -checknum(sp,fields) - register char const *sp; - int fields; -{ register int dotcount; - dotcount=0; - while(*sp) { - if (*sp=='.') dotcount++; - else if (!isdigit(*sp)) return false; - sp++; - } - return fields<0 ? dotcount&1 : dotcount==fields; +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 @@ -412,10 +442,10 @@ main(argc, argv) int argc; char *argv[]; { while (*(++argv)) { - workfilename = *argv; + workname = *argv; getoldkeys((RILE*)0); - VOID printf("%s: revision: %s, date: %s, author: %s, state: %s\n", - *argv, prevrev.string, prevdate.string, prevauthor.string, prevstate.string); + 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); } diff --git a/gnu/usr.bin/rcs/lib/rcskeys.c b/gnu/usr.bin/rcs/lib/rcskeys.c index 82850a7..2afdd07 100644 --- a/gnu/usr.bin/rcs/lib/rcskeys.c +++ b/gnu/usr.bin/rcs/lib/rcskeys.c @@ -1,9 +1,7 @@ -/* - * RCS keyword table and match operation - */ +/* RCS keyword table and match operation */ -/* Copyright (C) 1982, 1988, 1989 Walter Tichy - Copyright 1990, 1991 by Paul Eggert +/* 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. @@ -19,8 +17,9 @@ 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, 675 Mass Ave, Cambridge, MA 02139, USA. +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: @@ -28,9 +27,14 @@ Report problems and direct all questions to: */ - - -/* $Log: rcskeys.c,v $ +/* + * $Log: rcskeys.c,v $ + * 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.) @@ -60,14 +64,14 @@ Report problems and direct all questions to: #include "rcsbase.h" -libId(keysId, "$Id: rcskeys.c,v 5.2 1991/08/19 03:13:55 eggert Exp $") +libId(keysId, "$Id: rcskeys.c,v 5.4 1995/06/16 06:19:24 eggert Exp $") char const *const Keyword[] = { /* This must be in the same order as rcsbase.h's enum markers type. */ - nil, + 0, AUTHOR, DATE, HEADER, IDH, - LOCKER, LOG, RCSFILE, REVISION, SOURCE, STATE + LOCKER, LOG, NAME, RCSFILE, REVISION, SOURCE, STATE }; diff --git a/gnu/usr.bin/rcs/lib/rcslex.c b/gnu/usr.bin/rcs/lib/rcslex.c index 51e31f3..da57c03 100644 --- a/gnu/usr.bin/rcs/lib/rcslex.c +++ b/gnu/usr.bin/rcs/lib/rcslex.c @@ -1,17 +1,16 @@ -/* - * RCS file input - */ -/********************************************************************************* +/* lexical analysis of RCS files */ + +/****************************************************************************** * Lexical Analysis. * hashtable, Lexinit, nextlex, getlex, getkey, * getid, getnum, readstring, printstring, savestring, * checkid, fatserror, error, faterror, warn, diagnose * Testprogram: define LEXDB - ********************************************************************************* + ****************************************************************************** */ -/* Copyright (C) 1982, 1988, 1989 Walter Tichy - Copyright 1990, 1991 by Paul Eggert +/* 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. @@ -27,8 +26,9 @@ 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, 675 Mass Ave, Cambridge, MA 02139, USA. +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: @@ -38,7 +38,44 @@ Report problems and direct all questions to: -/* $Log: rcslex.c,v $ +/* + * $Log: rcslex.c,v $ + * Revision 5.19 1995/06/16 06:19:24 eggert + * Update FSF address. + * + * Revision 5.18 1995/06/01 16:23:43 eggert + * (map_fd_deallocate,mmap_deallocate,read_deallocate,nothing_to_deallocate): + * New functions. + * (Iclose): If large_memory and maps_memory, use them to deallocate mapping. + * (fd2RILE): Use map_fd if available. + * If one mapping method fails, try the next instead of giving up; + * if they all fail, fall back on ordinary read. + * Work around bug: root mmap over NFS succeeds, but accessing dumps core. + * Use MAP_FAILED macro for mmap failure, and `char *' instead of caddr_t. + * (advise_access): Use madvise only if this instance used mmap. + * (Iopen): Use fdSafer to get safer file descriptor. + * (aflush): Moved here from rcsedit.c. + * + * Revision 5.17 1994/03/20 04:52:58 eggert + * Don't worry if madvise fails. Add Orewind. Remove lint. + * + * Revision 5.16 1993/11/09 17:55:29 eggert + * Fix `label: }' typo. + * + * Revision 5.15 1993/11/03 17:42:27 eggert + * Improve quality of diagnostics by putting file names in them more often. + * Don't discard ignored phrases. + * + * Revision 5.14 1992/07/28 16:12:44 eggert + * Identifiers may now start with a digit and (unless they are symbolic names) + * may contain `.'. Avoid `unsigned'. Statement macro names now end in _. + * + * Revision 5.13 1992/02/17 23:02:27 eggert + * Work around NFS mmap SIGBUS problem. + * + * Revision 5.12 1992/01/06 02:42:34 eggert + * Use OPEN_O_BINARY if mode contains 'b'. + * * Revision 5.11 1991/11/03 03:30:44 eggert * Fix porting bug to ancient hosts lacking vfprintf. * @@ -132,7 +169,14 @@ Report problems and direct all questions to: #include "rcsbase.h" -libId(lexId, "$Id: rcslex.c,v 5.11 1991/11/03 03:30:44 eggert Exp $") +libId(lexId, "$Id: rcslex.c,v 5.19 1995/06/16 06:19:24 eggert Exp $") + +static char *checkidentifier P((char*,int,int)); +static void errsay P((char const*)); +static void fatsay P((char const*)); +static void lookup P((char const*)); +static void startsay P((const char*,const char*)); +static void warnsay P((char const*)); static struct hshentry *nexthsh; /*pointer to next hash entry, set by lookup*/ @@ -142,7 +186,7 @@ int hshenter; /*if true, next suitable lexeme will be entered */ /*into the symbol table. Handle with care. */ int nextc; /*next input character, initialized by Lexinit */ -unsigned long rcsline; /*current line-number of input */ +long rcsline; /*current line-number of input */ int nerror; /*counter for errors */ int quietflag; /*indicates quiet mode */ RILE * finptr; /*input file descriptor */ @@ -172,9 +216,9 @@ static int ignored_phrases; /* have we ignored phrases in this RCS file? */ void warnignore() { - if (! (ignored_phrases|quietflag)) { + if (!ignored_phrases) { ignored_phrases = true; - warn("Unknown phrases like `%s ...;' are in the RCS file.", NextString); + rcswarn("Unknown phrases like `%s ...;' are present.", NextString); } } @@ -205,7 +249,7 @@ lookup(str) /* empty slot found */ *p = n = ftalloc(struct hshentry); n->num = fstr_save(str); - n->nexthsh = nil; + n->nexthsh = 0; # ifdef LEXDB VOID printf("\nEntered: %s at %u ", str, ihash); # endif @@ -231,7 +275,7 @@ Lexinit() { register int c; for (c = hshsize; 0 <= --c; ) { - hshtab[c] = nil; + hshtab[c] = 0; } nerror = 0; @@ -241,7 +285,7 @@ Lexinit() ignored_phrases = false; rcsline = 1; bufrealloc(&tokbuf, 2); - Iget(finptr, nextc); + Iget_(finptr, nextc) nextlex(); /*initial token*/ } } @@ -288,46 +332,48 @@ nextlex() /* Note: falls into next case */ case SPACE: - GETC(frew, c); + GETC_(frew, c) continue; - case DIGIT: - sp = tokbuf.string; - limit = sp + tokbuf.size; - *sp++ = c; - for (;;) { - GETC(frew, c); - if ((d=ctab[c])!=DIGIT && d!=PERIOD) - break; - *sp++ = c; /* 1.2. and 1.2 are different */ - if (limit <= sp) - sp = bufenlarge(&tokbuf, &limit); - } - *sp = 0; - if (hshenter) - lookup(tokbuf.string); - else - NextString = fstr_save(tokbuf.string); - d = NUM; - break; - - - case LETTER: + case IDCHAR: + case LETTER: case Letter: + d = ID; + /* fall into */ + case DIGIT: + case PERIOD: sp = tokbuf.string; limit = sp + tokbuf.size; *sp++ = c; for (;;) { - GETC(frew, c); - if ((d=ctab[c])!=LETTER && d!=Letter && d!=DIGIT && d!=IDCHAR) + GETC_(frew, c) + switch (ctab[c]) { + case IDCHAR: + case LETTER: + case Letter: + d = ID; + /* fall into */ + case DIGIT: + case PERIOD: + *sp++ = c; + if (limit <= sp) + sp = bufenlarge(&tokbuf, &limit); + continue; + + default: break; - *sp++ = c; - if (limit <= sp) - sp = bufenlarge(&tokbuf, &limit); + } + break; } *sp = 0; + if (d == DIGIT || d == PERIOD) { + d = NUM; + if (hshenter) { + lookup(tokbuf.string); + break; + } + } NextString = fstr_save(tokbuf.string); - d = ID; /* may be ID or keyword */ break; case SBEGIN: /* long string */ @@ -338,7 +384,7 @@ nextlex() case COLON: case SEMI: - GETC(frew, c); + GETC_(frew, c) break; } break; } nextc = c; @@ -374,11 +420,11 @@ eoflex() ++rcsline; /* fall into */ case SPACE: - cachegeteof(c, {uncache(fin);return true;}); + cachegeteof_(c, {uncache(fin);return true;}) break; } if (fout) - aputc(c, fout); + aputc_(c, fout) } } @@ -442,7 +488,7 @@ getkeystring(key) getid() /* Function: Checks if nexttok is an identifier. If so, * advances the input by calling nextlex and returns a pointer - * to the identifier; otherwise returns nil. + * to the identifier; otherwise returns 0. * Treats keywords as identifiers. */ { @@ -451,14 +497,15 @@ getid() name = NextString; nextlex(); return name; - } else return nil; + } else + return 0; } struct hshentry * getnum() /* Function: Checks if nexttok is a number. If so, * advances the input by calling nextlex and returns a pointer - * to the hashtable entry. Otherwise returns nil. + * to the hashtable entry. Otherwise returns 0. * Doesn't work if hshenter is false. */ { @@ -467,46 +514,55 @@ struct hshentry * getnum() num=nexthsh; nextlex(); return num; - } else return nil; + } else + return 0; } struct cbuf getphrases(key) char const *key; -/* Get a series of phrases that do not start with KEY, yield resulting buffer. - * Stop when the next phrase starts with a token that is not an identifier, - * or is KEY. - * Assume !foutptr. - */ +/* +* Get a series of phrases that do not start with KEY. Yield resulting buffer. +* Stop when the next phrase starts with a token that is not an identifier, +* or is KEY. Copy input to foutptr if it is set. Unlike ignorephrases(), +* this routine assumes nextlex() has already been invoked before we start. +*/ { declarecache; register int c; - register char *p; - char const *limit; - register char const *ki, *kn; + register char const *kn; struct cbuf r; - struct buf b; register RILE *fin; + register FILE *frew; +# if large_memory +# define savech_(c) ; +# else + register char *p; + char const *limit; + struct buf b; +# define savech_(c) {if (limit<=p)p=bufenlarge(&b,&limit); *p++ =(c);} +# endif - if (nexttok!=ID || strcmp(NextString,key) == 0) { - r.string = 0; - r.size = 0; - return r; - } else { + if (nexttok!=ID || strcmp(NextString,key) == 0) + clear_buf(&r); + else { warnignore(); fin = finptr; + frew = foutptr; setupcache(fin); cache(fin); - bufautobegin(&b); - bufscpy(&b, NextString); +# if large_memory + r.string = (char const*)cacheptr() - strlen(NextString) - 1; +# else + bufautobegin(&b); + bufscpy(&b, NextString); + p = b.string + strlen(b.string); + limit = b.string + b.size; +# endif ffree1(NextString); - p = b.string + strlen(b.string); - limit = b.string + b.size; c = nextc; for (;;) { for (;;) { - if (limit <= p) - p = bufenlarge(&b, &limit); - *p++ = c; + savech_(c) switch (ctab[c]) { default: fatserror("unknown character `%c'", c); @@ -516,15 +572,13 @@ getphrases(key) /* fall into */ case COLON: case DIGIT: case LETTER: case Letter: case PERIOD: case SPACE: - cacheget(c); + GETC_(frew, c) continue; case SBEGIN: /* long string */ for (;;) { for (;;) { - if (limit <= p) - p = bufenlarge(&b, &limit); - cacheget(c); - *p++ = c; + GETC_(frew, c) + savech_(c) switch (c) { case '\n': ++rcsline; @@ -537,48 +591,50 @@ getphrases(key) } break; } - cacheget(c); + GETC_(frew, c) if (c != SDELIM) break; - if (limit <= p) - p = bufenlarge(&b, &limit); - *p++ = c; + savech_(c) } continue; case SEMI: - cacheget(c); + cacheget_(c) if (ctab[c] == NEWLN) { + if (frew) + aputc_(c, frew) ++rcsline; - if (limit <= p) - p = bufenlarge(&b, &limit); - *p++ = c; - cacheget(c); + savech_(c) + cacheget_(c) } +# if large_memory + r.size = (char const*)cacheptr() - 1 - r.string; +# endif for (;;) { switch (ctab[c]) { case NEWLN: ++rcsline; /* fall into */ case SPACE: - cacheget(c); + cacheget_(c) continue; default: break; } break; } + if (frew) + aputc_(c, frew) break; } break; } - switch (ctab[c]) { - case LETTER: - case Letter: + if (ctab[c] == Letter) { for (kn = key; c && *kn==c; kn++) - cacheget(c); + GETC_(frew, c) if (!*kn) switch (ctab[c]) { case DIGIT: case LETTER: case Letter: + case IDCHAR: case PERIOD: break; default: nextc = c; @@ -587,23 +643,26 @@ getphrases(key) uncache(fin); goto returnit; } - for (ki=key; ki<kn; ) { - if (limit <= p) - p = bufenlarge(&b, &limit); - *p++ = *ki++; - } - break; - - default: +# if !large_memory + { + register char const *ki; + for (ki=key; ki<kn; ) + savech_(*ki++) + } +# endif + } else { nextc = c; uncache(fin); nextlex(); - goto returnit; + break; } } - returnit: - return bufremember(&b, (size_t)(p - b.string)); + returnit:; +# if !large_memory + return bufremember(&b, (size_t)(p - b.string)); +# endif } + return r; } @@ -619,14 +678,14 @@ readstring() fin=finptr; frew=foutptr; setupcache(fin); cache(fin); for (;;) { - GETC(frew, c); + GETC_(frew, c) switch (c) { case '\n': ++rcsline; break; case SDELIM: - GETC(frew, c); + GETC_(frew, c) if (c != SDELIM) { /* end of string */ nextc = c; @@ -653,13 +712,13 @@ printstring() fout = stdout; setupcache(fin); cache(fin); for (;;) { - cacheget(c); + cacheget_(c) switch (c) { case '\n': ++rcsline; break; case SDELIM: - cacheget(c); + cacheget_(c) if (c != SDELIM) { nextc=c; uncache(fin); @@ -667,7 +726,7 @@ printstring() } break; } - aputc(c,fout); + aputc_(c,fout) } } @@ -695,13 +754,13 @@ savestring(target) setupcache(fin); cache(fin); tp = target->string; limit = tp + target->size; for (;;) { - GETC(frew, c); + GETC_(frew, c) switch (c) { case '\n': ++rcsline; break; case SDELIM: - GETC(frew, c); + GETC_(frew, c) if (c != SDELIM) { /* end of string */ nextc=c; @@ -719,44 +778,76 @@ savestring(target) } - char * -checkid(id, delimiter) + static char * +checkidentifier(id, delimiter, dotok) register char *id; int delimiter; + register int dotok; /* Function: check whether the string starting at id is an */ /* identifier and return a pointer to the delimiter*/ /* after the identifier. White space, delim and 0 */ /* are legal delimiters. Aborts the program if not*/ /* a legal identifier. Useful for checking commands*/ /* If !delim, the only delimiter is 0. */ +/* Allow '.' in identifier only if DOTOK is set. */ { - register enum tokens d; register char *temp; - register char c,tc; + register char c; register char delim = delimiter; + int isid = false; temp = id; - if ((d = ctab[(unsigned char)(c = *id)])==LETTER || d==Letter) { - while ((d = ctab[(unsigned char)(c = *++id)])==LETTER - || d==Letter || d==DIGIT || d==IDCHAR - ) - ; - if (c && (!delim || c!=delim && c!=' ' && c!='\t' && c!='\n')) { + for (;; id++) { + switch (ctab[(unsigned char)(c = *id)]) { + case IDCHAR: + case LETTER: + case Letter: + isid = true; + continue; + + case DIGIT: + continue; + + case PERIOD: + if (dotok) + continue; + break; + + default: + break; + } + break; + } + if ( ! isid + || (c && (!delim || (c!=delim && c!=' ' && c!='\t' && c!='\n'))) + ) { /* append \0 to end of id before error message */ - tc = c; - while( (c=(*++id))!=' ' && c!='\t' && c!='\n' && c!='\0' && c!=delim) ; + while ((c = *id) && c!=' ' && c!='\t' && c!='\n' && c!=delim) + id++; *id = '\0'; - faterror("invalid character %c in identifier `%s'",tc,temp); - } - } else { - /* append \0 to end of id before error message */ - while( (c=(*++id))!=' ' && c!='\t' && c!='\n' && c!='\0' && c!=delim) ; - *id = '\0'; - faterror("identifier `%s' doesn't start with letter", temp); - } + faterror("invalid %s `%s'", + dotok ? "identifier" : "symbol", temp + ); + } return id; } + char * +checkid(id, delimiter) + char *id; + int delimiter; +{ + return checkidentifier(id, delimiter, true); +} + + char * +checksym(sym, delimiter) + char *sym; + int delimiter; +{ + return checkidentifier(sym, delimiter, false); +} + void checksid(id) char *id; @@ -765,16 +856,92 @@ checksid(id) VOID checkid(id, 0); } + void +checkssym(sym) + char *sym; +{ + VOID checksym(sym, 0); +} + + +#if !large_memory +# define Iclose(f) fclose(f) +#else +# if !maps_memory + static int Iclose P((RILE *)); + static int + Iclose(f) + register RILE *f; + { + tfree(f->base); + f->base = 0; + return fclose(f->stream); + } +# else + static int Iclose P((RILE *)); + static int + Iclose(f) + register RILE *f; + { + (* f->deallocate) (f); + f->base = 0; + return close(f->fd); + } +# if has_map_fd + static void map_fd_deallocate P((RILE *)); + static void + map_fd_deallocate(f) + register RILE *f; + { + if (vm_deallocate( + task_self(), + (vm_address_t) f->base, + (vm_size_t) (f->lim - f->base) + ) != KERN_SUCCESS) + efaterror("vm_deallocate"); + } +# endif +# if has_mmap + static void mmap_deallocate P((RILE *)); + static void + mmap_deallocate(f) + register RILE *f; + { + if (munmap((char *) f->base, (size_t) (f->lim - f->base)) != 0) + efaterror("munmap"); + } +# endif + static void read_deallocate P((RILE *)); + static void + read_deallocate(f) + RILE *f; + { + tfree(f->base); + } + + static void nothing_to_deallocate P((RILE *)); + static void + nothing_to_deallocate(f) + RILE *f; + { + } +# endif +#endif + + +#if large_memory && maps_memory + static RILE *fd2_RILE P((int,char const*,struct stat*)); static RILE * -#if has_mmap && large_memory -fd2_RILE(fd, filename, status) +fd2_RILE(fd, name, status) #else -fd2RILE(fd, filename, mode, status) - char const *mode; + static RILE *fd2RILE P((int,char const*,char const*,struct stat*)); + static RILE * +fd2RILE(fd, name, type, status) + char const *type; #endif int fd; - char const *filename; + char const *name; register struct stat *status; { struct stat st; @@ -782,67 +949,127 @@ fd2RILE(fd, filename, mode, status) if (!status) status = &st; if (fstat(fd, status) != 0) - efaterror(filename); + efaterror(name); if (!S_ISREG(status->st_mode)) { - error("`%s' is not a regular file", filename); + error("`%s' is not a regular file", name); VOID close(fd); errno = EINVAL; return 0; } else { -# if ! (has_mmap && large_memory) +# if !(large_memory && maps_memory) FILE *stream; - if (!(stream = fdopen(fd, mode))) - efaterror(filename); + if (!(stream = fdopen(fd, type))) + efaterror(name); # endif # if !large_memory return stream; # else # define RILES 3 - { - static RILE rilebuf[RILES]; - - register RILE *f; - size_t s = status->st_size; - - if (s != status->st_size) - faterror("`%s' is enormous", filename); - for (f = rilebuf; f->base; f++) - if (f == rilebuf+RILES) - faterror("too many RILEs"); - if (!s) { - static unsigned char dummy; - f->base = &dummy; - } else { -# if has_mmap - if ( - (f->base = (unsigned char *)mmap( - (caddr_t)0, s, PROT_READ, MAP_SHARED, - fd, (off_t)0 - )) == (unsigned char *)-1 - ) - efaterror("mmap"); -# else - f->base = tnalloc(unsigned char, s); + { + static RILE rilebuf[RILES]; + + register RILE *f; + size_t s = status->st_size; + + if (s != status->st_size) + faterror("%s: too large", name); + for (f = rilebuf; f->base; f++) + if (f == rilebuf+RILES) + faterror("too many RILEs"); +# if maps_memory + f->deallocate = nothing_to_deallocate; +# endif + if (!s) { + static unsigned char nothing; + f->base = ¬hing; /* Any nonzero address will do. */ + } else { + f->base = 0; +# if has_map_fd + map_fd( + fd, (vm_offset_t)0, (vm_address_t*) &f->base, + TRUE, (vm_size_t)s + ); + f->deallocate = map_fd_deallocate; +# endif +# if has_mmap + if (!f->base) { + catchmmapints(); + f->base = (unsigned char *) mmap( + (char *)0, s, PROT_READ, MAP_SHARED, + fd, (off_t)0 + ); +# ifndef MAP_FAILED +# define MAP_FAILED (-1) # endif + if (f->base == (unsigned char *) MAP_FAILED) + f->base = 0; + else { +# if has_NFS && mmap_signal + /* + * On many hosts, the superuser + * can mmap an NFS file it can't read. + * So access the first page now, and print + * a nice message if a bus error occurs. + */ + readAccessFilenameBuffer(name, f->base); +# endif + } + f->deallocate = mmap_deallocate; + } +# endif + if (!f->base) { + f->base = tnalloc(unsigned char, s); +# if maps_memory + { + /* + * We can't map the file into memory for some reason. + * Read it into main memory all at once; this is + * the simplest substitute for memory mapping. + */ + char *bufptr = (char *) f->base; + size_t bufsiz = s; + do { + ssize_t r = read(fd, bufptr, bufsiz); + switch (r) { + case -1: + efaterror(name); + + case 0: + /* The file must have shrunk! */ + status->st_size = s -= bufsiz; + bufsiz = 0; + break; + + default: + bufptr += r; + bufsiz -= r; + break; + } + } while (bufsiz); + if (lseek(fd, (off_t)0, SEEK_SET) == -1) + efaterror(name); + f->deallocate = read_deallocate; } - f->ptr = f->base; - f->lim = f->base + s; -# if has_mmap - f->fd = fd; -# else - f->readlim = f->base; - f->stream = stream; # endif - if_advise_access(s, f, MADV_SEQUENTIAL); - return f; + } } + f->ptr = f->base; + f->lim = f->base + s; + f->fd = fd; +# if !maps_memory + f->readlim = f->base; + f->stream = stream; +# endif + if_advise_access(s, f, MADV_SEQUENTIAL); + return f; + } # endif } } -#if !has_mmap && large_memory +#if !maps_memory && large_memory int Igetmore(f) register RILE *f; @@ -868,59 +1095,42 @@ advise_access(f, advice) register RILE *f; int advice; { - if (madvise((caddr_t)f->base, (size_t)(f->lim - f->base), advice) != 0) - efaterror("madvise"); + if (f->deallocate == mmap_deallocate) + VOID madvise((char *)f->base, (size_t)(f->lim - f->base), advice); + /* Don't worry if madvise fails; it's only advisory. */ } #endif RILE * -#if has_mmap && large_memory -I_open(filename, status) +#if large_memory && maps_memory +I_open(name, status) #else -Iopen(filename, mode, status) - char const *mode; +Iopen(name, type, status) + char const *type; #endif - char const *filename; + char const *name; struct stat *status; -/* Open FILENAME for reading, yield its descriptor, and set *STATUS. */ +/* Open NAME for reading, yield its descriptor, and set *STATUS. */ { - int fd; + int fd = fdSafer(open(name, O_RDONLY +# if OPEN_O_BINARY + | (strchr(type,'b') ? OPEN_O_BINARY : 0) +# endif + )); - if ((fd = open(filename,O_RDONLY|O_BINARY)) < 0) + if (fd < 0) return 0; -# if has_mmap && large_memory - return fd2_RILE(fd, filename, status); +# if large_memory && maps_memory + return fd2_RILE(fd, name, status); # else - return fd2RILE(fd, filename, mode, status); + return fd2RILE(fd, name, type, status); # endif } -#if !large_memory -# define Iclose(f) fclose(f) -#else - static int - Iclose(f) - register RILE *f; - { -# if has_mmap - size_t s = f->lim - f->base; - if (s && munmap((caddr_t)f->base, s) != 0) - return -1; - f->base = 0; - return close(f->fd); -# else - tfree(f->base); - f->base = 0; - return fclose(f->stream); -# endif - } -#endif - - static int Oerrloop; - exiting void + void Oerror() { if (Oerrloop) @@ -929,8 +1139,8 @@ Oerror() efaterror("output error"); } -exiting void Ieof() { fatserror("unexpected end of file"); } -exiting void Ierror() { efaterror("input error"); } +void Ieof() { fatserror("unexpected end of file"); } +void Ierror() { efaterror("input error"); } void testIerror(f) FILE *f; { if (ferror(f)) Ierror(); } void testOerror(o) FILE *o; { if (ferror(o)) Oerror(); } @@ -951,19 +1161,17 @@ testIeof(f) void Irewind(f) FILE *f; { if (fseek(f,0L,SEEK_SET) != 0) Ierror(); } #endif -void eflush() -{ - if (fflush(stderr) != 0 && !Oerrloop) - Oerror(); -} +void Orewind(f) FILE *f; { if (fseek(f,0L,SEEK_SET) != 0) Oerror(); } +void aflush(f) FILE *f; { if (fflush(f) != 0) Oerror(); } +void eflush() { if (fflush(stderr)!=0 && !Oerrloop) Oerror(); } void oflush() { if (fflush(workstdout ? workstdout : stdout) != 0 && !Oerrloop) Oerror(); } - static exiting void + void fatcleanup(already_newline) int already_newline; { @@ -971,8 +1179,38 @@ fatcleanup(already_newline) exiterr(); } -static void errsay() { oflush(); aprintf(stderr,"%s error: ",cmdid); nerror++; } -static void fatsay() { oflush(); VOID fprintf(stderr,"%s error: ",cmdid); } + static void +startsay(s, t) + const char *s, *t; +{ + oflush(); + if (s) + aprintf(stderr, "%s: %s: %s", cmdid, s, t); + else + aprintf(stderr, "%s: %s", cmdid, t); +} + + static void +fatsay(s) + char const *s; +{ + startsay(s, ""); +} + + static void +errsay(s) + char const *s; +{ + fatsay(s); + nerror++; +} + + static void +warnsay(s) + char const *s; +{ + startsay(s, "warning: "); +} void eerror(s) char const *s; { enerror(errno,s); } @@ -981,20 +1219,20 @@ enerror(e,s) int e; char const *s; { - errsay(); + errsay((char const*)0); errno = e; perror(s); eflush(); } -exiting void efaterror(s) char const *s; { enfaterror(errno,s); } +void efaterror(s) char const *s; { enfaterror(errno,s); } - exiting void + void enfaterror(e,s) int e; char const *s; { - fatsay(); + fatsay((char const*)0); errno = e; perror(s); fatcleanup(true); @@ -1009,7 +1247,24 @@ error(char const *format,...) /* non-fatal error */ { va_list args; - errsay(); + errsay((char const*)0); + vararg_start(args, format); + fvfprintf(stderr, format, args); + va_end(args); + afputc('\n',stderr); + eflush(); +} + +#if has_prototypes + void +rcserror(char const *format,...) +#else + /*VARARGS1*/ void rcserror(format, va_alist) char const *format; va_dcl +#endif +/* non-fatal RCS file error */ +{ + va_list args; + errsay(RCSname); vararg_start(args, format); fvfprintf(stderr, format, args); va_end(args); @@ -1018,17 +1273,34 @@ error(char const *format,...) } #if has_prototypes - exiting void + void +workerror(char const *format,...) +#else + /*VARARGS1*/ void workerror(format, va_alist) char const *format; va_dcl +#endif +/* non-fatal working file error */ +{ + va_list args; + errsay(workname); + vararg_start(args, format); + fvfprintf(stderr, format, args); + va_end(args); + afputc('\n',stderr); + eflush(); +} + +#if has_prototypes + void fatserror(char const *format,...) #else - /*VARARGS1*/ exiting void + /*VARARGS1*/ void fatserror(format, va_alist) char const *format; va_dcl #endif -/* fatal syntax error */ +/* fatal RCS file syntax error */ { va_list args; oflush(); - VOID fprintf(stderr, "%s: %s:%lu: ", cmdid, RCSfilename, rcsline); + VOID fprintf(stderr, "%s: %s:%ld: ", cmdid, RCSname, rcsline); vararg_start(args, format); fvfprintf(stderr, format, args); va_end(args); @@ -1036,16 +1308,16 @@ fatserror(char const *format,...) } #if has_prototypes - exiting void + void faterror(char const *format,...) #else - /*VARARGS1*/ exiting void faterror(format, va_alist) + /*VARARGS1*/ void faterror(format, va_alist) char const *format; va_dcl #endif /* fatal error, terminates program after cleanup */ { va_list args; - fatsay(); + fatsay((char const*)0); vararg_start(args, format); fvfprintf(stderr, format, args); va_end(args); @@ -1054,20 +1326,76 @@ faterror(char const *format,...) #if has_prototypes void -warn(char const *format,...) +rcsfaterror(char const *format,...) #else - /*VARARGS1*/ void warn(format, va_alist) char const *format; va_dcl + /*VARARGS1*/ void rcsfaterror(format, va_alist) + char const *format; va_dcl #endif -/* prints a warning message */ +/* fatal RCS file error, terminates program after cleanup */ { va_list args; - oflush(); - aprintf(stderr,"%s warning: ",cmdid); + fatsay(RCSname); vararg_start(args, format); fvfprintf(stderr, format, args); va_end(args); - afputc('\n',stderr); - eflush(); + fatcleanup(false); +} + +#if has_prototypes + void +warn(char const *format,...) +#else + /*VARARGS1*/ void warn(format, va_alist) char const *format; va_dcl +#endif +/* warning */ +{ + va_list args; + if (!quietflag) { + warnsay((char *)0); + vararg_start(args, format); + fvfprintf(stderr, format, args); + va_end(args); + afputc('\n', stderr); + eflush(); + } +} + +#if has_prototypes + void +rcswarn(char const *format,...) +#else + /*VARARGS1*/ void rcswarn(format, va_alist) char const *format; va_dcl +#endif +/* RCS file warning */ +{ + va_list args; + if (!quietflag) { + warnsay(RCSname); + vararg_start(args, format); + fvfprintf(stderr, format, args); + va_end(args); + afputc('\n', stderr); + eflush(); + } +} + +#if has_prototypes + void +workwarn(char const *format,...) +#else + /*VARARGS1*/ void workwarn(format, va_alist) char const *format; va_dcl +#endif +/* working file warning */ +{ + va_list args; + if (!quietflag) { + warnsay(workname); + vararg_start(args, format); + fvfprintf(stderr, format, args); + va_end(args); + afputc('\n', stderr); + eflush(); + } } void @@ -1102,12 +1430,11 @@ diagnose(char const *format,...) void afputc(c, f) -/* Function: afputc(c,f) acts like aputc(c,f), but is smaller and slower. - */ +/* afputc(c,f); acts like aputc_(c,f) but is smaller and slower. */ int c; register FILE *f; { - aputc(c,f); + aputc_(c,f) } @@ -1138,6 +1465,7 @@ fvfprintf(FILE *stream, char const *format, va_list args) { #if has_vfprintf if (vfprintf(stream, format, args) < 0) + Oerror(); #else # if has__doprintf _doprintf(stream, format, args); @@ -1153,8 +1481,8 @@ fvfprintf(FILE *stream, char const *format, va_list args) # endif # endif if (ferror(stream)) -#endif Oerror(); +#endif } #if has_prototypes @@ -1235,7 +1563,7 @@ int argc; char * argv[]; exitmain(EXIT_SUCCESS); } -exiting void exiterr() { _exit(EXIT_FAILURE); } +void exiterr() { _exit(EXIT_FAILURE); } #endif diff --git a/gnu/usr.bin/rcs/lib/rcsmap.c b/gnu/usr.bin/rcs/lib/rcsmap.c index 0e7b23c..0345ef8 100644 --- a/gnu/usr.bin/rcs/lib/rcsmap.c +++ b/gnu/usr.bin/rcs/lib/rcsmap.c @@ -1,7 +1,7 @@ /* RCS map of character types */ /* Copyright (C) 1982, 1988, 1989 Walter Tichy - Copyright 1990, 1991 by Paul Eggert + Copyright 1990, 1991, 1995 by Paul Eggert Distributed under license by the Free Software Foundation, Inc. This file is part of RCS. @@ -17,8 +17,9 @@ 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, 675 Mass Ave, Cambridge, MA 02139, USA. +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: @@ -28,7 +29,7 @@ Report problems and direct all questions to: #include "rcsbase.h" -libId(mapId, "$Id: rcsmap.c,v 5.2 1991/08/19 03:13:55 eggert Exp $") +libId(mapId, "$Id: rcsmap.c,v 5.3 1995/06/16 06:19:24 eggert Exp $") /* map of character types */ /* ISO 8859/1 (Latin-1) */ diff --git a/gnu/usr.bin/rcs/lib/rcsrev.c b/gnu/usr.bin/rcs/lib/rcsrev.c index ce11f54..27b1747 100644 --- a/gnu/usr.bin/rcs/lib/rcsrev.c +++ b/gnu/usr.bin/rcs/lib/rcsrev.c @@ -1,9 +1,7 @@ -/* - * RCS revision number handling - */ +/* Handle RCS revision numbers. */ -/* Copyright (C) 1982, 1988, 1989 Walter Tichy - Copyright 1990, 1991 by Paul Eggert +/* 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. @@ -19,8 +17,9 @@ 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, 675 Mass Ave, Cambridge, MA 02139, USA. +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: @@ -28,10 +27,32 @@ Report problems and direct all questions to: */ - - - -/* $Log: rcsrev.c,v $ +/* + * $Log: rcsrev.c,v $ + * 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. * @@ -83,25 +104,21 @@ Report problems and direct all questions to: * in that case. */ - - -/* -#define REVTEST -*/ -/* version REVTEST is for testing the routines that generate a sequence - * of delta numbers needed to regenerate a given delta. - */ - #include "rcsbase.h" -libId(revId, "$Id: rcsrev.c,v 5.3 1991/08/19 03:13:55 eggert Exp $") +libId(revId, "$Id: rcsrev.c,v 5.10 1995/06/16 06:19:24 eggert Exp $") static char const *branchtip P((char const*)); -static struct hshentry *genbranch P((struct hshentry const*,char const*,unsigned,char const*,char const*,char const*,struct hshentries**)); +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*)); - unsigned + int countnumflds(s) char const *s; /* Given a pointer s to a dotted number (date or revision number), @@ -109,9 +126,9 @@ countnumflds(s) */ { register char const *sp; - register unsigned count; - if ((sp=s)==nil) return(0); - if (*sp == '\0') return(0); + register int count; + if (!(sp=s) || !*sp) + return 0; count = 1; do { if (*sp++ == '.') count++; @@ -123,12 +140,12 @@ countnumflds(s) getbranchno(revno,branchno) char const *revno; struct buf *branchno; -/* Given a non-nil revision number revno, getbranchno copies the number of the branch +/* 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 unsigned numflds; + register int numflds; register char *tp; bufscpy(branchno, revno); @@ -137,7 +154,7 @@ getbranchno(revno,branchno) tp = branchno->string; while (--numflds) while (*tp++ != '.') - ; + continue; *(tp-1)='\0'; } } @@ -156,8 +173,8 @@ int cmpnum(num1, num2) register size_t d1, d2; register int r; - s1=num1==nil?"":num1; - s2=num2==nil?"":num2; + s1 = num1 ? num1 : ""; + s2 = num2 ? num2 : ""; for (;;) { /* Give precedence to shorter one. */ @@ -167,8 +184,10 @@ int cmpnum(num1, num2) return -1; /* Strip leading zeros, then find number of digits. */ - while (*s1=='0') ++s1; for (d1=0; isdigit(s1[d1]); d1++) ; - while (*s2=='0') ++s2; for (d2=0; isdigit(s2[d2]); d2++) ; + 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) @@ -188,7 +207,7 @@ int cmpnum(num1, num2) int cmpnumfld(num1, num2, fld) char const *num1, *num2; - unsigned fld; + int fld; /* Compare the two dotted numbers at field fld. * num1 and num2 must have at least fld fields. * fld must be positive. @@ -202,25 +221,63 @@ int cmpnumfld(num1, num2, fld) /* 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++) ; - while (*s2=='0') ++s2; for (d2=0; isdigit(s2[d2]); d2++) ; + 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]; + char datebuf[datesize + zonelenmax]; - error("No revision on branch %s has%s%s%s%s%s%s.", + rcserror("No revision on branch %s has%s%s%s%s%s%s.", revno, date ? " a date before " : "", date ? date2str(date,datebuf) : "", @@ -234,11 +291,11 @@ cantfindbranch(revno, date, author, state) static void absent(revno, field) char const *revno; - unsigned field; + int field; { struct buf t; bufautobegin(&t); - error("%s %s absent", field&1?"revision":"branch", + rcserror("%s %s absent", field&1?"revision":"branch", partialno(&t,revno,field) ); bufautoend(&t); @@ -248,7 +305,7 @@ absent(revno, field) int compartial(num1, num2, length) char const *num1, *num2; - unsigned length; + int length; /* compare the first "length" fields of two dot numbers; the omitted field is considered to be larger than any number */ @@ -267,20 +324,21 @@ compartial(num1, num2, length) if (!*s1) return 1; if (!*s2) return -1; - while (*s1=='0') ++s1; for (d1=0; isdigit(s1[d1]); d1++) ; - while (*s2=='0') ++s2; for (d2=0; isdigit(s2[d2]); d2++) ; + 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++; - - if ( --length == 0 ) return 0; } } @@ -288,7 +346,7 @@ compartial(num1, num2, length) char * partialno(rev1,rev2,length) struct buf *rev1; char const *rev2; - register unsigned length; + register int length; /* Function: Copies length fields of revision number rev2 into rev1. * Return rev1's string. */ @@ -335,20 +393,20 @@ struct hshentry * genrevs(revno,date,author,state,store) * 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, nil is returned. + * If the proper delta could not be found, 0 is returned. */ { - unsigned length; + int length; register struct hshentry * next; int result; char const *branchnum; struct buf t; - char datebuf[datesize]; + char datebuf[datesize + zonelenmax]; bufautobegin(&t); if (!(next = Head)) { - error("RCS file empty"); + rcserror("RCS file empty"); goto norev; } @@ -360,7 +418,7 @@ struct hshentry * genrevs(revno,date,author,state,store) store1(&store, next); next = next->next; if (!next) { - error("branch number %s too low", partialno(&t,revno,1)); + rcserror("branch number %s too low", partialno(&t,revno,1)); goto norev; } } @@ -373,19 +431,19 @@ struct hshentry * genrevs(revno,date,author,state,store) if (length<=1){ /* pick latest one on given branch */ branchnum = next->num; /* works even for empty revno*/ - while ((next!=nil) && - (cmpnumfld(branchnum,next->num,1)==0) && - !( - (date==nil?1:(cmpnum(date,next->date)>=0)) && - (author==nil?1:(strcmp(author,next->author)==0)) && - (state ==nil?1:(strcmp(state, next->state) ==0)) - ) - ) + 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==nil) || + if (!next || (cmpnumfld(branchnum,next->num,1)!=0))/*overshot*/ { cantfindbranch( length ? revno : partialno(&t,branchnum,1), @@ -395,7 +453,7 @@ struct hshentry * genrevs(revno,date,author,state,store) } else { store1(&store, next); } - *store = nil; + *store = 0; return next; } @@ -409,8 +467,8 @@ struct hshentry * genrevs(revno,date,author,state,store) break; } - if ((next==nil) || (cmpnumfld(revno,next->num,1)!=0)) { - error("revision number %s too low", partialno(&t,revno,2)); + 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)) { @@ -424,29 +482,33 @@ struct hshentry * genrevs(revno,date,author,state,store) if (length>2) return genbranch(next,revno,length,date,author,state,store); else { /* length == 2*/ - if ((date!=nil) && (cmpnum(date,next->date)<0)){ - error("Revision %s has date %s.", + if (date && cmpdate(date,next->date)<0) { + rcserror("Revision %s has date %s.", next->num, date2str(next->date, datebuf) ); - return nil; - } - if ((author!=nil)&&(strcmp(author,next->author)!=0)) { - error("Revision %s has author %s.",next->num,next->author); - return nil; + return 0; + } + if (author && strcmp(author,next->author)!=0) { + rcserror("Revision %s has author %s.", + next->num, next->author + ); + return 0; } - if ((state!=nil)&&(strcmp(state,next->state)!=0)) { - error("Revision %s has state %s.",next->num, - next->state==nil?"<empty>":next->state); - return nil; + if (state && strcmp(state,next->state)!=0) { + rcserror("Revision %s has state %s.", + next->num, + next->state ? next->state : "<empty>" + ); + return 0; } - *store=nil; + *store = 0; return next; } norev: bufautoend(&t); - return nil; + return 0; } @@ -456,7 +518,7 @@ struct hshentry * genrevs(revno,date,author,state,store) genbranch(bpoint, revno, length, date, author, state, store) struct hshentry const *bpoint; char const *revno; - unsigned length; + int length; char const *date, *author, *state; struct hshentries **store; /* Function: given a branchpoint, a revision number, date, author, and state, @@ -464,15 +526,15 @@ genbranch(bpoint, revno, length, date, author, state, store) * 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 nil on error + * Return 0 on error. */ { - unsigned field; + int field; register struct hshentry * next, * trail; register struct branchhead const *bhead; int result; struct buf t; - char datebuf[datesize]; + char datebuf[datesize + zonelenmax]; field = 3; bhead = bpoint->branches; @@ -480,9 +542,11 @@ genbranch(bpoint, revno, length, date, author, state, store) do { if (!bhead) { bufautobegin(&t); - error("no side branches present for %s", partialno(&t,revno,field-1)); + rcserror("no side branches present for %s", + partialno(&t,revno,field-1) + ); bufautoend(&t); - return nil; + return 0; } /*find branch head*/ @@ -491,31 +555,33 @@ genbranch(bpoint, revno, length, date, author, state, store) bhead = bhead->nextbranch; if (!bhead) { bufautobegin(&t); - error("branch number %s too high",partialno(&t,revno,field)); + rcserror("branch number %s too high", + partialno(&t,revno,field) + ); bufautoend(&t); - return nil; + return 0; } } if (result<0) { absent(revno, field); - return nil; + return 0; } next = bhead->hsh; if (length==field) { /* pick latest one on that branch */ - trail=nil; - do { if ((date==nil?1:(cmpnum(date,next->date)>=0)) && - (author==nil?1:(strcmp(author,next->author)==0)) && - (state ==nil?1:(strcmp(state, next->state) ==0)) + 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!=nil); + } while (next); - if (trail==nil) { + if (!trail) { cantfindbranch(revno, date, author, state); - return nil; + return 0; } else { /* print up to last one suitable */ next = bhead->hsh; while (next!=trail) { @@ -524,7 +590,7 @@ genbranch(bpoint, revno, length, date, author, state, store) } store1(&store, next); } - *store = nil; + *store = 0; return next; } @@ -533,44 +599,49 @@ genbranch(bpoint, revno, length, date, author, state, store) /* check low */ if (cmpnumfld(revno,next->num,field+1)<0) { bufautobegin(&t); - error("revision number %s too low", partialno(&t,revno,field+1)); + rcserror("revision number %s too low", + partialno(&t,revno,field+1) + ); bufautoend(&t); - return(nil); + return 0; } do { store1(&store, next); trail = next; next = next->next; - } while ((next!=nil) && - (cmpnumfld(revno,next->num,field+1) >=0)); + } 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(nil); + return 0; } if (length == field+1) { - if ((date!=nil) && (cmpnum(date,trail->date)<0)){ - error("Revision %s has date %s.", + if (date && cmpdate(date,trail->date)<0) { + rcserror("Revision %s has date %s.", trail->num, date2str(trail->date, datebuf) ); - return nil; + return 0; } - if ((author!=nil)&&(strcmp(author,trail->author)!=0)) { - error("Revision %s has author %s.",trail->num,trail->author); - return nil; + if (author && strcmp(author,trail->author)!=0) { + rcserror("Revision %s has author %s.", + trail->num, trail->author + ); + return 0; } - if ((state!=nil)&&(strcmp(state,trail->state)!=0)) { - error("Revision %s has state %s.",trail->num, - trail->state==nil?"<empty>":trail->state); - return nil; + 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 = nil; + *store = 0; return trail; } @@ -580,17 +651,14 @@ 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. Returns nil if not present. + * revision number. Return 0 if not present. */ { register struct assoc const *next; - next = Symbols; - while (next!=nil) { + for (next = Symbols; next; next = next->nextassoc) if (strcmp(id, next->symbol)==0) return next->num; - else next=next->nextassoc; - } - return nil; + return 0; } int expandsym(source, target) @@ -617,13 +685,12 @@ fexpandsym(source, target, fp) register char const *sp, *bp; register char *tp; char const *tlim; - register enum tokens d; - unsigned dots; + int dots; sp = source; bufalloc(target, 1); tp = target->string; - if (!sp || !*sp) { /*accept nil pointer as a legal value*/ + if (!sp || !*sp) { /* Accept 0 pointer as a legal value. */ *tp='\0'; return true; } @@ -631,7 +698,7 @@ fexpandsym(source, target, fp) if (!getoldkeys(fp)) return false; if (!*prevrev.string) { - error("working file lacks revision number"); + workerror("working file lacks revision number"); return false; } bufscpy(target, prevrev.string); @@ -641,72 +708,122 @@ fexpandsym(source, target, fp) dots = 0; for (;;) { - switch (ctab[(unsigned char)*sp]) { - case DIGIT: - while (*sp=='0' && isdigit(sp[1])) - /* skip leading zeroes */ - sp++; - do { - if (tlim <= tp) - tp = bufenlarge(target, &tlim); - } while (isdigit(*tp++ = *sp++)); - --sp; - tp[-1] = '\0'; - break; + 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; - case LETTER: - case Letter: - { - register char *p = tp; - register size_t s = tp - target->string; - do { - if (tlim <= p) - p = bufenlarge(target, &tlim); - *p++ = *sp++; - } while ((d=ctab[(unsigned char)*sp])==LETTER || - d==Letter || d==DIGIT || - (d==IDCHAR)); - if (tlim <= p) - p = bufenlarge(target, &tlim); - *p = 0; - tp = target->string + s; - } + if (id) { bp = lookupsym(tp); - if (bp==nil) { - error("Symbolic number %s is undefined.", tp); + if (!bp) { + rcserror("Symbolic name `%s' is undefined.",tp); return false; } - do { - if (tlim <= tp) - tp = bufenlarge(target, &tlim); - } while ((*tp++ = *bp++)); - break; + } 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); - default: - goto improper; - } switch (*sp++) { - case '\0': return true; - case '.': break; - default: goto improper; - } - if (!*sp) { - if (dots & 1) - goto improper; - if (!(bp = branchtip(target->string))) - return false; - bufscpy(target, bp); + 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; } - ++dots; - tp[-1] = '.'; + break; } - improper: - error("improper revision number: %s", source); + 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; @@ -728,6 +845,11 @@ tiprev() #ifdef REVTEST +/* +* Test the routines that generate a sequence of delta numbers +* needed to regenerate a given delta. +*/ + char const cmdid[] = "revtest"; int @@ -772,9 +894,9 @@ int argc; char * argv[]; gets(author); aprintf(stderr,"%s; ",author); aprintf(stderr,"State: "); gets(state); aprintf(stderr, "%s;\n", state); - target = genrevs(numricrevno.string, *date?date:(char *)nil, *author?author:(char *)nil, - *state?state:(char*)nil, &gendeltas); - if (target!=nil) { + 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; @@ -785,6 +907,6 @@ int argc; char * argv[]; exitmain(EXIT_SUCCESS); } -exiting void exiterr() { _exit(EXIT_FAILURE); } +void exiterr() { _exit(EXIT_FAILURE); } #endif diff --git a/gnu/usr.bin/rcs/lib/rcssyn.c b/gnu/usr.bin/rcs/lib/rcssyn.c index 31086c2..f254bf7 100644 --- a/gnu/usr.bin/rcs/lib/rcssyn.c +++ b/gnu/usr.bin/rcs/lib/rcssyn.c @@ -1,16 +1,15 @@ -/* - * RCS file input - */ -/********************************************************************************* +/* RCS file syntactic analysis */ + +/****************************************************************************** * Syntax Analysis. * Keyword table * Testprogram: define SYNTEST * Compatibility with Release 2: define COMPAT2=1 - ********************************************************************************* + ****************************************************************************** */ -/* Copyright (C) 1982, 1988, 1989 Walter Tichy - Copyright 1990, 1991 by Paul Eggert +/* 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. @@ -26,8 +25,9 @@ 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, 675 Mass Ave, Cambridge, MA 02139, USA. +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: @@ -35,8 +35,32 @@ Report problems and direct all questions to: */ - -/* $Log: rcssyn.c,v $ +/* + * $Log: rcssyn.c,v $ + * 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. * @@ -127,54 +151,55 @@ Report problems and direct all questions to: * generates files of release 3 format. Need not be defined if no * old RCS files generated with release 2 exist. */ -/* version SYNTEST inputs a RCS file and then prints out its internal - * data structures. -*/ #include "rcsbase.h" -libId(synId, "$Id: rcssyn.c,v 5.8 1991/08/19 03:13:55 eggert Exp $") +libId(synId, "$Id: rcssyn.c,v 5.15 1995/06/16 06:19:24 eggert Exp $") -/* forward */ 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 - Kdesc[] = "desc", - Klog[] = "log", - Ktext[] = "text"; - -static char const Kaccess[] = "access", Kauthor[] = "author", Kbranch[] = "branch", - K_branches[]= "branches", 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 - Ksymbols[] = "symbols"; + K_branches[]= "branches"; static struct buf Commleader; -static struct cbuf Ignored; struct cbuf Comment; +struct cbuf Ignored; struct access * AccessList; struct assoc * Symbols; -struct lock * Locks; +struct rcslock *Locks; int Expand; int StrictLocks; struct hshentry * Head; char const * Dbranch; -unsigned TotalDeltas; +int TotalDeltas; static void @@ -204,11 +229,11 @@ getadmin() register char const *id; struct access * newaccess; struct assoc * newassoc; - struct lock * newlock; + struct rcslock *newlock; struct hshentry * delta; struct access **LastAccess; struct assoc **LastSymbol; - struct lock **LastLock; + struct rcslock **LastLock; struct buf b; struct cbuf cb; @@ -218,7 +243,7 @@ getadmin() Head = getdnum(); getsemi(Khead); - Dbranch = nil; + Dbranch = 0; if (getkeyopt(Kbranch)) { if ((delta = getnum())) Dbranch = delta->num; @@ -240,18 +265,18 @@ getadmin() getkey(Kaccess); LastAccess = &AccessList; - while (id=getid()) { + while ((id = getid())) { newaccess = ftalloc(struct access); newaccess->login = id; *LastAccess = newaccess; LastAccess = &newaccess->nextaccess; } - *LastAccess = nil; + *LastAccess = 0; getsemi(Kaccess); getkey(Ksymbols); LastSymbol = &Symbols; - while (id = getid()) { + while ((id = getid())) { if (!getlex(COLON)) fatserror("missing ':' in symbolic name definition"); if (!(delta=getnum())) { @@ -264,31 +289,31 @@ getadmin() LastSymbol = &newassoc->nextassoc; } } - *LastSymbol = nil; + *LastSymbol = 0; getsemi(Ksymbols); getkey(Klocks); LastLock = &Locks; - while (id = getid()) { + 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 lock); + newlock = ftalloc(struct rcslock); newlock->login=id; newlock->delta=delta; *LastLock = newlock; LastLock = &newlock->nextlock; } } - *LastLock = nil; + *LastLock = 0; getsemi(Klocks); if ((StrictLocks = getkeyopt(Kstrict))) getsemi(Kstrict); - Comment.size = 0; + clear_buf(&Comment); if (getkeyopt(Kcomment)) { if (nexttok==STRING) { Comment = savestring(&Commleader); @@ -316,7 +341,7 @@ getadmin() char const *const expand_names[] = { /* These must agree with *_EXPAND in rcsbase.h. */ - "kv","kvl","k","v","o", + "kv", "kvl", "k", "v", "o", "b", 0 }; @@ -343,20 +368,30 @@ strn2expmode(s, n) void -ignorephrase() -/* Ignore a phrase introduced by a later version of RCS. */ +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. +*/ { - warnignore(); - hshenter=false; for (;;) { - switch (nexttok) { - case SEMI: hshenter=true; nextlex(); return; - case ID: - case NUM: ffree1(NextString); break; - case STRING: readstring(); break; - default: break; - } - nextlex(); + 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; + } } } @@ -374,7 +409,7 @@ getdelta() return false; hshenter = false; /*Don't enter dates into hashtable*/ - Delta->date = getkeyval(Kdate, NUM, false); + Delta->date = getkeyval(Kdate, NUM, false); hshenter=true; /*reset hshenter for revision numbers.*/ Delta->author = getkeyval(Kauthor, ID, false); @@ -389,13 +424,13 @@ getdelta() *LastBranch = NewBranch; LastBranch = &NewBranch->nextbranch; } - *LastBranch = nil; + *LastBranch = 0; getsemi(K_branches); getkey(Knext); Delta->next = num = getdnum(); getsemi(Knext); - Delta->lockedby = nil; + Delta->lockedby = 0; Delta->log.string = 0; Delta->selector = true; Delta->ig = getphrases(Kdesc); @@ -410,9 +445,10 @@ gettree() * updates the lockedby fields. */ { - struct lock const *currlock; + struct rcslock const *currlock; - while (getdelta()); + while (getdelta()) + continue; currlock=Locks; while (currlock) { currlock->delta->lockedby = currlock->login; @@ -453,7 +489,7 @@ getkeyval(keyword, token, optional) * the actual character string of <id> or <num> is returned. */ { - register char const *val = nil; + register char const *val = 0; getkey(keyword); if (nexttok==token) { @@ -468,220 +504,10 @@ getkeyval(keyword, token, optional) } - - - void -putadmin(fout) -register FILE * fout; -/* Function: Print the <admin> node read with getadmin() to file fout. - * Assumption: Variables AccessList, Symbols, Locks, StrictLocks, - * and Head have been set. - */ -{ - struct assoc const *curassoc; - struct lock const *curlock; - struct access const *curaccess; - - aprintf(fout, "%s\t%s;\n", Khead, 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; -/* Function: prints a <delta> node to fout; - */ -{ - struct branchhead const *nextbranch; - - if (node == nil) 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; -/* Function: prints the delta tree in preorder to fout, starting with root. - */ -{ - struct branchhead const *nextbranch; - - if (root==nil) return; - - if (root->selector) - putdelta(root,fout); - - puttree(root->next,fout); - - nextbranch = root->branches; - while (nextbranch) { - puttree(nextbranch->hsh,fout); - nextbranch = nextbranch->nextbranch; - } -} - - - static exiting void unexpected_EOF() { - faterror("unexpected EOF in diff output"); -} - -int putdtext(num,log,srcfilename,fout,diffmt) - char const *num, *srcfilename; - struct cbuf log; - FILE *fout; - int diffmt; -/* Function: write a deltatext-node to fout. - * num points to the deltanumber, log to the logmessage, and - * sourcefile contains the text. Doubles up all SDELIMs in both the - * log and the text; Makes sure the log message ends in \n. - * returns false on error. - * If diffmt is true, also checks that text is valid diff -n output. - */ -{ - RILE *fin; - int result; - if (!(fin = Iopen(srcfilename, "r", (struct stat*)0))) { - eerror(srcfilename); - return false; - } - result = putdftext(num,log,fin,fout,diffmt); - Ifclose(fin); - return result; -} - - 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); -} - - int -putdftext(num,log,finfile,foutfile,diffmt) - char const *num; - struct cbuf log; - 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,num,Klog); - /* put log */ - putstring(fout, true, log, true); - /* put text */ - aprintf(fout, "\n%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); - return true; + rcsfaterror("unexpected EOF in diff output"); } void @@ -693,18 +519,18 @@ initdiffcmd(dc) dc->dafter = 0; } - static exiting void + static void badDiffOutput(buf) char const *buf; { - faterror("bad diff output line: %s", buf); + rcsfaterror("bad diff output line: %s", buf); } - static exiting void + static void diffLineNumberTooLarge(buf) char const *buf; { - faterror("diff line number too large: %s", buf); + rcsfaterror("diff line number too large: %s", buf); } int @@ -726,16 +552,16 @@ getdiffcmd(finfile, delimiter, foutfile, dc) register FILE *fout; register char *p; register RILE *fin; - unsigned long line1, nlines, t; + long line1, nlines, t; char buf[BUFSIZ]; fin = finfile; fout = foutfile; setupcache(fin); cache(fin); - cachegeteof(c, { if (delimiter) unexpected_EOF(); return -1; } ); + cachegeteof_(c, { if (delimiter) unexpected_EOF(); return -1; } ) if (delimiter) { if (c==SDELIM) { - cacheget(c); + cacheget_(c) if (c==SDELIM) { buf[0] = c; buf[1] = 0; @@ -751,23 +577,22 @@ getdiffcmd(finfile, delimiter, foutfile, dc) p = buf; do { if (buf+BUFSIZ-2 <= p) { - faterror("diff output command line too long"); + rcsfaterror("diff output command line too long"); } *p++ = c; - cachegeteof(c, unexpected_EOF();) ; + 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)) { - t = line1 * 10; if ( - ULONG_MAX/10 < line1 || - (line1 = t + (c - '0')) < t + LONG_MAX/10 < line1 || + (t = line1 * 10, (line1 = t + (c - '0')) < t) ) diffLineNumberTooLarge(buf); c = *p++; @@ -776,14 +601,15 @@ getdiffcmd(finfile, delimiter, foutfile, dc) c = *p++; nlines = 0; while (isdigit(c)) { - t = nlines * 10; if ( - ULONG_MAX/10 < nlines || - (nlines = t + (c - '0')) < t + 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); } @@ -792,13 +618,13 @@ getdiffcmd(finfile, delimiter, foutfile, dc) switch (buf[0]) { case 'a': if (line1 < dc->adprev) { - faterror("backward insertion in diff output: %s", buf); + rcsfaterror("backward insertion in diff output: %s", buf); } dc->adprev = line1 + 1; break; case 'd': if (line1 < dc->adprev || line1 < dc->dafter) { - faterror("backward deletion in diff output: %s", buf); + rcsfaterror("backward deletion in diff output: %s", buf); } dc->adprev = line1; dc->dafter = line1 + nlines; @@ -818,6 +644,8 @@ getdiffcmd(finfile, delimiter, foutfile, dc) #ifdef SYNTEST +/* Input an RCS file and print its internal data structures. */ + char const cmdid[] = "syntest"; int @@ -834,10 +662,10 @@ int argc; char * argv[]; } Lexinit(); getadmin(); - putadmin(stdout); + fdlock = STDOUT_FILENO; + putadmin(); gettree(); - puttree(Head,stdout); getdesc(true); @@ -849,9 +677,6 @@ int argc; char * argv[]; exitmain(EXIT_SUCCESS); } - -exiting void exiterr() { _exit(EXIT_FAILURE); } - +void exiterr() { _exit(EXIT_FAILURE); } #endif - diff --git a/gnu/usr.bin/rcs/lib/rcstime.c b/gnu/usr.bin/rcs/lib/rcstime.c new file mode 100644 index 0000000..a49a857 --- /dev/null +++ b/gnu/usr.bin/rcs/lib/rcstime.c @@ -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, "$Id: rcstime.c,v 1.4 1995/06/16 06:19:24 eggert Exp $") + +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; +} diff --git a/gnu/usr.bin/rcs/lib/rcsutil.c b/gnu/usr.bin/rcs/lib/rcsutil.c index c523ccf1..d226ff4 100644 --- a/gnu/usr.bin/rcs/lib/rcsutil.c +++ b/gnu/usr.bin/rcs/lib/rcsutil.c @@ -1,9 +1,7 @@ -/* - * RCS utilities - */ +/* RCS utility functions */ -/* Copyright (C) 1982, 1988, 1989 Walter Tichy - Copyright 1990, 1991 by Paul Eggert +/* 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. @@ -19,8 +17,9 @@ 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, 675 Mass Ave, Cambridge, MA 02139, USA. +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: @@ -31,7 +30,59 @@ Report problems and direct all questions to: -/* $Log: rcsutil.c,v $ +/* + * $Log: rcsutil.c,v $ + * Revision 5.20 1995/06/16 06:19:24 eggert + * (catchsig): Remove `return'. + * Update FSF address. + * + * Revision 5.19 1995/06/02 18:19:00 eggert + * (catchsigaction): New name for `catchsig', for sa_sigaction signature. + * Use nRCS even if !has_psiginfo, to remove unused variable warning. + * (setup_catchsig): Use sa_sigaction only if has_sa_sigaction. + * Use ENOTSUP only if defined. + * + * Revision 5.18 1995/06/01 16:23:43 eggert + * (catchsig, restoreints, setup_catchsig): Use SA_SIGINFO, not has_psiginfo, + * to determine whether to use SA_SIGINFO feature, + * but also check at runtime whether the feature works. + * (catchsig): If an mmap_signal occurs, report the affected file name. + * (unsupported_SA_SIGINFO, accessName): New variables. + * (setup_catchsig): If using SA_SIGINFO, use sa_sigaction, not sa_handler. + * If SA_SIGINFO fails, fall back on sa_handler method. + * + * (readAccessFilenameBuffer, dupSafer, fdSafer, fopenSafer): New functions. + * (concatenate): Remove. + * + * (runv): Work around bad_wait_if_SIGCHLD_ignored bug. + * Remove reference to OPEN_O_WORK. + * + * Revision 5.17 1994/03/20 04:52:58 eggert + * Specify subprocess input via file descriptor, not file name. + * Avoid messing with I/O buffers in the child process. + * Define dup in terms of F_DUPFD if it exists. + * Move setmtime to rcsedit.c. Remove lint. + * + * Revision 5.16 1993/11/09 17:40:15 eggert + * -V now prints version on stdout and exits. + * + * Revision 5.15 1993/11/03 17:42:27 eggert + * Use psiginfo and setreuid if available. Move date2str to maketime.c. + * + * Revision 5.14 1992/07/28 16:12:44 eggert + * Add -V. has_sigaction overrides sig_zaps_handler. Fix -M bug. + * Add mmap_signal, which minimizes signal handling for non-mmap hosts. + * + * Revision 5.13 1992/02/17 23:02:28 eggert + * Work around NFS mmap SIGBUS problem. Add -T support. + * + * Revision 5.12 1992/01/24 18:44:19 eggert + * Work around NFS mmap bug that leads to SIGBUS core dumps. lint -> RCS_lint + * + * Revision 5.11 1992/01/06 02:42:34 eggert + * O_BINARY -> OPEN_O_WORK + * while (E) ; -> while (E) continue; + * * Revision 5.10 1991/10/07 17:32:46 eggert * Support piece tables even if !has_mmap. * @@ -136,7 +187,7 @@ Report problems and direct all questions to: #include "rcsbase.h" -libId(utilId, "$Id: rcsutil.c,v 5.10 1991/10/07 17:32:46 eggert Exp $") +libId(utilId, "$Id: rcsutil.c,v 5.20 1995/06/16 06:19:24 eggert Exp $") #if !has_memcmp int @@ -170,7 +221,7 @@ memcpy(s1, s2, n) } #endif -#if lint +#if RCS_lint malloc_type lintalloc; #endif @@ -187,6 +238,7 @@ struct alloclist { static struct alloclist *alloced; + static malloc_type okalloc P((malloc_type)); static malloc_type okalloc(p) malloc_type p; @@ -242,7 +294,7 @@ ffree() tfree(p->alloc); tfree(p); } - alloced = nil; + alloced = 0; } void @@ -297,16 +349,18 @@ getusername(suspicious) /* Prefer getenv() unless suspicious; it's much faster. */ # if getlogin_is_secure (suspicious - || - !(name = cgetenv("LOGNAME")) - && !(name = cgetenv("USER"))) + || ( + !(name = cgetenv("LOGNAME")) + && !(name = cgetenv("USER")) + )) && !(name = getlogin()) # else suspicious - || + || ( !(name = cgetenv("LOGNAME")) && !(name = cgetenv("USER")) && !(name = getlogin()) + ) # endif ) { #if has_getuid && has_getpwuid @@ -320,7 +374,7 @@ getusername(suspicious) #if has_setuid faterror("setuid not supported"); #else - faterror("Who are you? Please set LOGNAME."); + faterror("Who are you? Please setenv LOGNAME."); #endif #endif } @@ -343,65 +397,191 @@ getusername(suspicious) */ static sig_atomic_t volatile heldsignal, holdlevel; +#ifdef SA_SIGINFO + static int unsupported_SA_SIGINFO; + static siginfo_t bufsiginfo; + static siginfo_t *volatile heldsiginfo; +#endif - static signal_type -catchsig(s) - int s; -{ - char const *sname; - char buf[BUFSIZ]; -#if sig_zaps_handler - /* If a signal arrives before we reset the signal handler, we lose. */ - VOID signal(s, SIG_IGN); -#endif - if (holdlevel) { - heldsignal = s; - return; - } - ignoreints(); - setrid(); - if (!quietflag) { - sname = nil; -#if has_sys_siglist && defined(NSIG) - if ((unsigned)s < NSIG) { -# ifndef sys_siglist - extern char const *sys_siglist[]; -# endif - sname = sys_siglist[s]; - } +#if has_NFS && has_mmap && large_memory && mmap_signal + static char const *accessName; + + void + readAccessFilenameBuffer(filename, p) + char const *filename; + unsigned char const *p; + { + unsigned char volatile t; + accessName = filename; + t = *p; + accessName = 0; + } #else - switch (s) { -#ifdef SIGHUP - case SIGHUP: sname = "Hangup"; break; +# define accessName ((char const *) 0) #endif -#ifdef SIGINT + + +#if !has_psignal + +# define psignal my_psignal + static void my_psignal P((int,char const*)); + static void +my_psignal(sig, s) + int sig; + char const *s; +{ + char const *sname = "Unknown signal"; +# if has_sys_siglist && defined(NSIG) + if ((unsigned)sig < NSIG) + sname = sys_siglist[sig]; +# else + switch (sig) { +# ifdef SIGHUP + case SIGHUP: sname = "Hangup"; break; +# endif +# ifdef SIGINT case SIGINT: sname = "Interrupt"; break; -#endif -#ifdef SIGPIPE +# endif +# ifdef SIGPIPE case SIGPIPE: sname = "Broken pipe"; break; -#endif -#ifdef SIGQUIT +# endif +# ifdef SIGQUIT case SIGQUIT: sname = "Quit"; break; -#endif -#ifdef SIGTERM +# endif +# ifdef SIGTERM case SIGTERM: sname = "Terminated"; break; -#endif -#ifdef SIGXCPU +# endif +# ifdef SIGXCPU case SIGXCPU: sname = "Cputime limit exceeded"; break; -#endif -#ifdef SIGXFSZ +# endif +# ifdef SIGXFSZ case SIGXFSZ: sname = "Filesize limit exceeded"; break; -#endif +# endif +# if has_mmap && large_memory +# if defined(SIGBUS) && mmap_signal==SIGBUS + case SIGBUS: sname = "Bus error"; break; +# endif +# if defined(SIGSEGV) && mmap_signal==SIGSEGV + case SIGSEGV: sname = "Segmentation fault"; break; +# endif +# endif } +# endif + + /* Avoid calling sprintf etc., in case they're not reentrant. */ + { + char const *p; + char buf[BUFSIZ], *b = buf; + for (p = s; *p; *b++ = *p++) + continue; + *b++ = ':'; + *b++ = ' '; + for (p = sname; *p; *b++ = *p++) + continue; + *b++ = '\n'; + VOID write(STDERR_FILENO, buf, b - buf); + } +} +#endif + +static signal_type catchsig P((int)); +#ifdef SA_SIGINFO + static signal_type catchsigaction P((int,siginfo_t*,void*)); #endif - if (sname) - VOID sprintf(buf, "\nRCS: %s. Cleaning up.\n", sname); - else - VOID sprintf(buf, "\nRCS: Signal %d. Cleaning up.\n", s); - VOID write(STDERR_FILENO, buf, strlen(buf)); + + static signal_type +catchsig(s) + int s; +#ifdef SA_SIGINFO +{ + catchsigaction(s, (siginfo_t *)0, (void *)0); +} + static signal_type +catchsigaction(s, i, c) + int s; + siginfo_t *i; + void *c; +#endif +{ +# if sig_zaps_handler + /* If a signal arrives before we reset the handler, we lose. */ + VOID signal(s, SIG_IGN); +# endif + +# ifdef SA_SIGINFO + if (!unsupported_SA_SIGINFO) + i = 0; +# endif + + if (holdlevel) { + heldsignal = s; +# ifdef SA_SIGINFO + if (i) { + bufsiginfo = *i; + heldsiginfo = &bufsiginfo; + } +# endif + return; + } + + ignoreints(); + setrid(); + if (!quietflag) { + /* Avoid calling sprintf etc., in case they're not reentrant. */ + char const *p; + char buf[BUFSIZ], *b = buf; + + if ( ! ( +# if has_mmap && large_memory && mmap_signal + /* Check whether this signal was planned. */ + s == mmap_signal && accessName +# else + 0 +# endif + )) { + char const *nRCS = "\nRCS"; +# if defined(SA_SIGINFO) && has_si_errno && has_mmap && large_memory && mmap_signal + if (s == mmap_signal && i && i->si_errno) { + errno = i->si_errno; + perror(nRCS++); + } +# endif +# if defined(SA_SIGINFO) && has_psiginfo + if (i) + psiginfo(i, nRCS); + else + psignal(s, nRCS); +# else + psignal(s, nRCS); +# endif } - exiterr(); + + for (p = "RCS: "; *p; *b++ = *p++) + continue; +# if has_mmap && large_memory && mmap_signal + if (s == mmap_signal) { + p = accessName; + if (!p) + p = "Was a file changed by some other process? "; + else { + char const *p1; + for (p1 = p; *p1; p1++) + continue; + VOID write(STDERR_FILENO, buf, b - buf); + VOID write(STDERR_FILENO, p, p1 - p); + b = buf; + p = ": Permission denied. "; + } + while (*p) + *b++ = *p++; + } +# endif + for (p = "Cleaning up.\n"; *p; *b++ = *p++) + continue; + VOID write(STDERR_FILENO, buf, b - buf); + } + exiterr(); } void @@ -414,62 +594,62 @@ ignoreints() restoreints() { if (!--holdlevel && heldsignal) +# ifdef SA_SIGINFO + VOID catchsigaction(heldsignal, heldsiginfo, (void *)0); +# else VOID catchsig(heldsignal); +# endif } -static int const sig[] = { -#ifdef SIGHUP - SIGHUP, -#endif -#ifdef SIGINT - SIGINT, -#endif -#ifdef SIGPIPE - SIGPIPE, -#endif -#ifdef SIGQUIT - SIGQUIT, -#endif -#ifdef SIGTERM - SIGTERM, -#endif -#ifdef SIGXCPU - SIGXCPU, -#endif -#ifdef SIGXFSZ - SIGXFSZ, -#endif -}; -#define SIGS (sizeof(sig)/sizeof(*sig)) - +static void setup_catchsig P((int const*,int)); #if has_sigaction + static void check_sig P((int)); static void check_sig(r) int r; { if (r != 0) - efaterror("signal"); + efaterror("signal handling"); } static void - setup_catchsig() + setup_catchsig(sig, sigs) + int const *sig; + int sigs; { - register int i; - sigset_t blocked; + register int i, j; struct sigaction act; - check_sig(sigemptyset(&blocked)); - for (i=SIGS; 0<=--i; ) - check_sig(sigaddset(&blocked, sig[i])); - for (i=SIGS; 0<=--i; ) { - check_sig(sigaction(sig[i], (struct sigaction*)nil, &act)); + for (i=sigs; 0<=--i; ) { + check_sig(sigaction(sig[i], (struct sigaction*)0, &act)); if (act.sa_handler != SIG_IGN) { - act.sa_handler = catchsig; - act.sa_mask = blocked; - check_sig(sigaction(sig[i], &act, (struct sigaction*)nil)); + act.sa_handler = catchsig; +# ifdef SA_SIGINFO + if (!unsupported_SA_SIGINFO) { +# if has_sa_sigaction + act.sa_sigaction = catchsigaction; +# else + act.sa_handler = catchsigaction; +# endif + act.sa_flags |= SA_SIGINFO; + } +# endif + for (j=sigs; 0<=--j; ) + check_sig(sigaddset(&act.sa_mask, sig[j])); + if (sigaction(sig[i], &act, (struct sigaction*)0) != 0) { +# if defined(SA_SIGINFO) && defined(ENOTSUP) + if (errno == ENOTSUP && !unsupported_SA_SIGINFO) { + /* Turn off use of SA_SIGINFO and try again. */ + unsupported_SA_SIGINFO = 1; + i++; + continue; + } +# endif + check_sig(-1); + } } } } @@ -478,16 +658,18 @@ static int const sig[] = { #if has_sigblock static void - setup_catchsig() + setup_catchsig(sig, sigs) + int const *sig; + int sigs; { register int i; int mask; mask = 0; - for (i=SIGS; 0<=--i; ) + for (i=sigs; 0<=--i; ) mask |= sigmask(sig[i]); mask = sigblock(mask); - for (i=SIGS; 0<=--i; ) + for (i=sigs; 0<=--i; ) if ( signal(sig[i], catchsig) == SIG_IGN && signal(sig[i], SIG_IGN) != catchsig @@ -499,11 +681,13 @@ static int const sig[] = { #else static void - setup_catchsig() + setup_catchsig(sig, sigs) + int const *sig; + int sigs; { register i; - for (i=SIGS; 0<=--i; ) + for (i=sigs; 0<=--i; ) if ( signal(sig[i], SIG_IGN) != SIG_IGN && signal(sig[i], catchsig) != SIG_IGN @@ -514,16 +698,68 @@ static int const sig[] = { #endif #endif + +static int const regsigs[] = { +# ifdef SIGHUP + SIGHUP, +# endif +# ifdef SIGINT + SIGINT, +# endif +# ifdef SIGPIPE + SIGPIPE, +# endif +# ifdef SIGQUIT + SIGQUIT, +# endif +# ifdef SIGTERM + SIGTERM, +# endif +# ifdef SIGXCPU + SIGXCPU, +# endif +# ifdef SIGXFSZ + SIGXFSZ, +# endif +}; + void catchints() { static int catching_ints; if (!catching_ints) { - catching_ints = true; - setup_catchsig(); + catching_ints = true; + setup_catchsig(regsigs, (int) (sizeof(regsigs)/sizeof(*regsigs))); } } +#if has_mmap && large_memory && mmap_signal + + /* + * If you mmap an NFS file, and someone on another client removes the last + * link to that file, and you later reference an uncached part of that file, + * you'll get a SIGBUS or SIGSEGV (depending on the operating system). + * Catch the signal and report the problem to the user. + * Unfortunately, there's no portable way to differentiate between this + * problem and actual bugs in the program. + * This NFS problem is rare, thank goodness. + * + * This can also occur if someone truncates the file, even without NFS. + */ + + static int const mmapsigs[] = { mmap_signal }; + + void + catchmmapints() + { + static int catching_mmap_ints; + if (!catching_mmap_ints) { + catching_mmap_ints = true; + setup_catchsig(mmapsigs, (int)(sizeof(mmapsigs)/sizeof(*mmapsigs))); + } + } +#endif + #endif /* has_signal */ @@ -535,7 +771,7 @@ fastcopy(inf,outf) */ { #if large_memory -# if has_mmap +# if maps_memory awrite((char const*)inf->ptr, (size_t)(inf->lim - inf->ptr), outf); inf->ptr = inf->lim; # else @@ -586,10 +822,81 @@ awrite(buf, chars, f) Oerror(); } +/* dup a file descriptor; the result must not be stdin, stdout, or stderr. */ + static int dupSafer P((int)); + static int +dupSafer(fd) + int fd; +{ +# ifdef F_DUPFD + return fcntl(fd, F_DUPFD, STDERR_FILENO + 1); +# else + int e, f, i, used = 0; + while (STDIN_FILENO <= (f = dup(fd)) && f <= STDERR_FILENO) + used |= 1<<f; + e = errno; + for (i = STDIN_FILENO; i <= STDERR_FILENO; i++) + if (used & (1<<i)) + VOID close(i); + errno = e; + return f; +# endif +} +/* Renumber a file descriptor so that it's not stdin, stdout, or stderr. */ + int +fdSafer(fd) + int fd; +{ + if (STDIN_FILENO <= fd && fd <= STDERR_FILENO) { + int f = dupSafer(fd); + int e = errno; + VOID close(fd); + errno = e; + fd = f; + } + return fd; +} +/* Like fopen, except the result is never stdin, stdout, or stderr. */ + FILE * +fopenSafer(filename, type) + char const *filename; + char const *type; +{ + FILE *stream = fopen(filename, type); + if (stream) { + int fd = fileno(stream); + if (STDIN_FILENO <= fd && fd <= STDERR_FILENO) { + int f = dupSafer(fd); + if (f < 0) { + int e = errno; + VOID fclose(stream); + errno = e; + return 0; + } + if (fclose(stream) != 0) { + int e = errno; + VOID close(f); + errno = e; + return 0; + } + stream = fdopen(f, type); + } + } + return stream; +} +#ifdef F_DUPFD +# undef dup +# define dup(fd) fcntl(fd, F_DUPFD, 0) +#endif + + +#if has_fork || has_spawn + + static int movefd P((int,int)); static int movefd(old, new) int old, new; @@ -604,6 +911,7 @@ movefd(old, new) return close(old)==0 ? new : -1; } + static int fdreopen P((int,char const*,int)); static int fdreopen(fd, file, flags) int fd; @@ -620,38 +928,26 @@ fdreopen(fd, file, flags) return movefd(newfd, fd); } -#if !has_spawn - static void -tryopen(fd,file,flags) - int fd, flags; - char const *file; -{ - if (file && fdreopen(fd,file,flags) != fd) - efaterror(file); -} -#else - static int -tryopen(fd,file,flags) - int fd, flags; - char const *file; -{ - int newfd = -1; - if (file && ((newfd=dup(fd)) < 0 || fdreopen(fd,file,flags) != fd)) - efaterror(file); - return newfd; -} +#if has_spawn + static void redirect P((int,int)); static void redirect(old, new) int old, new; +/* +* Move file descriptor OLD to NEW. +* If OLD is -1, do nothing. +* If OLD is -2, just close NEW. +*/ { - if (0 <= old && (close(new) != 0 || movefd(old,new) < 0)) + if ((old != -1 && close(new) != 0) || (0 <= old && movefd(old,new) < 0)) efaterror("spawn I/O redirection"); } #endif +#else /* !has_fork && !has_spawn */ -#if !has_fork && !has_spawn + static void bufargcat P((struct buf*,int,char const*)); static void bufargcat(b, c, s) register struct buf *b; @@ -681,53 +977,145 @@ bufargcat(b, c, s) *p++ = '\''; *p = 0; } + #endif +#if !has_spawn && has_fork /* -* Run a command specified by the strings in 'inoutargs'. -* inoutargs[0], if nonnil, is the name of the input file. -* inoutargs[1], if nonnil, is the name of the output file. -* inoutargs[2..] form the command to be run. +* Output the string S to stderr, without touching any I/O buffers. +* This is useful if you are a child process, whose buffers are usually wrong. +* Exit immediately if the write does not completely succeed. +*/ +static void write_stderr P((char const *)); + static void +write_stderr(s) + char const *s; +{ + size_t slen = strlen(s); + if (write(STDERR_FILENO, s, slen) != slen) + _exit(EXIT_TROUBLE); +} +#endif + +/* +* Run a command. +* infd, if not -1, is the input file descriptor. +* outname, if nonzero, is the name of the output file. +* args[1..] form the command to be run; args[0] might be modified. */ int -runv(inoutargs) - char const **inoutargs; +runv(infd, outname, args) + int infd; + char const *outname, **args; { - register char const **p; int wstatus; +#if bad_wait_if_SIGCHLD_ignored + static int fixed_SIGCHLD; + if (!fixed_SIGCHLD) { + fixed_SIGCHLD = true; +# ifndef SIGCHLD +# define SIGCHLD SIGCLD +# endif + VOID signal(SIGCHLD, SIG_DFL); + } +#endif + oflush(); eflush(); { #if has_spawn int in, out; - p = inoutargs; - in = tryopen(STDIN_FILENO, *p++, O_BINARY|O_RDONLY); - out = tryopen(STDOUT_FILENO, *p++, O_BINARY|O_CREAT|O_TRUNC|O_WRONLY); - wstatus = spawn_RCS(0, *p, (char*const*)p); - if (wstatus == -1 && errno == ENOEXEC) { - *--p = RCS_SHELL; - wstatus = spawnv(0, *p, (char*const*)p); + char const *file; + + in = -1; + if (infd != -1 && infd != STDIN_FILENO) { + if ((in = dup(STDIN_FILENO)) < 0) { + if (errno != EBADF) + efaterror("spawn input setup"); + in = -2; + } else { +# ifdef F_DUPFD + if (close(STDIN_FILENO) != 0) + efaterror("spawn input close"); +# endif + } + if ( +# ifdef F_DUPFD + fcntl(infd, F_DUPFD, STDIN_FILENO) != STDIN_FILENO +# else + dup2(infd, STDIN_FILENO) != STDIN_FILENO +# endif + ) + efaterror("spawn input redirection"); } + + out = -1; + if (outname) { + if ((out = dup(STDOUT_FILENO)) < 0) { + if (errno != EBADF) + efaterror("spawn output setup"); + out = -2; + } + if (fdreopen( + STDOUT_FILENO, outname, + O_CREAT | O_TRUNC | O_WRONLY + ) < 0) + efaterror(outname); + } + + wstatus = spawn_RCS(0, args[1], (char**)(args + 1)); +# ifdef RCS_SHELL + if (wstatus == -1 && errno == ENOEXEC) { + args[0] = RCS_SHELL; + wstatus = spawnv(0, args[0], (char**)args); + } +# endif redirect(in, STDIN_FILENO); redirect(out, STDOUT_FILENO); #else #if has_fork pid_t pid; -# if !has_waitpid - pid_t w; -# endif if (!(pid = vfork())) { - p = inoutargs; - tryopen(STDIN_FILENO, *p++, O_BINARY|O_RDONLY); - tryopen(STDOUT_FILENO, *p++, O_BINARY|O_CREAT|O_TRUNC|O_WRONLY); - VOID exec_RCS(*p, (char*const*)p); - if (errno == ENOEXEC) { - *--p = RCS_SHELL; - VOID execv(*p, (char*const*)p); + char const *notfound; + if (infd != -1 && infd != STDIN_FILENO && ( +# ifdef F_DUPFD + (VOID close(STDIN_FILENO), + fcntl(infd, F_DUPFD, STDIN_FILENO) != STDIN_FILENO) +# else + dup2(infd, STDIN_FILENO) != STDIN_FILENO +# endif + )) { + /* Avoid perror since it may misuse buffers. */ + write_stderr(args[1]); + write_stderr(": I/O redirection failed\n"); + _exit(EXIT_TROUBLE); } - VOID write(STDERR_FILENO, *p, strlen(*p)); - VOID write(STDERR_FILENO, ": not found\n", 12); + + if (outname) + if (fdreopen( + STDOUT_FILENO, outname, + O_CREAT | O_TRUNC | O_WRONLY + ) < 0) { + /* Avoid perror since it may misuse buffers. */ + write_stderr(args[1]); + write_stderr(": "); + write_stderr(outname); + write_stderr(": cannot create\n"); + _exit(EXIT_TROUBLE); + } + VOID exec_RCS(args[1], (char**)(args + 1)); + notfound = args[1]; +# ifdef RCS_SHELL + if (errno == ENOEXEC) { + args[0] = notfound = RCS_SHELL; + VOID execv(args[0], (char**)args); + } +# endif + + /* Avoid perror since it may misuse buffers. */ + write_stderr(notfound); + write_stderr(": not found\n"); _exit(EXIT_TROUBLE); } if (pid < 0) @@ -736,83 +1124,71 @@ runv(inoutargs) if (waitpid(pid, &wstatus, 0) < 0) efaterror("waitpid"); # else - do { - if ((w = wait(&wstatus)) < 0) - efaterror("wait"); - } while (w != pid); + { + pid_t w; + do { + if ((w = wait(&wstatus)) < 0) + efaterror("wait"); + } while (w != pid); + } # endif #else static struct buf b; + char const *p; /* Use system(). On many hosts system() discards signals. Yuck! */ - p = inoutargs+2; + p = args + 1; bufscpy(&b, *p); while (*++p) bufargcat(&b, ' ', *p); - if (inoutargs[0]) - bufargcat(&b, '<', inoutargs[0]); - if (inoutargs[1]) - bufargcat(&b, '>', inoutargs[1]); + if (infd != -1 && infd != STDIN_FILENO) { + char redirection[32]; + VOID sprintf(redirection, "<&%d", infd); + bufscat(&b, redirection); + } + if (outname) + bufargcat(&b, '>', outname); wstatus = system(b.string); #endif #endif } - if (!WIFEXITED(wstatus)) - faterror("%s failed", inoutargs[2]); + if (!WIFEXITED(wstatus)) { + if (WIFSIGNALED(wstatus)) { + psignal(WTERMSIG(wstatus), args[1]); + fatcleanup(1); + } + faterror("%s failed for unknown reason", args[1]); + } return WEXITSTATUS(wstatus); } #define CARGSMAX 20 /* * Run a command. -* The first two arguments are the input and output files (if nonnil); -* the rest specify the command and its arguments. +* infd, if not -1, is the input file descriptor. +* outname, if nonzero, is the name of the output file. +* The remaining arguments specify the command and its arguments. */ int #if has_prototypes -run(char const *infile, char const *outfile, ...) +run(int infd, char const *outname, ...) #else /*VARARGS2*/ -run(infile, outfile, va_alist) - char const *infile; - char const *outfile; +run(infd, outname, va_alist) + int infd; + char const *outname; va_dcl #endif { va_list ap; char const *rgargs[CARGSMAX]; - register i = 0; - rgargs[0] = infile; - rgargs[1] = outfile; - vararg_start(ap, outfile); - for (i = 2; (rgargs[i++] = va_arg(ap, char const*)); ) + register int i; + vararg_start(ap, outname); + for (i = 1; (rgargs[i++] = va_arg(ap, char const*)); ) if (CARGSMAX <= i) faterror("too many command arguments"); va_end(ap); - return runv(rgargs); -} - - - char const * -date2str(date, datebuf) - char const date[datesize]; - char datebuf[datesize]; -/* -* Format a user-readable form of the RCS format DATE into the buffer DATEBUF. -* Yield DATEBUF. -*/ -{ - register char const *p = date; - - while (*p++ != '.') - ; - 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 - ); - return datebuf; + return runv(infd, outname, rgargs); } @@ -825,23 +1201,28 @@ setRCSversion(str) static int oldversion; register char const *s = str + 2; - int v = VERSION_DEFAULT; - - if (oldversion) - redefined('V'); - oldversion = true; if (*s) { + int v = VERSION_DEFAULT; + + if (oldversion) + redefined('V'); + oldversion = true; v = 0; while (isdigit(*s)) v = 10*v + *s++ - '0'; if (*s) - faterror("%s isn't a number", str); - if (v < VERSION_min || VERSION_max < v) - faterror("%s out of range %d..%d", str, VERSION_min, VERSION_max); + error("%s isn't a number", str); + else if (v < VERSION_min || VERSION_max < v) + error("%s out of range %d..%d", + str, VERSION_min, VERSION_max + ); + + RCSversion = VERSION(v); + } else { + printf("RCS version %s\n", RCS_version_string); + exit(0); } - - RCSversion = VERSION(v); } int @@ -850,7 +1231,7 @@ getRCSINIT(argc, argv, newargv) char **argv, ***newargv; { register char *p, *q, **pp; - unsigned n; + size_t n; if (!(q = cgetenv("RCSINIT"))) *newargv = argv; @@ -919,7 +1300,7 @@ getRCSINIT(argc, argv, newargv) } copyrest: while ((*pp++ = *argv++)) - ; + continue; } return argc; } @@ -947,8 +1328,11 @@ getRCSINIT(argc, argv, newargv) */ static void -set_uid_to(u) - uid_t u; +#if has_prototypes +set_uid_to(uid_t u) +#else + set_uid_to(u) uid_t u; +#endif /* Become user u. */ { static int looping; @@ -956,8 +1340,13 @@ set_uid_to(u) if (euid() == ruid()) return; #if (has_fork||has_spawn) && DIFF_ABSOLUTE - if (seteuid(u) != 0) - efaterror("setuid"); +# if has_setreuid + if (setreuid(u==euid() ? ruid() : euid(), u) != 0) + efaterror("setuid"); +# else + if (seteuid(u) != 0) + efaterror("setuid"); +# endif #endif if (geteuid() != u) { if (looping) @@ -992,3 +1381,12 @@ setrid() set_uid_to(ruid()); } #endif + + time_t +now() +{ + static time_t t; + if (!t && time(&t) == -1) + efaterror("time"); + return t; +} diff --git a/gnu/usr.bin/rcs/lib/version.c b/gnu/usr.bin/rcs/lib/version.c new file mode 100644 index 0000000..81f5585 --- /dev/null +++ b/gnu/usr.bin/rcs/lib/version.c @@ -0,0 +1,2 @@ +#include "rcsbase.h" +char const RCS_version_string[] = "5.7"; diff --git a/gnu/usr.bin/rcs/merge/Makefile b/gnu/usr.bin/rcs/merge/Makefile index d14afb2..9022bc4 100644 --- a/gnu/usr.bin/rcs/merge/Makefile +++ b/gnu/usr.bin/rcs/merge/Makefile @@ -1,7 +1,8 @@ -PROG= merge - +PROG= merge SRCS= merge.c -LDADD= -L${.CURDIR}/../lib/obj -lrcs CFLAGS+= -I${.CURDIR}/../lib +LDADD= ${LIBRCS} +DPADD= ${LIBRCS} +.include "../../Makefile.inc" .include <bsd.prog.mk> diff --git a/gnu/usr.bin/rcs/merge/merge.1 b/gnu/usr.bin/rcs/merge/merge.1 index 8b1957f..e4860d2 100644 --- a/gnu/usr.bin/rcs/merge/merge.1 +++ b/gnu/usr.bin/rcs/merge/merge.1 @@ -2,22 +2,14 @@ .ds Rv \\$3 .ds Dt \\$4 .. -.Id $Id: merge.1,v 5.3 1991/02/28 19:18:45 eggert Exp $ +.Id $Id: merge.1,v 5.7 1995/06/01 16:23:43 eggert Exp $ .TH MERGE 1 \*(Dt GNU .SH NAME merge \- three-way file merge .SH SYNOPSIS .B merge [ -.B \-L -.I label1 -[ -.B \-L -.I label3 -] ] [ -.B \-p -] [ -.B \-q +.I "options" ] .I "file1 file2 file3" .SH DESCRIPTION @@ -28,11 +20,8 @@ to .I file3 into .IR file1 . -The result goes to standard output if -.B \-p -is present, into -.I file1 -otherwise. +The result ordinarily goes into +.IR file1 . .B merge is useful for combining separate changes to an original. Suppose .I file2 @@ -46,57 +35,101 @@ Then .B merge combines both changes. .PP -An overlap occurs if both +A conflict occurs if both .I file1 and .I file3 have changes in a common segment of lines. -On a few older hosts where -.B diff3 -does not support the -.B \-E -option, -.B merge -does not detect overlaps, and merely supplies the changed lines from -.I file3. -On most hosts, if overlaps occur, +If a conflict is found, .B merge -outputs a message (unless the -.B \-q -option is given), -and includes both alternatives -in the result. The alternatives are delimited as follows: +normally outputs a warning and brackets the conflict with +.B <<<<<<< +and +.B >>>>>>> +lines. +A typical conflict will look like this: .LP .RS .nf -.BI <<<<<<< " file1" -.I "lines in file1" +.BI <<<<<<< " file A" +.I "lines in file A" .B "=======" -.I "lines in file3" -.BI >>>>>>> " file3" +.I "lines in file B" +.BI >>>>>>> " file B" .RE .fi .LP -If there are overlaps, the user should edit the result and delete one of the +If there are conflicts, the user should edit the result and delete one of the alternatives. -If the -.BI \-L "\ label1" +.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 -.BI \-L "\ label3" -options are given, the labels are output in place of the names -.I file1 +.B z +instead of from files +.BR a , +.B b and -.I file3 -in overlap reports. +.BR c . +.TP +.BI \-p +Send results to standard output instead of overwriting +.IR file1 . +.TP +.BI \-q +Quiet; do not warn about conflicts. +.BI \-V +Print \*r's version number. .SH DIAGNOSTICS -Exit status is 0 for no overlaps, 1 for some overlaps, 2 for trouble. +Exit status is 0 for no conflicts, 1 for some conflicts, 2 for trouble. .SH IDENTIFICATION Author: Walter F. Tichy. .br -Revision Number: \*(Rv; Release Date: \*(Dt. +Manual Page Revision: \*(Rv; Release Date: \*(Dt. .br -Copyright \(co 1982, 1988, 1989 by Walter F. Tichy. +Copyright \(co 1982, 1988, 1989 Walter F. Tichy. .br -Copyright \(co 1990, 1991 by Paul Eggert. +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 diff --git a/gnu/usr.bin/rcs/merge/merge.c b/gnu/usr.bin/rcs/merge/merge.c index 4067c18..44d33a3 100644 --- a/gnu/usr.bin/rcs/merge/merge.c +++ b/gnu/usr.bin/rcs/merge/merge.c @@ -1,6 +1,6 @@ /* merge - three-way file merge */ -/* Copyright 1991 by Paul Eggert +/* Copyright 1991, 1992, 1993, 1994, 1995 Paul Eggert Distributed under license by the Free Software Foundation, Inc. This file is part of RCS. @@ -16,8 +16,9 @@ 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, 675 Mass Ave, Cambridge, MA 02139, USA. +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: @@ -27,44 +28,59 @@ Report problems and direct all questions to: #include "rcsbase.h" +static void badoption P((char const*)); static char const usage[] = - "\nmerge: usage: merge [-p] [-q] [-L label1 [-L label3]] file1 file2 file3\n"; + "\nmerge: usage: merge [-AeEpqxX3] [-L lab [-L lab [-L lab]]] file1 file2 file3"; - static exiting void + static void badoption(a) char const *a; { - faterror("unknown option: %s%s", a-2, usage); + error("unknown option: %s%s", a, usage); } -mainProg(mergeId, "merge", "$Id: merge.c,v 1.2 1991/08/19 03:13:55 eggert Exp $") +mainProg(mergeId, "merge", "$Id: merge.c,v 1.8 1995/06/16 06:19:24 eggert Exp $") { register char const *a; - char const *label[2], *arg[3]; + char const *arg[3], *label[3], *edarg = 0; int labels, tostdout; labels = 0; tostdout = false; - while ((a = *++argv) && *a++ == '-') { + 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 (1<labels) + 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); + badoption(a - 2); + continue; } if (*a) - badoption(a); - --argc; + badoption(a - 2); } if (argc != 4) @@ -77,19 +93,19 @@ mainProg(mergeId, "merge", "$Id: merge.c,v 1.2 1991/08/19 03:13:55 eggert Exp $" arg[1] = argv[1]; arg[2] = argv[2]; - switch (labels) { - case 0: label[0] = arg[0]; /* fall into */ - case 1: label[1] = arg[2]; - } + for (; labels < 3; labels++) + label[labels] = arg[labels]; - exitmain(merge(tostdout, label, arg)); + if (nerror) + exiterr(); + exitmain(merge(tostdout, edarg, label, arg)); } -#if lint +#if RCS_lint # define exiterr mergeExit #endif - exiting void + void exiterr() { tempunlink(); diff --git a/gnu/usr.bin/rcs/rcs/Makefile b/gnu/usr.bin/rcs/rcs/Makefile index d62c8d1..344ab6e 100644 --- a/gnu/usr.bin/rcs/rcs/Makefile +++ b/gnu/usr.bin/rcs/rcs/Makefile @@ -1,10 +1,11 @@ -PROG= rcs - -SRCS= rcs.c -LDADD= -L${.CURDIR}/../lib/obj -lrcs +PROG= rcs +SRCS= rcs.c CFLAGS+= -I${.CURDIR}/../lib +LDADD= ${LIBRCS} +DPADD= ${LIBRCS} -MAN1= rcs.0 rcsintro.0 -MAN5= rcsfile.0 +MAN1= rcs.1 rcsintro.1 +MAN5= rcsfile.5 +.include "../../Makefile.inc" .include <bsd.prog.mk> diff --git a/gnu/usr.bin/rcs/rcs/rcs.1 b/gnu/usr.bin/rcs/rcs/rcs.1 index 9866a9c..38daf8b 100644 --- a/gnu/usr.bin/rcs/rcs/rcs.1 +++ b/gnu/usr.bin/rcs/rcs/rcs.1 @@ -2,16 +2,26 @@ .ds Rv \\$3 .ds Dt \\$4 .. -.Id $Id: rcs.1,v 5.6 1991/09/26 23:16:17 eggert Exp $ +.Id $Id: rcs.1,v 5.13 1995/06/05 08:28:35 eggert Exp $ .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 -.RI [ " options " ] " file " .\|.\|. +.IR "options file " .\|.\|. .SH DESCRIPTION .B rcs creates new \*r files or changes attributes of existing ones. @@ -72,22 +82,29 @@ is omitted, the default branch is reset to the (dynamically) highest branch on the trunk. .TP .BI \-c string -sets the comment leader to +Set the comment leader to .IR string . -The comment leader -is printed before every log message line generated by the keyword -.B $\&Log$ -during checkout (see -.BR co (1)). -This is useful for programming -languages without multi-line comments. An initial -.B ci , +.BR ci , or an .B "rcs\ \-i" without .BR \-c , -guesses the comment leader from the suffix of the working file. +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 @@ -120,9 +137,7 @@ If .I rev is omitted, lock the latest revision on the default branch. Locking prevents overlapping changes. -A lock is removed with -.B ci -or +If someone else already holds the lock, the lock is broken as with .B "rcs\ \-u" (see below). .TP @@ -133,7 +148,7 @@ 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 may unlock it. +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. @@ -163,6 +178,13 @@ Replace revision 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 @@ -235,7 +257,7 @@ from revision .I rev to the end of the branch containing .IR rev . -None of the outdated revisions may have branches or locks. +None of the outdated revisions can have branches or locks. .TP .B \-q Run quietly; do not print diagnostics. @@ -247,7 +269,7 @@ Run interactively, even if the standard input is not a terminal. Set the state attribute of the revision .I rev to -.I state . +.IR state . If .I rev is a branch number, assume the latest revision on that branch. @@ -276,7 +298,7 @@ Write descriptive text from the contents of the named into the \*r file, deleting the existing text. The .IR file -pathname may not begin with +pathname cannot begin with .BR \- . If .I file @@ -298,6 +320,19 @@ 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 . @@ -312,6 +347,19 @@ 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 @@ -359,14 +407,14 @@ The exit status is zero if and only if all operations were successful. .SH IDENTIFICATION Author: Walter F. Tichy. .br -Revision Number: \*(Rv; Release Date: \*(Dt. +Manual Page Revision: \*(Rv; Release Date: \*(Dt. .br -Copyright \(co 1982, 1988, 1989 by Walter F. Tichy. +Copyright \(co 1982, 1988, 1989 Walter F. Tichy. .br -Copyright \(co 1990, 1991 by Paul Eggert. +Copyright \(co 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert. .SH "SEE ALSO" -co(1), ci(1), ident(1), rcsdiff(1), rcsintro(1), rcsmerge(1), rlog(1), -rcsfile(5) +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, @@ -374,6 +422,15 @@ Walter F. Tichy, .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 diff --git a/gnu/usr.bin/rcs/rcs/rcs.c b/gnu/usr.bin/rcs/rcs/rcs.c index 70e7ffc..526667d 100644 --- a/gnu/usr.bin/rcs/rcs/rcs.c +++ b/gnu/usr.bin/rcs/rcs/rcs.c @@ -1,8 +1,7 @@ -/* - * RCS create/change operation - */ -/* Copyright (C) 1982, 1988, 1989 Walter Tichy - Copyright 1990, 1991 by Paul Eggert +/* Change RCS file attributes. */ + +/* 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. @@ -18,8 +17,9 @@ 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, 675 Mass Ave, Cambridge, MA 02139, USA. +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: @@ -27,10 +27,45 @@ Report problems and direct all questions to: */ - - - -/* $Log: rcs.c,v $ +/* + * $Log: rcs.c,v $ + * Revision 5.21 1995/06/16 06:19:24 eggert + * Update FSF address. + * + * Revision 5.20 1995/06/01 16:23:43 eggert + * (main): Warn if no options were given. Punctuate messages properly. + * + * (sendmail): Rewind mailmess before flushing it. + * Output another warning if mail should work but fails. + * + * (buildeltatext): Pass "--binary" if -kb and if --binary makes a difference. + * + * Revision 5.19 1994/03/17 14:05:48 eggert + * Use ORCSerror to clean up after a fatal error. Remove lint. + * Specify subprocess input via file descriptor, not file name. Remove lint. + * Flush stderr after prompt. + * + * Revision 5.18 1993/11/09 17:40:15 eggert + * -V now prints version on stdout and exits. Don't print usage twice. + * + * Revision 5.17 1993/11/03 17:42:27 eggert + * Add -z. Don't lose track of -m or -t when there are no other changes. + * Don't discard ignored phrases. Improve quality of diagnostics. + * + * Revision 5.16 1992/07/28 16:12:44 eggert + * rcs -l now asks whether you want to break the lock. + * Add -V. Set RCS file's mode and time at right moment. + * + * Revision 5.15 1992/02/17 23:02:20 eggert + * Add -T. + * + * Revision 5.14 1992/01/27 16:42:53 eggert + * Add -M. Avoid invoking umask(); it's one less thing to configure. + * Add support for bad_creat0. lint -> RCS_lint + * + * Revision 5.13 1992/01/06 02:42:34 eggert + * Avoid changing RCS file in common cases where no change can occur. + * * Revision 5.12 1991/11/20 17:58:08 eggert * Don't read the delta tree from a nonexistent RCS file. * @@ -205,69 +240,80 @@ struct delrevpair { int code; }; +static int branchpoint P((struct hshentry*,struct hshentry*)); +static int breaklock P((struct hshentry const*)); static int buildeltatext P((struct hshentries const*)); +static int doaccess P((void)); +static int doassoc P((void)); +static int dolocks P((void)); +static int domessages P((void)); +static int rcs_setstate P((char const*,char const*)); static int removerevs P((void)); static int sendmail P((char const*,char const*)); -static struct Lockrev *rmnewlocklst P((struct Lockrev const*)); -static void breaklock P((struct hshentry const*)); +static int setlock P((char const*)); +static struct Lockrev **rmnewlocklst P((char const*)); +static struct hshentry *searchcutpt P((char const*,int,struct hshentries*)); static void buildtree P((void)); static void cleanup P((void)); -static void doaccess P((void)); -static void doassoc P((void)); -static void dolocks P((void)); -static void domessages P((void)); static void getaccessor P((char*,enum changeaccess)); static void getassoclst P((int,char*)); static void getchaccess P((char const*,enum changeaccess)); static void getdelrev P((char*)); static void getmessage P((char*)); static void getstates P((char*)); -static void rcs_setstate P((char const*,char const*)); static void scanlogtext P((struct hshentry*,int)); -static void setlock P((char const*)); static struct buf numrev; static char const *headstate; static int chgheadstate, exitstatus, lockhead, unlockcaller; +static int suppress_mail; static struct Lockrev *newlocklst, *rmvlocklst; -static struct Message *messagelst, *lastmessage; -static struct Status *statelst, *laststate; -static struct Symrev *assoclst, *lastassoc; +static struct Message *messagelst, **nextmessage; +static struct Status *statelst, **nextstate; +static struct Symrev *assoclst, **nextassoc; static struct chaccess *chaccess, **nextchaccess; static struct delrevpair delrev; static struct hshentry *cuthead, *cuttail, *delstrt; static struct hshentries *gendeltas; -mainProg(rcsId, "rcs", "$Id: rcs.c,v 5.12 1991/11/20 17:58:08 eggert Exp $") +mainProg(rcsId, "rcs", "$Id: rcs.c,v 5.21 1995/06/16 06:19:24 eggert Exp $") { static char const cmdusage[] = - "\nrcs usage: rcs -{ae}logins -Afile -{blu}[rev] -cstring -{iLU} -{nNs}name[:rev] -orange -t[file] -Vn file ..."; + "\nrcs usage: rcs -{ae}logins -Afile -{blu}[rev] -cstring -{iILqTU} -ksubst -mrev:msg -{nN}name[:[rev]] -orange -sstate[:rev] -t[text] -Vn -xsuff -zzone file ..."; char *a, **newargv, *textfile; char const *branchsym, *commsyml; - int branchflag, expmode, initflag; - int e, r, strictlock, strict_selected, textflag; - mode_t defaultRCSmode; /* default mode for new RCS files */ - mode_t RCSmode; + int branchflag, changed, expmode, initflag; + int strictlock, strict_selected, textflag; + int keepRCStime, Ttimeflag; + size_t commsymlen; struct buf branchnum; - struct stat workstat; - struct Lockrev *curlock, * rmvlock, *lockpt; + struct Lockrev *lockpt; + struct Lockrev **curlock, **rmvlock; struct Status * curstate; nosetid(); + nextassoc = &assoclst; nextchaccess = &chaccess; - branchsym = commsyml = textfile = nil; + nextmessage = &messagelst; + nextstate = &statelst; + branchsym = commsyml = textfile = 0; branchflag = strictlock = false; bufautobegin(&branchnum); - curlock = rmvlock = nil; - defaultRCSmode = 0; + commsymlen = 0; + curlock = &newlocklst; + rmvlock = &rmvlocklst; expmode = -1; suffixes = X_DEFAULT; initflag= textflag = false; strict_selected = 0; + Ttimeflag = false; /* preprocessing command options */ + if (1 < argc && argv[1][0] != '-') + warn("No options were given; this usage is obsolescent."); + argc = getRCSINIT(argc, argv, &newargv); argv = newargv; while (a = *++argv, 0<--argc && *a++=='-') { @@ -286,6 +332,7 @@ mainProg(rcsId, "rcs", "$Id: rcs.c,v 5.12 1991/11/20 17:58:08 eggert Exp $") case 'c': /* change comment symbol */ if (commsyml) redefined('c'); commsyml = a; + commsymlen = strlen(a); break; case 'a': /* add new accessor */ @@ -294,11 +341,11 @@ mainProg(rcsId, "rcs", "$Id: rcs.c,v 5.12 1991/11/20 17:58:08 eggert Exp $") case 'A': /* append access list according to accessfile */ if (!*a) { - error("missing file name after -A"); + error("missing pathname after -A"); break; } *argv = a; - if (0 < pairfilenames(1,argv,rcsreadopen,true,false)) { + if (0 < pairnames(1,argv,rcsreadopen,true,false)) { while (AccessList) { getchaccess(str_save(AccessList->login),append); AccessList = AccessList->nextaccess; @@ -317,14 +364,10 @@ mainProg(rcsId, "rcs", "$Id: rcs.c,v 5.12 1991/11/20 17:58:08 eggert Exp $") lockhead = true; break; } - lockpt = talloc(struct Lockrev); + *curlock = lockpt = talloc(struct Lockrev); lockpt->revno = a; - lockpt->nextrev = nil; - if ( curlock ) - curlock->nextrev = lockpt; - else - newlocklst = lockpt; - curlock = lockpt; + lockpt->nextrev = 0; + curlock = &lockpt->nextrev; break; case 'u': /* release lock of a locked revision */ @@ -332,33 +375,28 @@ mainProg(rcsId, "rcs", "$Id: rcs.c,v 5.12 1991/11/20 17:58:08 eggert Exp $") unlockcaller=true; break; } - lockpt = talloc(struct Lockrev); + *rmvlock = lockpt = talloc(struct Lockrev); lockpt->revno = a; - lockpt->nextrev = nil; - if (rmvlock) - rmvlock->nextrev = lockpt; - else - rmvlocklst = lockpt; - rmvlock = lockpt; - - curlock = rmnewlocklst(lockpt); + lockpt->nextrev = 0; + rmvlock = &lockpt->nextrev; + curlock = rmnewlocklst(lockpt->revno); break; case 'L': /* set strict locking */ - if (strict_selected++) { /* Already selected L or U? */ + if (strict_selected) { if (!strictlock) /* Already selected -U? */ - warn("-L overrides -U."); + warn("-U overridden by -L"); } strictlock = true; + strict_selected = true; break; case 'U': /* release strict locking */ - if (strict_selected++) { /* Already selected L or U? */ + if (strict_selected) { if (strictlock) /* Already selected -L? */ - warn("-L overrides -U."); + warn("-L overridden by -U"); } - else - strictlock = false; + strict_selected = true; break; case 'n': /* add new association: error, if name exists */ @@ -381,6 +419,10 @@ mainProg(rcsId, "rcs", "$Id: rcs.c,v 5.12 1991/11/20 17:58:08 eggert Exp $") getmessage(a); break; + case 'M': /* do not send mail */ + suppress_mail = true; + break; + case 'o': /* delete revisions */ if (delrev.strt) redefined('o'); if (!*a) { @@ -406,6 +448,12 @@ mainProg(rcsId, "rcs", "$Id: rcs.c,v 5.12 1991/11/20 17:58:08 eggert Exp $") } break; + case 'T': /* do not update last-mod time for minor changes */ + if (*a) + goto unknown; + Ttimeflag = true; + break; + case 'I': interactiveflag = true; break; @@ -422,41 +470,38 @@ mainProg(rcsId, "rcs", "$Id: rcs.c,v 5.12 1991/11/20 17:58:08 eggert Exp $") setRCSversion(*argv); break; + case 'z': + zone_set(a); + break; + case 'k': /* set keyword expand mode */ if (0 <= expmode) redefined('k'); if (0 <= (expmode = str2expmode(a))) break; /* fall into */ default: - faterror("unknown option: %s%s", *argv, cmdusage); + unknown: + error("unknown option: %s%s", *argv, cmdusage); }; } /* end processing of options */ - if (argc<1) faterror("no input file%s", cmdusage); - if (nerror) { - diagnose("%s aborted\n",cmdid); - exitmain(EXIT_FAILURE); - } - if (initflag) { - defaultRCSmode = umask((mode_t)0); - VOID umask(defaultRCSmode); - defaultRCSmode = (S_IRUSR|S_IRGRP|S_IROTH) & ~defaultRCSmode; - } + /* Now handle all pathnames. */ + if (nerror) cleanup(); + else if (argc < 1) faterror("no input file%s", cmdusage); + else for (; 0 < argc; cleanup(), ++argv, --argc) { - /* now handle all filenames */ - do { ffree(); if ( initflag ) { - switch (pairfilenames(argc, argv, rcswriteopen, false, false)) { + switch (pairnames(argc, argv, rcswriteopen, false, false)) { case -1: break; /* not exist; ok */ case 0: continue; /* error */ - case 1: error("file %s exists already", RCSfilename); + case 1: rcserror("already exists"); continue; } } else { - switch (pairfilenames(argc, argv, rcswriteopen, true, false)) { + switch (pairnames(argc, argv, rcswriteopen, true, false)) { case -1: continue; /* not exist */ case 0: continue; /* errors */ case 1: break; /* file exists; ok*/ @@ -464,115 +509,134 @@ mainProg(rcsId, "rcs", "$Id: rcs.c,v 5.12 1991/11/20 17:58:08 eggert Exp $") } - /* now RCSfilename contains the name of the RCS file, and - * workfilename contains the name of the working file. + /* + * RCSname contains the name of the RCS file, and + * workname contains the name of the working file. * if !initflag, finptr contains the file descriptor for the * RCS file. The admin node is initialized. */ - diagnose("RCS file: %s\n", RCSfilename); + diagnose("RCS file: %s\n", RCSname); - RCSmode = defaultRCSmode; - if (initflag) { - if (stat(workfilename, &workstat) == 0) - RCSmode = workstat.st_mode; - } else { + changed = initflag | textflag; + keepRCStime = Ttimeflag; + if (!initflag) { if (!checkaccesslist()) continue; gettree(); /* Read the delta tree. */ - RCSmode = RCSstat.st_mode; } - RCSmode &= ~(S_IWUSR|S_IWGRP|S_IWOTH); /* update admin. node */ - if (strict_selected) StrictLocks = strictlock; - if (commsyml) { + if (strict_selected) { + changed |= StrictLocks ^ strictlock; + StrictLocks = strictlock; + } + if ( + commsyml && + ( + commsymlen != Comment.size || + memcmp(commsyml, Comment.string, commsymlen) != 0 + ) + ) { Comment.string = commsyml; Comment.size = strlen(commsyml); + changed = true; + } + if (0 <= expmode && Expand != expmode) { + Expand = expmode; + changed = true; } - if (0 <= expmode) Expand = expmode; /* update default branch */ if (branchflag && expandsym(branchsym, &branchnum)) { if (countnumflds(branchnum.string)) { - Dbranch = branchnum.string; + if (cmpnum(Dbranch, branchnum.string) != 0) { + Dbranch = branchnum.string; + changed = true; + } } else - Dbranch = nil; - } + if (Dbranch) { + Dbranch = 0; + changed = true; + } + } - doaccess(); /* Update access list. */ + changed |= doaccess(); /* Update access list. */ - doassoc(); /* Update association list. */ + changed |= doassoc(); /* Update association list. */ - dolocks(); /* Update locks. */ + changed |= dolocks(); /* Update locks. */ - domessages(); /* Update log messages. */ + changed |= domessages(); /* Update log messages. */ /* update state attribution */ if (chgheadstate) { /* change state of default branch or head */ - if (Dbranch==nil) { - if (Head==nil) - warn("can't change states in an empty tree"); - else Head->state = headstate; - } else { - rcs_setstate(Dbranch,headstate); /* Can't set directly */ - } - } - curstate = statelst; - while( curstate ) { - rcs_setstate(curstate->revno,curstate->status); - curstate = curstate->nextstatus; + if (!Dbranch) { + if (!Head) + rcswarn("can't change states in an empty tree"); + else if (strcmp(Head->state, headstate) != 0) { + Head->state = headstate; + changed = true; + } + } else + changed |= rcs_setstate(Dbranch,headstate); } + for (curstate = statelst; curstate; curstate = curstate->nextstatus) + changed |= rcs_setstate(curstate->revno,curstate->status); - cuthead = cuttail = nil; + cuthead = cuttail = 0; if (delrev.strt && removerevs()) { /* rebuild delta tree if some deltas are deleted */ if ( cuttail ) - VOID genrevs(cuttail->num, (char *)nil,(char *)nil, - (char *)nil, &gendeltas); + VOID genrevs( + cuttail->num, (char *)0, (char *)0, (char *)0, + &gendeltas + ); buildtree(); + changed = true; + keepRCStime = false; } if (nerror) continue; - putadmin(frewrite); + putadmin(); if ( Head ) puttree(Head, frewrite); putdesc(textflag,textfile); if ( Head) { - if (!delrev.strt && !messagelst) { - /* No revision was deleted and no message was changed. */ - fastcopy(finptr, frewrite); - } else { + if (delrev.strt || messagelst) { if (!cuttail || buildeltatext(gendeltas)) { advise_access(finptr, MADV_SEQUENTIAL); - scanlogtext((struct hshentry *)nil, false); + scanlogtext((struct hshentry *)0, false); /* copy rest of delta text nodes that are not deleted */ + changed = true; } } } - Izclose(&finptr); - if ( ! nerror ) { /* move temporary file to RCS file if no error */ - /* update mode */ - ignoreints(); - r = chnamemod(&frewrite, newRCSfilename, RCSfilename, RCSmode); - e = errno; - keepdirtemp(newRCSfilename); - restoreints(); - if (r != 0) { - enerror(e, RCSfilename); - error("saved in %s", newRCSfilename); - dirtempunlink(); - break; - } - diagnose("done\n"); - } else { - diagnose("%s aborted; %s unchanged.\n",cmdid,RCSfilename); - } - } while (cleanup(), - ++argv, --argc >=1); + + if (initflag) { + /* Adjust things for donerewrite's sake. */ + if (stat(workname, &RCSstat) != 0) { +# if bad_creat0 + mode_t m = umask(0); + (void) umask(m); + RCSstat.st_mode = (S_IRUSR|S_IRGRP|S_IROTH) & ~m; +# else + changed = -1; +# endif + } + RCSstat.st_nlink = 0; + keepRCStime = false; + } + if (donerewrite(changed, + keepRCStime ? RCSstat.st_mtime : (time_t)-1 + ) != 0) + break; + + diagnose("done\n"); + } tempunlink(); exitmain(exitstatus); @@ -584,13 +648,14 @@ cleanup() if (nerror) exitstatus = EXIT_FAILURE; Izclose(&finptr); Ozclose(&fcopy); - Ozclose(&frewrite); + ORCSclose(); dirtempunlink(); } - exiting void + void exiterr() { + ORCSerror(); dirtempunlink(); tempunlink(); _exit(EXIT_FAILURE); @@ -609,9 +674,10 @@ char * sp; char const *temp; int c; - while( (c=(*++sp)) == ' ' || c == '\t' || c =='\n') ; + while ((c = *++sp) == ' ' || c == '\t' || c =='\n') + continue; temp = sp; - sp = checkid(sp, ':'); /* check for invalid symbolic name */ + sp = checksym(sp, ':'); /* check for invalid symbolic name */ c = *sp; *sp = '\0'; while( c == ' ' || c == '\t' || c == '\n') c = *++sp; @@ -624,18 +690,15 @@ char * sp; pt->ssymbol = temp; pt->override = flag; if (c == '\0') /* delete symbol */ - pt->revno = nil; + pt->revno = 0; else { - while( (c = *++sp) == ' ' || c == '\n' || c == '\t') ; + while ((c = *++sp) == ' ' || c == '\n' || c == '\t') + continue; pt->revno = sp; } - pt->nextsym = nil; - if (lastassoc) - lastassoc->nextsym = pt; - else - assoclst = pt; - lastassoc = pt; - return; + pt->nextsym = 0; + *nextassoc = pt; + nextassoc = &pt->nextsym; } @@ -646,10 +709,11 @@ getchaccess(login, command) { register struct chaccess *pt; - *nextchaccess = pt = talloc(struct chaccess); + pt = talloc(struct chaccess); pt->login = login; pt->command = command; - pt->nextchaccess = nil; + pt->nextchaccess = 0; + *nextchaccess = pt; nextchaccess = &pt->nextchaccess; } @@ -668,10 +732,11 @@ getaccessor(opt, command) register char *sp; sp = opt; - while( ( c = *++sp) == ' ' || c == '\n' || c == '\t' || c == ',') ; + while ((c = *++sp) == ' ' || c == '\n' || c == '\t' || c == ',') + continue; if ( c == '\0') { if (command == erase && sp-opt == 1) { - getchaccess((char const*)nil, command); + getchaccess((char*)0, command); return; } error("missing login name after option -a or -e"); @@ -709,11 +774,8 @@ getmessage(option) pt->revno = option; pt->message = cb; pt->nextmessage = 0; - if (lastmessage) - lastmessage->nextmessage = pt; - else - messagelst = pt; - lastmessage = pt; + *nextmessage = pt; + nextmessage = &pt->nextmessage; } @@ -728,7 +790,8 @@ char *sp; struct Status *pt; register c; - while( (c=(*++sp)) ==' ' || c == '\t' || c == '\n') ; + while ((c = *++sp) ==' ' || c == '\t' || c == '\n') + continue; temp = sp; sp = checkid(sp,':'); /* check for invalid state attribute */ c = *sp; *sp = '\0'; @@ -744,16 +807,14 @@ char *sp; return; } - while( (c = *++sp) == ' ' || c == '\t' || c == '\n') ; + while ((c = *++sp) == ' ' || c == '\t' || c == '\n') + continue; pt = talloc(struct Status); pt->status = temp; pt->revno = sp; - pt->nextstatus = nil; - if (laststate) - laststate->nextstatus = pt; - else - statelst = pt; - laststate = pt; + pt->nextstatus = 0; + *nextstate = pt; + nextstate = &pt->nextstatus; } @@ -769,7 +830,8 @@ char *sp; int separator; pt = &delrev; - while((c = (*++sp)) == ' ' || c == '\n' || c == '\t') ; + while ((c = (*++sp)) == ' ' || c == '\n' || c == '\t') + continue; /* Support old ambiguous '-' syntax; this will go away. */ if (strchr(sp,':')) @@ -781,11 +843,12 @@ char *sp; } if (c == separator) { /* -o:rev */ - while( (c = (*++sp)) == ' ' || c == '\n' || c == '\t') ; + while ((c = (*++sp)) == ' ' || c == '\n' || c == '\t') + continue; pt->strt = sp; pt->code = 1; while( c != ' ' && c != '\n' && c != '\t' && c != '\0') c =(*++sp); *sp = '\0'; - pt->end = nil; + pt->end = 0; return; } else { @@ -795,15 +858,18 @@ char *sp; *sp = '\0'; while( c == ' ' || c == '\n' || c == '\t' ) c = *++sp; if ( c == '\0' ) { /* -o rev or branch */ - pt->end = nil; pt->code = 0; + pt->code = 0; + pt->end = 0; return; } if (c != separator) { - faterror("invalid range %s %s after -o", pt->strt, sp); + error("invalid range %s %s after -o", pt->strt, sp); } - while( (c = *++sp) == ' ' || c == '\n' || c == '\t') ; + while ((c = *++sp) == ' ' || c == '\n' || c == '\t') + continue; if (!c) { /* -orev: */ - pt->end = nil; pt->code = 2; + pt->code = 2; + pt->end = 0; return; } } @@ -821,11 +887,11 @@ scanlogtext(delta,edit) struct hshentry *delta; int edit; /* Function: Scans delta text nodes up to and including the one given - * by delta, or up to last one present, if delta==nil. - * For the one given by delta (if delta!=nil), the log message is saved into + * by delta, or up to last one present, if !delta. + * For the one given by delta (if delta), the log message is saved into * delta->log if delta==cuttail; the text is edited if EDIT is set, else copied. * Assumes the initial lexeme must be read in first. - * Does not advance nexttok after it is finished, except if delta==nil. + * Does not advance nexttok after it is finished, except if !delta. */ { struct hshentry const *nextdelta; @@ -835,12 +901,14 @@ scanlogtext(delta,edit) foutptr = 0; if (eoflex()) { if(delta) - faterror("can't find delta for revision %s", delta->num); + rcsfaterror("can't find delta for revision %s", + delta->num + ); return; /* no more delta text nodes */ } nextlex(); if (!(nextdelta=getnum())) - faterror("delta number corrupted"); + fatserror("delta number corrupted"); if (nextdelta->selector) { foutptr = frewrite; aprintf(frewrite,DELNUMFORM,nextdelta->num,Klog); @@ -850,17 +918,19 @@ scanlogtext(delta,edit) cb = savestring(&curlogbuf); if (!delta->log.string) delta->log = cleanlogmsg(curlogbuf.string, cb.size); - } else if (nextdelta->log.string && nextdelta->selector) { - foutptr = 0; - readstring(); - foutptr = frewrite; - putstring(foutptr, false, nextdelta->log, true); - afputc(nextc, foutptr); - } else {readstring(); - } - nextlex(); - while (nexttok==ID && strcmp(NextString,Ktext)!=0) - ignorephrase(); + nextlex(); + delta->igtext = getphrases(Ktext); + } else { + if (nextdelta->log.string && nextdelta->selector) { + foutptr = 0; + readstring(); + foutptr = frewrite; + putstring(foutptr, false, nextdelta->log, true); + afputc(nextc, foutptr); + } else + readstring(); + ignorephrases(Ktext); + } getkeystring(Ktext); if (delta==nextdelta) @@ -870,74 +940,70 @@ scanlogtext(delta,edit) } /* got the one we're looking for */ if (edit) - editstring((struct hshentry *)nil); + editstring((struct hshentry*)0); else enterstring(); } - static struct Lockrev * + static struct Lockrev ** rmnewlocklst(which) - struct Lockrev const *which; -/* Function: remove lock to revision which->revno from newlocklst */ - + char const *which; +/* Remove lock to revision WHICH from newlocklst. */ { - struct Lockrev * pt, *pre; + struct Lockrev *pt, **pre; - while( newlocklst && (! strcmp(newlocklst->revno, which->revno))){ - struct Lockrev *pn = newlocklst->nextrev; - tfree(newlocklst); - newlocklst = pn; - } - - pt = pre = newlocklst; - while( pt ) { - if ( ! strcmp(pt->revno, which->revno) ) { - pre->nextrev = pt->nextrev; + pre = &newlocklst; + while ((pt = *pre)) + if (strcmp(pt->revno, which) != 0) + pre = &pt->nextrev; + else { + *pre = pt->nextrev; tfree(pt); - pt = pre->nextrev; - } - else { - pre = pt; - pt = pt->nextrev; - } - } + } return pre; } - static void + static int doaccess() { register struct chaccess *ch; register struct access **p, *t; + register int changed = false; for (ch = chaccess; ch; ch = ch->nextchaccess) { switch (ch->command) { case erase: - if (!ch->login) - AccessList = nil; - else - for (p = &AccessList; (t = *p); ) - if (strcmp(ch->login, t->login) == 0) + if (!ch->login) { + if (AccessList) { + AccessList = 0; + changed = true; + } + } else + for (p = &AccessList; (t = *p); p = &t->nextaccess) + if (strcmp(ch->login, t->login) == 0) { *p = t->nextaccess; - else - p = &t->nextaccess; + changed = true; + break; + } break; case append: for (p = &AccessList; ; p = &t->nextaccess) if (!(t = *p)) { *p = t = ftalloc(struct access); t->login = ch->login; - t->nextaccess = nil; + t->nextaccess = 0; + changed = true; break; } else if (strcmp(ch->login, t->login) == 0) break; break; } } + return changed; } @@ -951,26 +1017,29 @@ sendmail(Delta, who) { #ifdef SENDMAIL char const *messagefile; - int old1, old2, c; + int old1, old2, c, status; FILE * mailmess; #endif aprintf(stderr, "Revision %s is already locked by %s.\n", Delta, who); + if (suppress_mail) + return true; if (!yesorno(false, "Do you want to break the lock? [ny](n): ")) return false; /* go ahead with breaking */ #ifdef SENDMAIL messagefile = maketemp(0); - if (!(mailmess = fopen(messagefile, "w"))) { + if (!(mailmess = fopenSafer(messagefile, "w+"))) { efaterror(messagefile); } aprintf(mailmess, "Subject: Broken lock on %s\n\nYour lock on revision %s of file %s\nhas been broken by %s for the following reason:\n", - basename(RCSfilename), Delta, getfullRCSname(), getcaller() + basefilename(RCSname), Delta, getfullRCSname(), getcaller() ); aputs("State the reason for breaking the lock:\n(terminate with single '.' or end of file)\n>> ", stderr); + eflush(); old1 = '\n'; old2 = ' '; for (; ;) { @@ -984,23 +1053,28 @@ sendmail(Delta, who) else { afputc(old1, mailmess); old2 = old1; old1 = c; - if (c=='\n') aputs(">> ", stderr); + if (c == '\n') { + aputs(">> ", stderr); + eflush(); + } } } + Orewind(mailmess); + aflush(mailmess); + status = run(fileno(mailmess), (char*)0, SENDMAIL, who, (char*)0); Ozclose(&mailmess); - - if (run(messagefile, (char*)nil, SENDMAIL, who, (char*)nil)) - warn("Mail may have failed."), -#else - warn("Mail notification of broken locks is not available."), + if (status == 0) + return true; + warn("Mail failed."); #endif - warn("Please tell `%s' why you broke the lock.", who); + warn("Mail notification of broken locks is not available."); + warn("Please tell `%s' why you broke the lock.", who); return(true); } - static void + static int breaklock(delta) struct hshentry const *delta; /* function: Finds the lock held by caller on delta, @@ -1009,36 +1083,28 @@ breaklock(delta) * Prints an error message if there is no such lock or error. */ { - register struct lock * next, * trail; + register struct rcslock *next, **trail; char const *num; - struct lock dummy; num=delta->num; - dummy.nextlock=next=Locks; - trail = &dummy; - while (next!=nil) { + for (trail = &Locks; (next = *trail); trail = &next->nextlock) if (strcmp(num, next->delta->num) == 0) { if ( strcmp(getcaller(),next->login) != 0 && !sendmail(num, next->login) ) { - error("%s still locked by %s", num, next->login); - return; + rcserror("revision %s still locked by %s", + num, next->login + ); + return false; } - break; /* exact match */ + diagnose("%s unlocked\n", next->delta->num); + *trail = next->nextlock; + next->delta->lockedby = 0; + return true; } - trail=next; - next=next->nextlock; - } - if (next!=nil) { - /*found one */ - diagnose("%s unlocked\n",next->delta->num); - trail->nextlock=next->nextlock; - next->delta->lockedby=nil; - Locks=dummy.nextlock; - } else { - error("no lock set on revision %s", num); - } + rcserror("no lock set on revision %s", num); + return false; } @@ -1046,14 +1112,14 @@ breaklock(delta) static struct hshentry * searchcutpt(object, length, store) char const *object; - unsigned length; + int length; struct hshentries *store; /* Function: Search store and return entry with number being object. */ -/* cuttail = nil, if the entry is Head; otherwise, cuttail */ +/* cuttail = 0, if the entry is Head; otherwise, cuttail */ /* is the entry point to the one with number being object */ { - cuthead = nil; + cuthead = 0; while (compartial(store->first->num, object, length)) { cuthead = store->first; store = store->rest; @@ -1073,36 +1139,22 @@ struct hshentry *strt, *tail; { struct hshentry *pt; - struct lock const *lockpt; - int flag; - + struct rcslock const *lockpt; - pt = strt; - flag = false; - while( pt != tail) { + for (pt = strt; pt != tail; pt = pt->next) { if ( pt->branches ){ /* a branch point */ - flag = true; - error("can't remove branch point %s", pt->num); - } - lockpt = Locks; - while(lockpt && lockpt->delta != pt) - lockpt = lockpt->nextlock; - if ( lockpt ) { - flag = true; - error("can't remove locked revision %s",pt->num); - } - pt = pt->next; - } - - if ( ! flag ) { - pt = strt; - while( pt != tail ) { - pt->selector = false; - diagnose("deleting revision %s\n",pt->num); - pt = pt->next; + rcserror("can't remove branch point %s", pt->num); + return true; } + for (lockpt = Locks; lockpt; lockpt = lockpt->nextlock) + if (lockpt->delta == pt) { + rcserror("can't remove locked revision %s", pt->num); + return true; + } + pt->selector = false; + diagnose("deleting revision %s\n",pt->num); } - return flag; + return false; } @@ -1111,34 +1163,33 @@ struct hshentry *strt, *tail; removerevs() /* Function: get the revision range to be removed, and place the */ /* first revision removed in delstrt, the revision before */ -/* delstrt in cuthead( nil, if delstrt is head), and the */ -/* revision after the last removed revision in cuttail(nil */ +/* delstrt in cuthead (0, if delstrt is head), and the */ +/* revision after the last removed revision in cuttail (0 */ /* if the last is a leaf */ { struct hshentry *target, *target2, *temp; - unsigned length; - int flag; + int length; + int cmp; - flag = false; if (!expandsym(delrev.strt, &numrev)) return 0; - target = genrevs(numrev.string, (char*)nil, (char*)nil, (char*)nil, &gendeltas); + target = genrevs(numrev.string,(char*)0,(char*)0,(char*)0,&gendeltas); if ( ! target ) return 0; - if (cmpnum(target->num, numrev.string)) flag = true; + cmp = cmpnum(target->num, numrev.string); length = countnumflds(numrev.string); if (delrev.code == 0) { /* -o rev or -o branch */ if (length & 1) temp=searchcutpt(target->num,length+1,gendeltas); - else if (flag) { - error("Revision %s doesn't exist.", numrev.string); + else if (cmp) { + rcserror("Revision %s doesn't exist.", numrev.string); return 0; } else temp = searchcutpt(numrev.string, length, gendeltas); cuttail = target->next; if ( branchpoint(temp, cuttail) ) { - cuttail = nil; + cuttail = 0; return 0; } delstrt = temp; /* first revision to be removed */ @@ -1146,7 +1197,7 @@ removerevs() } if (length & 1) { /* invalid branch after -o */ - error("invalid branch range %s after -o", numrev.string); + rcserror("invalid branch range %s after -o", numrev.string); return 0; } @@ -1162,7 +1213,7 @@ removerevs() cuttail = cuttail->next; } if ( branchpoint(temp, cuttail) ){ - cuttail = nil; + cuttail = 0; return 0; } delstrt = temp; @@ -1172,23 +1223,23 @@ removerevs() if (delrev.code == 2) { /* -o rev- */ if ( length == 2 ) { temp = searchcutpt(target->num, 1,gendeltas); - if ( flag) + if (cmp) cuttail = target; else cuttail = target->next; } else { - if ( flag){ + if (cmp) { cuthead = target; if ( !(temp = target->next) ) return 0; } else temp = searchcutpt(target->num, length, gendeltas); getbranchno(temp->num, &numrev); /* get branch number */ - target = genrevs(numrev.string, (char*)nil, (char*)nil, (char*)nil, &gendeltas); + VOID genrevs(numrev.string, (char*)0, (char*)0, (char*)0, &gendeltas); } if ( branchpoint( temp, cuttail ) ) { - cuttail = nil; + cuttail = 0; return 0; } delstrt = temp; @@ -1199,28 +1250,29 @@ removerevs() if (!expandsym(delrev.end, &numrev)) return 0; if ( length != countnumflds(numrev.string) - || length>2 && compartial(numrev.string, target->num, length-1) + || (length>2 && compartial(numrev.string, target->num, length-1)) ) { - error("invalid revision range %s-%s", target->num, numrev.string); + rcserror("invalid revision range %s-%s", + target->num, numrev.string + ); return 0; } - target2 = genrevs(numrev.string,(char*)nil,(char*)nil,(char*)nil,&gendeltas); + target2 = genrevs(numrev.string,(char*)0,(char*)0,(char*)0,&gendeltas); if ( ! target2 ) return 0; if ( length > 2) { /* delete revisions on branches */ if ( cmpnum(target->num, target2->num) > 0) { - if (cmpnum(target2->num, numrev.string)) - flag = true; - else - flag = false; + cmp = cmpnum(target2->num, numrev.string); temp = target; target = target2; target2 = temp; } - if ( flag ) { + if (cmp) { if ( ! cmpnum(target->num, target2->num) ) { - error("Revisions %s-%s don't exist.", delrev.strt,delrev.end); + rcserror("Revisions %s-%s don't exist.", + delrev.strt, delrev.end + ); return 0; } cuthead = target; @@ -1237,13 +1289,12 @@ removerevs() target2 = temp; } else - if (cmpnum(target2->num, numrev.string)) - flag = true; - else - flag = false; - if ( flag ) { + cmp = cmpnum(target2->num, numrev.string); + if (cmp) { if ( ! cmpnum(target->num, target2->num) ) { - error("Revisions %s-%s don't exist.", delrev.strt, delrev.end); + rcserror("Revisions %s-%s don't exist.", + delrev.strt, delrev.end + ); return 0; } cuttail = target2; @@ -1253,7 +1304,7 @@ removerevs() temp = searchcutpt(target->num, length, gendeltas); } if ( branchpoint(temp, cuttail) ) { - cuttail = nil; + cuttail = 0; return 0; } delstrt = temp; @@ -1262,32 +1313,29 @@ removerevs() - static void + static int doassoc() -/* Function: add or delete(if revno is nil) association */ -/* which is stored in assoclst */ - +/* Add or delete (if !revno) association that is stored in assoclst. */ { char const *p; + int changed = false; struct Symrev const *curassoc; - struct assoc * pre, * pt; + struct assoc **pre, *pt; /* add new associations */ - curassoc = assoclst; - while( curassoc ) { - if ( curassoc->revno == nil ) { /* delete symbol */ - pre = pt = Symbols; - while( pt && strcmp(pt->symbol,curassoc->ssymbol) ) { - pre = pt; - pt = pt->nextassoc; - } - if ( pt ) - if ( pre == pt ) - Symbols = pt->nextassoc; - else - pre->nextassoc = pt->nextassoc; - else - warn("can't delete nonexisting symbol %s",curassoc->ssymbol); + for (curassoc = assoclst; curassoc; curassoc = curassoc->nextsym) { + char const *ssymbol = curassoc->ssymbol; + + if (!curassoc->revno) { /* delete symbol */ + for (pre = &Symbols; ; pre = &pt->nextassoc) + if (!(pt = *pre)) { + rcswarn("can't delete nonexisting symbol %s", ssymbol); + break; + } else if (strcmp(pt->symbol, ssymbol) == 0) { + *pre = pt->nextassoc; + changed = true; + break; + } } else { if (curassoc->revno[0]) { @@ -1295,20 +1343,19 @@ doassoc() if (expandsym(curassoc->revno, &numrev)) p = fstr_save(numrev.string); } else if (!(p = tiprev())) - error("no latest revision to associate with symbol %s", - curassoc->ssymbol + rcserror("no latest revision to associate with symbol %s", + ssymbol ); if (p) - VOID addsymbol(p, curassoc->ssymbol, curassoc->override); + changed |= addsymbol(p, ssymbol, curassoc->override); } - curassoc = curassoc->nextsym; } - + return changed; } - static void + static int dolocks() /* Function: remove lock for caller or first lock if unlockcaller is set; * remove locks which are stored in rmvlocklst, @@ -1318,64 +1365,60 @@ dolocks() { struct Lockrev const *lockpt; struct hshentry *target; + int changed = false; if (unlockcaller) { /* find lock for caller */ if ( Head ) { if (Locks) { switch (findlock(true, &target)) { case 0: - breaklock(Locks->delta); /* remove most recent lock */ + /* remove most recent lock */ + changed |= breaklock(Locks->delta); break; case 1: diagnose("%s unlocked\n",target->num); + changed = true; break; } } else { - warn("No locks are set."); + rcswarn("No locks are set."); } } else { - warn("can't unlock an empty tree"); + rcswarn("can't unlock an empty tree"); } } /* remove locks which are stored in rmvlocklst */ - lockpt = rmvlocklst; - while( lockpt ) { + for (lockpt = rmvlocklst; lockpt; lockpt = lockpt->nextrev) if (expandsym(lockpt->revno, &numrev)) { - target = genrevs(numrev.string, (char *)nil, (char *)nil, (char *)nil, &gendeltas); + target = genrevs(numrev.string, (char *)0, (char *)0, (char *)0, &gendeltas); if ( target ) if (!(countnumflds(numrev.string)&1) && cmpnum(target->num,numrev.string)) - error("can't unlock nonexisting revision %s",lockpt->revno); + rcserror("can't unlock nonexisting revision %s", + lockpt->revno + ); else - breaklock(target); + changed |= breaklock(target); /* breaklock does its own diagnose */ } - lockpt = lockpt->nextrev; - } /* add new locks which stored in newlocklst */ - lockpt = newlocklst; - while( lockpt ) { - setlock(lockpt->revno); - lockpt = lockpt->nextrev; - } - - if (lockhead) { /* lock default branch or head */ - if (Dbranch) { - setlock(Dbranch); - } else if (Head) { - if (0 <= addlock(Head)) - diagnose("%s locked\n",Head->num); - } else { - warn("can't lock an empty tree"); - } - } - + for (lockpt = newlocklst; lockpt; lockpt = lockpt->nextrev) + changed |= setlock(lockpt->revno); + + if (lockhead) /* lock default branch or head */ + if (Dbranch) + changed |= setlock(Dbranch); + else if (Head) + changed |= setlock(Head->num); + else + rcswarn("can't lock an empty tree"); + return changed; } - static void + static int setlock(rev) char const *rev; /* Function: Given a revision or branch number, finds the corresponding @@ -1383,25 +1426,36 @@ setlock(rev) */ { struct hshentry *target; + int r; if (expandsym(rev, &numrev)) { - target = genrevs(numrev.string, (char*)nil, (char*)nil, - (char*)nil, &gendeltas); + target = genrevs(numrev.string, (char*)0, (char*)0, + (char*)0, &gendeltas); if ( target ) if (!(countnumflds(numrev.string)&1) && cmpnum(target->num,numrev.string)) - error("can't lock nonexisting revision %s", numrev.string); - else - if (0 <= addlock(target)) - diagnose("%s locked\n", target->num); - } + rcserror("can't lock nonexisting revision %s", + numrev.string + ); + else { + if ((r = addlock(target, false)) < 0 && breaklock(target)) + r = addlock(target, true); + if (0 <= r) { + if (r) + diagnose("%s locked\n", target->num); + return r; + } + } + } + return 0; } - static void + static int domessages() { struct hshentry *target; struct Message *p; + int changed = false; for (p = messagelst; p; p = p->nextmessage) if ( @@ -1409,12 +1463,19 @@ domessages() (target = genrevs( numrev.string, (char*)0, (char*)0, (char*)0, &gendeltas )) - ) + ) { + /* + * We can't check the old log -- it's much later in the file. + * We pessimistically assume that it changed. + */ target->log = p->message; + changed = true; + } + return changed; } - static void + static int rcs_setstate(rev,status) char const *rev, *status; /* Function: Given a revision or branch number, finds the corresponding delta @@ -1424,15 +1485,19 @@ rcs_setstate(rev,status) struct hshentry *target; if (expandsym(rev, &numrev)) { - target = genrevs(numrev.string, (char*)nil, (char*)nil, - (char*)nil, &gendeltas); + target = genrevs(numrev.string, (char*)0, (char*)0, + (char*)0, &gendeltas); if ( target ) if (!(countnumflds(numrev.string)&1) && cmpnum(target->num,numrev.string)) - error("can't set state of nonexisting revision %s to %s", - numrev.string, status); - else + rcserror("can't set state of nonexisting revision %s", + numrev.string + ); + else if (strcmp(target->state, status) != 0) { target->state = status; - } + return true; + } + } + return false; } @@ -1446,15 +1511,15 @@ buildeltatext(deltas) /* change to delta text */ { register FILE *fcut; /* temporary file to rebuild delta tree */ - char const *cutfilename, *diffilename; + char const *cutname; - cutfilename = nil; + fcut = 0; cuttail->selector = false; scanlogtext(deltas->first, false); if ( cuthead ) { - cutfilename = maketemp(3); - if (!(fcut = fopen(cutfilename, FOPEN_W_WORK))) { - efaterror(cutfilename); + cutname = maketemp(3); + if (!(fcut = fopenSafer(cutname, FOPEN_WPLUS_WORK))) { + efaterror(cutname); } while (deltas->first != cuthead) { @@ -1463,25 +1528,36 @@ buildeltatext(deltas) } snapshotedit(fcut); - Ofclose(fcut); + Orewind(fcut); + aflush(fcut); } while (deltas->first != cuttail) scanlogtext((deltas = deltas->rest)->first, true); - finishedit((struct hshentry *)nil, (FILE*)0, true); + finishedit((struct hshentry*)0, (FILE*)0, true); Ozclose(&fcopy); - if ( cuthead ) { - diffilename = maketemp(0); - switch (run((char*)nil,diffilename, - DIFF DIFF_FLAGS, cutfilename, resultfile, (char*)nil - )) { + if (fcut) { + char const *diffname = maketemp(0); + char const *diffv[6 + !!OPEN_O_BINARY]; + char const **diffp = diffv; + *++diffp = DIFF; + *++diffp = DIFFFLAGS; +# if OPEN_O_BINARY + if (Expand == BINARY_EXPAND) + *++diffp == "--binary"; +# endif + *++diffp = "-"; + *++diffp = resultname; + *++diffp = 0; + switch (runv(fileno(fcut), diffname, diffv)) { case DIFF_FAILURE: case DIFF_SUCCESS: break; - default: faterror ("diff failed"); + default: rcsfaterror("diff failed"); } - return putdtext(cuttail->num,cuttail->log,diffilename,frewrite,true); + Ofclose(fcut); + return putdtext(cuttail,diffname,frewrite,true); } else - return putdtext(cuttail->num,cuttail->log,resultfile,frewrite,false); + return putdtext(cuttail,resultname,frewrite,false); } @@ -1512,9 +1588,9 @@ buildtree() pre->nextbranch = pt->nextbranch; } else { - if ( cuttail == nil && !quietflag) { + if (!cuttail && !quietflag) { if (!yesorno(false, "Do you really want to delete all revisions? [ny](n): ")) { - error("No revision deleted"); + rcserror("No revision deleted"); Delta = delstrt; while( Delta) { Delta->selector = true; @@ -1528,7 +1604,7 @@ buildtree() return; } -#if lint +#if RCS_lint /* This lets us lint everything all at once. */ char const cmdid[] = ""; diff --git a/gnu/usr.bin/rcs/rcs/rcsfile.5 b/gnu/usr.bin/rcs/rcs/rcsfile.5 index d0dbbb8..5a1929f 100644 --- a/gnu/usr.bin/rcs/rcs/rcsfile.5 +++ b/gnu/usr.bin/rcs/rcs/rcsfile.5 @@ -1,8 +1,11 @@ +.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 $Id: rcsfile.5,v 5.1 1991/08/19 03:13:55 eggert Exp $ +.Id $Id: rcsfile.5in,v 5.6 1995/06/05 08:28:35 eggert Exp $ .ds r \s-1RCS\s0 .if n .ds - \%-- .if t .ds - \(em @@ -18,39 +21,43 @@ 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, an \*r file must end in a newline character. +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 may contain arbitrary binary data. +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 may be repeated zero or more times; -`{' and '}+' enclose phrases that must appear at least once and may be +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 \w'\f2deltatext\fP 'u +\w'::= 'u +\nxu+\w' 'u -.fc ~ +.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 {\f2id\fP \f3:\fP \f2num\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 } @@ -71,13 +78,13 @@ nonterminal symbols are in { \f2newphrase\fP }* \f3text\fP \f2string\fP .LP -\f2num\fP ::= {\f2digit\fP{\f3.\fP}}+ +\f2num\fP ::= {\f2digit\fP | \f3.\fP}+ .LP -\f2digit\fP ::= \f30\fP | \f31\fP | .\|.\|. | \f39\fP +\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 ::= \f2letter\fP{\f2idchar\fP}* +\f2id\fP ::= {\f2num\fP} \f2idchar\fP {\f2idchar\fP | \f2num\fP}* .LP -\f2letter\fP ::= any letter +\f2sym\fP ::= {\f2digit\fP}* \f2idchar\fP {\f2idchar\fP | \f2digit\fP}* .LP \f2idchar\fP ::= any visible graphic character except \f2special\fP .LP @@ -91,12 +98,35 @@ nonterminal symbols are in .fi .PP Identifiers are case sensitive. Keywords are in lower case only. -The sets of keywords and identifiers may overlap. -In most environments RCS uses the ISO 8859/1 encoding: -letters are octal codes 101\-132, 141\-172, 300\-326, 330\-366 and 370-377, +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 @@ -131,7 +161,7 @@ All nodes whose numbers consist of .RI 2 n fields -.RI ( n >=2) +.RI ( n \(>=2) (e.g., 3.1.1.1, 2.1.2.2, etc.) are linked as follows. All nodes whose first @@ -151,11 +181,11 @@ 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 38 -Example: -.if t .in +0.5i +.ne 36 .cs 1 20 .eo @@ -195,12 +225,183 @@ Example: \ / .ec -.if t .in .cs 1 -.ce -Fig. 1: A revision tree .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 +.\} .PP .SH IDENTIFICATION .de VL @@ -209,13 +410,14 @@ Fig. 1: A revision tree Author: Walter F. Tichy, Purdue University, West Lafayette, IN, 47907. .br -Revision Number: \*(Rv; Release Date: \*(Dt. +Manual Page Revision: \*(Rv; Release Date: \*(Dt. .br -Copyright \(co 1982, 1988, 1989 by Walter F. Tichy. +Copyright \(co 1982, 1988, 1989 Walter F. Tichy. .br -Copyright \(co 1990, 1991 by Paul Eggert. +Copyright \(co 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert. .SH SEE ALSO -ci(1), co(1), ident(1), rcs(1), rcsdiff(1), rcsmerge(1), rlog(1), +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, diff --git a/gnu/usr.bin/rcs/rcs/rcsintro.1 b/gnu/usr.bin/rcs/rcs/rcsintro.1 index a76caa0..fa97043 100644 --- a/gnu/usr.bin/rcs/rcs/rcsintro.1 +++ b/gnu/usr.bin/rcs/rcs/rcsintro.1 @@ -2,10 +2,20 @@ .ds Rv \\$3 .ds Dt \\$4 .. -.Id $Id: rcsintro.1,v 5.1 1991/04/21 12:00:46 eggert Exp $ +.Id $Id: rcsintro.1,v 5.3 1993/11/03 17:42:27 eggert Exp $ .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 .. @@ -276,11 +286,11 @@ details. .SH IDENTIFICATION Author: Walter F. Tichy. .br -Revision Number: \*(Rv; Release Date: \*(Dt. +Manual Page Revision: \*(Rv; Release Date: \*(Dt. .br -Copyright \(co 1982, 1988, 1989 by Walter F. Tichy. +Copyright \(co 1982, 1988, 1989 Walter F. Tichy. .br -Copyright \(co 1990, 1991 by Paul Eggert. +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 diff --git a/gnu/usr.bin/rcs/rcsclean/Makefile b/gnu/usr.bin/rcs/rcsclean/Makefile index fc0c626..fe538a0 100644 --- a/gnu/usr.bin/rcs/rcsclean/Makefile +++ b/gnu/usr.bin/rcs/rcsclean/Makefile @@ -1,7 +1,8 @@ -PROG= rcsclean - -SRCS= rcsclean.c -LDADD= -L${.CURDIR}/../lib/obj -lrcs +PROG= rcsclean +SRCS= rcsclean.c CFLAGS+= -I${.CURDIR}/../lib +LDADD= ${LIBRCS} +DPADD= ${LIBRCS} +.include "../../Makefile.inc" .include <bsd.prog.mk> diff --git a/gnu/usr.bin/rcs/rcsclean/rcsclean.1 b/gnu/usr.bin/rcs/rcsclean/rcsclean.1 index 07ed722..be58c94 100644 --- a/gnu/usr.bin/rcs/rcsclean/rcsclean.1 +++ b/gnu/usr.bin/rcs/rcsclean/rcsclean.1 @@ -2,7 +2,7 @@ .ds Rv \\$3 .ds Dt \\$4 .. -.Id $Id: rcsclean.1,v 1.8 1991/11/03 01:09:19 eggert Exp $ +.Id $Id: rcsclean.1,v 1.12 1993/11/03 17:42:27 eggert Exp $ .ds r \&\s-1RCS\s0 .if n .ds - \%-- .if t .ds - \(em @@ -14,7 +14,11 @@ rcsclean \- clean up working files .RI [ options "] [ " file " .\|.\|. ]" .SH DESCRIPTION .B rcsclean -removes working files that were checked out and never modified. +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, @@ -32,13 +36,13 @@ 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. -Names are paired as explained in -.BR ci (1). .PP The number of the revision to which the working file is compared may be attached to any of the options @@ -58,15 +62,15 @@ uses the latest revision on the default branch, normally the root. .B rcsclean is useful for .B clean -targets in Makefiles. +targets in makefiles. See also .BR rcsdiff (1), which prints out the differences, and .BR ci (1), which -normally asks whether to check in a file -if it was not changed. +normally reverts to the previous revision +if a file was not changed. .SH OPTIONS .TP .BI \-k subst @@ -89,9 +93,22 @@ Do not log the actions taken on standard output. .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 . @@ -106,6 +123,14 @@ 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 @@ -147,19 +172,20 @@ Useful options include .BR \-q , .BR \-V , +.BR \-x , and -.BR \-x . +.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 -Revision Number: \*(Rv; Release Date: \*(Dt. +Manual Page Revision: \*(Rv; Release Date: \*(Dt. .br -Copyright \(co 1982, 1988, 1989 by Walter F. Tichy. +Copyright \(co 1982, 1988, 1989 Walter F. Tichy. .br -Copyright \(co 1990, 1991 by Paul Eggert. +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) diff --git a/gnu/usr.bin/rcs/rcsclean/rcsclean.c b/gnu/usr.bin/rcs/rcsclean/rcsclean.c index ba24ab7..3d8ecd3 100644 --- a/gnu/usr.bin/rcs/rcsclean/rcsclean.c +++ b/gnu/usr.bin/rcs/rcsclean/rcsclean.c @@ -1,6 +1,6 @@ -/* rcsclean - clean up working files */ +/* Clean up working files. */ -/* Copyright 1991 by Paul Eggert +/* Copyright 1991, 1992, 1993, 1994, 1995 Paul Eggert Distributed under license by the Free Software Foundation, Inc. This file is part of RCS. @@ -16,8 +16,9 @@ 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, 675 Mass Ave, Cambridge, MA 02139, USA. +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: @@ -37,16 +38,17 @@ static void cleanup P((void)); static RILE *workptr; static int exitstatus; -mainProg(rcscleanId, "rcsclean", "$Id: rcsclean.c,v 5.1 1991/11/03 01:11:44 eggert Exp $") +mainProg(rcscleanId, "rcsclean", "$Id: rcsclean.c,v 5.9 1995/06/16 06:19:24 eggert Exp $") { static char const usage[] = - "\nrcsclean: usage: rcsclean [-ksubst] [-{nqru}[rev]] [-Vn] [-xsuffixes] [file ...]"; + "\nrcsclean: usage: rcsclean -ksubst -{nqru}[rev] -T -Vn -xsuff -zzone file ..."; static struct buf revision; char *a, **newargv; char const *rev, *p; - int changelock, expmode, perform, unlocked, unlockflag, waslocked; + int dounlock, expmode, perform, unlocked, unlockflag, waslocked; + int Ttimeflag; struct hshentries *deltas; struct hshentry *delta; struct stat workstat; @@ -54,25 +56,26 @@ mainProg(rcscleanId, "rcsclean", "$Id: rcsclean.c,v 5.1 1991/11/03 01:11:44 egge setrid(); expmode = -1; - rev = nil; + rev = 0; suffixes = X_DEFAULT; perform = true; unlockflag = false; + Ttimeflag = false; argc = getRCSINIT(argc, argv, &newargv); argv = newargv; for (;;) { - if (--argc <= 0) { + if (--argc < 1) { # if has_dirent argc = get_directory(".", &newargv); argv = newargv; break; # else - faterror("no file names specified"); + faterror("no pathnames specified"); # endif } a = *++argv; - if (*a++ != '-') + if (!*a || *a++ != '-') break; switch (*a++) { case 'k': @@ -98,6 +101,12 @@ mainProg(rcscleanId, "rcsclean", "$Id: rcsclean.c,v 5.1 1991/11/03 01:11:44 egge } break; + case 'T': + if (*a) + goto unknown; + Ttimeflag = true; + break; + case 'u': unlockflag = true; goto handle_revision; @@ -110,25 +119,42 @@ mainProg(rcscleanId, "rcsclean", "$Id: rcsclean.c,v 5.1 1991/11/03 01:11:44 egge suffixes = a; break; + case 'z': + zone_set(a); + break; + default: unknown: - faterror("unknown option: %s%s", *argv, usage); + error("unknown option: %s%s", *argv, usage); } } - do { + dounlock = perform & unlockflag; + + if (nerror) + cleanup(); + else + for (; 0 < argc; cleanup(), ++argv, --argc) { + ffree(); if (!( - 0 < pairfilenames( + 0 < pairnames( argc, argv, - unlockflag&perform ? rcswriteopen : rcsreadopen, + dounlock ? rcswriteopen : rcsreadopen, true, true ) && - (workptr = Iopen(workfilename,FOPEN_R_WORK,&workstat)) + (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; @@ -155,11 +181,13 @@ mainProg(rcscleanId, "rcsclean", "$Id: rcsclean.c,v 5.1 1991/11/03 01:11:44 egge waslocked = delta && delta->lockedby; locker_expansion = unlock(delta); unlocked = locker_expansion & unlockflag; - changelock = unlocked & perform; if (unlocked<waslocked && workstat.st_mode&(S_IWUSR|S_IWGRP|S_IWOTH)) continue; - if (!dorewrite(unlockflag, changelock)) + if (unlocked && !checkaccesslist()) + continue; + + if (dorewrite(dounlock, unlocked) != 0) continue; if (0 <= expmode) @@ -174,31 +202,33 @@ mainProg(rcscleanId, "rcsclean", "$Id: rcsclean.c,v 5.1 1991/11/03 01:11:44 egge getdesc(false); if ( - !delta ? workstat.st_size!=0 : + !delta ? workstat.st_size!=0 : 0 < rcsfcmp( - workptr, &workstat, - buildrevision(deltas, delta, (FILE*)0, false), - delta + workptr, &workstat, + buildrevision(deltas, delta, (FILE*)0, false), + delta ) ) continue; if (quietflag < unlocked) - aprintf(stdout, "rcs -u%s %s\n", delta->num, RCSfilename); + aprintf(stdout, "rcs -u%s %s\n", delta->num, RCSname); - if_advise_access(changelock && deltas->first != delta, - finptr, MADV_SEQUENTIAL - ); - if (!donerewrite(changelock)) - continue; + 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", workfilename); + aprintf(stdout, "rm -f %s\n", workname); Izclose(&workptr); - if (perform && un_link(workfilename) != 0) - eerror(workfilename); + if (perform && un_link(workname) != 0) + eerror(workname); - } while (cleanup(), ++argv, 0 < --argc); + } tempunlink(); if (!quietflag) @@ -213,16 +243,17 @@ cleanup() Izclose(&finptr); Izclose(&workptr); Ozclose(&fcopy); - Ozclose(&frewrite); + ORCSclose(); dirtempunlink(); } -#if lint -# define exiterr rcscleanExit +#if RCS_lint +# define exiterr rcscleanExit #endif - exiting void + void exiterr() { + ORCSerror(); dirtempunlink(); tempunlink(); _exit(EXIT_FAILURE); @@ -232,7 +263,7 @@ exiterr() unlock(delta) struct hshentry *delta; { - register struct lock **al, *l; + register struct rcslock **al, *l; if (delta && delta->lockedby && strcmp(getcaller(),delta->lockedby)==0) for (al = &Locks; (l = *al); al = &l->nextlock) @@ -269,7 +300,7 @@ get_directory(dirname, aargv) 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])) + if (en[0]=='.' && (!en[1] || (en[1]=='.' && !en[2]))) continue; if (rcssuffix(en)) continue; @@ -281,7 +312,12 @@ get_directory(dirname, aargv) VOID strcpy(a+chars, en); chars += s; } - if (errno || closedir(d) != 0) +# 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); diff --git a/gnu/usr.bin/rcs/rcsdiff/Makefile b/gnu/usr.bin/rcs/rcsdiff/Makefile index 837c241..45ce23f 100644 --- a/gnu/usr.bin/rcs/rcsdiff/Makefile +++ b/gnu/usr.bin/rcs/rcsdiff/Makefile @@ -1,7 +1,8 @@ -PROG= rcsdiff - -SRCS= rcsdiff.c -LDADD= -L${.CURDIR}/../lib/obj -lrcs +PROG= rcsdiff +SRCS= rcsdiff.c CFLAGS+= -I${.CURDIR}/../lib +LDADD= ${LIBRCS} +DPADD= ${LIBRCS} +.include "../../Makefile.inc" .include <bsd.prog.mk> diff --git a/gnu/usr.bin/rcs/rcsdiff/rcsdiff.1 b/gnu/usr.bin/rcs/rcsdiff/rcsdiff.1 index b78bbdd..373c337 100644 --- a/gnu/usr.bin/rcs/rcsdiff/rcsdiff.1 +++ b/gnu/usr.bin/rcs/rcsdiff/rcsdiff.1 @@ -2,7 +2,7 @@ .ds Rv \\$3 .ds Dt \\$4 .. -.Id $Id: rcsdiff.1,v 5.3 1991/04/21 12:00:46 eggert Exp $ +.Id $Id: rcsdiff.1,v 5.5 1993/11/03 17:42:27 eggert Exp $ .ds r \&\s-1RCS\s0 .if n .ds - \%-- .if t .ds - \(em @@ -20,10 +20,14 @@ rcsdiff \- compare RCS revisions [ .BI \-r rev2 ] ] [ -.BI \-V n +.B \-T +] [ +.RI "\f3\-V\fP[" n ] ] [ .BI \-x suffixes ] [ +.BI \-z zone +] [ .I "diff options" ] .I "file .\|.\|." @@ -68,9 +72,11 @@ See .BR co (1) for details about -.B \-V +.BR \-T , +.BR \-V , +.B \-x and -.BR \-x . +.BR \-z . Otherwise, all options of .BR diff (1) that apply to regular files are accepted, with the same meaning as for @@ -136,11 +142,11 @@ Exit status is 0 for no differences during any comparison, .SH IDENTIFICATION Author: Walter F. Tichy. .br -Revision Number: \*(Rv; Release Date: \*(Dt. +Manual Page Revision: \*(Rv; Release Date: \*(Dt. .br -Copyright \(co 1982, 1988, 1989 by Walter F. Tichy. +Copyright \(co 1982, 1988, 1989 Walter F. Tichy. .br -Copyright \(co 1990, 1991 by Paul Eggert. +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 diff --git a/gnu/usr.bin/rcs/rcsdiff/rcsdiff.c b/gnu/usr.bin/rcs/rcsdiff/rcsdiff.c index 7155c8d..26b29c2 100644 --- a/gnu/usr.bin/rcs/rcsdiff/rcsdiff.c +++ b/gnu/usr.bin/rcs/rcsdiff/rcsdiff.c @@ -1,13 +1,7 @@ -/* - * RCS rcsdiff operation - */ -/***************************************************************************** - * generate difference between RCS revisions - ***************************************************************************** - */ +/* Compare RCS revisions. */ -/* Copyright (C) 1982, 1988, 1989 Walter Tichy - Copyright 1990, 1991 by Paul Eggert +/* 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. @@ -23,8 +17,9 @@ 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, 675 Mass Ave, Cambridge, MA 02139, USA. +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: @@ -32,10 +27,38 @@ Report problems and direct all questions to: */ - - - -/* $Log: rcsdiff.c,v $ +/* + * $Log: rcsdiff.c,v $ + * 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. * @@ -129,29 +152,30 @@ static int exitstatus; static RILE *workptr; static struct stat workstat; -mainProg(rcsdiffId, "rcsdiff", "$Id: rcsdiff.c,v 5.10 1991/10/07 17:32:46 eggert Exp $") +mainProg(rcsdiffId, "rcsdiff", "$Id: rcsdiff.c,v 5.19 1995/06/16 06:19:24 eggert Exp $") { static char const cmdusage[] = - "\nrcsdiff usage: rcsdiff [-q] [-rrev1 [-rrev2]] [-Vn] [diff options] file ..."; + "\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, *versionarg; + 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[9]; - char const **diffv, **diffp; /* argv for subsidiary diff */ + 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; @@ -159,38 +183,42 @@ mainProg(rcsdiffId, "rcsdiff", "$Id: rcsdiff.c,v 5.10 1991/10/07 17:32:46 eggert bufautobegin(&commarg); bufautobegin(&numericrev); revnums = 0; - rev1 = rev2 = xrev2 = nil; + rev1 = rev2 = xrev2 = 0; #if DIFF_L file_labels = 0; #endif - expandarg = versionarg = 0; + expandarg = suffixarg = versionarg = zonearg = 0; + no_diff_means_no_output = true; suffixes = X_DEFAULT; - /* Room for args + 2 i/o [+ 2 labels] + 1 file + 1 trailing null. */ - diffp = diffv = tnalloc(char const*, argc + 4 + 2*DIFF_L); - *diffp++ = nil; - *diffp++ = nil; + /* + * 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) { + while ((c = *a++)) switch (c) { case 'r': switch (++revnums) { case 1: rev1=a; break; case 2: rev2=a; break; - default: faterror("too many revision numbers"); + 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 - case 'L': - if (++file_labels == 2) + if (c == 'L' && ++file_labels == 2) faterror("too many -L options"); - /* fall into */ #endif - case 'C': case 'D': case 'F': case 'I': *dcp++ = c; if (*a) do *dcp++ = *a++; @@ -203,7 +231,10 @@ mainProg(rcsdiffId, "rcsdiff", "$Id: rcsdiff.c,v 5.10 1991/10/07 17:32:46 eggert *diffp++ = *argv++; } break; - case 'B': case 'H': case 'T': + 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': @@ -215,8 +246,18 @@ mainProg(rcsdiffId, "rcsdiff", "$Id: rcsdiff.c,v 5.10 1991/10/07 17:32:46 eggert 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); @@ -227,7 +268,8 @@ mainProg(rcsdiffId, "rcsdiff", "$Id: rcsdiff.c,v 5.10 1991/10/07 17:32:46 eggert goto option_handled; /* fall into */ default: - faterror("unknown option: %s%s", *argv, cmdusage); + unknown: + error("unknown option: %s%s", *argv, cmdusage); }; option_handled: if (dcp != *argv+1) { @@ -236,12 +278,10 @@ mainProg(rcsdiffId, "rcsdiff", "$Id: rcsdiff.c,v 5.10 1991/10/07 17:32:46 eggert } } /* end of option processing */ - if (argc<1) faterror("no input file%s", cmdusage); - - for (pp = diffv+3, c = 0; pp<diffp; ) + for (pp = diffv+2, c = 0; pp<diffp; ) c += strlen(*pp++) + 1; diffvstr = a = tnalloc(char, c + 1); - for (pp = diffv+3; pp<diffp; ) { + for (pp = diffv+2; pp<diffp; ) { p = *pp++; *a++ = ' '; while ((*a = *p++)) @@ -250,30 +290,37 @@ mainProg(rcsdiffId, "rcsdiff", "$Id: rcsdiff.c,v 5.10 1991/10/07 17:32:46 eggert *a = 0; #if DIFF_L - diff_label1 = diff_label2 = nil; + diff_label1 = diff_label2 = 0; if (file_labels < 2) { if (!file_labels) diff_label1 = diffp++; diff_label2 = diffp++; } #endif - diffp[2] = nil; - - cov[0] = 0; - cov[2] = CO; - cov[3] = "-q"; - - /* now handle all filenames */ - do { + 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 (pairfilenames(argc, argv, rcsreadopen, true, false) <= 0) + if (pairnames(argc, argv, rcsreadopen, true, false) <= 0) continue; - diagnose("===================================================================\nRCS file: %s\n",RCSfilename); + diagnose("===================================================================\nRCS file: %s\n",RCSname); if (!rev2) { /* Make sure work file is readable, and get its status. */ - if (!(workptr = Iopen(workfilename,FOPEN_R_WORK,&workstat))) { - eerror(workfilename); + if (!(workptr = Iopen(workname, FOPEN_R_WORK, &workstat))) { + eerror(workname); continue; } } @@ -281,15 +328,15 @@ mainProg(rcsdiffId, "rcsdiff", "$Id: rcsdiff.c,v 5.10 1991/10/07 17:32:46 eggert gettree(); /* reads in the delta tree */ - if (Head==nil) { - error("no revisions present"); + 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 *)nil,(char *)nil,(char *)nil,&gendeltas))) continue; + if (!(target=genrevs(numericrev.string,(char *)0,(char *)0,(char *)0,&gendeltas))) continue; xrev1=target->num; #if DIFF_L if (diff_label1) @@ -304,8 +351,10 @@ mainProg(rcsdiffId, "rcsdiff", "$Id: rcsdiff.c,v 5.10 1991/10/07 17:32:46 eggert workptr )) continue; - if (!(target=genrevs(numericrev.string,(char *)nil,(char *)nil,(char *)nil,&gendeltas))) 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 @@ -320,55 +369,60 @@ mainProg(rcsdiffId, "rcsdiff", "$Id: rcsdiff.c,v 5.10 1991/10/07 17:32:46 eggert *diff_label2 = setup_label(&labelbuf[1], target->num, target->date); else { time2date(workstat.st_mtime, date2); - *diff_label2 = setup_label(&labelbuf[1], workfilename, date2); + *diff_label2 = setup_label(&labelbuf[1], (char*)0, date2); } #endif diagnose("retrieving revision %s\n", xrev1); bufscpy(&commarg, "-p"); - bufscat(&commarg, xrev1); + bufscat(&commarg, rev1); /* not xrev1, for $Name's sake */ - cov[1] = diffp[0] = maketemp(0); - pp = &cov[4]; + pp = &cov[3 + !DIFF_L]; *pp++ = commarg.string; - if (lexpandarg) - *pp++ = lexpandarg; - if (versionarg) - *pp++ = versionarg; - *pp++ = RCSfilename; + if (lexpandarg) *pp++ = lexpandarg; + if (suffixarg) *pp++ = suffixarg; + if (versionarg) *pp++ = versionarg; + if (zonearg) *pp++ = zonearg; + *pp++ = RCSname; *pp = 0; - if (runv(cov)) { - error("co failed"); + 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] = workfilename; - if (workfilename[0] == '+') { - /* Some diffs have options with leading '+'. */ - char *dp = ftnalloc(char, strlen(workfilename)+3); + diffp[1] = workname; + if (*workname == '-') { + char *dp = ftnalloc(char, strlen(workname)+3); diffp[1] = dp; *dp++ = '.'; *dp++ = SLASH; - VOID strcpy(dp, workfilename); + VOID strcpy(dp, workname); } } else { diagnose("retrieving revision %s\n",xrev2); bufscpy(&commarg, "-p"); - bufscat(&commarg, xrev2); - cov[1] = diffp[1] = maketemp(1); - cov[4] = commarg.string; - if (runv(cov)) { - error("co failed"); + 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, workfilename); + diagnose("diff%s -r%s %s\n", diffvstr, xrev1, workname); else diagnose("diff%s -r%s -r%s\n", diffvstr, xrev1, xrev2); - switch (runv(diffv)) { + diffp[2] = 0; + switch (runv(-1, (char*)0, diffv)) { case DIFF_SUCCESS: break; case DIFF_FAILURE: @@ -376,11 +430,9 @@ mainProg(rcsdiffId, "rcsdiff", "$Id: rcsdiff.c,v 5.10 1991/10/07 17:32:46 eggert exitstatus = DIFF_FAILURE; break; default: - error("diff failed"); + workerror("diff failed"); } - } while (cleanup(), - ++argv, --argc >=1); - + } tempunlink(); exitmain(exitstatus); @@ -394,10 +446,10 @@ cleanup() Izclose(&workptr); } -#if lint +#if RCS_lint # define exiterr rdiffExit #endif - exiting void + void exiterr() { tempunlink(); @@ -406,17 +458,24 @@ exiterr() #if DIFF_L static char const * -setup_label(b, name, date) +setup_label(b, num, date) struct buf *b; - char const *name; + char const *num; char const date[datesize]; { char *p; - size_t l = strlen(name) + 3; - bufalloc(b, l+datesize); + char datestr[datesize + zonelenmax]; + VOID date2str(date, datestr); + bufalloc(b, + strlen(workname) + + sizeof datestr + 4 + + (num ? strlen(num) : 0) + ); p = b->string; - VOID sprintf(p, "-L%s\t", name); - VOID date2str(date, p+l); + 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 diff --git a/gnu/usr.bin/rcs/rcsfreeze/Makefile b/gnu/usr.bin/rcs/rcsfreeze/Makefile index 825d4bf..a71f9d7 100644 --- a/gnu/usr.bin/rcs/rcsfreeze/Makefile +++ b/gnu/usr.bin/rcs/rcsfreeze/Makefile @@ -1,7 +1,8 @@ -# Do nothing for the following -obj clean cleandir depend rcsfreeze all: - @echo No need to make $@ for rcsfreeze\; ignored +MAN1= rcsfreeze.1 -install: - install -c -o bin -g bin -m 555 rcsfreeze.sh /usr/bin/rcsfreeze - install -c -o bin -g bin -m 444 rcsfreeze.1 /usr/share/man/man1 +afterinstall: + ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \ + ${.CURDIR}/rcsfreeze.sh ${DESTDIR}${BINDIR}/rcsfreeze + +.include "../../Makefile.inc" +.include <bsd.prog.mk> diff --git a/gnu/usr.bin/rcs/rcsfreeze/rcsfreeze.sh b/gnu/usr.bin/rcs/rcsfreeze/rcsfreeze.sh index 4219979..96954f0 100644 --- a/gnu/usr.bin/rcs/rcsfreeze/rcsfreeze.sh +++ b/gnu/usr.bin/rcs/rcsfreeze/rcsfreeze.sh @@ -2,7 +2,7 @@ # rcsfreeze - assign a symbolic revision number to a configuration of RCS files -# $Id: rcsfreeze.sh,v 4.4 1991/04/21 11:58:24 eggert Exp $ +# $Id: rcsfreeze.sh,v 4.6 1993/11/03 17:42:27 eggert Exp $ # The idea is to run rcsfreeze each time a new version is checked # in. A unique symbolic revision number (C_[number], where number @@ -25,22 +25,22 @@ # {RCS/}.rcsfreeze.ver version number # {RCS/}.rscfreeze.log log messages, most recent first -PATH=/usr/gnu/bin:/usr/local/bin:/bin:/usr/bin:/usr/ucb:$PATH +PATH=/usr/local/bin:/bin:/usr/bin:/usr/ucb:$PATH export PATH DATE=`date` || exit # Check whether we have an RCS subdirectory, so we can have the right # prefix for our paths. -if [ -d RCS ] -then RCSDIR=RCS/ -else RCSDIR= +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 -[ -r $VERSIONFILE ] || { echo 0 >$VERSIONFILE && >>$LOGFILE; } || exit +test -r $VERSIONFILE || { echo 0 >$VERSIONFILE && >>$LOGFILE; } || exit # Get Version number, increase it, write back to file. VERSIONNUMBER=`cat $VERSIONFILE` && @@ -79,22 +79,21 @@ trap 'rm -f $TMPLOG; exit 1' 1 2 13 15 # combine old and new logfiles cp $TMPLOG $LOGFILE && -rm -f $TMPLOG || exit -trap 1 2 13 15 +rm -f $TMPLOG && # Now the real work begins by assigning a symbolic revision number -# to each rcs file. Take the most recent version of the main trunk. +# to each rcs file. Take the most recent version on the default branch. -status= - -for FILE in ${RCSDIR}* +# 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 -# get the revision number of the most recent revision - HEAD=`rlog -h $FILE` && - REV=`echo "$HEAD" | sed -n 's/^head:[ ]*//p'` && -# assign symbolic name to it. - echo >&2 "rcsfreeze: $REV $FILE" && - rcs -q -n$SYMREVNAME:$REV $FILE || status=$? + if test -f "$DOTFILE" + then + DOTFILES="${RCSDIR}.*,v" + break + fi done -exit $status +exec rcs -q -n$SYMREVNAME: ${RCSDIR}*$EXT $DOTFILES diff --git a/gnu/usr.bin/rcs/rcsmerge/Makefile b/gnu/usr.bin/rcs/rcsmerge/Makefile index 0c1f643..9fd8afa 100644 --- a/gnu/usr.bin/rcs/rcsmerge/Makefile +++ b/gnu/usr.bin/rcs/rcsmerge/Makefile @@ -1,7 +1,8 @@ -PROG= rcsmerge - -SRCS= rcsmerge.c -LDADD= -L${.CURDIR}/../lib/obj -lrcs +PROG= rcsmerge +SRCS= rcsmerge.c CFLAGS+= -I${.CURDIR}/../lib +LDADD= ${LIBRCS} +DPADD= ${LIBRCS} +.include "../../Makefile.inc" .include <bsd.prog.mk> diff --git a/gnu/usr.bin/rcs/rcsmerge/rcsmerge.1 b/gnu/usr.bin/rcs/rcsmerge/rcsmerge.1 index 82871b0..96b6a46 100644 --- a/gnu/usr.bin/rcs/rcsmerge/rcsmerge.1 +++ b/gnu/usr.bin/rcs/rcsmerge/rcsmerge.1 @@ -2,7 +2,7 @@ .ds Rv \\$3 .ds Dt \\$4 .. -.Id $Id: rcsmerge.1,v 5.3 1991/08/19 03:13:55 eggert Exp $ +.Id $Id: rcsmerge.1,v 5.6 1995/06/01 16:23:43 eggert Exp $ .ds r \&\s-1RCS\s0 .if n .ds - \%-- .if t .ds - \(em @@ -38,6 +38,35 @@ the overlapping regions as explained in 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 @@ -51,6 +80,11 @@ 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. @@ -65,6 +99,13 @@ 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 . @@ -79,6 +120,14 @@ 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 . @@ -123,11 +172,11 @@ Exit status is 0 for no overlaps, 1 for some overlaps, 2 for trouble. .SH IDENTIFICATION Author: Walter F. Tichy. .br -Revision Number: \*(Rv; Release Date: \*(Dt. +Manual Page Revision: \*(Rv; Release Date: \*(Dt. .br -Copyright \(co 1982, 1988, 1989 by Walter F. Tichy. +Copyright \(co 1982, 1988, 1989 Walter F. Tichy. .br -Copyright \(co 1990, 1991 by Paul Eggert. +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) diff --git a/gnu/usr.bin/rcs/rcsmerge/rcsmerge.c b/gnu/usr.bin/rcs/rcsmerge/rcsmerge.c index e5d4394..5a27426 100644 --- a/gnu/usr.bin/rcs/rcsmerge/rcsmerge.c +++ b/gnu/usr.bin/rcs/rcsmerge/rcsmerge.c @@ -1,13 +1,7 @@ -/* - * rcsmerge operation - */ -/***************************************************************************** - * join 2 revisions with respect to a third - ***************************************************************************** - */ +/* Merge RCS revisions. */ -/* Copyright (C) 1982, 1988, 1989 Walter Tichy - Copyright 1990, 1991 by Paul Eggert +/* 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. @@ -23,8 +17,9 @@ 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, 675 Mass Ave, Cambridge, MA 02139, USA. +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: @@ -32,9 +27,34 @@ Report problems and direct all questions to: */ - - -/* $Log: rcsmerge.c,v $ +/* + * $Log: rcsmerge.c,v $ + * 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. * @@ -98,17 +118,17 @@ Report problems and direct all questions to: static char const co[] = CO; -mainProg(rcsmergeId, "rcsmerge", "$Id: rcsmerge.c,v 5.7 1991/11/20 17:58:09 eggert Exp $") +mainProg(rcsmergeId, "rcsmerge", "$Id: rcsmerge.c,v 5.15 1995/06/16 06:19:24 eggert Exp $") { static char const cmdusage[] = - "\nrcsmerge usage: rcsmerge -rrev1 [-rrev2] [-p] [-Vn] file"; + "\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[2]; /*revision numbers*/ - char const *expandarg, *versionarg; + char const *rev[3], *xrev[3]; /*revision numbers*/ + char const *edarg, *expandarg, *suffixarg, *versionarg, *zonearg; int tostdout; int status; RILE *workptr; @@ -119,10 +139,10 @@ mainProg(rcsmergeId, "rcsmerge", "$Id: rcsmerge.c,v 5.7 1991/11/20 17:58:09 egge bufautobegin(&commarg); bufautobegin(&numericrev); - rev[0] = rev[1] = nil; + edarg = rev[1] = rev[2] = 0; status = 0; /* Keep lint happy. */ tostdout = false; - expandarg = versionarg = quietarg; /* i.e. a no-op */ + expandarg = suffixarg = versionarg = zonearg = quietarg; /* no-op */ suffixes = X_DEFAULT; argc = getRCSINIT(argc, argv, &newargv); @@ -140,16 +160,33 @@ mainProg(rcsmergeId, "rcsmerge", "$Id: rcsmerge.c,v 5.7 1991/11/20 17:58:09 egge break; /* falls into -r */ case 'r': - if (!rev[0]) - rev[0] = a; - else if (!rev[1]) + if (!rev[1]) rev[1] = a; + else if (!rev[2]) + rev[2] = a; else - faterror("too many revision numbers"); + 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); @@ -161,90 +198,88 @@ mainProg(rcsmergeId, "rcsmerge", "$Id: rcsmerge.c,v 5.7 1991/11/20 17:58:09 egge break; /* fall into */ default: - faterror("unknown option: %s%s", *argv, cmdusage); + unknown: + error("unknown option: %s%s", *argv, cmdusage); }; } /* end of option processing */ - if (argc<1) faterror("no input file%s", cmdusage); - if (!rev[0]) faterror("no base revision number given"); + if (!rev[1]) faterror("no base revision number given"); - /* now handle all filenames */ + /* Now handle all pathnames. */ - if (0 < pairfilenames(argc, argv, rcsreadopen, true, false)) { + 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]!=nil)) - warn("too many arguments"); - diagnose("RCS file: %s\n", RCSfilename); - if (!(workptr = Iopen(workfilename, - FOPEN_R_WORK, - (struct stat*)0 - ))) - efaterror(workfilename); + 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==nil) faterror("no revisions present"); + if (!Head) rcsfaterror("no revisions present"); - if (!*rev[0]) - rev[0] = Dbranch ? Dbranch : Head->num; - if (!fexpandsym(rev[0], &numericrev, workptr)) - goto end; - if (!(target=genrevs(numericrev.string, (char *)nil, (char *)nil, (char *)nil,&gendeltas))) goto end; - rev[0] = target->num; - if (!rev[1] || !*rev[1]) + if (!*rev[1]) rev[1] = Dbranch ? Dbranch : Head->num; - if (!fexpandsym(rev[1], &numericrev, workptr)) - goto end; - if (!(target=genrevs(numericrev.string, (char *)nil, (char *)nil, (char *)nil,&gendeltas))) goto end; - rev[1] = target->num; - - if (strcmp(rev[0],rev[1]) == 0) { - if (tostdout) { - FILE *o; -# if text_equals_binary_stdio || text_work_stdio - o = stdout; -# else - if (!(o=fdopen(STDOUT_FILENO,FOPEN_W_WORK))) - efaterror("stdout"); -# endif - fastcopy(workptr,o); - Ofclose(o); - } - goto end; - } - Izclose(&workptr); + 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=0; i<2; i++) { - diagnose("retrieving revision %s\n", rev[i]); + for (i=1; i<=2; i++) { + diagnose("retrieving revision %s\n", xrev[i]); bufscpy(&commarg, "-p"); - bufscat(&commarg, rev[i]); + bufscat(&commarg, rev[i]); /* not xrev[i], for $Name's sake */ if (run( - (char*)0, + -1, /* Do not collide with merger.c maketemp(). */ - arg[i+1] = maketemp(i+3), - co, quietarg, commarg.string, expandarg, - versionarg, RCSfilename, (char*)0 + arg[i] = maketemp(i+2), + co, quietarg, commarg.string, + expandarg, suffixarg, versionarg, zonearg, + RCSname, (char*)0 )) - faterror("co failed"); + 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); + } + } } - diagnose("Merging differences between %s and %s into %s%s\n", - rev[0], rev[1], workfilename, - tostdout?"; result to stdout":""); - arg[0] = rev[0] = workfilename; - status = merge(tostdout, rev, arg); + Izclose(&workptr); + } } - -end: - Izclose(&workptr); tempunlink(); exitmain(nerror ? DIFF_TROUBLE : status); } -#if lint +#if RCS_lint # define exiterr rmergeExit #endif - exiting void + void exiterr() { tempunlink(); diff --git a/gnu/usr.bin/rcs/rcstest b/gnu/usr.bin/rcs/rcstest index e0b6c82..47eab4f 100755 --- a/gnu/usr.bin/rcs/rcstest +++ b/gnu/usr.bin/rcs/rcstest @@ -1,4 +1,4 @@ -#!/bin/sh +#! /bin/sh # Test RCS's functions. # The RCS commands are searched for in the PATH as usual; @@ -15,10 +15,10 @@ # The current directory and ./RCS must be readable, writable, and searchable. -# $Id: rcstest,v 5.8 1991/11/20 17:58:10 eggert Exp $ +# $Id: rcstest,v 5.14 1995/06/16 06:19:24 eggert Exp $ -# Copyright 1990, 1991 by Paul Eggert +# Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert # Distributed under license by the Free Software Foundation, Inc. # # This file is part of RCS. @@ -34,13 +34,23 @@ # 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, 675 Mass Ave, Cambridge, MA 02139, USA. +# 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 @@ -55,26 +65,26 @@ case $1 in *) echo >&2 "$0: usage: $0 [-v]"; exit 2 esac -test -d RCS || { - echo >&2 "$0: RCS: not a directory; please \`mkdir RCS' first." - exit 1 -} +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';; +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 + echo "#warning: $DIFF -c does not work, so diagnostics may be cryptic" + diff=$DIFF esac rcs -i -L -ta.11 $q a.c && -<$RCSfile || { +test -r $RCSfile || { echo "#rcs -i -L failed; perhaps RCS is not properly installed." exit 1 } @@ -84,7 +94,7 @@ rm -f $RCSfile || exit 2 cp a.11 a.c && ci -ta.11 -mm $q a.c && -<$RCSfile && +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' @@ -99,6 +109,7 @@ 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; } @@ -108,21 +119,24 @@ 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 && -<a.c || { echo "#ci -u didn't create a working file"; exit 1; } +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; } @@ -166,42 +180,28 @@ case $LOGNAME in esac esac esac -date=`date -u 2>/dev/null` || -date=`TZ=GMT0 date 2>/dev/null` || -date=`TZ= date` || exit 2 -set $date -case $2 in -Jan) m=01;; Feb) m=02;; Mar) m=03;; Apr) m=04;; May) m=05;; Jun) m=06;; -Jul) m=07;; Aug) m=08;; Sep) m=09;; Oct) m=10;; Nov) m=11;; Dec) m=12;; -*) echo >&2 "$0: $2: unknown month name"; exit 2 -esac -case $3 in -?) d=0$3;; -*) d=$3 -esac -case $6 in -[0-9][0-9][0-9][0-9]*) D=$6/$m/$d;; -*) - case $5 in - [0-9][0-9][0-9][0-9]*) D=$5/$m/$d;; - *) echo >&2 "$0: bad date format: $date"; exit 2 - 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 -T=$4 -case $PWD in -'') PWD=`pwd` -esac && +PWD=`pwd` && export PWD && +rm -f a.c && co -l $q a.c && sed 's/@/$/g' >a.kv <<EOF @Author: w @ -@Date: $D $T @ -@Header: $PWD$SLASH$RCSfile 2.1 $D $T w s @ -@Id: a.c 2.1 $D $T w s @ +@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 $D $T w + * @Log: a.c @ + * Revision 2.1 $date w * m * +@Name: Oz @ @RCSfile: a.c @ @Revision: 2.1 @ @Source: $PWD$SLASH$RCSfile @ @@ -210,25 +210,28 @@ 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 -e '/^\$/!d' -e 's/\$$/: old $/' a.k >a.o && +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" -ss -ww -u2.1 -mm $q 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 -p -ko $q a.c >a.oo && +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 || exit 2 -rcs -o2.1 $q a.c && +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 '/^[^$]/d' a.kv >a.i && +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 && @@ -259,40 +262,73 @@ 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.kv a.c || { echo "#rcs -N failed"; exit 1; } +$diff a.N a.c || { echo "#rcs -N failed"; exit 1; } +rm -f a.c && co -l $q a.c && -rcs -c':::' $q a.c && -echo '$''Log$' >a.c && +echo ':::$''Log$' >a.c && ci -u -mm $q a.c && -test " `sed '$!d' a.c`" = ' :::' || { echo "#rcs -c failed"; exit 1; } +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.kv a.c || { echo "#rcs -o failed"; exit 1; } +$diff a.e a.c || { echo "#rcs -o failed"; exit 1; } -rcsdiff -r1.1 -r2.1 $q a.c >a.0 +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.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 k kv kvl o v +for i in b k kv kvl o v do rm -f a.c && cp a.$i a.c && - rcsdiff -k$i $q a.c || { echo "#rcsdiff -k$i failed"; exit 1; } + 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 @@ -324,7 +360,7 @@ b1 c d1 EOF -rcsmerge -r2.2 -r2.3 $q a.c +rcsmerge -E -r2.2 -r2.3 $q a.c case $? in 0) if $diff a.0 a.c >/dev/null @@ -343,16 +379,36 @@ case $? in echo "#rcsmerge bad status"; exit 1 esac -nl=' -' +# 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 "$nl" '\200' >a.24 && + 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' "$nl" >a.c && + co -p $q a.c | $tr '\200' '\012' >a.c && rcsdiff -r2.3 $q a.c >/dev/null && echo 2.5 >a.c && @@ -378,15 +434,15 @@ locks: strict access list: symbolic names: N: 2.1 + Oz: 2.1 n: 1.8 -comment leader: ":::" keyword substitution: kv total revisions: 13; selected revisions: 1 description: 1.1 ---------------------------- revision 2.1 -date: $D $T; author: w; state: s; lines: +13 -1 +date: $date; author: w; state: s; lines: +14 -1 ============================================================================= EOF test $? = 0 || { echo "#rlog failed"; exit 1; } @@ -394,4 +450,5 @@ test $? = 0 || { echo "#rlog failed"; exit 1; } test ! -f $lockfile || { echo "#lock file not removed"; exit 1; } -exec rm -f a.* $RCSfile $RCS_alt +rm -f a.* $RCSfile $RCS_alt +$rmdir RCS diff --git a/gnu/usr.bin/rcs/rlog/Makefile b/gnu/usr.bin/rcs/rlog/Makefile index b6a1268..bdbf68f 100644 --- a/gnu/usr.bin/rcs/rlog/Makefile +++ b/gnu/usr.bin/rcs/rlog/Makefile @@ -1,7 +1,8 @@ -PROG= rlog - -SRCS= rlog.c -LDADD= -L${.CURDIR}/../lib/obj -lrcs +PROG= rlog +SRCS= rlog.c CFLAGS+= -I${.CURDIR}/../lib +LDADD= ${LIBRCS} +DPADD= ${LIBRCS} +.include "../../Makefile.inc" .include <bsd.prog.mk> diff --git a/gnu/usr.bin/rcs/rlog/rlog.1 b/gnu/usr.bin/rcs/rlog/rlog.1 index fa627ff..cc4b45c9 100644 --- a/gnu/usr.bin/rcs/rlog/rlog.1 +++ b/gnu/usr.bin/rcs/rlog/rlog.1 @@ -2,9 +2,10 @@ .ds Rv \\$3 .ds Dt \\$4 .. -.Id $Id: rlog.1,v 5.3 1991/08/22 06:50:48 eggert Exp $ -.ds g \&\s-1UTC\s0 +.Id $Id: rlog.1,v 5.9 1995/06/16 06:19:24 eggert Exp $ +.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 @@ -34,13 +35,17 @@ reverse chronological order for each branch. For each revision, 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 (\*g). +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 '+1n-1/1n -.TP \nn +.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 @@ -64,6 +69,9 @@ 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. @@ -80,23 +88,28 @@ selects the revisions that were deposited between .I d1 and .I d2 -inclusive. +exclusive. A range of the form .BI < d or .IB d > selects -all revisions dated -.I d -or earlier. +all revisions earlier than +.IR d . A range of the form .IB d < or .BI > d selects -all revisions dated -.I d -or later. +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 @@ -174,6 +187,13 @@ 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 @@ -203,6 +223,40 @@ 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 @@ -229,11 +283,11 @@ The exit status is zero if and only if all operations were successful. .SH IDENTIFICATION Author: Walter F. Tichy. .br -Revision Number: \*(Rv; Release Date: \*(Dt. +Manual Page Revision: \*(Rv; Release Date: \*(Dt. .br -Copyright \(co 1982, 1988, 1989 by Walter F. Tichy. +Copyright \(co 1982, 1988, 1989 Walter F. Tichy. .br -Copyright \(co 1990, 1991 by Paul Eggert. +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) diff --git a/gnu/usr.bin/rcs/rlog/rlog.c b/gnu/usr.bin/rcs/rlog/rlog.c index b18b0c9..7fb9b4d 100644 --- a/gnu/usr.bin/rcs/rlog/rlog.c +++ b/gnu/usr.bin/rcs/rlog/rlog.c @@ -1,13 +1,7 @@ -/* - * RLOG operation - */ -/***************************************************************************** - * print contents of RCS files - ***************************************************************************** - */ +/* Print log messages and other information about RCS files. */ -/* Copyright (C) 1982, 1988, 1989 Walter Tichy - Copyright 1990, 1991 by Paul Eggert +/* 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. @@ -23,8 +17,9 @@ 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, 675 Mass Ave, Cambridge, MA 02139, USA. +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: @@ -32,10 +27,42 @@ Report problems and direct all questions to: */ - - - -/* $Log: rlog.c,v $ +/* + * $Log: rlog.c,v $ + * Revision 5.18 1995/06/16 06:19:24 eggert + * Update FSF address. + * + * Revision 5.17 1995/06/01 16:23:43 eggert + * (struct rcslockers): Renamed from `struct lockers'. + * (getnumericrev): Return error indication instead of ignoring errors. + * (main): Check it. Don't use dateform. + * (recentdate, extdate): cmpnum -> cmpdate + * + * Revision 5.16 1994/04/13 16:30:34 eggert + * Fix bug; `rlog -lxxx' inverted the sense of -l. + * + * Revision 5.15 1994/03/17 14:05:48 eggert + * -d'<DATE' now excludes DATE; the new syntax -d'<=DATE' includes it. + * Emulate -V4's white space generation more precisely. + * Work around SVR4 stdio performance bug. Remove lint. + * + * Revision 5.14 1993/11/09 17:40:15 eggert + * -V now prints version on stdout and exits. + * + * Revision 5.13 1993/11/03 17:42:27 eggert + * Add -N, -z. Ignore -T. + * + * Revision 5.12 1992/07/28 16:12:44 eggert + * Don't miss B.0 when handling branch B. Diagnose missing `,' in -r. + * Add -V. Avoid `unsigned'. Statement macro names now end in _. + * + * Revision 5.11 1992/01/24 18:44:19 eggert + * Don't duplicate unexpected_EOF's function. lint -> RCS_lint + * + * Revision 5.10 1992/01/06 02:42:34 eggert + * Update usage string. + * while (E) ; -> while (E) continue; + * * Revision 5.9 1991/09/17 19:07:40 eggert * Getscript() didn't uncache partial lines. * @@ -131,9 +158,9 @@ Report problems and direct all questions to: #include "rcsbase.h" -struct lockers { /* lockers in locker option; stored */ +struct rcslockers { /* lockers in locker option; stored */ char const * login; /* lockerlist */ - struct lockers * lockerlink; + struct rcslockers * lockerlink; } ; struct stateattri { /* states in state option; stored in */ @@ -147,28 +174,29 @@ struct authors { /* login names in author option; */ } ; struct Revpairs{ /* revision or branch range in -r */ - unsigned numfld; /* option; stored in revlist */ + int numfld; /* option; stored in revlist */ char const * strtrev; char const * endrev; struct Revpairs * rnext; } ; struct Datepairs{ /* date range in -d option; stored in */ + struct Datepairs *dnext; char strtdate[datesize]; /* duelst and datelist */ char enddate[datesize]; - struct Datepairs * dnext; + char ne_date; /* datelist only; distinguishes < from <= */ }; static char extractdelta P((struct hshentry const*)); static int checkrevpair P((char const*,char const*)); +static int extdate P((struct hshentry*)); +static int getnumericrev P((void)); static struct hshentry const *readdeltalog P((void)); -static unsigned extdate P((struct hshentry*)); static void cleanup P((void)); static void exttree P((struct hshentry*)); static void getauthor P((char*)); static void getdatepair P((char*)); static void getlocker P((char*)); -static void getnumericrev P((void)); static void getrevpairs P((char*)); static void getscript P((struct hshentry*)); static void getstate P((char*)); @@ -187,30 +215,32 @@ static int lockflag; static struct Datepairs *datelist, *duelst; static struct Revpairs *revlist, *Revlst; static struct authors *authorlist; -static struct lockers *lockerlist; +static struct rcslockers *lockerlist; static struct stateattri *statelist; -mainProg(rlogId, "rlog", "$Id: rlog.c,v 5.9 1991/09/17 19:07:40 eggert Exp $") +mainProg(rlogId, "rlog", "$Id: rlog.c,v 5.18 1995/06/16 06:19:24 eggert Exp $") { static char const cmdusage[] = - "\nrlog usage: rlog -{bhLRt} -ddates -l[lockers] -rrevs -sstates -w[logins] -Vn file ..."; + "\nrlog usage: rlog -{bhLNRt} -ddates -l[lockers] -r[revs] -sstates -Vn -w[logins] -xsuff -zzone file ..."; register FILE *out; char *a, **newargv; struct Datepairs *currdate; - char const *accessListString, *accessFormat, *commentFormat; + char const *accessListString, *accessFormat; char const *headFormat, *symbolFormat; struct access const *curaccess; struct assoc const *curassoc; struct hshentry const *delta; - struct lock const *currlock; + struct rcslock const *currlock; int descflag, selectflag; int onlylockflag; /* print only files with locks */ - int onlyRCSflag; /* print only RCS file name */ - unsigned revno; + int onlyRCSflag; /* print only RCS pathname */ + int pre5; + int shownames; + int revno; - descflag = selectflag = true; + descflag = selectflag = shownames = true; onlylockflag = onlyRCSflag = false; out = stdout; suffixes = X_DEFAULT; @@ -224,6 +254,10 @@ mainProg(rlogId, "rlog", "$Id: rlog.c,v 5.9 1991/09/17 19:07:40 eggert Exp $") onlylockflag = true; break; + case 'N': + shownames = false; + break; + case 'R': onlyRCSflag =true; break; @@ -270,49 +304,63 @@ mainProg(rlogId, "rlog", "$Id: rlog.c,v 5.9 1991/09/17 19:07:40 eggert Exp $") suffixes = a; break; + case 'z': + zone_set(a); + break; + + case 'T': + /* Ignore -T, so that RCSINIT can contain -T. */ + if (*a) + goto unknown; + break; + case 'V': setRCSversion(*argv); break; default: - faterror("unknown option: %s%s", *argv, cmdusage); + unknown: + error("unknown option: %s%s", *argv, cmdusage); }; } /* end of option processing */ - if (argc<1) faterror("no input file%s", cmdusage); - if (! (descflag|selectflag)) { warn("-t overrides -h."); descflag = true; } - if (RCSversion < VERSION(5)) { + pre5 = RCSversion < VERSION(5); + if (pre5) { accessListString = "\naccess list: "; accessFormat = " %s"; - commentFormat = "\ncomment leader: \""; - headFormat = "\nRCS file: %s; Working file: %s\nhead: %s%s\nbranch: %s%s\nlocks: "; - insDelFormat = " lines added/del: %lu/%lu"; + headFormat = "RCS file: %s; Working file: %s\nhead: %s%s\nbranch: %s%s\nlocks: "; + insDelFormat = " lines added/del: %ld/%ld"; symbolFormat = " %s: %s;"; } else { accessListString = "\naccess list:"; accessFormat = "\n\t%s"; - commentFormat = "\ncomment leader: \""; - headFormat = "\nRCS file: %s\nWorking file: %s\nhead:%s%s\nbranch:%s%s\nlocks:%s"; - insDelFormat = " lines: +%lu -%lu"; + headFormat = "RCS file: %s\nWorking file: %s\nhead:%s%s\nbranch:%s%s\nlocks:%s"; + insDelFormat = " lines: +%ld -%ld"; symbolFormat = "\n\t%s: %s"; } - /* now handle all filenames */ - do { + /* 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 (pairfilenames(argc, argv, rcsreadopen, true, false) <= 0) + if (pairnames(argc, argv, rcsreadopen, true, false) <= 0) continue; - /* now RCSfilename contains the name of the RCS file, and finptr - * the file descriptor. Workfilename contains the name of the - * working file. + /* + * RCSname contains the name of the RCS file, + * and finptr the file descriptor; + * workname contains the name of the working file. */ /* Keep only those locks given by -l. */ @@ -324,13 +372,25 @@ mainProg(rlogId, "rlog", "$Id: rlog.c,v 5.9 1991/09/17 19:07:40 eggert Exp $") continue; if ( onlyRCSflag ) { - aprintf(out, "%s\n", RCSfilename); + aprintf(out, "%s\n", RCSname); continue; } - /* print RCS filename , working filename and optional + + gettree(); + + if (!getnumericrev()) + continue; + + /* + * Output the first character with putc, not printf. + * Otherwise, an SVR4 stdio bug buffers output inefficiently. + */ + aputc_('\n', out) + + /* print RCS pathname, working pathname and optional administrative information */ /* could use getfullRCSname() here, but that is very slow */ - aprintf(out, headFormat, RCSfilename, workfilename, + aprintf(out, headFormat, RCSname, workname, Head ? " " : "", Head ? Head->num : "", Dbranch ? " " : "", Dbranch ? Dbranch : "", StrictLocks ? " strict" : "" @@ -341,8 +401,8 @@ mainProg(rlogId, "rlog", "$Id: rlog.c,v 5.9 1991/09/17 19:07:40 eggert Exp $") currlock->delta->num); currlock = currlock->nextlock; } - if (StrictLocks && RCSversion<VERSION(5)) - aputs(" strict", out); + if (StrictLocks && pre5) + aputs(" ; strict" + (Locks?3:0), out); aputs(accessListString, out); /* print access list */ curaccess = AccessList; @@ -351,40 +411,40 @@ mainProg(rlogId, "rlog", "$Id: rlog.c,v 5.9 1991/09/17 19:07:40 eggert Exp $") curaccess = curaccess->nextaccess; } - aputs("\nsymbolic names:", out); /* print symbolic names */ - for (curassoc=Symbols; curassoc; curassoc=curassoc->nextassoc) - aprintf(out, symbolFormat, curassoc->symbol, curassoc->num); - aputs(commentFormat, out); - awrite(Comment.string, Comment.size, out); - aputs("\"\n", out); - if (VERSION(5)<=RCSversion || Expand != KEYVAL_EXPAND) - aprintf(out, "keyword substitution: %s\n", + if (shownames) { + aputs("\nsymbolic names:", out); /* print symbolic names */ + for (curassoc=Symbols; curassoc; curassoc=curassoc->nextassoc) + aprintf(out, symbolFormat, curassoc->symbol, curassoc->num); + } + if (pre5) { + aputs("\ncomment leader: \"", out); + awrite(Comment.string, Comment.size, out); + afputc('\"', out); + } + if (!pre5 || Expand != KEYVAL_EXPAND) + aprintf(out, "\nkeyword substitution: %s", expand_names[Expand] ); - gettree(); - - aprintf(out, "total revisions: %u", TotalDeltas); + aprintf(out, "\ntotal revisions: %d", TotalDeltas); revno = 0; if (Head && selectflag & descflag) { - getnumericrev(); /* get numeric revision or branch names */ - exttree(Head); /* get most recently date of the dates pointed by duelst */ currdate = duelst; while( currdate) { - VOID sprintf(currdate->strtdate,DATEFORM,0,0,0,0,0,0); + VOID strcpy(currdate->strtdate, "0.0.0.0.0.0"); recentdate(Head, currdate); currdate = currdate->dnext; } revno = extdate(Head); - aprintf(out, ";\tselected revisions: %u", revno); + aprintf(out, ";\tselected revisions: %d", revno); } afputc('\n',out); @@ -394,17 +454,16 @@ mainProg(rlogId, "rlog", "$Id: rlog.c,v 5.9 1991/09/17 19:07:40 eggert Exp $") } if (revno) { while (! (delta = readdeltalog())->selector || --revno) - ; + continue; if (delta->next && countnumflds(delta->num)==2) /* Read through delta->next to get its insertlns. */ while (readdeltalog() != delta->next) - ; + continue; putrunk(); putree(Head); } aputs("=============================================================================\n",out); - } while (cleanup(), - ++argv, --argc >= 1); + } Ofclose(out); exitmain(exitstatus); } @@ -416,10 +475,10 @@ cleanup() Izclose(&finptr); } -#if lint +#if RCS_lint # define exiterr rlogExit #endif - exiting void + void exiterr() { _exit(EXIT_FAILURE); @@ -447,7 +506,7 @@ putree(root) order on each branch */ { - if ( root == nil ) return; + if (!root) return; putree(root->next); @@ -462,8 +521,7 @@ putforest(branchroot) struct branchhead const *branchroot; /* function: print branches that has the same direct ancestor */ { - - if ( branchroot == nil ) return; + if (!branchroot) return; putforest(branchroot->nextbranch); @@ -480,8 +538,7 @@ putabranch(root) /* function : print one branch */ { - - if ( root == nil) return; + if (!root) return; putabranch(root->next); @@ -507,17 +564,19 @@ putadelta(node,editscript,trunk) size_t n; struct branchhead const *newbranch; struct buf branchnum; - char datebuf[datesize]; + char datebuf[datesize + zonelenmax]; + int pre5 = RCSversion < VERSION(5); if (!node->selector) return; out = stdout; aprintf(out, - "----------------------------\nrevision %s", node->num + "----------------------------\nrevision %s%s", + node->num, pre5 ? " " : "" ); if ( node->lockedby ) - aprintf(out, "\tlocked by: %s;", node->lockedby); + aprintf(out, pre5+"\tlocked by: %s;", node->lockedby); aprintf(out, "\ndate: %s; author: %s; state: %s;", date2str(node->date, datebuf), @@ -556,9 +615,6 @@ putadelta(node,editscript,trunk) } - - - static struct hshentry const * readdeltalog() /* Function : get the log message and skip the text of a deltatext node. @@ -583,9 +639,7 @@ readdeltalog() cb = savestring(&logbuf); Delta->log = bufremember(&logbuf, cb.size); - nextlex(); - while (nexttok==ID && strcmp(NextString,Ktext)!=0) - ignorephrase(); + ignorephrases(Ktext); getkeystring(Ktext); Delta->insertlns = Delta->deletelns = 0; if ( Delta != Head) @@ -607,7 +661,7 @@ struct hshentry * Delta; declarecache; register RILE *fin; register int c; - register unsigned long i; + register long i; struct diffcmd dc; fin = finptr; @@ -623,16 +677,16 @@ struct hshentry * Delta; cache(fin); do { for (;;) { - cacheget(c); + cacheget_(c) switch (c) { default: continue; case SDELIM: - cacheget(c); + cacheget_(c) if (c == SDELIM) continue; if (--i) - fatserror("unexpected end to edit script"); + unexpected_EOF(); nextc = c; uncache(fin); return; @@ -661,10 +715,10 @@ struct hshentry *root; { struct branchhead const *newbranch; - if (root == nil) return; + if (!root) return; root->selector = extractdelta(root); - root->log.string = nil; + root->log.string = 0; exttree(root->next); newbranch = root->branches; @@ -685,26 +739,26 @@ char * argv; { register char c; - struct lockers * newlocker; + struct rcslockers *newlocker; argv--; - while( ( c = (*++argv)) == ',' || c == ' ' || c == '\t' || - c == '\n' || c == ';') ; + while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';') + continue; if ( c == '\0') { - lockerlist=nil; + lockerlist = 0; return; } while( c != '\0' ) { - newlocker = talloc(struct lockers); + newlocker = talloc(struct rcslockers); newlocker->lockerlink = lockerlist; newlocker->login = argv; lockerlist = newlocker; - while ( ( c = (*++argv)) != ',' && c != '\0' && c != ' ' - && c != '\t' && c != '\n' && c != ';') ; + while ((c = *++argv) && c!=',' && c!=' ' && c!='\t' && c!='\n' && c!=';') + continue; *argv = '\0'; if ( c == '\0' ) return; - while( ( c = (*++argv)) == ',' || c == ' ' || c == '\t' || - c == '\n' || c == ';') ; + while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';') + continue; } } @@ -721,12 +775,12 @@ char *argv; struct authors * newauthor; argv--; - while( ( c = (*++argv)) == ',' || c == ' ' || c == '\t' || - c == '\n' || c == ';') ; + while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';') + continue; if ( c == '\0' ) { authorlist = talloc(struct authors); authorlist->login = getusername(false); - authorlist->nextauthor = nil; + authorlist->nextauthor = 0; return; } @@ -735,12 +789,12 @@ char *argv; newauthor->nextauthor = authorlist; newauthor->login = argv; authorlist = newauthor; - while( ( c = *++argv) != ',' && c != '\0' && c != ' ' - && c != '\t' && c != '\n' && c != ';') ; + while ((c = *++argv) && c!=',' && c!=' ' && c!='\t' && c!='\n' && c!=';') + continue; * argv = '\0'; if ( c == '\0') return; - while( ( c = (*++argv)) == ',' || c == ' ' || c == '\t' || - c == '\n' || c == ';') ; + while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';') + continue; } } @@ -758,10 +812,10 @@ char * argv; struct stateattri *newstate; argv--; - while( ( c = (*++argv)) == ',' || c == ' ' || c == '\t' || - c == '\n' || c == ';') ; + while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';') + continue; if ( c == '\0'){ - warn("missing state attributes after -s options"); + error("missing state attributes after -s options"); return; } @@ -770,12 +824,12 @@ char * argv; newstate->nextstate = statelist; newstate->status = argv; statelist = newstate; - while( (c = (*++argv)) != ',' && c != '\0' && c != ' ' - && c != '\t' && c != '\n' && c != ';') ; + while ((c = *++argv) && c!=',' && c!=' ' && c!='\t' && c!='\n' && c!=';') + continue; *argv = '\0'; if ( c == '\0' ) return; - while( ( c = (*++argv)) == ',' || c == ' ' || c == '\t' || - c == '\n' || c == ';') ; + while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';') + continue; } } @@ -787,25 +841,21 @@ trunclocks() /* id's on lockerlist. Do not truncate if lockerlist empty. */ { - struct lockers const *plocker; - struct lock * plocked, * nextlocked; + struct rcslockers const *plocker; + struct rcslock *p, **pp; - if ( (lockerlist == nil) || (Locks == nil)) return; + if (!lockerlist) return; /* shorten Locks to those contained in lockerlist */ - plocked = Locks; - Locks = nil; - while( plocked != nil) { - plocker = lockerlist; - while((plocker != nil) && ( strcmp(plocker->login, plocked->login)!=0)) - plocker = plocker->lockerlink; - nextlocked = plocked->nextlock; - if ( plocker != nil) { - plocked->nextlock = Locks; - Locks = plocked; - } - plocked = nextlocked; - } + for (pp = &Locks; (p = *pp); ) + for (plocker = lockerlist; ; ) + if (strcmp(plocker->login, p->login) == 0) { + pp = &p->nextlock; + break; + } else if (!(plocker = plocker->lockerlink)) { + *pp = p->nextlock; + break; + } } @@ -821,10 +871,10 @@ recentdate(root, pd) { struct branchhead const *newbranch; - if ( root == nil) return; + if (!root) return; if (root->selector) { - if ( cmpnum(root->date, pd->strtdate) >= 0 && - cmpnum(root->date, pd->enddate) <= 0) + if ( cmpdate(root->date, pd->strtdate) >= 0 && + cmpdate(root->date, pd->enddate) <= 0) VOID strcpy(pd->strtdate, root->date); } @@ -841,7 +891,7 @@ recentdate(root, pd) - static unsigned + static int extdate(root) struct hshentry * root; /* function: select revisions which are in the date range specified */ @@ -850,7 +900,7 @@ struct hshentry * root; { struct branchhead const *newbranch; struct Datepairs const *pdate; - unsigned revno; + int revno, ne; if (!root) return 0; @@ -858,20 +908,25 @@ struct hshentry * root; if ( datelist || duelst) { pdate = datelist; while( pdate ) { - if ( (pdate->strtdate)[0] == '\0' || cmpnum(root->date,pdate->strtdate) >= 0){ - if ((pdate->enddate)[0] == '\0' || cmpnum(pdate->enddate,root->date) >= 0) + ne = pdate->ne_date; + if ( + (!pdate->strtdate[0] + || ne <= cmpdate(root->date, pdate->strtdate)) + && + (!pdate->enddate[0] + || ne <= cmpdate(pdate->enddate, root->date)) + ) break; - } pdate = pdate->dnext; } - if ( pdate == nil) { + if (!pdate) { pdate = duelst; for (;;) { if (!pdate) { root->selector = false; break; } - if ( cmpnum(root->date, pdate->strtdate) == 0) + if (cmpdate(root->date, pdate->strtdate) == 0) break; pdate = pdate->dnext; } @@ -896,11 +951,11 @@ extractdelta(pdelta) /* statelist, revlist and yield true if pdelta is selected. */ { - struct lock const *plock; + struct rcslock const *plock; struct stateattri const *pstate; struct authors const *pauthor; struct Revpairs const *prevision; - unsigned length; + int length; if ((pauthor = authorlist)) /* only certain authors wanted */ while (strcmp(pauthor->login, pdelta->author) != 0) @@ -946,10 +1001,10 @@ getdatepair(argv) int switchflag; argv--; - while( ( c = (*++argv)) == ',' || c == ' ' || c == '\t' || - c == '\n' || c == ';') ; + while ((c = *++argv)==',' || c==' ' || c=='\t' || c=='\n' || c==';') + continue; if ( c == '\0' ) { - warn("missing date/time after -d"); + error("missing date/time after -d"); return; } @@ -958,9 +1013,13 @@ getdatepair(argv) nextdate = talloc(struct Datepairs); if ( c == '<' ) { /* case: -d <date */ c = *++argv; + if (!(nextdate->ne_date = c!='=')) + c = *++argv; (nextdate->strtdate)[0] = '\0'; } else if (c == '>') { /* case: -d'>date' */ c = *++argv; + if (!(nextdate->ne_date = c!='=')) + c = *++argv; (nextdate->enddate)[0] = '\0'; switchflag = true; } else { @@ -978,7 +1037,11 @@ getdatepair(argv) goto end; } else { /* case: -d date< or -d date>; see switchflag */ - while ( (c= *++argv) == ' ' || c=='\t' || c=='\n'); + int eq = argv[1]=='='; + nextdate->ne_date = !eq; + argv += eq; + while ((c = *++argv) == ' ' || c=='\t' || c=='\n') + continue; if ( c == ';' || c == '\0') { /* second date missing */ if (switchflag) @@ -1000,14 +1063,17 @@ getdatepair(argv) nextdate->dnext = datelist; datelist = nextdate; end: + if (RCSversion < VERSION(5)) + nextdate->ne_date = 0; if ( c == '\0') return; - while( (c = *++argv) == ';' || c == ' ' || c == '\t' || c =='\n'); + while ((c = *++argv) == ';' || c == ' ' || c == '\t' || c =='\n') + continue; } } - static void + static int getnumericrev() /* function: get the numeric name of revisions which stored in revlist */ /* and then stored the numeric names in Revlst */ @@ -1015,12 +1081,12 @@ getnumericrev() { struct Revpairs * ptr, *pt; - unsigned n; + int n; struct buf s, e; char const *lrev; struct buf const *rstart, *rend; - Revlst = nil; + Revlst = 0; ptr = revlist; bufautobegin(&s); bufautobegin(&e); @@ -1031,48 +1097,48 @@ getnumericrev() switch (ptr->numfld) { - case 1: /* -r rev */ - if (expandsym(ptr->strtrev, &s)) { - rend = &s; - n = countnumflds(s.string); - if (!n && (lrev = tiprev())) { - bufscpy(&s, lrev); - n = countnumflds(lrev); - } - } + case 1: /* -rREV */ + if (!expandsym(ptr->strtrev, &s)) + goto freebufs; + rend = &s; + n = countnumflds(s.string); + if (!n && (lrev = tiprev())) { + bufscpy(&s, lrev); + n = countnumflds(lrev); + } break; - case 2: /* -r rev- */ - if (expandsym(ptr->strtrev, &s)) { - bufscpy(&e, s.string); - n = countnumflds(s.string); - (n<2 ? e.string : strrchr(e.string,'.'))[0] = 0; - } + case 2: /* -rREV: */ + if (!expandsym(ptr->strtrev, &s)) + goto freebufs; + bufscpy(&e, s.string); + n = countnumflds(s.string); + (n<2 ? e.string : strrchr(e.string,'.'))[0] = 0; break; - case 3: /* -r -rev */ - if (expandsym(ptr->endrev, &e)) { - if ((n = countnumflds(e.string)) < 2) - bufscpy(&s, ".1"); - else { - bufscpy(&s, e.string); - VOID strcpy(strrchr(s.string,'.'), ".1"); - } - } + case 3: /* -r:REV */ + if (!expandsym(ptr->endrev, &e)) + goto freebufs; + if ((n = countnumflds(e.string)) < 2) + bufscpy(&s, ".0"); + else { + bufscpy(&s, e.string); + VOID strcpy(strrchr(s.string,'.'), ".0"); + } break; - default: /* -r rev1-rev2 */ - if ( + default: /* -rREV1:REV2 */ + if (!( expandsym(ptr->strtrev, &s) && expandsym(ptr->endrev, &e) && checkrevpair(s.string, e.string) - ) { - n = countnumflds(s.string); - /* Swap if out of order. */ - if (compartial(s.string,e.string,n) > 0) { - rstart = &e; - rend = &s; - } + )) + goto freebufs; + n = countnumflds(s.string); + /* Swap if out of order. */ + if (compartial(s.string,e.string,n) > 0) { + rstart = &e; + rend = &s; } break; } @@ -1095,8 +1161,11 @@ getnumericrev() pt->rnext=Revlst; Revlst=pt; pt->numfld = countnumflds(pt->strtrev); } + + freebufs: bufautoend(&s); bufautoend(&e); + return !ptr; } @@ -1109,13 +1178,13 @@ checkrevpair(num1,num2) fields( if length <= 2, may be different if first field) */ { - unsigned length = countnumflds(num1); + int length = countnumflds(num1); if ( countnumflds(num2) != length - || 2 < length && compartial(num1, num2, length-1) != 0 + || (2 < length && compartial(num1, num2, length-1) != 0) ) { - error("invalid branch or revision pair %s : %s", num1, num2); + rcserror("invalid branch or revision pair %s : %s", num1, num2); return false; } @@ -1172,7 +1241,8 @@ register char * argv; while (c==' ' || c=='\t' || c=='\n') c = *++argv; if (c == separator) { - while( (c =(*++argv)) == ' ' || c == '\t' || c =='\n') ; + while ((c = *++argv) == ' ' || c == '\t' || c =='\n') + continue; nextrevpair->endrev = argv; for (;; c = *++argv) { switch (c) { @@ -1183,8 +1253,8 @@ register char * argv; break; case ':': case '-': if (c == separator) - continue; - break; + break; + continue; } break; } @@ -1192,13 +1262,15 @@ register char * argv; while (c==' ' || c=='\t' || c =='\n') c = *++argv; nextrevpair->numfld = - !nextrevpair->endrev[0] ? 2 /* -rrev- */ : - !nextrevpair->strtrev[0] ? 3 /* -r-rev */ : - 4 /* -rrev1-rev2 */; + !nextrevpair->endrev[0] ? 2 /* -rREV: */ : + !nextrevpair->strtrev[0] ? 3 /* -r:REV */ : + 4 /* -rREV1:REV2 */; } if (!c) break; - if (c!=',' && c!=';') + else if (c==',' || c==';') + c = *++argv; + else error("missing `,' near `%c%s'", c, argv+1); } } |